diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index 956b53cbf3ea..72911f0a5550 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -282,7 +282,7 @@ public void play(){ for(ItemStack stack : state.rules.loadout){ //make sure to cap storage - entity.items.add(stack.item, Math.min(stack.amount, entity.storageCapacity - entity.items.get(stack.item))); + entity.items.add(stack.item, Math.min(stack.amount, entity.capacity() - entity.items.get(stack.item))); } } } diff --git a/core/src/mindustry/core/World.java b/core/src/mindustry/core/World.java index 5eae5a4ea89c..afceb492b6b8 100644 --- a/core/src/mindustry/core/World.java +++ b/core/src/mindustry/core/World.java @@ -212,7 +212,7 @@ public void beginMapLoad(){ /** * Call to signify the end of map loading. Updates tile proximities and sets up physics for the world. - * A WorldLoadEvent will be fire. + * A WorldLoadEvent will be fired. */ public void endMapLoad(){ Events.fire(new WorldLoadEndEvent()); diff --git a/core/src/mindustry/editor/MapEditorDialog.java b/core/src/mindustry/editor/MapEditorDialog.java index 1dfc7ad0d4e8..8b218699f4b7 100644 --- a/core/src/mindustry/editor/MapEditorDialog.java +++ b/core/src/mindustry/editor/MapEditorDialog.java @@ -328,8 +328,15 @@ private void editInGame(){ ui.loadAnd(() -> { lastSavedRules = state.rules; hide(); + + Teams data = new Teams(); + //keep the old itemCap for each team, otherwise it breaks + state.teams.active.each(t -> + data.get(t.team).itemCap = t.itemCap + ); + state.teams = data; + //only reset the player; logic.reset() will clear entities, which we do not want - state.teams = new Teams(); player.reset(); state.rules = Gamemode.editor.apply(lastSavedRules.copy()); state.rules.limitMapArea = false; diff --git a/core/src/mindustry/game/SectorInfo.java b/core/src/mindustry/game/SectorInfo.java index c38421401caf..1036c06f43b6 100644 --- a/core/src/mindustry/game/SectorInfo.java +++ b/core/src/mindustry/game/SectorInfo.java @@ -191,7 +191,7 @@ public void write(){ entity.items.clear(); entity.items.add(items); //ensure capacity. - entity.items.each((i, a) -> entity.items.set(i, Mathf.clamp(a, 0, entity.storageCapacity))); + entity.items.each((i, a) -> entity.items.set(i, Mathf.clamp(a, 0, entity.capacity()))); } } @@ -221,7 +221,7 @@ public void prepare(Sector sector){ attack = state.rules.attackMode; hasCore = entity != null; bestCoreType = !hasCore ? Blocks.air : state.rules.defaultTeam.cores().max(e -> e.block.size).block; - storageCapacity = entity != null ? entity.storageCapacity : 0; + storageCapacity = entity != null ? entity.capacity() : 0; hasSpawns = spawner.countSpawns() > 0; lastPresetName = sector.preset == null ? null : sector.preset.name; lastWidth = world.width(); diff --git a/core/src/mindustry/game/Teams.java b/core/src/mindustry/game/Teams.java index daf63795fd80..eab8b0c95f72 100644 --- a/core/src/mindustry/game/Teams.java +++ b/core/src/mindustry/game/Teams.java @@ -15,6 +15,7 @@ import mindustry.world.*; import mindustry.world.blocks.payloads.*; import mindustry.world.blocks.storage.CoreBlock.*; +import mindustry.world.modules.*; import java.util.*; @@ -298,6 +299,8 @@ public static class TeamData{ public int unitCap; /** Total unit count. */ public int unitCount; + /** Current item cap. */ + public int itemCap; /** Counts for each type of unit. Do not access directly. */ public @Nullable int[] typeCounts; /** Cached buildings by type. */ @@ -308,6 +311,8 @@ public static class TeamData{ public Seq players = new Seq<>(false); /** All buildings. Updated on team change / building addition or removal. Includes even buildings that do not update(). */ public Seq buildings = new Seq<>(false); + /** The item module for cores of this team. Null if no core was loaded. */ + public @Nullable ItemModule items; /** Units of this team by type. Updated each frame. */ public @Nullable Seq[] unitsByType; diff --git a/core/src/mindustry/type/Sector.java b/core/src/mindustry/type/Sector.java index b76fceb31299..09b0d5186515 100644 --- a/core/src/mindustry/type/Sector.java +++ b/core/src/mindustry/type/Sector.java @@ -221,7 +221,7 @@ public void addItems(ItemSeq items){ if(isBeingPlayed()){ if(state.rules.defaultTeam.core() != null){ ItemModule storage = state.rules.defaultTeam.items(); - int cap = state.rules.defaultTeam.core().storageCapacity; + int cap = state.rules.defaultTeam.data().itemCap; items.each((item, amount) -> storage.add(item, Math.min(cap - storage.get(item), amount))); } }else if(hasBase()){ diff --git a/core/src/mindustry/ui/fragments/HintsFragment.java b/core/src/mindustry/ui/fragments/HintsFragment.java index 41f4e427cd4b..089c3a9da6d8 100644 --- a/core/src/mindustry/ui/fragments/HintsFragment.java +++ b/core/src/mindustry/ui/fragments/HintsFragment.java @@ -298,7 +298,7 @@ public enum DefaultHint implements Hint{ ), coreIncinerate( - () -> state.isCampaign() && state.rules.defaultTeam.core() != null && state.rules.defaultTeam.core().items.get(Items.copper) >= state.rules.defaultTeam.core().storageCapacity - 10, + () -> state.isCampaign() && state.rules.defaultTeam.core() != null && state.rules.defaultTeam.core().items.get(Items.copper) >= state.rules.defaultTeam.data().itemCap - 10, () -> false ) ; diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index 711d6c971c9d..9893448735b3 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -366,7 +366,7 @@ public void deconstruct(Unit builder, @Nullable CoreBuild core, float amount){ if(clampedAmount > 0 && accumulated > 0){ //if it's positive, add it to the core if(core != null && requirements[i].item.unlockedNowHost()){ //only accept items that are unlocked - int accepting = Math.min(accumulated, core.storageCapacity - core.items.get(requirements[i].item)); + int accepting = Math.min(accumulated, core.capacity() - core.items.get(requirements[i].item)); //transfer items directly, as this is not production. if(!state.rules.infiniteResources) core.items.add(requirements[i].item, accepting); itemsLeft[i] += accepting; @@ -387,7 +387,7 @@ public void deconstruct(Unit builder, @Nullable CoreBuild core, float amount){ int remaining = target - itemsLeft[i]; if(requirements[i].item.unlockedNowHost()){ - core.items.add(requirements[i].item, Mathf.clamp(remaining, 0, core.storageCapacity - core.items.get(requirements[i].item))); + core.items.add(requirements[i].item, Mathf.clamp(remaining, 0, core.capacity() - core.items.get(requirements[i].item))); } itemsLeft[i] = target; } diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index 5adb03cb6fbc..7577f37bc7bf 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -143,9 +143,9 @@ public void setBars(){ super.setBars(); addBar("capacity", (CoreBuild e) -> new Bar( - () -> Core.bundle.format("bar.capacity", UI.formatAmount(e.storageCapacity)), + () -> Core.bundle.format("bar.capacity", UI.formatAmount(e.team.data().itemCap)), () -> Pal.items, - () -> e.items.total() / ((float)e.storageCapacity * content.items().count(UnlockableContent::unlockedNow)) + () -> e.items.total() / ((float)e.team.data().itemCap * content.items().count(UnlockableContent::unlockedNow)) )); } @@ -252,7 +252,8 @@ public void drawPlace(int x, int y, int rotation, boolean valid){ } public class CoreBuild extends Building implements LaunchAnimator{ - public int storageCapacity; + private int storageCapacity; + public boolean noEffect = false; public Team lastDamage = Team.derelict; public float iframes = -1f; @@ -530,7 +531,6 @@ public void damage(@Nullable Team source, float damage){ @Override public void created(){ super.created(); - Events.fire(new CoreChangeEvent(this)); } @@ -542,6 +542,7 @@ public void changeTeam(Team next){ super.changeTeam(next); + items = next.data().items; onProximityUpdate(); Events.fire(new CoreChangeEvent(this)); @@ -549,7 +550,7 @@ public void changeTeam(Team next){ @Override public double sense(LAccess sensor){ - if(sensor == LAccess.itemCapacity) return storageCapacity; + if(sensor == LAccess.itemCapacity) return team.data().itemCap; if(sensor == LAccess.maxUnits) return Units.getCap(team); return super.sense(sensor); } @@ -684,6 +685,10 @@ public void afterDestroyed(){ } } + public int capacity(){ + return team.data().itemCap; + } + @Override public void drawLight(){ Drawf.light(x, y, lightRadius, Pal.accent, 0.65f + Mathf.absin(20f, 0.1f)); @@ -696,46 +701,41 @@ public boolean acceptItem(Building source, Item item){ @Override public int getMaximumAccepted(Item item){ - return state.rules.coreIncinerates ? Integer.MAX_VALUE/2 : storageCapacity; + return state.rules.coreIncinerates ? Integer.MAX_VALUE/2 : team.data().itemCap; } @Override public void onProximityUpdate(){ super.onProximityUpdate(); - for(Building other : state.teams.cores(team)){ - if(other.tile != tile){ - this.items = other.items; - } + if(team.data().items == null){ + // this is the first core of this team, assign its ItemModule as the default one + team.data().items = items; + }else{ + items = team.data().items; } + state.teams.registerCore(this); + int previousCapacity = storageCapacity; - storageCapacity = itemCapacity + proximity.sum(e -> owns(e) ? e.block.itemCapacity : 0); + storageCapacity = itemCapacity; proximity.each(this::owns, t -> { t.items = items; + storageCapacity += t.block.itemCapacity; ((StorageBuild)t).linkedCore = this; }); - for(Building other : state.teams.cores(team)){ - if(other.tile == tile) continue; - storageCapacity += other.block.itemCapacity + other.proximity.sum(e -> owns(other, e) ? e.block.itemCapacity : 0); - } + team.data().itemCap += storageCapacity - previousCapacity; if(!world.isGenerating()){ - for(Item item : content.items()){ - items.set(item, Math.min(items.get(item), storageCapacity)); - } - } - - for(CoreBuild other : state.teams.cores(team)){ - other.storageCapacity = storageCapacity; + clampItems(); } } @Override public void handleStack(Item item, int amount, Teamc source){ boolean incinerate = incinerateNonBuildable && !item.buildable; - int realAmount = incinerate ? 0 : Math.min(amount, storageCapacity - items.get(item)); + int realAmount = incinerate ? 0 : Math.min(amount, team.data().itemCap - items.get(item)); super.handleStack(item, realAmount, source); if(team == state.rules.defaultTeam && state.isCampaign()){ @@ -760,6 +760,12 @@ public int removeStack(Item item, int amount){ return result; } + public void clampItems(){ + for(Item item : content.items()){ + items.set(item, Math.min(items.get(item), team.data().itemCap)); + } + } + @Override public void drawSelect(){ //do not draw a pointless single outline when there's no storage @@ -801,20 +807,31 @@ public void damage(float amount){ public void onRemoved(){ int totalCapacity = proximity.sum(e -> e.items != null && e.items == items ? e.block.itemCapacity : 0); - proximity.each(e -> owns(e) && e.items == items && owns(e), t -> { + proximity.each(e -> owns(e) && e.items == items, t -> { StorageBuild ent = (StorageBuild)t; ent.linkedCore = null; ent.items = new ItemModule(); for(Item item : content.items()){ ent.items.set(item, (int)Math.min(ent.block.itemCapacity, items.get(item) * (float)ent.block.itemCapacity / totalCapacity)); } + + // check if there are any other nearby cores + Point2[] edges = Edges.getEdges(ent.block.size); + for(Point2 edge : edges){ + Building b = world.build(ent.tileX() + edge.x, ent.tileY() + edge.y); + if(b != this && b instanceof CoreBuild c){ + c.onProximityUpdate(); + if(ent.linkedCore == c){ + break; + } + } + } }); + team.data().itemCap -= storageCapacity; state.teams.unregisterCore(this); - for(CoreBuild other : state.teams.cores(team)){ - other.onProximityUpdate(); - } + clampItems(); } @Override @@ -844,7 +861,7 @@ public void handleItem(Building source, Item item){ state.rules.sector.info.handleCoreItem(item, 1); } - if(items.get(item) >= storageCapacity || incinerate){ + if(items.get(item) >= team.data().itemCap || incinerate){ //create item incineration effect at random intervals if(!noEffect){ incinerateEffect(this, source); @@ -853,7 +870,7 @@ public void handleItem(Building source, Item item){ }else{ super.handleItem(source, item); } - }else if(((state.rules.coreIncinerates && items.get(item) >= storageCapacity) || incinerate) && !noEffect){ + }else if(((state.rules.coreIncinerates && items.get(item) >= team.data().itemCap) || incinerate) && !noEffect){ //create item incineration effect at random intervals incinerateEffect(this, source); noEffect = false; diff --git a/core/src/mindustry/world/blocks/storage/StorageBlock.java b/core/src/mindustry/world/blocks/storage/StorageBlock.java index eb2c86281cb1..98eb4a4f982a 100644 --- a/core/src/mindustry/world/blocks/storage/StorageBlock.java +++ b/core/src/mindustry/world/blocks/storage/StorageBlock.java @@ -46,7 +46,7 @@ public static void incinerateEffect(Building self, Building source){ } public class StorageBuild extends Building{ - public @Nullable Building linkedCore; + public @Nullable CoreBuild linkedCore; @Override public boolean acceptItem(Building source, Item item){ @@ -61,10 +61,10 @@ public boolean canUnload(){ @Override public void handleItem(Building source, Item item){ if(linkedCore != null){ - if(linkedCore.items.get(item) >= ((CoreBuild)linkedCore).storageCapacity){ + if(linkedCore.items.get(item) >= team.data().itemCap){ incinerateEffect(this, source); } - ((CoreBuild)linkedCore).noEffect = true; + linkedCore.noEffect = true; linkedCore.handleItem(source, item); }else{ super.handleItem(source, item); diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index 267f11374d51..cac25a270fdf 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -681,7 +681,7 @@ protected void registerCommands(){ } for(Item item : content.items()){ - state.teams.cores(team).first().items.set(item, state.teams.cores(team).first().storageCapacity); + state.teams.cores(team).first().items.set(item, state.teams.get(team).itemCap); } info("Core filled.");