diff --git a/annotations/src/main/resources/revisions/Bullet/2.json b/annotations/src/main/resources/revisions/Bullet/2.json new file mode 100644 index 0000000000..04b1c437de --- /dev/null +++ b/annotations/src/main/resources/revisions/Bullet/2.json @@ -0,0 +1 @@ +{version:2,fields:[{name:collided,type:arc.struct.IntSeq,size:-1},{name:damage,type:float,size:4},{name:data,type:java.lang.Object,size:-1},{name:lifetime,type:float,size:4},{name:owner,type:Entityc,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:type,type:mindustry.entities.bullet.BulletType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/Bullet/3.json b/annotations/src/main/resources/revisions/Bullet/3.json new file mode 100644 index 0000000000..30b9ec33cc --- /dev/null +++ b/annotations/src/main/resources/revisions/Bullet/3.json @@ -0,0 +1 @@ +{version:3,fields:[{name:collided,type:arc.struct.IntSeq,size:-1},{name:damage,type:float,size:4},{name:data,type:java.lang.Object,size:-1},{name:lifetime,type:float,size:4},{name:owner,type:mindustry.gen.Entityc,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:type,type:mindustry.entities.bullet.BulletType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/EffectState/2.json b/annotations/src/main/resources/revisions/EffectState/2.json new file mode 100644 index 0000000000..fb05e5f923 --- /dev/null +++ b/annotations/src/main/resources/revisions/EffectState/2.json @@ -0,0 +1 @@ +{version:2,fields:[{name:color,type:arc.graphics.Color,size:-1},{name:data,type:java.lang.Object,size:-1},{name:effect,type:mindustry.entities.Effect,size:-1},{name:lifetime,type:float,size:4},{name:offsetX,type:float,size:4},{name:offsetY,type:float,size:4},{name:parent,type:Posc,size:-1},{name:rotation,type:float,size:4},{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/annotations/src/main/resources/revisions/EffectState/3.json b/annotations/src/main/resources/revisions/EffectState/3.json new file mode 100644 index 0000000000..41991e73c4 --- /dev/null +++ b/annotations/src/main/resources/revisions/EffectState/3.json @@ -0,0 +1 @@ +{version:3,fields:[{name:color,type:arc.graphics.Color,size:-1},{name:data,type:java.lang.Object,size:-1},{name:effect,type:mindustry.entities.Effect,size:-1},{name:lifetime,type:float,size:4},{name:offsetX,type:float,size:4},{name:offsetY,type:float,size:4},{name:parent,type:mindustry.gen.Posc,size:-1},{name:rotation,type:float,size:4},{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/assets-raw/sprites/units/draug-cell.png b/core/assets-raw/sprites/units/draug-cell.png deleted file mode 100644 index 439d7b8ea3..0000000000 Binary files a/core/assets-raw/sprites/units/draug-cell.png and /dev/null differ diff --git a/core/assets-raw/sprites/units/draug.png b/core/assets-raw/sprites/units/draug.png deleted file mode 100644 index 004e23ea96..0000000000 Binary files a/core/assets-raw/sprites/units/draug.png and /dev/null differ diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index c10d04a89a..fce8973805 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -78,8 +78,8 @@ public class Vars implements Loadable{ public static final float miningRange = 70f; /** range for building */ public static final float buildingRange = 220f; - /** duration of time between events in ticks */ - public static final float eventRate = 5 * Time.toMinutes; + /** duration of time between turns in ticks */ + public static final float turnDuration = 20 * Time.toMinutes; /** min armor fraction damage; e.g. 0.05 = at least 5% damage */ public static final float minArmorDamage = 0.05f; /** launch animation duration */ @@ -287,7 +287,7 @@ public class Vars implements Loadable{ logBuffer.add(result); }else if(!headless){ if(!OS.isWindows){ - for(String code : ColorCodes.codes.values()){ + for(String code : ColorCodes.values){ result = result.replace(code, ""); } } diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 2620658090..7a0406b157 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -534,7 +534,7 @@ public class UnitTypes implements ContentList{ isCounted = false; flying = true; - mineSpeed = 2f; + mineSpeed = 12f; mineTier = 1; buildSpeed = 0.5f; drag = 0.05f; diff --git a/core/src/mindustry/core/Control.java b/core/src/mindustry/core/Control.java index dc34511ddd..3a2ddf5eb5 100644 --- a/core/src/mindustry/core/Control.java +++ b/core/src/mindustry/core/Control.java @@ -507,7 +507,7 @@ public class Control implements ApplicationListener, Loadable{ platform.updateRPC(); } - if(Core.input.keyTap(Binding.pause) && !scene.hasDialog() && !scene.hasKeyboard() && !ui.restart.isShown() && (state.is(State.paused) || state.is(State.playing))){ + if(Core.input.keyTap(Binding.pause) && !state.isOutOfTime() && !scene.hasDialog() && !scene.hasKeyboard() && !ui.restart.isShown() && (state.is(State.paused) || state.is(State.playing))){ state.set(state.is(State.playing) ? State.paused : State.playing); } diff --git a/core/src/mindustry/core/GameState.java b/core/src/mindustry/core/GameState.java index 88cb0b6dac..b006aae76f 100644 --- a/core/src/mindustry/core/GameState.java +++ b/core/src/mindustry/core/GameState.java @@ -47,6 +47,11 @@ public class GameState{ return rules.sector != null; } + /** @return whether the player is in a campaign and they are out of sector time */ + public boolean isOutOfTime(){ + return isCampaign() && isGame() && getSector().getTimeSpent() >= turnDuration; + } + public boolean hasSector(){ return rules.sector != null; } diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index 7aad8957a7..aaf19238f4 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -120,7 +120,7 @@ public class Logic implements ApplicationListener{ } } - state.rules.sector.setLastSecond(universe.seconds()); + state.rules.sector.setSecondsPassed(0); } //enable infinite ammo for wave team by default @@ -281,7 +281,7 @@ public class Logic implements ApplicationListener{ sector.save.save(); //run a turn, since launching takes up a turn - universe.runEvents(); + universe.runTurn(); //TODO apply extra damage to sector //sector.setTurnsPassed(sector.getTurnsPassed() + 3); @@ -316,6 +316,11 @@ public class Logic implements ApplicationListener{ state.enemies = Groups.unit.count(u -> u.team() == state.rules.waveTeam && u.type().isCounted); } + //force pausing when the player is out of sector time + if(state.isOutOfTime()){ + state.set(State.paused); + } + if(!state.isPaused()){ state.secinfo.update(); diff --git a/core/src/mindustry/core/UI.java b/core/src/mindustry/core/UI.java index 904dcb9b80..63078827e9 100644 --- a/core/src/mindustry/core/UI.java +++ b/core/src/mindustry/core/UI.java @@ -467,6 +467,15 @@ public class UI implements ApplicationListener, Loadable{ dialog.show(); } + public void announce(String text){ + Table t = new Table(); + t.background(Styles.black3).margin(8f) + .add(text).style(Styles.outlineLabel); + t.update(() -> t.setPosition(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f, Align.center)); + t.actions(Actions.fadeOut(3, Interp.pow4In)); + Core.scene.add(t); + } + public void showOkText(String title, String text, Runnable confirmed){ BaseDialog dialog = new BaseDialog(title); dialog.cont.add(text).width(500f).wrap().pad(4f).get().setAlignment(Align.center, Align.center); diff --git a/core/src/mindustry/game/EventType.java b/core/src/mindustry/game/EventType.java index ea93772b6d..256fdae6c3 100644 --- a/core/src/mindustry/game/EventType.java +++ b/core/src/mindustry/game/EventType.java @@ -45,6 +45,7 @@ public class EventType{ public static class PlayEvent{} public static class ResetEvent{} public static class WaveEvent{} + public static class TurnEvent{} /** Called when the player places a line, mobile or desktop.*/ public static class LineConfirmEvent{} /** Called when a turret recieves ammo, but only when the tutorial is active! */ diff --git a/core/src/mindustry/game/SectorInfo.java b/core/src/mindustry/game/SectorInfo.java index 3e1713d0ef..b9d7502e6a 100644 --- a/core/src/mindustry/game/SectorInfo.java +++ b/core/src/mindustry/game/SectorInfo.java @@ -17,6 +17,7 @@ public class SectorInfo{ private static final int valueWindow = 60; /** refresh period of export in ticks */ private static final float refreshPeriod = 60; + /** Core input statistics. */ public ObjectMap production = new ObjectMap<>(); /** Export statistics. */ @@ -31,6 +32,8 @@ public class SectorInfo{ public boolean hasCore = true; /** Sector that was launched from. */ public @Nullable Sector origin; + /** Time spent at this sector. Do not use unless you know what you're doing. */ + public transient float internalTimeSpent; /** Counter refresh state. */ private transient Interval time = new Interval(); @@ -73,11 +76,21 @@ public class SectorInfo{ hasCore = entity != null; bestCoreType = !hasCore ? Blocks.air : state.rules.defaultTeam.cores().max(e -> e.block.size).block; storageCapacity = entity != null ? entity.storageCapacity : 0; + + //update sector's internal time spent counter1 + state.rules.sector.setTimeSpent(internalTimeSpent); } /** Update averages of various stats. * Called every frame. */ public void update(){ + internalTimeSpent += Time.delta(); + + //time spent exceeds turn duration! + if(internalTimeSpent >= turnDuration && internalTimeSpent - Time.delta() < turnDuration){ + universe.displayTimeEnd(); + } + //create last stored core items if(lastCoreItems == null){ lastCoreItems = new int[content.items().size]; diff --git a/core/src/mindustry/game/Universe.java b/core/src/mindustry/game/Universe.java index fcd862d9a1..b068b1337b 100644 --- a/core/src/mindustry/game/Universe.java +++ b/core/src/mindustry/game/Universe.java @@ -5,6 +5,8 @@ import arc.math.*; import arc.struct.*; import arc.util.*; import mindustry.content.*; +import mindustry.core.GameState.*; +import mindustry.game.EventType.*; import mindustry.type.*; import mindustry.world.blocks.storage.*; @@ -14,8 +16,7 @@ import static mindustry.Vars.*; public class Universe{ private long seconds; private float secondCounter; - private int event; - private float eventCounter; + private int turn; private Schematic lastLoadout; private Seq lastLaunchResources = new Seq<>(); @@ -30,6 +31,10 @@ public class Universe{ updatePlanet(Planets.sun); } + public int turn(){ + return turn; + } + private void updatePlanet(Planet planet){ planet.position.setZero(); planet.addParentOffset(planet.position); @@ -41,6 +46,14 @@ public class Universe{ } } + public void displayTimeEnd(){ + if(!headless){ + state.set(State.paused); + + ui.announce("Next turn incoming."); + } + } + /** Update planet rotations, global time and relevant state. */ public void update(){ secondCounter += Time.delta() / 60f; @@ -55,13 +68,6 @@ public class Universe{ } } - //update event counter - happens only in-game - eventCounter += Time.delta(); - - if(eventCounter >= eventRate){ - runEvents(); - } - if(state.hasSector()){ //update sector light float light = state.getSector().getLight(); @@ -110,11 +116,31 @@ public class Universe{ } /** Runs possible events. Resets event counter. */ - public void runEvents(){ - event++; - eventCounter = 0; + public void runTurn(){ + turn++; + int newSecondsPassed = (int)(turnDuration / 60); + + //update relevant sectors + for(Planet planet : content.planets()){ + for(Sector sector : planet.sectors){ + if(sector.hasSave()){ + int spent = (int)(sector.getTimeSpent() / 60); + int actuallyPassed = Math.max(newSecondsPassed - spent, 0); + + //increment seconds passed for this sector by the time that just passed with this turn + if(!sector.isBeingPlayed()){ + sector.setSecondsPassed(sector.getSecondsPassed() + actuallyPassed); + } + + //reset time spent to 0 + sector.setTimeSpent(0f); + } + } + } //TODO events + + Events.fire(new TurnEvent()); } public float secondsMod(float mod, float scale){ @@ -131,14 +157,12 @@ public class Universe{ private void save(){ Core.settings.put("utime", seconds); - Core.settings.put("event", event); - Core.settings.put("eventtime", eventCounter); + Core.settings.put("turn", turn); } private void load(){ seconds = Core.settings.getLong("utime"); - event = Core.settings.getInt("event"); - eventCounter = Core.settings.getFloat("eventtime"); + turn = Core.settings.getInt("turn"); } } diff --git a/core/src/mindustry/io/SaveVersion.java b/core/src/mindustry/io/SaveVersion.java index 6b88bacb1b..9ee9081754 100644 --- a/core/src/mindustry/io/SaveVersion.java +++ b/core/src/mindustry/io/SaveVersion.java @@ -113,6 +113,11 @@ public abstract class SaveVersion extends SaveFileReader{ if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get(); lastReadBuild = map.getInt("build", -1); + //load time spent on sector into state + if(state.rules.sector != null){ + state.secinfo.internalTimeSpent = state.rules.sector.getStoredTimeSpent(); + } + if(!headless){ Tmp.v1.tryFromString(map.get("viewpos")); Core.camera.position.set(Tmp.v1); diff --git a/core/src/mindustry/type/Sector.java b/core/src/mindustry/type/Sector.java index 9598496b1f..0a5004b2f5 100644 --- a/core/src/mindustry/type/Sector.java +++ b/core/src/mindustry/type/Sector.java @@ -131,12 +131,42 @@ public class Sector{ return Core.settings.getInt(key("spawn-position"), Point2.pack(world.width() / 2, world.height() / 2)); } - public void setLastSecond(long number){ - put("last-second", number); + /** @return time spent in this sector this turn in ticks. */ + public float getTimeSpent(){ + //return currently counting time spent if being played on + if(isBeingPlayed()) return state.secinfo.internalTimeSpent; + + //else return the stored value + return getStoredTimeSpent(); + } + + public void setTimeSpent(float time){ + put("time-spent", time); + + //update counting time + if(isBeingPlayed()){ + state.secinfo.internalTimeSpent = time; + } + } + + public String displayTimeRemaining(){ + float amount = Vars.turnDuration - getTimeSpent(); + int seconds = (int)(amount / 60); + int sf = seconds % 60; + return (seconds / 60) + ":" + (sf < 10 ? "0" : "") + sf; + } + + /** @return the stored amount of time spent in this sector this turn in ticks. */ + public float getStoredTimeSpent(){ + return Core.settings.getFloat(key("time-spent")); + } + + public void setSecondsPassed(long number){ + put("seconds-passed", number); } public long getSecondsPassed(){ - return universe.seconds() - Core.settings.getLong(key("last-second")); + return Core.settings.getLong(key("seconds-passed")); } private String key(String key){ diff --git a/core/src/mindustry/ui/Styles.java b/core/src/mindustry/ui/Styles.java index d4838ae382..52bb6d29d0 100644 --- a/core/src/mindustry/ui/Styles.java +++ b/core/src/mindustry/ui/Styles.java @@ -23,7 +23,7 @@ import static mindustry.gen.Tex.*; @StyleDefaults public class Styles{ - public static Drawable black, black9, black8, black6, black3, none, flatDown, flatOver; + public static Drawable black, black9, black8, black6, black3, black5, none, flatDown, flatOver; public static ButtonStyle defaultb, waveb; public static TextButtonStyle defaultt, squaret, nodet, cleart, discordt, infot, clearPartialt, clearTogglet, clearToggleMenut, togglet, transt; public static ImageButtonStyle defaulti, nodei, righti, emptyi, emptytogglei, selecti, cleari, clearFulli, clearPartiali, clearPartial2i, clearTogglei, clearTransi, clearToggleTransi, clearTogglePartiali; @@ -40,6 +40,7 @@ public class Styles{ black9 = whiteui.tint(0f, 0f, 0f, 0.9f); black8 = whiteui.tint(0f, 0f, 0f, 0.8f); black6 = whiteui.tint(0f, 0f, 0f, 0.6f); + black5 = whiteui.tint(0f, 0f, 0f, 0.5f); black3 = whiteui.tint(0f, 0f, 0f, 0.3f); none = whiteui.tint(0f, 0f, 0f, 0f); flatDown = createFlatDown(); diff --git a/core/src/mindustry/ui/dialogs/PausedDialog.java b/core/src/mindustry/ui/dialogs/PausedDialog.java index 11bfa98e5a..ce8b3e9d8d 100644 --- a/core/src/mindustry/ui/dialogs/PausedDialog.java +++ b/core/src/mindustry/ui/dialogs/PausedDialog.java @@ -34,6 +34,12 @@ public class PausedDialog extends BaseDialog{ }); if(!mobile){ + //TODO localize + cont.label(() -> state.getSector() == null ? "" : + "[lightgray]Next turn in [accent]" + state.getSector().displayTimeRemaining()) + .visible(() -> state.getSector() != null).colspan(2); + cont.row(); + float dw = 220f; cont.defaults().width(dw).height(55).pad(5f); @@ -45,7 +51,10 @@ public class PausedDialog extends BaseDialog{ //}else{ // cont.button("$database", Icon.book, ui.database::show); //} - cont.button("placeholder", Icon.warning, () -> ui.showInfo("go away")); + //TODO remove + cont.button("launch core", Icon.warning, () -> ui.planet.show(state.getSector(), player.team().core())) + .disabled(b -> player.team().core() == null || !player.team().core().items.has(player.team().core().block.requirements)) + .visible(() -> state.isCampaign()); cont.button("$settings", Icon.settings, ui.settings::show); if(!state.rules.tutorial){ diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 544ee06e40..72dd3200bf 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -7,6 +7,7 @@ import arc.graphics.gl.*; import arc.input.*; import arc.math.*; import arc.math.geom.*; +import arc.scene.*; import arc.scene.event.*; import arc.scene.ui.*; import arc.scene.ui.layout.*; @@ -197,19 +198,36 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ cont.clear(); titleTable.remove(); - //add listener to the background rect, so it doesn't get unnecessary touch input - cont.rect((x, y, w, h) -> planets.render(this)).grow().get().addListener(new ElementGestureListener(){ - @Override - public void tap(InputEvent event, float x, float y, int count, KeyCode button){ - if(hovered != null && (mode == launch ? canLaunch(hovered) && hovered != launchSector : hovered.unlocked())){ - selected = hovered; - } - if(selected != null){ - updateSelected(); - } + cont.stack( + new Element(){ + { + //add listener to the background rect, so it doesn't get unnecessary touch input + addListener(new ElementGestureListener(){ + @Override + public void tap(InputEvent event, float x, float y, int count, KeyCode button){ + if(hovered != null && (mode == launch ? canLaunch(hovered) && hovered != launchSector : hovered.unlocked())){ + selected = hovered; + } + + if(selected != null){ + updateSelected(); + } + } + }); } - }); + + @Override + public void draw(){ + planets.render(PlanetDialog.this); + } + }, + new Table(t -> { + //TODO localize + t.top(); + t.label(() -> "Turn " + universe.turn()).style(Styles.outlineLabel).color(Pal.accent); + })).grow(); + } @Override diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index 349aa44bc3..fc43390be6 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -47,6 +47,7 @@ public class HudFragment extends Fragment{ //TODO details and stuff Events.on(SectorCaptureEvent.class, e ->{ + //TODO localize showToast("Sector[accent] captured[]!"); }); @@ -55,6 +56,16 @@ public class HudFragment extends Fragment{ coreItems.resetUsed(); }); + Events.on(TurnEvent.class, e -> { + ui.announce("[accent][[ Turn " + universe.turn() + " ]"); + }); + + //paused table + parent.fill(t -> { + t.top().visible(() -> state.isPaused() && !state.isOutOfTime()).touchable(Touchable.disabled); + t.table(Styles.black5, top -> top.add("$paused").style(Styles.outlineLabel).pad(8f)).growX(); + }); + //TODO tear this all down //menu at top left parent.fill(cont -> { @@ -254,6 +265,27 @@ public class HudFragment extends Fragment{ .update(label -> label.getColor().set(Color.orange).lerp(Color.scarlet, Mathf.absin(Time.time(), 2f, 1f)))).touchable(Touchable.disabled); }); + //paused table for when the player is out of time + parent.fill(t -> { + t.top().visible(() -> state.isOutOfTime()); + t.table(Styles.black5, top -> { + //TODO localize when done + top.add("Out of sector time.").style(Styles.outlineLabel).color(Pal.accent).update(l -> l.color.a = Mathf.absin(Time.globalTime(), 7f, 1f)).colspan(2); + top.row(); + + top.defaults().pad(2).size(150f, 54f); + top.button("Next Turn", () -> { + universe.runTurn(); + state.set(State.playing); + }); + + top.button("Back to Planet", () -> { + ui.paused.runExitSave(); + ui.planet.show(); + }); + }).margin(8).growX(); + }); + //tutorial text parent.fill(t -> { Runnable resize = () -> { @@ -276,12 +308,6 @@ public class HudFragment extends Fragment{ Events.on(ResizeEvent.class, e -> resize.run()); }); - //paused table - parent.fill(t -> { - t.top().visible(() -> state.isPaused()).touchable(Touchable.disabled); - t.table(Tex.buttonTrans, top -> top.add("$paused").pad(5f)); - }); - //'saving' indicator parent.fill(t -> { t.bottom().visible(() -> control.saves.isSaving()); @@ -335,14 +361,6 @@ public class HudFragment extends Fragment{ }).visible(() -> state.isCampaign() && content.items().contains(i -> state.secinfo.getExport(i) > 0)); }); - //TODO move, select loadout, consume resources - parent.fill(t -> { - t.bottom().visible(() -> state.isCampaign() && player.team().core() != null); - - t.button("test launch", Icon.warning, () -> ui.planet.show(state.getSector(), player.team().core())) - .width(150f).disabled(b -> player.team().core() == null || !player.team().core().items.has(player.team().core().block.requirements)); //disable core when missing resources for launch - }); - blockfrag.build(parent); } diff --git a/gradle.properties b/gradle.properties index 143e1905b4..34f08723e2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=52dc318f51cfca6d9a0f3097760c0fb615977330 +archash=6ed1b560047eaaf9ae83176526de51e5a49cadc3