From 2c4379acd1334da5aef0928f48c1c875313f2ff1 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 9 Aug 2019 12:18:08 -0400 Subject: [PATCH] Tutorial improvements --- core/assets/bundles/bundle.properties | 32 ++++++------ core/assets/sprites/uiskin.json | 4 +- core/src/io/anuke/mindustry/core/Control.java | 5 +- core/src/io/anuke/mindustry/game/Saves.java | 2 +- .../src/io/anuke/mindustry/game/Tutorial.java | 50 ++++++++++++++++--- .../mindustry/ui/dialogs/PausedDialog.java | 2 +- .../ui/dialogs/SettingsMenuDialog.java | 11 ++++ .../mindustry/ui/fragments/HudFragment.java | 14 ++++-- .../world/blocks/production/Fracker.java | 14 +++--- 9 files changed, 96 insertions(+), 38 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index ebf69f31b7..e5de75be4a 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -158,7 +158,7 @@ openlink = Open Link copylink = Copy Link back = Back quit.confirm = Are you sure you want to quit? -quit.confirm.tutorial = Are you sure you want to quit the tutorial? +quit.confirm.tutorial = Are you sure you know what you're doing?\nThe tutorial can be re-taken in[accent] Settings->Game->Re-Take Tutorial.[] loading = [accent]Loading... saving = [accent]Saving... wave = [accent]Wave {0} @@ -310,6 +310,7 @@ ping = Ping: {0}ms language.restart = Please restart your game for the language settings to take effect. settings = Settings tutorial = Tutorial +tutorial.retake = Re-Take Tutorial editor = Editor mapeditor = Map Editor donate = Donate @@ -862,20 +863,23 @@ unit.chaos-array.name = Chaos Array unit.eradicator.name = Eradicator unit.lich.name = Lich unit.reaper.name = Reaper -tutorial.intro = You have entered the[scarlet] Mindustry Tutorial.[]\n\nBegin by[accent] mining copper[]. Tap a copper ore vein near your core to do this.\n\n[accent]{0}/{1} copper -tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\n\nClick the drill tab in the bottom right, then select the[accent] mechanical drill[]. Place it on a copper vein by clicking.\n[accent]Right-click[] to stop building. -tutorial.drill.mobile = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\n\nTap the drill tab in the bottom right, then select the[accent] mechanical drill[]. Place it on a copper vein by tapping, then press the[accent] checkmark[] below to confirm your selection.\nPress the[accent] X button[] to cancel placement. -tutorial.blockinfo = Each block has different stats. Each drill can only mine certain ores.\nTo check a block's info and stats,[accent] tap the "?" button while selecting it in the build menu.[]\n\n[yellow]Access the Mechanical Drill's stats now.[] -tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core.\n[yellow]Hold down the mouse to place in a line.[]\nHold[accent] CTRL[] while selecting a line to place diagonally.\n\n[accent]{0}/{1} conveyors\n[accent]0/1 items delivered -tutorial.conveyor.mobile = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core.[yellow] Place in a line by holding down your finger for a few seconds[] and dragging in a direction.\n\n[accent]{0}/{1} conveyors\n[accent]0/1 items delivered +tutorial.next = [lightgray] +tutorial.intro = You have entered the[scarlet] Mindustry Tutorial.[]\nBegin by[accent] mining copper[]. Tap a copper ore vein near your core to do this.\n\n[accent]{0}/{1} copper +tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nClick the drill tab in the bottom right.\nSelect the[accent] mechanical drill[]. Place it on a copper vein by clicking.\n[accent]Right-click[] to stop building. +tutorial.drill.mobile = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nTap the drill tab in the bottom right.\nSelect the[accent] mechanical drill[].\nPlace it on a copper vein by tapping, then press the[accent] checkmark[] below to confirm your selection.\nPress the[accent] X button[] to cancel placement. +tutorial.blockinfo = Each block has different stats. Each drill can only mine certain ores.\nTo check a block's info and stats,[accent] tap the "?" button while selecting it in the build menu.[]\n\n[accent]Access the Mechanical Drill's stats now.[] +tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core.\n[accent]Hold down the mouse to place in a line.[]\nHold[accent] CTRL[] while selecting a line to place diagonally.\n\n[accent]{0}/{1} conveyors\n[accent]0/1 items delivered +tutorial.conveyor.mobile = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core.\n[accent] Place in a line by holding down your finger for a few seconds[] and dragging in a direction.\n\n[accent]{0}/{1} conveyors\n[accent]0/1 items delivered tutorial.turret = Defensive structures must be built to repel the[lightgray] enemy[].\nBuild a[accent] duo turret[] near your base. -tutorial.drillturret = Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill near to the turret. Lead conveyors into the turret to supply it with copper.\n\n[accent]Ammo delivered: 0/1 -tutorial.pause = During battle, you are able to[accent] pause the game.[]\nYou may queue buildings while paused.\n\n[accent]Press space to pause and unpause. -tutorial.pause.mobile = During battle, you are able to[accent] pause the game.[]\nYou may queue buildings while paused.\n\n[accent]Press this button in the top left to pause and unpause. -tutorial.breaking = Blocks frequently need to be destroyed.\n[accent]Hold down right-click[] to destroy all blocks in a selection.[]\n\n[yellow]Destroy all the scrap blocks to the right of your core. -tutorial.breaking.mobile = Blocks frequently need to be destroyed.\n[accent]Select deconstruction mode[], then tap a block to begin breaking it.\nDestroy an area by holding down your finger for a few seconds[] and dragging in a direction.\nPress the checkmark button to confirm breaking.\n\n[yellow]Destroy all the scrap blocks to the right of your core. -tutorial.withdraw = In some situations, taking items directly from blocks is necessary.\nTo do this, [accent]tap a block[] with items in it, then [accent]tap the item[] in the inventory. Multiple items can be withdrawn by [accent]tapping and holding[].\n\n[yellow]Withdraw some copper from the core.[] -tutorial.deposit = Deposit items into blocks by dragging from your ship to the destination block.\n\n[yellow]Deposit your copper back into the core.[] +tutorial.drillturret = Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill near to the turret\n Lead conveyors into the turret to supply it with copper.\n\n[accent]Ammo delivered: 0/1 +tutorial.pause = During battle, you are able to[accent] pause the game.[]\nYou may queue buildings while paused.\n\n[accent]Press space to pause. +tutorial.pause.mobile = During battle, you are able to[accent] pause the game.[]\nYou may queue buildings while paused.\n\n[accent]Press this button in the top left to pause. +tutorial.unpause = Now press space again to unpause. +tutorial.unpause.mobile = Now press it again to unpause. +tutorial.breaking = Blocks frequently need to be destroyed.\n[accent]Hold down right-click[] to destroy all blocks in a selection.[]\n\n[accent]Destroy all the scrap blocks to the right of your core. +tutorial.breaking.mobile = Blocks frequently need to be destroyed.\n[accent]Select deconstruction mode[], then tap a block to begin breaking it.\nDestroy an area by holding down your finger for a few seconds[] and dragging in a direction.\nPress the checkmark button to confirm breaking.\n\n[accent]Destroy all the scrap blocks to the right of your core. +tutorial.withdraw = In some situations, taking items directly from blocks is necessary.\nTo do this, [accent]tap a block[] with items in it, then [accent]tap the item[] in the inventory. Multiple items can be withdrawn by [accent]tapping and holding[].\n\n[accent]Withdraw some copper from the core.[] +tutorial.deposit = Deposit items into blocks by dragging from your ship to the destination block.\n\n[accent]Deposit your copper back into the core.[] tutorial.waves = The[lightgray] enemy[] approaches.\n\nDefend the core for 2 waves.[accent] Click[] to shoot.\nBuild more turrets and drills. Mine more copper. tutorial.waves.mobile = The[lightgray] enemy[] approaches.\n\nDefend the core for 2 waves. Your ship will automatically fire at enemies.\nBuild more turrets and drills. Mine more copper. tutorial.launch = Once you reach a specific wave, you are able to[accent] launch the core[], leaving your defenses behind and[accent] obtaining all the resources in your core.[]\nThese resources can then be used to research new technology.\n\n[accent]Press the launch button. diff --git a/core/assets/sprites/uiskin.json b/core/assets/sprites/uiskin.json index ced18f0594..30e049a889 100644 --- a/core/assets/sprites/uiskin.json +++ b/core/assets/sprites/uiskin.json @@ -52,7 +52,9 @@ ButtonStyle: { default: { down: button-down, - up: button + up: button, + over: button-over, + disabled: button-disabled }, square: { over: button-square-over, diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index d6d3dab468..6a7582a9b1 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -228,7 +228,8 @@ public class Control implements ApplicationListener{ }); } - public void playTutorial(Zone zone){ + public void playTutorial(){ + Zone zone = Zones.groundZero; ui.loadAnd(() -> { logic.reset(); Net.reset(); @@ -313,7 +314,7 @@ public class Control implements ApplicationListener{ //play tutorial on stop if(!settings.getBool("tutorial", false)){ - Core.app.post(() -> playTutorial(Zones.groundZero)); + Core.app.post(this::playTutorial); } //display UI scale changed dialog diff --git a/core/src/io/anuke/mindustry/game/Saves.java b/core/src/io/anuke/mindustry/game/Saves.java index a307a315ec..86873d0a3b 100644 --- a/core/src/io/anuke/mindustry/game/Saves.java +++ b/core/src/io/anuke/mindustry/game/Saves.java @@ -72,7 +72,7 @@ public class Saves{ lastTimestamp = Time.millis(); } - if(!state.is(State.menu) && !state.gameOver && current != null && current.isAutosave()){ + if(!state.is(State.menu) && !state.gameOver && current != null && current.isAutosave() && !state.rules.tutorial){ time += Time.delta(); if(time > Core.settings.getInt("saveinterval") * 60){ saving = true; diff --git a/core/src/io/anuke/mindustry/game/Tutorial.java b/core/src/io/anuke/mindustry/game/Tutorial.java index b91e86a971..41229723e8 100644 --- a/core/src/io/anuke/mindustry/game/Tutorial.java +++ b/core/src/io/anuke/mindustry/game/Tutorial.java @@ -24,6 +24,7 @@ public class Tutorial{ private ObjectSet events = new ObjectSet<>(); private ObjectIntMap blocksPlaced = new ObjectIntMap<>(); + private int sentence; public TutorialStage stage = TutorialStage.values()[0]; public Tutorial(){ @@ -43,7 +44,7 @@ public class Tutorial{ /** update tutorial state, transition if needed */ public void update(){ - if(stage.done.get()){ + if(stage.done.get() && !canNext()){ next(); }else{ stage.update(); @@ -52,15 +53,18 @@ public class Tutorial{ /** draw UI overlay */ public void draw(){ - stage.draw(); + if(!Core.scene.hasDialog()){ + stage.draw(); + } } /** Resets tutorial state. */ public void reset(){ - stage = TutorialStage.values()[4]; + stage = TutorialStage.values()[0]; stage.begin(); blocksPlaced.clear(); events.clear(); + sentence = 0; } /** Goes on to the next tutorial step. */ @@ -69,11 +73,32 @@ public class Tutorial{ stage.begin(); blocksPlaced.clear(); events.clear(); + sentence = 0; + } + + public boolean canNext(){ + return sentence + 1 < stage.sentences.size; + } + + public void nextSentence(){ + if(canNext()){ + sentence ++; + } + } + + public boolean canPrev(){ + return sentence > 0; + } + + public void prevSentence(){ + if(canPrev()){ + sentence --; + } } public enum TutorialStage{ intro( - line -> Core.bundle.format(line, item(Items.copper), mineCopper), + line -> Strings.format(line, item(Items.copper), mineCopper), () -> item(Items.copper) >= mineCopper ), drill(() -> placed(Blocks.mechanicalDrill, 1)){ @@ -90,7 +115,7 @@ public class Tutorial{ } }, conveyor( - line -> Core.bundle.format(line, Math.min(placed(Blocks.conveyor), 2), 2), + line -> Strings.format(line, Math.min(placed(Blocks.conveyor), 2), 2), () -> placed(Blocks.conveyor, 2) && event("lineconfirm") && event("coreitem")){ void draw(){ outline("category-distribution"); @@ -111,6 +136,13 @@ public class Tutorial{ } } }, + unpause(() -> !state.isPaused()){ + void draw(){ + if(mobile){ + outline("pause"); + } + } + }, breaking(TutorialStage::blocksBroken){ void begin(){ placeBlocks(); @@ -157,21 +189,23 @@ public class Tutorial{ protected final String line = Core.bundle.has("tutorial." + name() + ".mobile") && mobile ? "tutorial." + name() + ".mobile" : "tutorial." + name(); protected final Function text; + protected final Array sentences; protected final BooleanProvider done; TutorialStage(Function text, BooleanProvider done){ this.text = text; this.done = done; + this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty()); } TutorialStage(BooleanProvider done){ - this.text = line -> Core.bundle.get(line); - this.done = done; + this(line -> line, done); } /** displayed tutorial stage text.*/ public String text(){ - return text.get(line); + String line = sentences.get(control.tutorial.sentence); + return line.contains("{") ? text.get(line) : line; } /** called every frame when this stage is active.*/ diff --git a/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java index 6a7072a58e..d1c056c3ba 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java @@ -106,7 +106,7 @@ public class PausedDialog extends FloatingDialog{ return; } - if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave()){ + if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || state.rules.tutorial){ state.set(State.menu); return; } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java index f10b12aff9..77bdd2bcbe 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -185,6 +185,17 @@ public class SettingsMenuDialog extends SettingsDialog{ } }); + game.pref(new Setting(){ + @Override + public void add(SettingsTable table){ + table.addButton("$tutorial.retake", () -> { + control.playTutorial(); + }).size(220f, 60f).pad(6).left(); + table.add(); + table.row(); + } + }); + graphics.sliderPref("uiscale", 100, 25, 400, 25, s -> { if(Core.graphics.getFrameId() > 10){ Log.info("changed"); diff --git a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java index 030bcafaef..c86bb19d30 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java @@ -13,7 +13,6 @@ import io.anuke.arc.scene.actions.*; import io.anuke.arc.scene.event.*; import io.anuke.arc.scene.style.*; import io.anuke.arc.scene.ui.*; -import io.anuke.arc.scene.ui.layout.UnitScl; import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.util.*; import io.anuke.mindustry.core.GameState.*; @@ -314,11 +313,20 @@ public class HudFragment extends Fragment{ //tutorial text parent.fill(t -> { - t.touchable(Touchable.disabled); Runnable resize = () -> { t.clearChildren(); t.top().right().visible(() -> state.rules.tutorial); - t.table("button-trans", f -> f.labelWrap(() -> control.tutorial.stage.text()).width(!Core.graphics.isPortrait() ? 450f : 180f).pad(3f)); + t.stack(new Button("default"){{ + marginLeft(48f); + labelWrap(() -> control.tutorial.stage.text() + (control.tutorial.canNext() ? "\n\n" + Core.bundle.get("tutorial.next") : "")).width(!Core.graphics.isPortrait() ? 400f : 160f).pad(2f); + clicked(() -> control.tutorial.nextSentence()); + setDisabled(() -> !control.tutorial.canNext()); + }}, + new Table(f -> { + f.left().addImageButton("icon-arrow-left-small", "empty", iconsizesmall, () -> { + control.tutorial.prevSentence(); + }).width(44f).growY().visible(() -> control.tutorial.canPrev()); + })); }; resize.run(); diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Fracker.java b/core/src/io/anuke/mindustry/world/blocks/production/Fracker.java index 6f0051c999..20804daa28 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Fracker.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Fracker.java @@ -1,12 +1,10 @@ package io.anuke.mindustry.world.blocks.production; -import io.anuke.arc.Core; -import io.anuke.arc.graphics.g2d.Draw; -import io.anuke.arc.graphics.g2d.TextureRegion; -import io.anuke.mindustry.entities.type.TileEntity; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.meta.BlockStat; -import io.anuke.mindustry.world.meta.StatUnit; +import io.anuke.arc.*; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.mindustry.entities.type.*; +import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.meta.*; public class Fracker extends SolidPump{ protected final float itemUseTime = 100f; @@ -41,7 +39,7 @@ public class Fracker extends SolidPump{ @Override public boolean canProduce(Tile tile){ - return tile.entity.liquids.get(result) < liquidCapacity; + return tile.entity.liquids.get(result) < liquidCapacity - 0.01f; } @Override