From 1100803af4a87c6d191646d41ccc5765a259bbdd Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 27 Jul 2020 20:18:07 -0400 Subject: [PATCH] Global sector items / Mod file reading --- core/assets/bundles/bundle.properties | 3 +- core/assets/scripts/base.js | 4 + core/src/mindustry/core/Logic.java | 22 ++--- core/src/mindustry/game/SectorInfo.java | 11 --- core/src/mindustry/game/Universe.java | 15 ++++ core/src/mindustry/mod/Scripts.java | 28 +++--- core/src/mindustry/type/ItemSeq.java | 85 +++++++++++++++++++ core/src/mindustry/type/Sector.java | 65 ++++++++++++++ core/src/mindustry/ui/ItemsDisplay.java | 42 ++++----- .../mindustry/ui/dialogs/PlanetDialog.java | 16 ++-- .../mindustry/ui/dialogs/ResearchDialog.java | 77 +++++++++++++---- .../mindustry/ui/fragments/HudFragment.java | 5 +- 12 files changed, 279 insertions(+), 94 deletions(-) create mode 100644 core/src/mindustry/type/ItemSeq.java diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index fed61ebf55..9ba28d903d 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -63,8 +63,7 @@ stat.delivered = Resources Launched: stat.playtime = Time Played:[accent] {0} stat.rank = Final Rank: [accent]{0} -launcheditems = [accent]Launched Items -launchinfo = [unlaunched][[LAUNCH] your core to obtain the items indicated in blue. +globalitems = [accent]Global Items map.delete = Are you sure you want to delete the map "[accent]{0}[]"? level.highscore = High Score: [accent]{0} level.select = Level Select diff --git a/core/assets/scripts/base.js b/core/assets/scripts/base.js index f1e2a04b61..a846580923 100755 --- a/core/assets/scripts/base.js +++ b/core/assets/scripts/base.js @@ -6,6 +6,10 @@ const onEvent = function(event, handler){ Vars.mods.getScripts().onEvent(event, handler) } +const readString = path => Vars.mods.getScripts().readString(path) + +const readBytes = path => Vars.mods.getScripts().readBytes(path) + var scriptName = "base.js" var modName = "none" diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index 1371ec72a0..74f36a3c5a 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -92,6 +92,9 @@ public class Logic implements ApplicationListener{ long seconds = state.rules.sector.getSecondsPassed(); CoreEntity core = state.rules.defaultTeam.core(); + //update correct storage capacity + state.rules.sector.save.meta.secinfo.storageCapacity = core.storageCapacity; + //apply fractional damage based on how many turns have passed for this sector float turnsPassed = seconds / (turnDuration / 60f); @@ -101,26 +104,11 @@ public class Logic implements ApplicationListener{ //add resources based on turns passed if(state.rules.sector.save != null && core != null){ - - //add produced items - //TODO move to received items - state.rules.sector.save.meta.secinfo.production.each((item, stat) -> { - core.items.add(item, (int)(stat.mean * seconds)); - }); - - //add received items - state.rules.sector.getReceivedItems().each(stack -> core.items.add(stack.item, stack.amount)); + //add new items recieved + state.rules.sector.calculateRecievedItems().each((item, amount) -> core.items.add(item, amount)); //clear received items state.rules.sector.setReceivedItems(new Seq<>()); - - //validation - for(Item item : content.items()){ - //ensure positive items - if(core.items.get(item) < 0) core.items.set(item, 0); - //cap the items - if(core.items.get(item) > core.storageCapacity) core.items.set(item, core.storageCapacity); - } } state.rules.sector.setSecondsPassed(0); diff --git a/core/src/mindustry/game/SectorInfo.java b/core/src/mindustry/game/SectorInfo.java index 11cb1c8ace..0460344f22 100644 --- a/core/src/mindustry/game/SectorInfo.java +++ b/core/src/mindustry/game/SectorInfo.java @@ -140,17 +140,6 @@ public class SectorInfo{ } } - /** @return the items in this sector now, taking into account production and items received. */ - public ObjectIntMap getCurrentItems(Sector sector){ - ObjectIntMap map = new ObjectIntMap<>(); - map.putAll(coreItems); - long seconds = sector.getSecondsPassed(); - production.each((item, stat) -> map.increment(item, (int)(stat.mean * seconds))); - //increment based on received items - sector.getReceivedItems().each(stack -> map.increment(stack.item, stack.amount)); - return map; - } - private void updateCoreDeltas(){ CoreEntity ent = state.rules.defaultTeam.core(); for(int i = 0; i < lastCoreItems.length; i++){ diff --git a/core/src/mindustry/game/Universe.java b/core/src/mindustry/game/Universe.java index 33f14eb1e7..c38ee07eb9 100644 --- a/core/src/mindustry/game/Universe.java +++ b/core/src/mindustry/game/Universe.java @@ -163,6 +163,21 @@ public class Universe{ save(); } + /** This method is expensive to call; only do so sparingly. */ + public ItemSeq getGlobalResources(){ + ItemSeq count = new ItemSeq(); + + for(Planet planet : content.planets()){ + for(Sector sector : planet.sectors){ + if(sector.hasSave()){ + count.add(sector.calculateItems()); + } + } + } + + return count; + } + public float secondsMod(float mod, float scale){ return (seconds / scale) % mod; } diff --git a/core/src/mindustry/mod/Scripts.java b/core/src/mindustry/mod/Scripts.java index a0b106e574..b57345f18a 100644 --- a/core/src/mindustry/mod/Scripts.java +++ b/core/src/mindustry/mod/Scripts.java @@ -19,13 +19,12 @@ import java.util.regex.*; public class Scripts implements Disposable{ private final Seq blacklist = Seq.with("net", "files", "reflect", "javax", "rhino", "file", "channels", "jdk", "runtime", "util.os", "rmi", "security", "org.", "sun.", "beans", "sql", "http", "exec", "compiler", "process", "system", - ".awt", "socket", "classloader", "oracle", "invoke", "arc.events", "java.util.function", "java.util.stream"); + ".awt", "socket", "classloader", "oracle", "invoke", "java.util.function", "java.util.stream"); private final Seq whitelist = Seq.with("mindustry.net", "netserver", "netclient", "com.sun.proxy.$proxy", "mindustry.gen."); private final Context context; private final Scriptable scope; private boolean errored; private LoadedMod currentMod = null; - private Seq events = new Seq<>(); public Scripts(){ Time.mark(); @@ -75,9 +74,18 @@ public class Scripts implements Disposable{ Log.log(level, "[@]: @", source, message); } + //utility mod functions + public void onEvent(Class type, Cons listener){ Events.on(type, listener); - events.add(new EventHandle(type, listener)); + } + + public String readString(String path){ + return Vars.tree.get(path).readString(); + } + + public byte[] readBytes(String path){ + return Vars.tree.get(path).readBytes(); } public void run(LoadedMod mod, Fi file){ @@ -107,23 +115,9 @@ public class Scripts implements Disposable{ @Override public void dispose(){ - for(EventHandle e : events){ - Events.remove(e.type, e.listener); - } - events.clear(); Context.exit(); } - private static class EventHandle{ - Class type; - Cons listener; - - public EventHandle(Class type, Cons listener){ - this.type = type; - this.listener = listener; - } - } - private class ScriptModuleProvider extends UrlModuleSourceProvider{ private Pattern directory = Pattern.compile("^(.+?)/(.+)"); diff --git a/core/src/mindustry/type/ItemSeq.java b/core/src/mindustry/type/ItemSeq.java new file mode 100644 index 0000000000..a7d528cf54 --- /dev/null +++ b/core/src/mindustry/type/ItemSeq.java @@ -0,0 +1,85 @@ +package mindustry.type; + +import arc.struct.*; +import mindustry.*; +import mindustry.world.modules.*; +import mindustry.world.modules.ItemModule.*; + +import java.util.*; + +public class ItemSeq implements Iterable{ + private final static ItemStack tmp = new ItemStack(); + + protected final int[] values; + public int total; + + public ItemSeq(){ + values = new int[Vars.content.items().size]; + } + + public void each(ItemConsumer cons){ + for(int i = 0; i < values.length; i++){ + if(values[i] > 0){ + cons.accept(Vars.content.item(i), values[i]); + } + } + } + + public Seq toSeq(){ + Seq out = new Seq<>(); + for(int i = 0; i < values.length; i++){ + if(values[i] > 0) out.add(new ItemStack(Vars.content.item(i), values[i])); + } + return out; + } + + public boolean has(Item item){ + return values[item.id] > 0; + } + + public int get(Item item){ + return values[item.id]; + } + + public void set(Item item, int amount){ + add(item, amount - values[item.id]); + } + + public void add(ItemModule itemModule){ + itemModule.each(this::add); + } + + public void add(ItemSeq seq){ + seq.each(this::add); + } + + public void add(ItemStack stack){ + add(stack.item, stack.amount); + } + + public void add(Item item){ + add(item, 1); + } + + public void add(Item item, int amount){ + values[item.id] += amount; + total += amount; + } + + public void remove(ItemStack stack){ + add(stack.item, -stack.amount); + } + + public void remove(Item item){ + add(item, -1); + } + + public void remove(Item item, int amount){ + add(item, -amount); + } + + @Override + public Iterator iterator(){ + return toSeq().iterator(); + } +} diff --git a/core/src/mindustry/type/Sector.java b/core/src/mindustry/type/Sector.java index 579b687417..d6aab89708 100644 --- a/core/src/mindustry/type/Sector.java +++ b/core/src/mindustry/type/Sector.java @@ -3,6 +3,7 @@ package mindustry.type; import arc.*; import arc.func.*; import arc.math.geom.*; +import arc.struct.ObjectIntMap.*; import arc.struct.*; import arc.util.ArcAnnotate.*; import arc.util.*; @@ -156,6 +157,70 @@ public class Sector{ Core.settings.putJson(key("received-items"), ItemStack.class, stacks); } + public void removeItem(Item item, int amount){ + if(isBeingPlayed()){ + if(state.rules.defaultTeam.core() != null){ + state.rules.defaultTeam.items().remove(item, amount); + } + }else{ + Seq recv = getReceivedItems(); + + ItemStack fit = recv.find(i -> i.item == item); + if(fit != null){ + fit.amount -= amount; + }else{ + recv.add(new ItemStack(item, amount)); + } + + setReceivedItems(recv); + } + } + + public ItemSeq calculateItems(){ + ItemSeq count = new ItemSeq(); + + //for sectors being played on, add items directly + if(isBeingPlayed()){ + count.add(state.rules.defaultTeam.items()); + }else if(save != null){ + //add items already present + for(Entry ent : save.meta.secinfo.coreItems){ + count.add(ent.key, ent.value); + } + + count.add(calculateRecievedItems()); + } + + return count; + } + + public ItemSeq calculateRecievedItems(){ + ItemSeq count = new ItemSeq(); + + if(save != null){ + int capacity = save.meta.secinfo.storageCapacity; + long seconds = state.rules.sector.getSecondsPassed(); + + //add produced items + state.rules.sector.save.meta.secinfo.production.each((item, stat) -> { + count.add(item, (int)(stat.mean * seconds)); + }); + + //add received items + state.rules.sector.getReceivedItems().each(stack -> count.add(stack.item, stack.amount)); + + //validation + for(Item item : content.items()){ + //ensure positive items + if(count.get(item) < 0) count.set(item, 0); + //cap the items + if(count.get(item) > capacity) count.set(item, capacity); + } + } + + return count; + } + //TODO these methods should maybe move somewhere else and/or be contained in a data object public void setSpawnPosition(int position){ put("spawn-position", position); diff --git a/core/src/mindustry/ui/ItemsDisplay.java b/core/src/mindustry/ui/ItemsDisplay.java index 8b1b67278c..8ab6d63b8b 100644 --- a/core/src/mindustry/ui/ItemsDisplay.java +++ b/core/src/mindustry/ui/ItemsDisplay.java @@ -1,23 +1,30 @@ package mindustry.ui; import arc.graphics.*; +import arc.math.*; +import arc.scene.actions.*; import arc.scene.ui.*; import arc.scene.ui.layout.*; +import arc.util.ArcAnnotate.*; import mindustry.core.*; import mindustry.gen.*; +import mindustry.graphics.*; import mindustry.type.*; import static mindustry.Vars.*; /** Displays a list of items, e.g. launched items.*/ public class ItemsDisplay extends Table{ - private StringBuilder builder = new StringBuilder(); public ItemsDisplay(){ - rebuild(); + rebuild(new ItemSeq()); } - public void rebuild(){ + public void rebuild(ItemSeq items){ + rebuild(items, null); + } + + public void rebuild(ItemSeq items, @Nullable boolean[] shine){ clear(); top().left(); margin(0); @@ -31,17 +38,21 @@ public class ItemsDisplay extends Table{ t.marginRight(30f); t.left(); for(Item item : content.items()){ - if(item.unlocked()){ - t.label(() -> format(item)).left(); - t.image(item.icon(Cicon.small)).size(8 * 3).padLeft(4).padRight(4); - t.add(item.localizedName).color(Color.lightGray).left(); - t.row(); + if(!items.has(item)) continue; + + Label label = t.add(UI.formatAmount(items.get(item))).left().get(); + t.image(item.icon(Cicon.small)).size(8 * 3).padLeft(4).padRight(4); + t.add(item.localizedName).color(Color.lightGray).left(); + t.row(); + + if(shine != null && shine[item.id]){ + label.setColor(Pal.accent); + label.actions(Actions.color(Color.white, 0.75f, Interp.fade)); } } }).get().setScrollingDisabled(true, false), false).setDuration(0.3f); - c.button("$launcheditems", Icon.downOpen, Styles.clearTogglet, col::toggle).update(t -> { - t.setText(state.isMenu() ? "$launcheditems" : "$launchinfo"); + c.button("$globalitems", Icon.downOpen, Styles.clearTogglet, col::toggle).update(t -> { t.setChecked(col.isCollapsed()); ((Image)t.getChildren().get(1)).setDrawable(col.isCollapsed() ? Icon.upOpen : Icon.downOpen); }).padBottom(4).left().fillX().margin(12f).minWidth(200f); @@ -49,15 +60,4 @@ public class ItemsDisplay extends Table{ c.add(col); }); } - - private String format(Item item){ - builder.setLength(0); - builder.append("[TODO implement]"); - //builder.append(UI.formatAmount(data.getItem(item))); - if(state.isGame() && player.team().data().hasCore() && player.team().core().items.get(item) > 0){ - builder.append(" [unlaunched]+ "); - builder.append(UI.formatAmount(state.teams.get(player.team()).core().items.get(item))); - } - return builder.toString(); - } } diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index a4764be9ef..842b11dcb5 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -11,7 +11,6 @@ import arc.scene.*; import arc.scene.event.*; import arc.scene.ui.*; import arc.scene.ui.layout.*; -import arc.struct.*; import arc.util.*; import arc.util.ArcAnnotate.*; import mindustry.core.*; @@ -342,17 +341,14 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ t.left(); t.table(res -> { - ObjectIntMap map = sector.save.meta.secinfo.getCurrentItems(sector); + ItemSeq items = sector.calculateItems(); int i = 0; - for(Item item : content.items()){ - int amount = Math.min(map.get(item), sector.save.meta.secinfo.storageCapacity); - if(amount > 0){ - res.image(item.icon(Cicon.small)).padRight(3); - res.add(UI.formatAmount(amount)).color(Color.lightGray); - if(++i % 2 == 0){ - res.row(); - } + for(ItemStack stack : items){ + res.image(stack.item.icon(Cicon.small)).padRight(3); + res.add(UI.formatAmount(stack.amount)).color(Color.lightGray); + if(++i % 2 == 0){ + res.row(); } } }); diff --git a/core/src/mindustry/ui/dialogs/ResearchDialog.java b/core/src/mindustry/ui/dialogs/ResearchDialog.java index f33684ff08..30d9a21d89 100644 --- a/core/src/mindustry/ui/dialogs/ResearchDialog.java +++ b/core/src/mindustry/ui/dialogs/ResearchDialog.java @@ -26,7 +26,6 @@ import mindustry.type.*; import mindustry.ui.*; import mindustry.ui.layout.*; import mindustry.ui.layout.TreeLayout.*; -import mindustry.world.modules.*; import java.util.*; @@ -37,20 +36,69 @@ public class ResearchDialog extends BaseDialog{ private ObjectSet nodes = new ObjectSet<>(); private TechTreeNode root = new TechTreeNode(TechTree.root, null); private Rect bounds = new Rect(); + private ItemsDisplay itemDisplay; private View view; + private ItemSeq items; + public ResearchDialog(){ super(""); titleTable.remove(); margin(0f).marginBottom(8); - cont.add(view = new View()).grow().get(); + cont.stack(view = new View(), itemDisplay = new ItemsDisplay()).grow(); shouldPause = true; shown(() -> { + + items = new ItemSeq(){ + //store sector item amounts for modifications + ObjectMap cache = new ObjectMap<>(); + + { + //add global counts of each sector + for(Planet planet : content.planets()){ + for(Sector sector : planet.sectors){ + if(sector.hasSave()){ + ItemSeq cached = sector.calculateItems(); + add(cached); + cache.put(sector, cached); + } + } + } + } + + //this is the only method that actually modifies the sequence itself. + @Override + public void add(Item item, int amount){ + //only have custom removal logic for when the sequence gets items taken out of it (e.g. research) + if(amount < 0){ + //remove items from each sector's storage, one by one + + //% that gets removed from each sector + double percentage = (double)amount / get(item); + int[] counter = {amount}; + cache.each((sector, seq) -> { + if(counter[0] == 0) return; + + //amount that will be removed + int toRemove = Math.min((int)Math.ceil(percentage * seq.get(item)), counter[0]); + + //actually remove it from the sector + sector.removeItem(item, toRemove); + seq.remove(item, toRemove); + counter[0] -= toRemove; + }); + } + + super.add(item, amount); + } + }; + checkNodes(root); treeLayout(); + }); hidden(ui.planet::setup); @@ -106,10 +154,6 @@ public class ResearchDialog extends BaseDialog{ }); } - ItemModule items(){ - return state.rules.defaultTeam.items(); - } - void treeLayout(){ float spacing = 20f; LayoutNode node = new LayoutNode(root, null); @@ -177,6 +221,8 @@ public class ResearchDialog extends BaseDialog{ l.visible = !locked; checkNodes(l); } + + itemDisplay.rebuild(items); } boolean selectable(TechNode node){ @@ -320,29 +366,30 @@ public class ResearchDialog extends BaseDialog{ panY = ry - bounds.y - oy; } - boolean canUnlock(TechNode node){ - return items().has(node.requirements) && selectable(node); - } - boolean canSpend(TechNode node){ //can spend when there's at least 1 item that can be spent - return selectable(node) && (node.requirements.length == 0 || Structs.contains(node.requirements, i -> items().has(i.item))); + return selectable(node) && (node.requirements.length == 0 || Structs.contains(node.requirements, i -> items.has(i.item))); } void spend(TechNode node){ boolean complete = true; boolean[] shine = new boolean[node.requirements.length]; + boolean[] usedShine = new boolean[content.items().size]; for(int i = 0; i < node.requirements.length; i++){ ItemStack req = node.requirements[i]; ItemStack completed = node.finishedRequirements[i]; //amount actually taken from inventory - int used = Math.min(req.amount - completed.amount, items().get(req.item)); + int used = Math.min(req.amount - completed.amount, items.get(req.item)); + items.remove(req.item, used); completed.amount += used; - if(used > 0) shine[i] = true; + if(used > 0){ + shine[i] = true; + usedShine[req.item.id] = true; + } //disable completion if the completed amount has not reached requirements if(completed.amount < req.amount){ @@ -356,11 +403,11 @@ public class ResearchDialog extends BaseDialog{ node.save(); rebuild(shine); + itemDisplay.rebuild(items, usedShine); } void unlock(TechNode node){ node.content.unlock(); - items().remove(node.requirements); showToast(Core.bundle.format("researched", node.content.localizedName)); checkNodes(root); hoverNode = null; @@ -462,7 +509,7 @@ public class ResearchDialog extends BaseDialog{ "") + UI.formatAmount(reqAmount)).get(); - Color targetColor = items().has(req.item) ? Color.lightGray : Color.scarlet; + Color targetColor = items.has(req.item) ? Color.lightGray : Color.scarlet; if(shiny){ label.setColor(Pal.accent); diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index df8ac3c4d0..585577fa0b 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -573,11 +573,14 @@ public class HudFragment extends Fragment{ dialog.show(); } + //TODO launching is disabled, possibly forever private boolean inLaunchWave(){ + return false; + /* return state.hasSector() && state.getSector().metCondition() && !net.client() && - state.wave % state.getSector().launchPeriod == 0 && !spawner.isSpawning(); + state.wave % state.getSector().launchPeriod == 0 && !spawner.isSpawning();*/ } private boolean canLaunch(){