Nondestructive unit cap enforcement
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user