diff --git a/annotations/src/main/resources/revisions/LaunchPayloadEntity/2.json b/annotations/src/main/resources/revisions/LaunchPayloadEntity/2.json new file mode 100644 index 0000000000..4dc9173474 --- /dev/null +++ b/annotations/src/main/resources/revisions/LaunchPayloadEntity/2.json @@ -0,0 +1 @@ +{version:2,fields:[{name:lifetime,type:float,size:4},{name:stacks,type:arc.struct.Array,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]} \ No newline at end of file diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 377170b822..6777e842d3 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -80,7 +80,7 @@ public class Vars implements Loadable{ public static final float miningRange = 70f; /** range for building */ public static final float buildingRange = 220f; - /** duration of one turn. */ + /** duration of one turn in ticks */ public static final float turnDuration = 5 * Time.toMinutes; /** for map generator dialog */ public static boolean updateEditorOnChange = false; diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 45d7b4b862..e6e44ff8c1 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -1320,7 +1320,7 @@ public class Blocks implements ContentList{ requirements(Category.effect, BuildVisibility.campaignOnly, ItemStack.with(Items.copper, 250, Items.silicon, 75, Items.lead, 100)); size = 3; itemCapacity = 100; - launchTime = 60f * 2; + launchTime = 60*3;//60f * 15; hasPower = true; consumes.power(4f); }}; diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 95caa2b6b5..c2266de60a 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -159,6 +159,12 @@ public class Fx{ Fill.circle(e.x, e.y, (7f - e.fin() * 7f)/2f); }), + rocketSmoke = new Effect(120, e -> { + color(Color.gray); + alpha(Mathf.clamp(e.fout()*1.6f - e.rotation*0.8f)); + Fill.circle(e.x, e.y, (1f + 6f * e.rotation) - e.fin()*2f); + }), + magmasmoke = new Effect(110, e -> { color(Color.gray); Fill.circle(e.x, e.y, e.fslope() * 6f); diff --git a/core/src/mindustry/core/Renderer.java b/core/src/mindustry/core/Renderer.java index f8ce3f41c0..ba793fc6be 100644 --- a/core/src/mindustry/core/Renderer.java +++ b/core/src/mindustry/core/Renderer.java @@ -225,8 +225,8 @@ public class Renderer implements ApplicationListener{ } if(bloom != null){ - Draw.draw(Layer.bullet - 0.001f, bloom::capture); - Draw.draw(Layer.effect + 0.001f, bloom::render); + Draw.draw(Layer.bullet - 0.01f, bloom::capture); + Draw.draw(Layer.effect + 0.01f, bloom::render); } Draw.z(Layer.plans); diff --git a/core/src/mindustry/entities/def/BuilderComp.java b/core/src/mindustry/entities/def/BuilderComp.java index ce3ba8af6f..76d2a20256 100644 --- a/core/src/mindustry/entities/def/BuilderComp.java +++ b/core/src/mindustry/entities/def/BuilderComp.java @@ -30,10 +30,12 @@ abstract class BuilderComp implements Unitc{ Queue requests = new Queue<>(); transient float buildSpeed = 1f; - //boolean building; + transient boolean building = true; @Override public void update(){ + if(!building) return; + float finalPlaceDst = state.rules.infiniteResources ? Float.MAX_VALUE : buildingRange; Iterator it = requests.iterator(); diff --git a/core/src/mindustry/game/Saves.java b/core/src/mindustry/game/Saves.java index 19d673f3e7..4c3afd013c 100644 --- a/core/src/mindustry/game/Saves.java +++ b/core/src/mindustry/game/Saves.java @@ -167,7 +167,7 @@ public class Saves{ public class SaveSlot{ public final Fi file; boolean requestedPreview; - SaveMeta meta; + public SaveMeta meta; public SaveSlot(Fi file){ this.file = file; @@ -242,6 +242,10 @@ public class Saves{ return isSector(); } + public ObjectFloatMap getProductionRates(){ + return meta.productionRates; + } + public String getPlayTime(){ return Strings.formatMillis(current == this ? totalPlaytime : meta.timePlayed); } diff --git a/core/src/mindustry/game/Stats.java b/core/src/mindustry/game/Stats.java index 53db40a3c5..2740428770 100644 --- a/core/src/mindustry/game/Stats.java +++ b/core/src/mindustry/game/Stats.java @@ -5,8 +5,6 @@ import arc.struct.*; import arc.util.*; import mindustry.type.*; -import java.util.*; - import static mindustry.Vars.content; public class Stats{ @@ -30,36 +28,44 @@ public class Stats{ /** Friendly buildings destroyed. */ public int buildingsDestroyed; - /** Item production means. Holds means per period. */ - private transient WindowedMean[] itemExport = new WindowedMean[content.items().size]; - /** Counters of items recieved this period. */ - private transient float[] itemCounters = new float[content.items().size]; /** Counter refresh state. */ private transient Interval time = new Interval(); - - public Stats(){ - for(int i = 0; i < itemExport.length; i++){ - itemExport[i] = new WindowedMean(exportWindow); - } - } + /** Export statistics. */ + public ObjectMap production = new ObjectMap<>(); /** Updates export statistics. */ public void handleItemExport(ItemStack stack){ - itemCounters[stack.item.id] += stack.amount; + production.getOr(stack.item, ProductionStat::new).counter += stack.amount; + } + + public float getExport(Item item){ + return production.getOr(item, ProductionStat::new).mean; } public void update(){ //refresh throughput if(time.get(refreshPeriod)){ - for(int i = 0; i < itemCounters.length; i++){ - itemExport[i].add(itemCounters[i]); - } + for(ProductionStat stat : production.values()){ + //initialize stat after loading + if(!stat.loaded){ + stat.means.fill(stat.mean); + stat.loaded = true; + } - Arrays.fill(itemCounters, 0); + stat.means.add(stat.counter); + stat.counter = 0; + stat.mean = stat.means.rawMean(); + } } } + public ObjectFloatMap productionRates(){ + ObjectFloatMap map = new ObjectFloatMap<>(); + production.each((item, value) -> map.put(item, value.mean)); + return map; + } + public RankResult calculateRank(Sector zone, boolean launched){ float score = 0; @@ -106,7 +112,15 @@ public class Stats{ } } + public enum Rank{ F, D, C, B, A, S, SS } + + public static class ProductionStat{ + public transient float counter; + public transient WindowedMean means = new WindowedMean(content.items().size); + public transient boolean loaded; + public float mean; + } } diff --git a/core/src/mindustry/game/Universe.java b/core/src/mindustry/game/Universe.java index a15549f2bb..8f7917cbb8 100644 --- a/core/src/mindustry/game/Universe.java +++ b/core/src/mindustry/game/Universe.java @@ -2,8 +2,10 @@ package mindustry.game; import arc.*; import arc.math.*; +import arc.struct.ObjectFloatMap.*; import arc.util.*; import mindustry.content.*; +import mindustry.io.*; import mindustry.type.*; import static mindustry.Vars.*; @@ -69,7 +71,24 @@ public class Universe{ } private void onTurn(){ - //create a random event here, e.g. invasion + //TODO run waves on hostile sectors, damage them + + //calculate passive items + for(Planet planet : content.planets()){ + for(Sector sector : planet.sectors){ + if(sector.hasSave()){ + SaveMeta meta = sector.save.meta; + + for(Entry entry : meta.productionRates){ + //total is calculated by items/sec (value) * turn duration in seconds + int total = (int)(entry.value * turnDuration / 60f); + + //add the items to global data + data.addItem(entry.key, total); + } + } + } + } } public float secondsMod(float mod, float scale){ diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index d25ee14842..80b71f2cc4 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -199,6 +199,10 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ public void update(){ player.typing(ui.chatfrag.shown()); + if(player.isBuilder()){ + player.builder().building(isBuilding); + } + if(!player.dead()){ controlledType = player.unit().type(); } diff --git a/core/src/mindustry/io/SaveMeta.java b/core/src/mindustry/io/SaveMeta.java index 04ce7e4af2..ee114a0f5f 100644 --- a/core/src/mindustry/io/SaveMeta.java +++ b/core/src/mindustry/io/SaveMeta.java @@ -3,6 +3,7 @@ package mindustry.io; import arc.struct.*; import mindustry.game.*; import mindustry.maps.*; +import mindustry.type.*; import static mindustry.Vars.maps; @@ -16,8 +17,11 @@ public class SaveMeta{ public Rules rules; public StringMap tags; public String[] mods; + /** These are in items/second. */ + public ObjectFloatMap productionRates; + public boolean hasProduction; - public SaveMeta(int version, long timestamp, long timePlayed, int build, String map, int wave, Rules rules, StringMap tags){ + public SaveMeta(int version, long timestamp, long timePlayed, int build, String map, int wave, Rules rules, ObjectFloatMap productionRates, StringMap tags){ this.version = version; this.build = build; this.timestamp = timestamp; @@ -27,5 +31,8 @@ public class SaveMeta{ this.rules = rules; this.tags = tags; this.mods = JsonIO.read(String[].class, tags.get("mods", "[]")); + this.productionRates = productionRates; + + productionRates.each(e -> hasProduction |= e.value > 0.001f); } } diff --git a/core/src/mindustry/io/SaveVersion.java b/core/src/mindustry/io/SaveVersion.java index adacd2b333..1ecc35931b 100644 --- a/core/src/mindustry/io/SaveVersion.java +++ b/core/src/mindustry/io/SaveVersion.java @@ -31,7 +31,17 @@ public abstract class SaveVersion extends SaveFileReader{ public SaveMeta getMeta(DataInput stream) throws IOException{ stream.readInt(); //length of data, doesn't matter here StringMap map = readStringMap(stream); - return new SaveMeta(map.getInt("version"), map.getLong("saved"), map.getLong("playtime"), map.getInt("build"), map.get("mapname"), map.getInt("wave"), JsonIO.read(Rules.class, map.get("rules", "{}")), map); + return new SaveMeta( + map.getInt("version"), + map.getLong("saved"), + map.getLong("playtime"), + map.getInt("build"), + map.get("mapname"), + map.getInt("wave"), + JsonIO.read(Rules.class, map.get("rules", "{}")), + JsonIO.read(Stats.class, map.get("stats", "{}")).productionRates(), + map + ); } @Override diff --git a/core/src/mindustry/type/Sector.java b/core/src/mindustry/type/Sector.java index af90470e44..531fa5e213 100644 --- a/core/src/mindustry/type/Sector.java +++ b/core/src/mindustry/type/Sector.java @@ -39,6 +39,10 @@ public class Sector{ this.data = data; } + public boolean hasSave(){ + return save != null; + } + public void generate(){ //TODO use simplex and a seed hostility = Math.max(Noise.snoise3(tile.v.x, tile.v.y, tile.v.z, 1f, 0.5f), 0); diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index afaa4f8295..0640b569f6 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -273,11 +273,11 @@ public class PlanetDialog extends FloatingDialog{ } if(sec.hostility >= 0.02f){ - drawSelection(sec, Color.scarlet, 0.11f * sec.hostility, 0.0001f); + drawSelection(sec, Color.scarlet, 0.11f * sec.hostility, -0.02f); } if(sec.save != null){ - drawSelection(sec, Color.lime, 0.03f, 0.0009f); + drawSelection(sec, Color.lime, 0.03f, -0.01f); } } @@ -355,6 +355,25 @@ public class PlanetDialog extends FloatingDialog{ } }).fillX().row(); + //production + if(selected.hasSave() && selected.save.meta.hasProduction){ + stable.add("Production:").row(); + stable.table(t -> { + t.left(); + + selected.save.meta.productionRates.each(entry -> { + int total = (int)(entry.value * turnDuration / 60f); + if(total > 1){ + t.image(entry.key.icon(Cicon.small)).padRight(3); + t.add(ui.formatAmount(total) + " /turn").color(Color.lightGray); + t.row(); + } + }); + }); + } + + + stable.row(); stable.button("Launch", Styles.transt, () -> { diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index 69c6209ff5..923ac9ffd9 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -21,6 +21,7 @@ import mindustry.gen.*; import mindustry.graphics.*; import mindustry.input.*; import mindustry.net.Packets.*; +import mindustry.type.*; import mindustry.ui.*; import mindustry.ui.dialogs.*; @@ -296,6 +297,41 @@ public class HudFragment extends Fragment{ p.touchable(Touchable.disabled); }); + //DEBUG: rate table + parent.fill(t -> { + t.bottom().left(); + t.table(Styles.black6, c -> { + Bits used = new Bits(content.items().size); + + Runnable rebuild = () -> { + c.clearChildren(); + + for(Item item : content.items()){ + if(state.stats.getExport(item) >= 1){ + c.image(item.icon(Cicon.small)); + c.label(() -> (int)state.stats.getExport(item) + " /s").color(Color.lightGray); + c.row(); + } + } + }; + + c.update(() -> { + boolean wrong = false; + for(Item item : content.items()){ + boolean has = state.stats.getExport(item) >= 1; + if(used.get(item.id) != has){ + used.set(item.id, has); + wrong = true; + } + } + if(wrong){ + rebuild.run(); + } + }); + }); + + }); + blockfrag.build(parent); } diff --git a/core/src/mindustry/world/blocks/storage/LaunchPad.java b/core/src/mindustry/world/blocks/storage/LaunchPad.java index 3db91d7ea3..cc7dc31b33 100644 --- a/core/src/mindustry/world/blocks/storage/LaunchPad.java +++ b/core/src/mindustry/world/blocks/storage/LaunchPad.java @@ -72,7 +72,7 @@ public class LaunchPad extends Block{ Draw.reset(); } - float cooldown = Mathf.clamp(timer.getTime(timerLaunch) / 60f); + float cooldown = Mathf.clamp(timer.getTime(timerLaunch) / 90f); Draw.mixcol(lightColor, 1f - cooldown); @@ -109,48 +109,71 @@ public class LaunchPad extends Block{ @Import float x,y; Array stacks = new Array<>(); + transient Interval in = new Interval(); @Override public void draw(){ float alpha = fout(Interp.pow5Out); - float cx = x + fin(Interp.pow2In) * 15f, cy = y + fin(Interp.pow5In) * 130f; - float rotation = fin() * 120f; + float scale = (1f - alpha) * 1.3f + 1f; + float cx = cx(), cy = cy(); + float rotation = fin() * (130f + Mathf.randomSeedRange(id(), 50f)); - Draw.z(Layer.effect); + Draw.z(Layer.effect + 0.001f); Draw.color(Pal.engine); float rad = 0.2f + fslope(); - Fill.light(cx, cy, 10, 25f * rad, Tmp.c2.set(Pal.engine), Tmp.c1.set(Pal.engine).a(0f)); + Fill.light(cx, cy, 10, 25f * (rad + scale-1f), Tmp.c2.set(Pal.engine).a(alpha), Tmp.c1.set(Pal.engine).a(0f)); + Draw.alpha(alpha); for(int i = 0; i < 4; i++){ - Drawf.tri(cx, cy, 6f, 40f * rad, i * 90f + rotation); + Drawf.tri(cx, cy, 6f, 40f * (rad + scale-1f), i * 90f + rotation); } Draw.color(); Draw.z(Layer.weather - 1); - Draw.alpha(alpha); - Draw.rect("launchpod", cx, cy, rotation); + TextureRegion region = Core.atlas.find("launchpod"); + float rw = region.getWidth() * Draw.scl * scale, rh = region.getHeight() * Draw.scl * scale; - Tmp.v1.trns(225f, fin(Interp.linear) * 250f); + Draw.alpha(alpha); + Draw.rect(region, cx, cy, rw, rh, rotation); + + Tmp.v1.trns(225f, fin(Interp.pow3In) * 250f); Draw.z(Layer.flyingUnit + 1); Draw.color(0, 0, 0, 0.22f * alpha); - Draw.rect("launchpod", cx + Tmp.v1.x, cy + Tmp.v1.y, rotation); + Draw.rect(region, cx + Tmp.v1.x, cy + Tmp.v1.y, rw, rh, rotation); Draw.reset(); } + float cx(){ + return x + fin(Interp.pow2In) * (12f + Mathf.randomSeedRange(id() + 3, 4f)); + } + + float cy(){ + return y + fin(Interp.pow5In) * (100f + Mathf.randomSeedRange(id() + 2, 30f)); + } + + @Override + public void update(){ + float r = 3f; + if(in.get(4f - fin()*2f)){ + Fx.rocketSmoke.at(cx() + Mathf.range(r), cy() + Mathf.range(r), fin()); + } + } + @Override public void remove(){ + + //actually launch the items upon removal if(team() == Vars.state.rules.defaultTeam){ for(ItemStack stack : stacks){ Vars.data.addItem(stack.item, stack.amount); Events.fire(new LaunchItemEvent(stack)); - Vars.state.stats.handleItemExport(stack); } } }