diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 622c2fbc97..2ab507d35b 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -591,6 +591,7 @@ bar.poweramount = Power: {0} bar.poweroutput = Power Output: {0} bar.items = Items: {0} bar.capacity = Capacity: {0} +bar.units = Units: {0}/{1} bar.liquid = Liquid bar.heat = Heat bar.power = Power diff --git a/core/src/mindustry/ai/BlockIndexer.java b/core/src/mindustry/ai/BlockIndexer.java index 7ad4e36c6f..fd312cc138 100644 --- a/core/src/mindustry/ai/BlockIndexer.java +++ b/core/src/mindustry/ai/BlockIndexer.java @@ -39,8 +39,10 @@ public class BlockIndexer{ private ObjectSet allOres = new ObjectSet<>(); /** Stores teams that are present here as tiles. */ private Array activeTeams = new Array<>(); - /** Maps teams to a map of flagged tiles by type. */ + /** Maps teams to a map of flagged tiles by flag. */ private TileArray[][] flagMap = new TileArray[Team.all().length][BlockFlag.all.length]; + /** Max units by team. */ + private int[] unitCaps = new int[Team.all().length]; /** Maps tile positions to their last known tile index data. */ private IntMap typeMap = new IntMap<>(); /** Empty set used for returning. */ @@ -55,6 +57,10 @@ public class BlockIndexer{ for(BlockFlag flag : index.flags){ getFlagged(index.team)[flag.ordinal()].remove(event.tile); } + + if(index.flags.contains(BlockFlag.unitModifier)){ + updateCap(index.team); + } } process(event.tile); updateQuadrant(event.tile); @@ -65,6 +71,7 @@ public class BlockIndexer{ scanOres.addAll(Item.getAllOres()); damagedTiles = new TileArray[Team.all().length]; flagMap = new TileArray[Team.all().length][BlockFlag.all.length]; + unitCaps = new int[Team.all().length]; for(int i = 0; i < flagMap.length; i++){ for(int j = 0; j < BlockFlag.all.length; j++){ @@ -296,6 +303,19 @@ public class BlockIndexer{ return null; } + /** @return extra unit cap of a team. This is added onto the base value. */ + public int getExtraUnits(Team team){ + return unitCaps[team.id]; + } + + private void updateCap(Team team){ + TileArray capped = getFlagged(team)[BlockFlag.unitModifier.ordinal()]; + unitCaps[team.id] = 0; + for(Tile capper : capped){ + unitCaps[team.id] += capper.block().unitCapModifier; + } + } + private void process(Tile tile){ if(tile.block().flags.size() > 0 && tile.team() != Team.derelict){ TileArray[] map = getFlagged(tile.team()); @@ -308,6 +328,11 @@ public class BlockIndexer{ map[flag.ordinal()] = arr; } + + if(tile.block().flags.contains(BlockFlag.unitModifier)){ + updateCap(tile.team()); + } + typeMap.put(tile.pos(), new TileIndex(tile.block().flags, tile.team())); } if(!activeTeams.contains(tile.team())){ diff --git a/core/src/mindustry/async/TeamIndexProcess.java b/core/src/mindustry/async/TeamIndexProcess.java index 35dfd55907..2be6415659 100644 --- a/core/src/mindustry/async/TeamIndexProcess.java +++ b/core/src/mindustry/async/TeamIndexProcess.java @@ -11,6 +11,7 @@ import mindustry.gen.*; public class TeamIndexProcess implements AsyncProcess{ private QuadTree[] trees = new QuadTree[Team.all().length]; private Array active = new Array<>(); + private int[] counts = new int[Team.all().length]; public QuadTree tree(Team team){ if(trees[team.uid] == null) trees[team.uid] = new QuadTree<>(Vars.world.getQuadBounds(new Rect())); @@ -18,9 +19,18 @@ public class TeamIndexProcess implements AsyncProcess{ return trees[team.uid]; } + public int count(Team team){ + return counts[team.id]; + } + + public void updateCount(Team team, int amount){ + counts[team.id] += amount; + } + @Override public void reset(){ active.clear(); + counts = new int[Team.all().length]; trees = new QuadTree[Team.all().length]; } @@ -38,8 +48,13 @@ public class TeamIndexProcess implements AsyncProcess{ } } + for(Team team : active){ + counts[team.id] = 0; + } + for(Unitc unit : Groups.unit){ tree(unit.team()).insert(unit); + counts[unit.team().id] ++; } } diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index 986b345cf1..7783dcfa2f 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -293,7 +293,10 @@ public class NetServer implements ApplicationListener{ }); clientCommands.register("t", "", "Send a message only to your teammates.", (args, player) -> { - Groups.player.each(p -> p.team() == player.team(), o -> o.sendMessage(args[0], player, "[#" + player.team().color.toString() + "]" + NetClient.colorizeName(player.id(), player.name()))); + String message = admins.filterMessage(player, args[0]); + if(message != null){ + Groups.player.each(p -> p.team() == player.team(), o -> o.sendMessage(message, player, "[#" + player.team().color.toString() + "]" + NetClient.colorizeName(player.id(), player.name()))); + } }); //duration of a a kick in seconds diff --git a/core/src/mindustry/entities/Units.java b/core/src/mindustry/entities/Units.java index fc6d297390..85221e1741 100644 --- a/core/src/mindustry/entities/Units.java +++ b/core/src/mindustry/entities/Units.java @@ -15,6 +15,15 @@ public class Units{ private static float cdist; private static boolean boolResult; + /** @return whether a new instance of a unit of this team can be created. */ + public static boolean canCreate(Team team){ + return teamIndex.count(team) < getCap(team); + } + + public static int getCap(Team team){ + return state.rules.unitCap + indexer.getExtraUnits(team); + } + /** @return whether this player can interact with a specific tile. if either of these are null, returns true.*/ public static boolean canInteract(Playerc player, Tilec tile){ return player == null || tile == null || tile.interactable(player.team()); diff --git a/core/src/mindustry/entities/def/UnitComp.java b/core/src/mindustry/entities/def/UnitComp.java index 6378aca4cf..9979360736 100644 --- a/core/src/mindustry/entities/def/UnitComp.java +++ b/core/src/mindustry/entities/def/UnitComp.java @@ -115,6 +115,16 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I type(this.type); } + @Override + public void add(){ + teamIndex.updateCount(team(), 1); + } + + @Override + public void remove(){ + teamIndex.updateCount(team(), -1); + } + @Override public void update(){ drag(type.drag * (isGrounded() ? (floorOn().dragMultiplier) : 1f)); diff --git a/core/src/mindustry/game/Rules.java b/core/src/mindustry/game/Rules.java index d00759cbd8..ff45c93766 100644 --- a/core/src/mindustry/game/Rules.java +++ b/core/src/mindustry/game/Rules.java @@ -58,6 +58,8 @@ public class Rules{ public float bossWaveMultiplier = 3f; /** How many times longer a launch wave takes. */ public float launchWaveMultiplier = 2f; + /** Base unit cap. Can still be increased by blocks. */ + public int unitCap = 10; /** Sector for saves that have them.*/ public @Nullable Sector sector; /** Region that save is on. Indicates campaign. TODO not implemented. */ diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index 64432e779f..4ad376ed3c 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -106,6 +106,9 @@ public class Block extends UnlockableContent{ public EnumSet flags = EnumSet.of(); /** Targeting priority of this block, as seen by enemies.*/ public TargetPriority priority = TargetPriority.base; + /** How much this block affects the unit cap by. + * The block flags must contain unitModifier in order for this to work. */ + public int unitCapModifier = 0; /** Whether the block can be tapped and selected to configure. */ public boolean configurable; /** Whether this block consumes touchDown events when tapped. */ diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index bb338057cd..859e6c1682 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -8,6 +8,7 @@ import arc.math.geom.*; import arc.struct.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.entities.*; import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.graphics.*; @@ -28,7 +29,8 @@ public class CoreBlock extends StorageBlock{ solid = true; update = true; hasItems = true; - flags = EnumSet.of(BlockFlag.core, BlockFlag.producer); + flags = EnumSet.of(BlockFlag.core, BlockFlag.producer, BlockFlag.unitModifier); + unitCapModifier = 30; activeSound = Sounds.respawning; activeSoundVolume = 1f; } @@ -59,6 +61,13 @@ public class CoreBlock extends StorageBlock{ () -> Pal.items, () -> e.items().total() / (float)(((CoreEntity)e).storageCapacity * content.items().count(i -> i.type == ItemType.material)) )); + + bars.add("units", e -> + new Bar( + () -> Core.bundle.format("bar.units", teamIndex.count(e.team()), Units.getCap(e.team())), + () -> Pal.power, + () -> (float)teamIndex.count(e.team()) / Units.getCap(e.team()) + )); } @Override diff --git a/core/src/mindustry/world/blocks/units/CommandCenter.java b/core/src/mindustry/world/blocks/units/CommandCenter.java deleted file mode 100644 index fa1c24ec86..0000000000 --- a/core/src/mindustry/world/blocks/units/CommandCenter.java +++ /dev/null @@ -1,134 +0,0 @@ -package mindustry.world.blocks.units; - -import arc.*; -import arc.graphics.*; -import arc.graphics.g2d.*; -import arc.scene.style.*; -import arc.scene.ui.*; -import arc.scene.ui.layout.*; -import arc.struct.*; -import arc.util.*; -import arc.util.io.*; -import mindustry.ai.BlockIndexer.*; -import mindustry.content.*; -import mindustry.entities.*; -import mindustry.entities.units.*; -import mindustry.game.EventType.*; -import mindustry.gen.*; -import mindustry.graphics.*; -import mindustry.ui.*; -import mindustry.world.*; -import mindustry.world.meta.*; - -import static mindustry.Vars.*; - -public class CommandCenter extends Block{ - protected TextureRegionDrawable[] commandRegions = new TextureRegionDrawable[UnitCommand.all.length]; - protected Color topColor = Pal.command; - protected Color bottomColor = Color.valueOf("5e5e5e"); - protected Effect effect = Fx.commandSend; - - public CommandCenter(String name){ - super(name); - - flags = EnumSet.of(BlockFlag.comandCenter); - destructible = true; - solid = true; - configurable = true; - config(Integer.class, (tile, value) -> { - UnitCommand command = UnitCommand.all[value]; - ((CommandCenter)tile.block()).effect.at(tile); - - for(Tile center : indexer.getAllied(tile.team(), BlockFlag.comandCenter)){ - if(center.block() instanceof CommandCenter){ - CommandCenterEntity entity = center.ent(); - entity.command = command; - } - } - - Groups.unit.each(t -> t.team() == tile.team(), u -> u.controller().command(command)); - Events.fire(new CommandIssueEvent(tile, command)); - }); - } - - @Override - public void load(){ - super.load(); - - if(ui != null){ - for(UnitCommand cmd : UnitCommand.all){ - commandRegions[cmd.ordinal()] = ui.getIcon("command" + Strings.capitalize(cmd.name())); - } - } - } - - public class CommandCenterEntity extends TileEntity{ - public UnitCommand command = UnitCommand.attack; - - @Override - public void draw(){ - super.draw(); - - float size = 6f; - - Draw.color(bottomColor); - Draw.rect(commandRegions[command.ordinal()].getRegion(), x, y - 1, size, size); - Draw.color(topColor); - Draw.rect(commandRegions[command.ordinal()].getRegion(), x, y, size, size); - Draw.color(); - } - - @Override - public void buildConfiguration(Table table){ - ButtonGroup group = new ButtonGroup<>(); - Table buttons = new Table(); - - for(UnitCommand cmd : UnitCommand.all){ - buttons.button(commandRegions[cmd.ordinal()], Styles.clearToggleTransi, () -> configure(cmd.ordinal())) - .size(44).group(group).update(b -> b.setChecked(command == cmd)); - } - table.add(buttons); - table.row(); - table.label(() -> command.localized()).style(Styles.outlineLabel).center().growX().get().setAlignment(Align.center); - } - - @Override - public void placed(){ - super.placed(); - TileArray set = indexer.getAllied(team, BlockFlag.comandCenter); - - if(set.size() > 0){ - CommandCenterEntity oe = set.first().ent(); - command = oe.command; - } - } - - @Override - public void onRemoved(){ - super.onRemoved(); - - TileArray set = indexer.getAllied(team, BlockFlag.comandCenter); - - if(set.size() == 1){ - Groups.unit.each(t -> t.team() == team, u -> u.controller().command(UnitCommand.all[0])); - } - } - - @Override - public Integer config(){ - return command.ordinal(); - } - - @Override - public void write(Writes write){ - super.write(write); - write.b(command.ordinal()); - } - - @Override - public void read(Reads read, byte revision){ - super.read(read, revision); - command = UnitCommand.all[read.b()]; - } - } -} diff --git a/core/src/mindustry/world/blocks/units/UnitFactory.java b/core/src/mindustry/world/blocks/units/UnitFactory.java index f659b6cf31..06ee604513 100644 --- a/core/src/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/mindustry/world/blocks/units/UnitFactory.java @@ -220,7 +220,7 @@ public class UnitFactory extends Block{ if(currentPlan != -1){ UnitPlan plan = plans[currentPlan]; - if(progress >= plan.time/* && !Units.anyEntities(tile, !plan.unit.flying)*/){ + if(progress >= plan.time/* && !Units.anyEntities(tile, !plan.unit.flying)*/ && Units.canCreate(team)){ progress = 0f; Call.onUnitFactorySpawn(tile); diff --git a/core/src/mindustry/world/meta/BlockFlag.java b/core/src/mindustry/world/meta/BlockFlag.java index 0fd2ca9c43..9a79b68d54 100644 --- a/core/src/mindustry/world/meta/BlockFlag.java +++ b/core/src/mindustry/world/meta/BlockFlag.java @@ -4,18 +4,14 @@ package mindustry.world.meta; public enum BlockFlag{ /** Enemy core; primary target for all units. */ core, - /** Rally point for units.*/ - rally, /** Producer of important goods. */ producer, /** A turret. */ turret, - /** Only the command center block.*/ - comandCenter, /** Repair point. */ repair, - /** Upgrade pad. */ - mechPad; + /** Any block that boosts unit capacity. */ + unitModifier; public final static BlockFlag[] all = values(); } diff --git a/gradle.properties b/gradle.properties index 460bf00e6b..42f8ec7f7b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=a9ac74c4165c71b4a2cddffe2d560717b9698d15 +archash=64304225a74ee3e2c2bd97fd1f23dc4042ba29e2