Nondestructive unit cap enforcement

This commit is contained in:
Anuken
2020-07-22 14:31:44 -04:00
parent b4594b2274
commit 701aa840b3
18 changed files with 86 additions and 15 deletions

View File

@@ -13,6 +13,7 @@ public class TeamIndexProcess implements AsyncProcess{
private QuadTree<Unit>[] trees = new QuadTree[Team.all.length];
private int[] counts = new int[Team.all.length];
private int[][] typeCounts = new int[Team.all.length][0];
private int[][] activeCounts = new int[Team.all.length][0];
public QuadTree<Unit> tree(Team team){
if(trees[team.id] == null) trees[team.id] = new QuadTree<>(Vars.world.getQuadBounds(new Rect()));
@@ -28,8 +29,11 @@ public class TeamIndexProcess implements AsyncProcess{
return typeCounts[team.id].length <= type.id ? 0 : typeCounts[team.id][type.id];
}
public void updateCount(Team team, UnitType type, int amount){
public int countActive(Team team, UnitType type){
return activeCounts[team.id].length <= type.id ? 0 : activeCounts[team.id][type.id];
}
public void updateCount(Team team, UnitType type, int amount){
counts[team.id] += amount;
if(typeCounts[team.id].length <= type.id){
typeCounts[team.id] = new int[Vars.content.units().size];
@@ -37,6 +41,13 @@ public class TeamIndexProcess implements AsyncProcess{
typeCounts[team.id][type.id] += amount;
}
public void updateActiveCount(Team team, UnitType type, int amount){
if(activeCounts[team.id].length <= type.id){
activeCounts[team.id] = new int[Vars.content.units().size];
}
activeCounts[team.id][type.id] += amount;
}
@Override
public void reset(){
counts = new int[Team.all.length];
@@ -52,6 +63,7 @@ public class TeamIndexProcess implements AsyncProcess{
}
Arrays.fill(typeCounts[team.id], 0);
Arrays.fill(activeCounts[team.id], 0);
}
Arrays.fill(counts, 0);
@@ -60,6 +72,7 @@ public class TeamIndexProcess implements AsyncProcess{
tree(unit.team).insert(unit);
updateCount(unit.team, unit.type(), 1);
if(!unit.deactivated) updateActiveCount(unit.team, unit.type(), 1);
}
}

View File

@@ -35,6 +35,10 @@ public class Units{
}
public static int getCap(Team team){
//wave team has no cap
if((team == state.rules.waveTeam && state.rules.waves) || (state.isCampaign() && team == state.rules.waveTeam)){
return Integer.MAX_VALUE;
}
return state.rules.unitCap + indexer.getExtraUnits(team);
}

View File

@@ -32,7 +32,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
private UnitController controller;
private UnitType type;
boolean spawnedByCore;
boolean spawnedByCore, deactivated;
transient float timer1, timer2;
@@ -118,6 +118,14 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
return controller instanceof AIController;
}
public int count(){
return teamIndex.countType(team, type);
}
public int cap(){
return Units.getCap(team);
}
private void setStats(UnitType type){
this.type = type;
this.maxHealth = type.health;
@@ -150,11 +158,10 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
teamIndex.updateCount(team, type, 1);
//check if over unit cap
if(teamIndex.countType(team, type) > Units.getCap(team)
&& !(team == state.rules.waveTeam && state.rules.waves) //can't be wave team on wave mode
&& !(state.isCampaign() && team == state.rules.waveTeam)){ //can't be campaign wave team
Fx.unitCapKill.at(this);
kill();
if(count() > cap() && !spawnedByCore){
deactivated = true;
}else{
teamIndex.updateActiveCount(team, type, 1);
}
}
@@ -175,8 +182,13 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
@Override
public void update(){
//activate the unit when possible
if(!net.client() && deactivated && teamIndex.countActive(team, type) < Units.getCap(team)){
teamIndex.updateActiveCount(team, type, 1);
deactivated = false;
}
type.update(base());
if(!deactivated) type.update(base());
drag = type.drag * (isGrounded() ? (floorOn().dragMultiplier) : 1f);
@@ -236,10 +248,15 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
}
//AI only updates on the server
if(!net.client() && !dead){
if(!net.client() && !dead && !deactivated){
controller.updateUnit();
}
//do not control anything when deactivated
if(deactivated){
controlWeapons(false, false);
}
//remove units spawned by the core
if(spawnedByCore && !isPlayer()){
Call.unitDespawn(base());

View File

@@ -233,7 +233,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}else if(unit == null){ //just clear the unit (is this used?)
player.clearUnit();
//make sure it's AI controlled, so players can't overwrite each other
}else if(unit.isAI() && unit.team == player.team()){
}else if(unit.isAI() && unit.team == player.team() && !unit.deactivated){
player.unit(unit);
Time.run(Fx.unitSpirit.lifetime, () -> Fx.unitControl.at(unit.x, unit.y, 0f, unit));
if(!player.dead()){
@@ -310,7 +310,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
if(controlledType != null && player.dead()){
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType);
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.deactivated);
if(unit != null){
Call.unitControl(player, unit);
@@ -320,7 +320,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public void checkUnit(){
if(controlledType != null){
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType);
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.deactivated);
if(unit == null && controlledType == UnitTypes.block){
unit = world.buildWorld(player.x, player.y) instanceof ControlBlock ? ((ControlBlock)world.buildWorld(player.x, player.y)).unit() : null;
}
@@ -843,7 +843,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
public @Nullable Unit selectedUnit(){
Unit unit = Units.closest(player.team(), Core.input.mouseWorld().x, Core.input.mouseWorld().y, 40f, Unit::isAI);
Unit unit = Units.closest(player.team(), Core.input.mouseWorld().x, Core.input.mouseWorld().y, 40f, u -> u.isAI() && !u.deactivated);
if(unit != null){
unit.hitbox(Tmp.r1);
Tmp.r1.grow(6f);

View File

@@ -112,7 +112,7 @@ public class UnitType extends UnlockableContent{
public void update(Unit unit){
if(abilities.size > 0){
for(mindustry.entities.abilities.Ability a : abilities){
for(Ability a : abilities){
a.update(unit);
}
}
@@ -135,10 +135,20 @@ public class UnitType extends UnlockableContent{
bars.row();
if(state.rules.unitAmmo){
bars.add(new Bar("blocks.ammo", Pal.ammo, () -> (float)unit.ammo / ammoCapacity));
bars.add(new Bar("blocks.ammo", Pal.ammo, () -> unit.ammo / ammoCapacity));
bars.row();
}
}).growX();
table.row();
if(unit.deactivated){
table.table(d -> {
d.left();
d.label(() -> Core.bundle.format("bar.limitreached", unit.count(), unit.cap(), Fonts.getUnicodeStr(name)));
}).left().visible(() -> unit.deactivated);
}
}
@Override
@@ -271,6 +281,10 @@ public class UnitType extends UnlockableContent{
unit.trns(-legOffset.x, -legOffset.y);
}
if(unit.deactivated){
drawDeactive(unit);
}
if(abilities.size > 0){
for(Ability a : abilities){
a.draw(unit);
@@ -279,6 +293,16 @@ public class UnitType extends UnlockableContent{
}
}
public void drawDeactive(Unit unit){
Draw.color(Color.scarlet);
Draw.alpha(0.8f);
float size = 8f;
Draw.rect(Icon.warning.getRegion(), unit.x, unit.y, size, size);
Draw.reset();
}
public <T extends Unit & Payloadc> void drawPayload(T unit){
if(unit.hasPayload()){
Payload pay = unit.payloads().first();