Global sector items / Mod file reading

This commit is contained in:
Anuken
2020-07-27 20:18:07 -04:00
parent 44e150638d
commit 1100803af4
12 changed files with 279 additions and 94 deletions

View File

@@ -63,8 +63,7 @@ stat.delivered = Resources Launched:
stat.playtime = Time Played:[accent] {0} stat.playtime = Time Played:[accent] {0}
stat.rank = Final Rank: [accent]{0} stat.rank = Final Rank: [accent]{0}
launcheditems = [accent]Launched Items globalitems = [accent]Global Items
launchinfo = [unlaunched][[LAUNCH] your core to obtain the items indicated in blue.
map.delete = Are you sure you want to delete the map "[accent]{0}[]"? map.delete = Are you sure you want to delete the map "[accent]{0}[]"?
level.highscore = High Score: [accent]{0} level.highscore = High Score: [accent]{0}
level.select = Level Select level.select = Level Select

View File

@@ -6,6 +6,10 @@ const onEvent = function(event, handler){
Vars.mods.getScripts().onEvent(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 scriptName = "base.js"
var modName = "none" var modName = "none"

View File

@@ -92,6 +92,9 @@ public class Logic implements ApplicationListener{
long seconds = state.rules.sector.getSecondsPassed(); long seconds = state.rules.sector.getSecondsPassed();
CoreEntity core = state.rules.defaultTeam.core(); 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 //apply fractional damage based on how many turns have passed for this sector
float turnsPassed = seconds / (turnDuration / 60f); float turnsPassed = seconds / (turnDuration / 60f);
@@ -101,26 +104,11 @@ public class Logic implements ApplicationListener{
//add resources based on turns passed //add resources based on turns passed
if(state.rules.sector.save != null && core != null){ if(state.rules.sector.save != null && core != null){
//add new items recieved
//add produced items state.rules.sector.calculateRecievedItems().each((item, amount) -> core.items.add(item, amount));
//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));
//clear received items //clear received items
state.rules.sector.setReceivedItems(new Seq<>()); 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); state.rules.sector.setSecondsPassed(0);

View File

@@ -140,17 +140,6 @@ public class SectorInfo{
} }
} }
/** @return the items in this sector now, taking into account production and items received. */
public ObjectIntMap<Item> getCurrentItems(Sector sector){
ObjectIntMap<Item> 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(){ private void updateCoreDeltas(){
CoreEntity ent = state.rules.defaultTeam.core(); CoreEntity ent = state.rules.defaultTeam.core();
for(int i = 0; i < lastCoreItems.length; i++){ for(int i = 0; i < lastCoreItems.length; i++){

View File

@@ -163,6 +163,21 @@ public class Universe{
save(); 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){ public float secondsMod(float mod, float scale){
return (seconds / scale) % mod; return (seconds / scale) % mod;
} }

View File

@@ -19,13 +19,12 @@ import java.util.regex.*;
public class Scripts implements Disposable{ public class Scripts implements Disposable{
private final Seq<String> blacklist = Seq.with("net", "files", "reflect", "javax", "rhino", "file", "channels", "jdk", private final Seq<String> 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", "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<String> whitelist = Seq.with("mindustry.net", "netserver", "netclient", "com.sun.proxy.$proxy", "mindustry.gen."); private final Seq<String> whitelist = Seq.with("mindustry.net", "netserver", "netclient", "com.sun.proxy.$proxy", "mindustry.gen.");
private final Context context; private final Context context;
private final Scriptable scope; private final Scriptable scope;
private boolean errored; private boolean errored;
private LoadedMod currentMod = null; private LoadedMod currentMod = null;
private Seq<EventHandle> events = new Seq<>();
public Scripts(){ public Scripts(){
Time.mark(); Time.mark();
@@ -75,9 +74,18 @@ public class Scripts implements Disposable{
Log.log(level, "[@]: @", source, message); Log.log(level, "[@]: @", source, message);
} }
//utility mod functions
public <T> void onEvent(Class<T> type, Cons<T> listener){ public <T> void onEvent(Class<T> type, Cons<T> listener){
Events.on(type, 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){ public void run(LoadedMod mod, Fi file){
@@ -107,23 +115,9 @@ public class Scripts implements Disposable{
@Override @Override
public void dispose(){ public void dispose(){
for(EventHandle e : events){
Events.remove(e.type, e.listener);
}
events.clear();
Context.exit(); 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 class ScriptModuleProvider extends UrlModuleSourceProvider{
private Pattern directory = Pattern.compile("^(.+?)/(.+)"); private Pattern directory = Pattern.compile("^(.+?)/(.+)");

View File

@@ -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<ItemStack>{
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<ItemStack> toSeq(){
Seq<ItemStack> 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<ItemStack> iterator(){
return toSeq().iterator();
}
}

View File

@@ -3,6 +3,7 @@ package mindustry.type;
import arc.*; import arc.*;
import arc.func.*; import arc.func.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.struct.ObjectIntMap.*;
import arc.struct.*; import arc.struct.*;
import arc.util.ArcAnnotate.*; import arc.util.ArcAnnotate.*;
import arc.util.*; import arc.util.*;
@@ -156,6 +157,70 @@ public class Sector{
Core.settings.putJson(key("received-items"), ItemStack.class, stacks); 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<ItemStack> 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<Item> 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 //TODO these methods should maybe move somewhere else and/or be contained in a data object
public void setSpawnPosition(int position){ public void setSpawnPosition(int position){
put("spawn-position", position); put("spawn-position", position);

View File

@@ -1,23 +1,30 @@
package mindustry.ui; package mindustry.ui;
import arc.graphics.*; import arc.graphics.*;
import arc.math.*;
import arc.scene.actions.*;
import arc.scene.ui.*; import arc.scene.ui.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.util.ArcAnnotate.*;
import mindustry.core.*; import mindustry.core.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.*; import mindustry.type.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
/** Displays a list of items, e.g. launched items.*/ /** Displays a list of items, e.g. launched items.*/
public class ItemsDisplay extends Table{ public class ItemsDisplay extends Table{
private StringBuilder builder = new StringBuilder();
public ItemsDisplay(){ 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(); clear();
top().left(); top().left();
margin(0); margin(0);
@@ -31,17 +38,21 @@ public class ItemsDisplay extends Table{
t.marginRight(30f); t.marginRight(30f);
t.left(); t.left();
for(Item item : content.items()){ for(Item item : content.items()){
if(item.unlocked()){ if(!items.has(item)) continue;
t.label(() -> format(item)).left();
t.image(item.icon(Cicon.small)).size(8 * 3).padLeft(4).padRight(4); Label label = t.add(UI.formatAmount(items.get(item))).left().get();
t.add(item.localizedName).color(Color.lightGray).left(); t.image(item.icon(Cicon.small)).size(8 * 3).padLeft(4).padRight(4);
t.row(); 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); }).get().setScrollingDisabled(true, false), false).setDuration(0.3f);
c.button("$launcheditems", Icon.downOpen, Styles.clearTogglet, col::toggle).update(t -> { c.button("$globalitems", Icon.downOpen, Styles.clearTogglet, col::toggle).update(t -> {
t.setText(state.isMenu() ? "$launcheditems" : "$launchinfo");
t.setChecked(col.isCollapsed()); t.setChecked(col.isCollapsed());
((Image)t.getChildren().get(1)).setDrawable(col.isCollapsed() ? Icon.upOpen : Icon.downOpen); ((Image)t.getChildren().get(1)).setDrawable(col.isCollapsed() ? Icon.upOpen : Icon.downOpen);
}).padBottom(4).left().fillX().margin(12f).minWidth(200f); }).padBottom(4).left().fillX().margin(12f).minWidth(200f);
@@ -49,15 +60,4 @@ public class ItemsDisplay extends Table{
c.add(col); 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();
}
} }

View File

@@ -11,7 +11,6 @@ import arc.scene.*;
import arc.scene.event.*; import arc.scene.event.*;
import arc.scene.ui.*; import arc.scene.ui.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*; import arc.util.*;
import arc.util.ArcAnnotate.*; import arc.util.ArcAnnotate.*;
import mindustry.core.*; import mindustry.core.*;
@@ -342,17 +341,14 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
t.left(); t.left();
t.table(res -> { t.table(res -> {
ObjectIntMap<Item> map = sector.save.meta.secinfo.getCurrentItems(sector); ItemSeq items = sector.calculateItems();
int i = 0; int i = 0;
for(Item item : content.items()){ for(ItemStack stack : items){
int amount = Math.min(map.get(item), sector.save.meta.secinfo.storageCapacity); res.image(stack.item.icon(Cicon.small)).padRight(3);
if(amount > 0){ res.add(UI.formatAmount(stack.amount)).color(Color.lightGray);
res.image(item.icon(Cicon.small)).padRight(3); if(++i % 2 == 0){
res.add(UI.formatAmount(amount)).color(Color.lightGray); res.row();
if(++i % 2 == 0){
res.row();
}
} }
} }
}); });

View File

@@ -26,7 +26,6 @@ import mindustry.type.*;
import mindustry.ui.*; import mindustry.ui.*;
import mindustry.ui.layout.*; import mindustry.ui.layout.*;
import mindustry.ui.layout.TreeLayout.*; import mindustry.ui.layout.TreeLayout.*;
import mindustry.world.modules.*;
import java.util.*; import java.util.*;
@@ -37,20 +36,69 @@ public class ResearchDialog extends BaseDialog{
private ObjectSet<TechTreeNode> nodes = new ObjectSet<>(); private ObjectSet<TechTreeNode> nodes = new ObjectSet<>();
private TechTreeNode root = new TechTreeNode(TechTree.root, null); private TechTreeNode root = new TechTreeNode(TechTree.root, null);
private Rect bounds = new Rect(); private Rect bounds = new Rect();
private ItemsDisplay itemDisplay;
private View view; private View view;
private ItemSeq items;
public ResearchDialog(){ public ResearchDialog(){
super(""); super("");
titleTable.remove(); titleTable.remove();
margin(0f).marginBottom(8); margin(0f).marginBottom(8);
cont.add(view = new View()).grow().get(); cont.stack(view = new View(), itemDisplay = new ItemsDisplay()).grow();
shouldPause = true; shouldPause = true;
shown(() -> { shown(() -> {
items = new ItemSeq(){
//store sector item amounts for modifications
ObjectMap<Sector, ItemSeq> 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); checkNodes(root);
treeLayout(); treeLayout();
}); });
hidden(ui.planet::setup); hidden(ui.planet::setup);
@@ -106,10 +154,6 @@ public class ResearchDialog extends BaseDialog{
}); });
} }
ItemModule items(){
return state.rules.defaultTeam.items();
}
void treeLayout(){ void treeLayout(){
float spacing = 20f; float spacing = 20f;
LayoutNode node = new LayoutNode(root, null); LayoutNode node = new LayoutNode(root, null);
@@ -177,6 +221,8 @@ public class ResearchDialog extends BaseDialog{
l.visible = !locked; l.visible = !locked;
checkNodes(l); checkNodes(l);
} }
itemDisplay.rebuild(items);
} }
boolean selectable(TechNode node){ boolean selectable(TechNode node){
@@ -320,29 +366,30 @@ public class ResearchDialog extends BaseDialog{
panY = ry - bounds.y - oy; panY = ry - bounds.y - oy;
} }
boolean canUnlock(TechNode node){
return items().has(node.requirements) && selectable(node);
}
boolean canSpend(TechNode node){ boolean canSpend(TechNode node){
//can spend when there's at least 1 item that can be spent //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){ void spend(TechNode node){
boolean complete = true; boolean complete = true;
boolean[] shine = new boolean[node.requirements.length]; boolean[] shine = new boolean[node.requirements.length];
boolean[] usedShine = new boolean[content.items().size];
for(int i = 0; i < node.requirements.length; i++){ for(int i = 0; i < node.requirements.length; i++){
ItemStack req = node.requirements[i]; ItemStack req = node.requirements[i];
ItemStack completed = node.finishedRequirements[i]; ItemStack completed = node.finishedRequirements[i];
//amount actually taken from inventory //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; 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 //disable completion if the completed amount has not reached requirements
if(completed.amount < req.amount){ if(completed.amount < req.amount){
@@ -356,11 +403,11 @@ public class ResearchDialog extends BaseDialog{
node.save(); node.save();
rebuild(shine); rebuild(shine);
itemDisplay.rebuild(items, usedShine);
} }
void unlock(TechNode node){ void unlock(TechNode node){
node.content.unlock(); node.content.unlock();
items().remove(node.requirements);
showToast(Core.bundle.format("researched", node.content.localizedName)); showToast(Core.bundle.format("researched", node.content.localizedName));
checkNodes(root); checkNodes(root);
hoverNode = null; hoverNode = null;
@@ -462,7 +509,7 @@ public class ResearchDialog extends BaseDialog{
"") "")
+ UI.formatAmount(reqAmount)).get(); + 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){ if(shiny){
label.setColor(Pal.accent); label.setColor(Pal.accent);

View File

@@ -573,11 +573,14 @@ public class HudFragment extends Fragment{
dialog.show(); dialog.show();
} }
//TODO launching is disabled, possibly forever
private boolean inLaunchWave(){ private boolean inLaunchWave(){
return false;
/*
return state.hasSector() && return state.hasSector() &&
state.getSector().metCondition() && state.getSector().metCondition() &&
!net.client() && !net.client() &&
state.wave % state.getSector().launchPeriod == 0 && !spawner.isSpawning(); state.wave % state.getSector().launchPeriod == 0 && !spawner.isSpawning();*/
} }
private boolean canLaunch(){ private boolean canLaunch(){