From 2d6dccc6d95b7e1503c43f649e7a43a35046dbca Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Sun, 19 Feb 2023 15:49:46 +0000 Subject: [PATCH 01/18] feat(tutorial): update and modularise tutorial --- .../SolGameServiceRegistry.java | 4 +- .../destinationsol/game/ObjectManager.java | 13 +- .../java/org/destinationsol/game/SolGame.java | 5 + .../game/screens/ChooseMercenaryScreen.java | 20 +- .../game/screens/HireShipsScreen.java | 8 +- .../game/screens/SellItems.java | 7 +- .../game/tutorial/NewTutorialManager.java | 221 ++++++++++++++++++ .../game/tutorial/TutorialStep.java | 21 ++ .../game/tutorial/steps/ButtonPressStep.java | 48 ++++ .../game/tutorial/steps/BuyItemStep.java | 56 +++++ .../game/tutorial/steps/BuyMercenaryStep.java | 70 ++++++ .../tutorial/steps/CheckGunReloadStep.java | 52 +++++ .../tutorial/steps/CheckItemEquippedStep.java | 47 ++++ .../game/tutorial/steps/CloseScreenStep.java | 57 +++++ .../tutorial/steps/CreateWaypointStep.java | 55 +++++ .../tutorial/steps/DestroyObjectsStep.java | 77 ++++++ .../DestroySpawnedAsteroidAroundHeroStep.java | 56 +++++ .../steps/DestroySpawnedShipsStep.java | 85 +++++++ .../game/tutorial/steps/FireGunStep.java | 57 +++++ .../steps/FlyToHeroFirstWaypointStep.java | 44 ++++ .../steps/FlyToNearestStationStep.java | 62 +++++ .../FlyToPlanetSellingMercenariesStep.java | 71 ++++++ .../game/tutorial/steps/FlyToPlanetStep.java | 51 ++++ .../FlyToRandomWaypointAroundHeroStep.java | 49 ++++ .../tutorial/steps/FlyToWaypointStep.java | 63 +++++ .../steps/ManageMercenariesGuidanceStep.java | 97 ++++++++ .../game/tutorial/steps/MapDragStep.java | 43 ++++ .../game/tutorial/steps/MessageStep.java | 47 ++++ .../game/tutorial/steps/OpenScreenStep.java | 57 +++++ .../steps/SelectEquippedWeaponStep.java | 47 ++++ .../game/tutorial/steps/SlowVelocityStep.java | 47 ++++ .../tutorial/steps/ThrustForwardsStep.java | 63 +++++ .../tutorial/steps/TurnLeftRightStep.java | 56 +++++ .../game/tutorial/steps/UseAbilityStep.java | 63 +++++ .../steps/WaitUntilFullyRepairedStep.java | 45 ++++ .../wrapper/TrackedFarObjectWrapper.java | 76 ++++++ .../wrapper/TrackedSolObjectWrapper.java | 119 ++++++++++ .../ui/nui/screens/MapScreen.java | 26 ++- .../ui/nui/screens/TalkScreen.java | 21 +- .../ui/nui/screens/TutorialScreen.java | 52 ++++- .../assets/skins/tutorialScreen.skin | 13 +- .../destinationsol/assets/ui/talkScreen.ui | 4 +- .../assets/ui/tutorialScreen.ui | 24 +- 43 files changed, 2170 insertions(+), 29 deletions(-) create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/TrackedFarObjectWrapper.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/TrackedSolObjectWrapper.java diff --git a/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java b/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java index 0484ab20c..c65875601 100644 --- a/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java +++ b/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java @@ -46,6 +46,7 @@ import org.destinationsol.game.planet.PlanetManager; import org.destinationsol.game.screens.GameScreens; import org.destinationsol.game.ship.ShipBuilder; +import org.destinationsol.game.tutorial.NewTutorialManager; import org.destinationsol.ui.TutorialManager; import org.terasology.context.Lifetime; import org.terasology.gestalt.di.ServiceRegistry; @@ -54,7 +55,8 @@ public class SolGameServiceRegistry extends ServiceRegistry { public SolGameServiceRegistry(boolean isTutorial) { this.with(SolGame.class).lifetime(Lifetime.Singleton); if (isTutorial) { - this.with(TutorialManager.class).lifetime(Lifetime.Singleton); +// this.with(TutorialManager.class).lifetime(Lifetime.Singleton); + this.with(NewTutorialManager.class).lifetime(Lifetime.Singleton); } this.with(EntitySystemManager.class); diff --git a/engine/src/main/java/org/destinationsol/game/ObjectManager.java b/engine/src/main/java/org/destinationsol/game/ObjectManager.java index f524be189..df8a1082e 100644 --- a/engine/src/main/java/org/destinationsol/game/ObjectManager.java +++ b/engine/src/main/java/org/destinationsol/game/ObjectManager.java @@ -141,11 +141,14 @@ public void update(SolGame game, float timeStep) { } if (isNear(fod, camPos, timeStep)) { SolObject o = fo.toObject(game); - // Ensure that StarPorts are added straight away so that we can see if they overlap - if (o instanceof StarPort) { - addObjNow(game, o); - } else { - addObjDelayed(o); + // Rarely a far object can produce a null sol object because the instance it references no longer exists. + if (o != null) { + // Ensure that StarPorts are added straight away so that we can see if they overlap + if (o instanceof StarPort) { + addObjNow(game, o); + } else { + addObjDelayed(o); + } } removeFo(it, fo); } diff --git a/engine/src/main/java/org/destinationsol/game/SolGame.java b/engine/src/main/java/org/destinationsol/game/SolGame.java index 36ddaf03e..7c08d96ef 100644 --- a/engine/src/main/java/org/destinationsol/game/SolGame.java +++ b/engine/src/main/java/org/destinationsol/game/SolGame.java @@ -58,6 +58,7 @@ import org.destinationsol.game.ship.ShipBuilder; import org.destinationsol.game.ship.SloMo; import org.destinationsol.game.ship.hulls.HullConfig; +import org.destinationsol.game.tutorial.NewTutorialManager; import org.destinationsol.mercenary.MercenaryUtils; import org.destinationsol.modules.ModuleManager; import org.destinationsol.ui.DebugCollector; @@ -139,6 +140,8 @@ public class SolGame { @Inject protected Optional tutorialManager; @Inject + protected Optional newTutorialManager; + @Inject protected BeanContext beanContext; @Inject protected GalaxyBuilder galaxyBuilder; @@ -191,6 +194,7 @@ public void createUpdateSystems() { updateSystems = new TreeMap<>(); List defaultSystems = new ArrayList<>(Arrays.asList(planetManager, solCam, chunkManager, mountDetectDrawer, objectManager, mapDrawer, soundManager, beaconHandler, drawableDebugger)); tutorialManager.ifPresent(defaultSystems::add); + newTutorialManager.ifPresent(defaultSystems::add); updateSystems.put(0, defaultSystems); List defaultPausedSystems = new ArrayList(); @@ -265,6 +269,7 @@ public void run() { gameScreens.consoleScreen.init(this); solApplication.getNuiManager().pushScreen(gameScreens.mainGameScreen); tutorialManager.ifPresent(TutorialManager::start); + newTutorialManager.ifPresent(NewTutorialManager::start); } private void addObjectsToPlanetManager() { diff --git a/engine/src/main/java/org/destinationsol/game/screens/ChooseMercenaryScreen.java b/engine/src/main/java/org/destinationsol/game/screens/ChooseMercenaryScreen.java index ffc5b8bac..daaa5ed86 100644 --- a/engine/src/main/java/org/destinationsol/game/screens/ChooseMercenaryScreen.java +++ b/engine/src/main/java/org/destinationsol/game/screens/ChooseMercenaryScreen.java @@ -24,7 +24,7 @@ import org.destinationsol.ui.SolInputManager; import org.destinationsol.ui.nui.NUIManager; import org.destinationsol.ui.nui.screens.InventoryScreen; -import org.destinationsol.ui.nui.widgets.KeyActivatedButton; +import org.destinationsol.ui.nui.widgets.UIWarnButton; import org.terasology.nui.backends.libgdx.GDXInputUtil; import org.terasology.nui.widgets.UIButton; @@ -41,7 +41,7 @@ public ChooseMercenaryScreen() { @Override public void initialise(SolApplication solApplication, InventoryScreen inventoryScreen) { - KeyActivatedButton giveButton = new KeyActivatedButton(); + UIWarnButton giveButton = new UIWarnButton(); giveButton.setText("Give Items"); giveButton.setKey(GDXInputUtil.GDXToNuiKey(solApplication.getOptions().getKeyShoot())); giveButton.subscribe(button -> { @@ -58,7 +58,7 @@ public void initialise(SolApplication solApplication, InventoryScreen inventoryS }); actionButtons[0] = giveButton; - KeyActivatedButton takeButton = new KeyActivatedButton(); + UIWarnButton takeButton = new UIWarnButton(); takeButton.setText("Take Items"); takeButton.setKey(GDXInputUtil.GDXToNuiKey(solApplication.getOptions().getKeyShoot2())); takeButton.subscribe(button -> { @@ -75,7 +75,7 @@ public void initialise(SolApplication solApplication, InventoryScreen inventoryS }); actionButtons[1] = takeButton; - KeyActivatedButton equipButton = new KeyActivatedButton(); + UIWarnButton equipButton = new UIWarnButton(); equipButton.setText("Equip Items"); equipButton.setKey(GDXInputUtil.GDXToNuiKey(solApplication.getOptions().getKeyDrop())); equipButton.subscribe(button -> { @@ -93,6 +93,18 @@ public void initialise(SolApplication solApplication, InventoryScreen inventoryS actionButtons[2] = equipButton; } + public UIWarnButton getGiveItemsButton() { + return (UIWarnButton) actionButtons[0]; + } + + public UIWarnButton getTakeItemsButton() { + return (UIWarnButton) actionButtons[1]; + } + + public UIWarnButton getEquipItemsButton() { + return (UIWarnButton) actionButtons[2]; + } + @Override public void update(SolApplication solApplication, InventoryScreen inventoryScreen) { boolean selNull = inventoryScreen.getSelectedItem() != null; diff --git a/engine/src/main/java/org/destinationsol/game/screens/HireShipsScreen.java b/engine/src/main/java/org/destinationsol/game/screens/HireShipsScreen.java index acba08202..52e3dc2d9 100644 --- a/engine/src/main/java/org/destinationsol/game/screens/HireShipsScreen.java +++ b/engine/src/main/java/org/destinationsol/game/screens/HireShipsScreen.java @@ -24,7 +24,7 @@ import org.destinationsol.mercenary.MercenaryUtils; import org.destinationsol.ui.nui.screens.InventoryScreen; import org.destinationsol.ui.nui.screens.TalkScreen; -import org.destinationsol.ui.nui.widgets.KeyActivatedButton; +import org.destinationsol.ui.nui.widgets.UIWarnButton; import org.terasology.nui.backends.libgdx.GDXInputUtil; import org.terasology.nui.widgets.UIButton; @@ -36,7 +36,7 @@ public HireShipsScreen() { @Override public void initialise(SolApplication solApplication, InventoryScreen inventoryScreen) { - KeyActivatedButton hireButton = new KeyActivatedButton(); + UIWarnButton hireButton = new UIWarnButton(); hireButton.setKey(GDXInputUtil.GDXToNuiKey(solApplication.getOptions().getKeyHireShip())); hireButton.subscribe(button -> { SolGame game = solApplication.getGame(); @@ -66,6 +66,10 @@ public UIButton[] getActionButtons() { return actionButtons; } + public UIWarnButton getHireControl() { + return (UIWarnButton) actionButtons[0]; + } + @Override public void update(SolApplication solApplication, InventoryScreen inventoryScreen) { SolGame game = solApplication.getGame(); diff --git a/engine/src/main/java/org/destinationsol/game/screens/SellItems.java b/engine/src/main/java/org/destinationsol/game/screens/SellItems.java index 13f5333d8..398deaf02 100644 --- a/engine/src/main/java/org/destinationsol/game/screens/SellItems.java +++ b/engine/src/main/java/org/destinationsol/game/screens/SellItems.java @@ -25,6 +25,7 @@ import org.destinationsol.ui.nui.screens.InventoryScreen; import org.destinationsol.ui.nui.screens.TalkScreen; import org.destinationsol.ui.nui.widgets.KeyActivatedButton; +import org.destinationsol.ui.nui.widgets.UIWarnButton; import org.terasology.nui.backends.libgdx.GDXInputUtil; import org.terasology.nui.widgets.UIButton; @@ -42,7 +43,7 @@ public SellItems() { @Override public void initialise(SolApplication solApplication, InventoryScreen inventoryScreen) { - KeyActivatedButton sellButton = new KeyActivatedButton(); + UIWarnButton sellButton = new UIWarnButton(); sellButton.setText("Sell"); sellButton.setKey(GDXInputUtil.GDXToNuiKey(solApplication.getOptions().getKeySellItem())); sellButton.subscribe(button -> { @@ -63,6 +64,10 @@ public void initialise(SolApplication solApplication, InventoryScreen inventoryS actionButtons[0] = sellButton; } + public UIWarnButton getSellControl() { + return (UIWarnButton) actionButtons[0]; + } + @Override public ItemContainer getItems(SolGame game) { Hero hero = game.getHero(); diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java new file mode 100644 index 000000000..33b24a232 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java @@ -0,0 +1,221 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial; + +import org.destinationsol.GameOptions; +import org.destinationsol.SolApplication; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.UpdateAwareSystem; +import org.destinationsol.game.tutorial.steps.ButtonPressStep; +import org.destinationsol.game.tutorial.steps.BuyItemStep; +import org.destinationsol.game.tutorial.steps.BuyMercenaryStep; +import org.destinationsol.game.tutorial.steps.CheckGunReloadStep; +import org.destinationsol.game.tutorial.steps.CheckItemEquippedStep; +import org.destinationsol.game.tutorial.steps.CloseScreenStep; +import org.destinationsol.game.tutorial.steps.CreateWaypointStep; +import org.destinationsol.game.tutorial.steps.DestroySpawnedAsteroidAroundHeroStep; +import org.destinationsol.game.tutorial.steps.DestroySpawnedShipsStep; +import org.destinationsol.game.tutorial.steps.FireGunStep; +import org.destinationsol.game.tutorial.steps.FlyToHeroFirstWaypointStep; +import org.destinationsol.game.tutorial.steps.FlyToNearestStationStep; +import org.destinationsol.game.tutorial.steps.FlyToPlanetSellingMercenariesStep; +import org.destinationsol.game.tutorial.steps.FlyToRandomWaypointAroundHeroStep; +import org.destinationsol.game.tutorial.steps.ManageMercenariesGuidanceStep; +import org.destinationsol.game.tutorial.steps.MapDragStep; +import org.destinationsol.game.tutorial.steps.MessageStep; +import org.destinationsol.game.tutorial.steps.OpenScreenStep; +import org.destinationsol.game.tutorial.steps.SelectEquippedWeaponStep; +import org.destinationsol.game.tutorial.steps.SlowVelocityStep; +import org.destinationsol.game.tutorial.steps.ThrustForwardsStep; +import org.destinationsol.game.tutorial.steps.TurnLeftRightStep; +import org.destinationsol.game.tutorial.steps.UseAbilityStep; +import org.destinationsol.game.tutorial.steps.WaitUntilFullyRepairedStep; +import org.destinationsol.ui.nui.NUIManager; +import org.destinationsol.ui.nui.screens.MainGameScreen; +import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Provider; + +public class NewTutorialManager implements UpdateAwareSystem { + private static final Logger logger = LoggerFactory.getLogger(NewTutorialManager.class); + private final NUIManager nuiManager; + private final TutorialScreen tutorialScreen; + private final SolApplication solApplication; + private final Provider solGame; + private TutorialStep[] steps; + private int stepNo; + + @Inject + public NewTutorialManager(NUIManager nuiManager, SolApplication solApplication, Provider game) { + this.nuiManager = nuiManager; + this.tutorialScreen = (TutorialScreen) nuiManager.createScreen("engine:tutorialScreen"); + this.solApplication = solApplication; + this.solGame = game; + } + + public void start() { + MainGameScreen mainGameScreen = solGame.get().getMainGameScreen(); + mainGameScreen.getTalkButton().setVisible(false); + mainGameScreen.getMapButton().setVisible(false); + mainGameScreen.getInventoryButton().setVisible(false); + mainGameScreen.getMercsButton().setVisible(false); + + tutorialScreen.clearAllTutorialBoxes(); + + GameOptions gameOptions = solApplication.getOptions(); + boolean isMobile = solApplication.isMobile(); + + steps = new TutorialStep[] { + new MessageStep(tutorialScreen, solGame.get(), "Section 1 - Movement"), + new TurnLeftRightStep(tutorialScreen, solGame.get(), "Turn left and right (" + + ((gameOptions.controlType == GameOptions.ControlType.MIXED) ? "Move Mouse" : + gameOptions.getKeyLeftName() + " and " + gameOptions.getKeyRightName()) + + ")."), + new ThrustForwardsStep(tutorialScreen, solGame.get(), + (gameOptions.controlType == GameOptions.ControlType.MOUSE || gameOptions.controlType == GameOptions.ControlType.MIXED) ? + "Thrust forwards (" + gameOptions.getKeyUpMouseName() + ")." : + !isMobile ? + "Thrust forwards (" + gameOptions.getKeyUpName() + ")." : + "Thrust forwards."), + new SlowVelocityStep(tutorialScreen, solGame.get(), 0.05f, "Turn around and slow to a stop."), + new FlyToRandomWaypointAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 2.0f, "Fly to the waypoint."), + new MessageStep(tutorialScreen, solGame.get(), "Section 2 - Weapons"), + new FireGunStep(tutorialScreen, solGame.get(),gameOptions, isMobile, "Fire your gun"), + new CheckGunReloadStep(tutorialScreen, solGame.get(), false, true, "Firing weapons drains your ammunition. Keep on firing."), + new CheckGunReloadStep(tutorialScreen, solGame.get(), false, false, + "When your weapon is depleted, it automatically reloads.\n" + + "You can't fire when your weapon is reloading."), + new UseAbilityStep(tutorialScreen, solGame.get(), gameOptions, isMobile, "Use your ability"), + new MessageStep(tutorialScreen, solGame.get(), "Abilities consume ability charges."), + new MessageStep(tutorialScreen, solGame.get(), "Section 3 - Map"), + new OpenScreenStep(tutorialScreen, nuiManager, + solGame.get().getScreens().mainGameScreen.getMapButton(), + solGame.get().getScreens().mapScreen, + "Open the map."), + new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomInButton(), "Zoom In"), + new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomOutButton(), "Zoom Out"), + new MapDragStep(tutorialScreen, solGame.get(), solGame.get().getMapDrawer(), "You can move around the map by clicking/tapping and dragging."), + new CreateWaypointStep(tutorialScreen, solGame.get(), + solGame.get().getScreens().mapScreen.getAddWaypointButton(), + "Create a waypoint."), + new CloseScreenStep(tutorialScreen, + nuiManager, + solGame.get().getScreens().mapScreen.getCloseButton(), + solGame.get().getScreens().mapScreen, + "Close the map."), + new FlyToHeroFirstWaypointStep(tutorialScreen, solGame.get(), "Fly to your waypoint."), + new MessageStep(tutorialScreen, solGame.get(), "Section 4 - Money"), + new DestroySpawnedAsteroidAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 2.0f, "Fire at the asteroid."), + new MessageStep(tutorialScreen, solGame.get(), "Asteroids drop loot - money in this case."), + new MessageStep(tutorialScreen, solGame.get(), "Section 5 - Items"), + new OpenScreenStep(tutorialScreen, nuiManager, + solGame.get().getScreens().mainGameScreen.getInventoryButton(), + solGame.get().getScreens().inventoryScreen, + "Open your inventory."), + new SelectEquippedWeaponStep(tutorialScreen, + solGame.get().getScreens().inventoryScreen, + "Select an equipped weapon."), + new CheckItemEquippedStep(tutorialScreen, + solGame.get().getScreens().inventoryScreen, + false, "Un-equip an item."), + new CheckItemEquippedStep(tutorialScreen, + solGame.get().getScreens().inventoryScreen, + false, "Equip an item."), + new MessageStep(tutorialScreen, solGame.get(), "Section 6 - Weapon Mounts"), + new MessageStep(tutorialScreen, solGame.get(), "All ships are equipped with up to two weapon mounts."), + new MessageStep(tutorialScreen, solGame.get(), "Weapon mounts vary in size and socket."), + new MessageStep(tutorialScreen, solGame.get(), "Weapon sizes are small or large."), + new MessageStep(tutorialScreen, solGame.get(), "Weapon sockets are fixed or rotating."), + new MessageStep(tutorialScreen, solGame.get(), "A weapon can only be equipped if both its size and socket match its mount."), + new MessageStep(tutorialScreen, solGame.get(), "Section 7 - Shops"), + new FlyToNearestStationStep(tutorialScreen, solGame.get(), "Fly to the station."), + new OpenScreenStep(tutorialScreen, nuiManager, + solGame.get().getScreens().mainGameScreen.getTalkButton(), + solGame.get().getScreens().talkScreen, + "Talk to the station."), + new BuyItemStep(tutorialScreen, + solGame.get().getScreens().talkScreen.getBuyButton(), + solGame.get().getScreens().inventoryScreen.getBuyItemsScreen().getBuyControl(), + "Buy an item."), + new MessageStep(tutorialScreen, solGame.get(), "Section 8 - Combat"), + new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:pirateSmall", + "core:blaster+core:gun", "Destroy all targets.", + "Enemy ships can be tough.\nOpen the pause menu and select Respawn."), + new MessageStep(tutorialScreen, solGame.get(), "Destroyed ships drop valuable loot."), + new MessageStep(tutorialScreen, solGame.get(), "Section 9 - Repair Kits"), + new WaitUntilFullyRepairedStep(tutorialScreen, solGame.get(), + "Stay still and wait until the repair kits have repaired your hull fully."), + new MessageStep(tutorialScreen, solGame.get(), "Section 10 - Hiring Mercenaries"), + new FlyToPlanetSellingMercenariesStep(tutorialScreen, solGame.get(), "Fly to a planetary station providing mercenaries."), + new MessageStep(tutorialScreen, solGame.get(), "When flying around planets, you'll be affected by gravity."), + new OpenScreenStep(tutorialScreen, nuiManager, + solGame.get().getScreens().mainGameScreen.getTalkButton(), + solGame.get().getScreens().talkScreen, + "Talk to the station."), + new BuyMercenaryStep(tutorialScreen, solGame.get(), 1000, "Try hiring a mercenary."), + new MessageStep(tutorialScreen, solGame.get(), "Let's see how your mercenary fights."), + new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:pirateSmall", + "core:blaster+core:gun 0.5|core:smallShield+core:shield", "Destroy all targets.", + "Enemy ships can be tough.\nOpen the pause menu and select Respawn."), + new MessageStep(tutorialScreen, solGame.get(), "Mercenaries will keep any money they collect as part of their payment."), + new MessageStep(tutorialScreen, solGame.get(), "Section 11 - Managing Mercenaries"), + new OpenScreenStep(tutorialScreen, nuiManager, + solGame.get().getScreens().mainGameScreen.getMercsButton(), + solGame.get().getScreens().inventoryScreen, + "Open the mercenaries menu."), + new ManageMercenariesGuidanceStep(tutorialScreen, nuiManager, + solGame.get().getScreens().inventoryScreen, + "Here you can manage your mercenaries.", + "Here you can give items to your mercenary.", + "Here you can take items back from your mercenary.", + "Here you can manage your mercenary's equipment.") + }; + + stepNo = 0; + steps[stepNo].start(); + } + + @Override + public void update(SolGame game, float timeStep) { + if (nuiManager.getTopScreen() != tutorialScreen) { + if (nuiManager.hasScreen(tutorialScreen)) { + tutorialScreen.moveToTop(); + } else { + nuiManager.pushScreen(tutorialScreen); + } + } + + if (stepNo >= steps.length) { + tutorialScreen.setTutorialText("The tutorial is finished. Shoot to return to the main menu."); + if (game.getHero().getShip().getPilot().isShoot()) { + solApplication.finishGame(); + } + return; + } + + if (steps[stepNo].checkComplete(timeStep)) { + stepNo++; + tutorialScreen.clearAllTutorialBoxes(); + if (stepNo < steps.length) { + steps[stepNo].start(); + } + } + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java new file mode 100644 index 000000000..48139ba14 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java @@ -0,0 +1,21 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial; +public abstract class TutorialStep { + public abstract void start(); + public abstract boolean checkComplete(float timeStep); +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java new file mode 100644 index 000000000..ee7cce421 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; + +public class ButtonPressStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final UIWarnButton button; + private final String message; + private boolean buttonPressed; + + public ButtonPressStep(TutorialScreen tutorialScreen, UIWarnButton button, String message) { + this.tutorialScreen = tutorialScreen; + this.button = button; + this.message = message; + } + + @Override + public void start() { + tutorialScreen.setTutorialText(message); + button.subscribe(button -> { + buttonPressed = true; + }); + } + + @Override + public boolean checkComplete(float timeStep) { + button.enableWarn(); + return buttonPressed; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java new file mode 100644 index 000000000..4154b38e4 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java @@ -0,0 +1,56 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; +import org.terasology.nui.HorizontalAlign; + +public class BuyItemStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final UIWarnButton buyButton; + private final UIWarnButton purchaseButton; + private final String message; + private boolean buyButtonPressed = false; + private boolean purchaseButtonPressed = false; + + public BuyItemStep(TutorialScreen tutorialScreen, UIWarnButton buyButton, UIWarnButton purchaseButton, String message) { + this.tutorialScreen = tutorialScreen; + this.buyButton = buyButton; + this.purchaseButton = purchaseButton; + this.message = message; + } + + public void start() { + tutorialScreen.setTutorialText(message, HorizontalAlign.LEFT); + buyButton.subscribe(button -> { + buyButtonPressed = true; + }); + purchaseButton.subscribe(button -> { + purchaseButtonPressed = true; + }); + } + public boolean checkComplete(float timeStep) { + if (!buyButtonPressed) { + buyButton.enableWarn(); + } else { + purchaseButton.enableWarn(); + } + return buyButtonPressed && purchaseButtonPressed; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java new file mode 100644 index 000000000..92cca3c5b --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java @@ -0,0 +1,70 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; +import org.terasology.nui.HorizontalAlign; + +public class BuyMercenaryStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final SolGame game; + private final int giftMoney; + private final String message; + private boolean hireButtonPressed = false; + private boolean hireMercenaryButtonPressed = false; + private UIWarnButton hireButton; + private UIWarnButton hireMercenaryButton; + + public BuyMercenaryStep(TutorialScreen tutorialScreen, SolGame game, int giftMoney, String message) { + this.tutorialScreen = tutorialScreen; + this.game = game; + this.giftMoney = giftMoney; + this.message = message; + } + + public void start() { + Hero hero = game.getHero(); + hero.setMoney(hero.getMoney() + giftMoney); + tutorialScreen.setTutorialText(message, HorizontalAlign.LEFT); + hireButton = game.getScreens().talkScreen.getHireButton(); + hireButton.subscribe(button -> { + hireButtonPressed = true; + }); + hireMercenaryButton = game.getScreens().inventoryScreen.getHireShipsScreen().getHireControl(); + hireMercenaryButton.subscribe(button -> { + hireMercenaryButtonPressed = true; + }); + } + public boolean checkComplete(float timeStep) { + Hero hero = game.getHero(); + if (hero.getMoney() < giftMoney) { + // Make sure that the player always has enough money to hire a mercenary. + hero.setMoney(giftMoney); + } + + if (!hireButtonPressed) { + hireButton.enableWarn(); + } else { + hireMercenaryButton.enableWarn(); + } + return hireButtonPressed && hireMercenaryButtonPressed; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java new file mode 100644 index 000000000..23ab8e1a9 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java @@ -0,0 +1,52 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.TutorialScreen; +public class CheckGunReloadStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final SolGame game; + private final boolean isSecondary; + private final boolean isReloading; + private final String message; + + public CheckGunReloadStep(TutorialScreen tutorialScreen, SolGame game, boolean isSecondary, boolean isReloading, String message) { + this.tutorialScreen = tutorialScreen; + this.game = game; + this.isSecondary = isSecondary; + this.isReloading = isReloading; + this.message = message; + } + + @Override + public void start() { + tutorialScreen.setTutorialText(message); + } + + @Override + public boolean checkComplete(float timeStep) { + Hero hero = game.getHero(); + if (isReloading) { + return hero.getHull().getGun(isSecondary).reloadAwait >= 0.0f; + } else { + return hero.getHull().getGun(isSecondary).reloadAwait <= 0.0f; + } + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java new file mode 100644 index 000000000..0730454d7 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.InventoryScreen; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class CheckItemEquippedStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final InventoryScreen inventoryScreen; + private final boolean equipped; + private final String message; + + public CheckItemEquippedStep(TutorialScreen tutorialScreen, InventoryScreen inventoryScreen, + boolean equipped, String message) { + this.tutorialScreen = tutorialScreen; + this.inventoryScreen = inventoryScreen; + this.equipped = equipped; + this.message = message; + } + + public void start() { + tutorialScreen.setTutorialText(message); + } + public boolean checkComplete(float timeStep) { + if (equipped) { + return inventoryScreen.getSelectedItem().isEquipped() > 0; + } else { + return inventoryScreen.getSelectedItem().isEquipped() == 0; + } + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java new file mode 100644 index 000000000..86de9502f --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.common.Nullable; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.NUIManager; +import org.destinationsol.ui.nui.NUIScreenLayer; +import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; + +public class CloseScreenStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final NUIManager nuiManager; + private final UIWarnButton closeButton; + private final NUIScreenLayer screen; + private final String message; + + public CloseScreenStep(TutorialScreen tutorialScreen, NUIManager nuiManager, @Nullable UIWarnButton closeButton, + NUIScreenLayer screen, String message) { + this.tutorialScreen = tutorialScreen; + this.nuiManager = nuiManager; + this.closeButton = closeButton; + this.screen = screen; + this.message = message; + } + + @Override + public void start() { + if (closeButton != null) { + closeButton.setVisible(true); + } + tutorialScreen.setTutorialText(message); + } + + @Override + public boolean checkComplete(float timeStep) { + if (closeButton != null) { + closeButton.enableWarn(); + } + return !nuiManager.hasScreen(screen); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java new file mode 100644 index 000000000..ea128ad71 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java @@ -0,0 +1,55 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; +import org.terasology.nui.HorizontalAlign; + +public class CreateWaypointStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final SolGame game; + private final UIWarnButton addWaypointButton; + private final String message; + private boolean buttonPressed = false; + + public CreateWaypointStep(TutorialScreen tutorialScreen, SolGame game, UIWarnButton addWaypointButton, String message) { + this.tutorialScreen = tutorialScreen; + this.game = game; + this.addWaypointButton = addWaypointButton; + this.message = message; + } + + public void start() { + tutorialScreen.setTutorialText(message, HorizontalAlign.LEFT); + addWaypointButton.subscribe(button -> { + addWaypointButton.enableWarn(); + buttonPressed = true; + }); + } + public boolean checkComplete(float timeStep) { + if (!buttonPressed) { + addWaypointButton.enableWarn(); + } + + Hero hero = game.getHero(); + return hero.getWaypoints().size() > 0; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java new file mode 100644 index 000000000..0001e8270 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java @@ -0,0 +1,77 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import org.destinationsol.assets.Assets; +import org.destinationsol.game.Hero; +import org.destinationsol.game.ObjectManager; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.SolObject; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.Waypoint; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class DestroyObjectsStep extends TutorialStep { + protected final TutorialScreen tutorialScreen; + protected final SolGame game; + protected final SolObject[] objects; + protected final String message; + protected final Waypoint[] objectWaypoints; + + public DestroyObjectsStep(TutorialScreen tutorialScreen, SolGame game, SolObject[] objects, String message) { + this.tutorialScreen = tutorialScreen; + this.game = game; + this.objects = objects; + this.message = message; + this.objectWaypoints = new Waypoint[objects.length]; + } + + @Override + public void start() { + tutorialScreen.setTutorialText(message); + + Hero hero = game.getHero(); + ObjectManager objectManager = game.getObjectManager(); + + TextureAtlas.AtlasRegion targetWaypointTexture = Assets.getAtlasRegion("engine:mapObjects/beaconAttack"); + for (int objectNo = 0; objectNo < objects.length; objectNo++) { + objectWaypoints[objectNo] = new Waypoint(objects[objectNo].getPosition(), Color.RED, targetWaypointTexture); + hero.addWaypoint(objectWaypoints[objectNo]); + objectManager.addObjDelayed(objectWaypoints[objectNo]); + } + } + + @Override + public boolean checkComplete(float timeStep) { + boolean allObjectsDestroyed = true; + for (int objectNo = 0; objectNo < objects.length; objectNo++) { + SolObject object = objects[objectNo]; + Waypoint waypoint = objectWaypoints[objectNo]; + + waypoint.position.set(object.getPosition()); + if (object.shouldBeRemoved(game)) { + game.getHero().removeWaypoint(waypoint); + game.getObjectManager().removeObjDelayed(waypoint); + } else { + allObjectsDestroyed = false; + } + } + return allObjectsDestroyed; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java new file mode 100644 index 000000000..55a8709f2 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java @@ -0,0 +1,56 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.common.SolRandom; +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.SolObject; +import org.destinationsol.game.asteroid.Asteroid; +import org.destinationsol.game.tutorial.steps.wrapper.TrackedSolObjectWrapper; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class DestroySpawnedAsteroidAroundHeroStep extends DestroyObjectsStep { + private final float minDistance; + private final float spawnRadius; + + public DestroySpawnedAsteroidAroundHeroStep(TutorialScreen tutorialScreen, SolGame game, + float minDistance, float spawnRadius, String message) { + super(tutorialScreen, game, new SolObject[1], message); + this.minDistance = minDistance; + this.spawnRadius = spawnRadius; + } + @Override + public void start() { + Hero hero = game.getHero(); + Vector2 asteroidPosition = hero.getPosition().cpy(); + while (!game.isPlaceEmpty(asteroidPosition, true)) { + asteroidPosition.set( + hero.getPosition().x + SolRandom.randomFloat(minDistance, minDistance + spawnRadius), + hero.getPosition().y + SolRandom.randomFloat(minDistance, minDistance + spawnRadius) + ); + } + + Asteroid asteroid = game.getAsteroidBuilder().buildNew(game, asteroidPosition, Vector2.Zero, 1.0f, null); + objects[0] = new TrackedSolObjectWrapper(asteroid); + game.getObjectManager().addObjDelayed(asteroid); + + super.start(); + } + +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java new file mode 100644 index 000000000..4cc909c7d --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java @@ -0,0 +1,85 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.Const; +import org.destinationsol.common.SolRandom; +import org.destinationsol.game.Faction; +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.SolObject; +import org.destinationsol.game.input.AiPilot; +import org.destinationsol.game.input.Guardian; +import org.destinationsol.game.input.Pilot; +import org.destinationsol.game.ship.FarShip; +import org.destinationsol.game.ship.hulls.HullConfig; +import org.destinationsol.game.tutorial.steps.wrapper.TrackedSolObjectWrapper; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class DestroySpawnedShipsStep extends DestroyObjectsStep { + private final int shipCount; + private final String hullConfig; + private final String items; + private final String respawnMessage; + + public DestroySpawnedShipsStep(TutorialScreen tutorialScreen, SolGame game, int shipCount, + String hullConfig, String items, String attackMessage, String respawnMessage) { + super(tutorialScreen, game, new SolObject[shipCount], attackMessage); + this.shipCount = shipCount; + this.hullConfig = hullConfig; + this.items = items; + this.respawnMessage = respawnMessage; + } + + @Override + public void start() { + Hero hero = game.getHero(); + + HullConfig enemyConfig = game.getHullConfigManager().getConfig(hullConfig); + for (int enemyNo = 0; enemyNo < shipCount; enemyNo++) { + Vector2 enemyPosition = hero.getPosition().cpy(); + while (!game.isPlaceEmpty(enemyPosition, false)) { + enemyPosition.set(hero.getPosition().x + SolRandom.randomFloat(2, 10), hero.getPosition().y + SolRandom.randomFloat(2, 10)); + } + + Guardian dp = new Guardian(game, enemyConfig, hero.getPilot(), hero.getPosition(), hero.getHull().getHullConfig(), 0); + Pilot pilot = new AiPilot(dp, true, Faction.EHAR, false, null, Const.AI_DET_DIST); + int money = 60; + FarShip enemy = game.getShipBuilder().buildNewFar(game, enemyPosition, null, + 0, 0, pilot, items, + enemyConfig, null, false, money, null, true); + TrackedSolObjectWrapper enemyShip = new TrackedSolObjectWrapper(enemy.toObject(game)); + game.getObjectManager().addObjDelayed(enemyShip); + objects[enemyNo] = enemyShip; + } + + super.start(); + } + + @Override + public boolean checkComplete(float timeStep) { + Hero hero = game.getHero(); + if (hero.isDead()) { + tutorialScreen.setTutorialText(respawnMessage); + } else { + tutorialScreen.setTutorialText(message); + } + + return super.checkComplete(timeStep); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java new file mode 100644 index 000000000..d47334145 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.GameOptions; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.screens.ShipUiControl; +import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.ui.nui.screens.UIShipControlsScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; + +public class FireGunStep extends MessageStep { + private final GameOptions gameOptions; + private final boolean isMobile; + private UIWarnButton fireButton; + + public FireGunStep(TutorialScreen tutorialScreen, SolGame game, GameOptions gameOptions, boolean isMobile, String message) { + super(tutorialScreen, game, message); + this.gameOptions = gameOptions; + this.isMobile = isMobile; + } + + public void start() { + if (!isMobile) { + tutorialScreen.setTutorialText(message + " (" + gameOptions.getKeyShootName() + ")."); + } else { + tutorialScreen.setTutorialText(message + "."); + } + + ShipUiControl shipUiControl = game.getScreens().oldMainGameScreen.getShipControl(); + if (shipUiControl instanceof UIShipControlsScreen) { + fireButton = ((UIShipControlsScreen) shipUiControl).getGun1Button(); + } + } + + public boolean checkComplete(float timeStep) { + if (fireButton != null) { + fireButton.enableWarn(); + } + + return super.checkComplete(timeStep) && game.getHero().getPilot().isShoot(); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java new file mode 100644 index 000000000..690511884 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java @@ -0,0 +1,44 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class FlyToHeroFirstWaypointStep extends FlyToWaypointStep { + public FlyToHeroFirstWaypointStep(TutorialScreen tutorialScreen, SolGame game, String message) { + super(tutorialScreen, game, Vector2.Zero, message); + } + + @Override + public void start() { + waypoint = game.getHero().getWaypoints().get(0); + tutorialScreen.setTutorialText(message); + } + + @Override + public boolean checkComplete(float timeStep) { + Hero hero = game.getHero(); + if (!hero.getWaypoints().contains(waypoint) && hero.getWaypoints().size() > 0) { + // Change the target waypoint just in-case the player removes it. + waypoint = hero.getWaypoints().get(0); + } + return super.checkComplete(timeStep); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java new file mode 100644 index 000000000..981912a0f --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java @@ -0,0 +1,62 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.SolObject; +import org.destinationsol.game.ship.FarShip; +import org.destinationsol.game.ship.SolShip; +import org.destinationsol.game.ship.hulls.HullConfig; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class FlyToNearestStationStep extends FlyToWaypointStep { + public FlyToNearestStationStep(TutorialScreen tutorialScreen, SolGame game, String message) { + super(tutorialScreen, game, Vector2.Zero, message); + } + + @Override + public void start() { + Vector2 heroPosition = game.getHero().getPosition(); + float nearestStationDistance = Integer.MAX_VALUE; + Vector2 nearestStationPosition = new Vector2(Integer.MAX_VALUE, Integer.MAX_VALUE); + + for (SolObject solObject : game.getObjectManager().getObjects()) { + if (solObject instanceof SolShip && + ((SolShip) solObject).getHull().getHullConfig().getType() == HullConfig.Type.STATION) { + float stationDistance = solObject.getPosition().dst(heroPosition); + if (stationDistance < nearestStationDistance) { + nearestStationDistance = stationDistance; + nearestStationPosition.set(solObject.getPosition()); + nearestStationPosition.add(((SolShip) solObject).getHull().getHullConfig().getForceBeaconPositions().get(0)); + } + } + } + for (FarShip farShip : game.getObjectManager().getFarShips()) { + if (farShip.getHullConfig().getType() == HullConfig.Type.STATION) { + float stationDistance = farShip.getPosition().dst(heroPosition); + if (stationDistance < nearestStationDistance) { + nearestStationDistance = stationDistance; + nearestStationPosition.set(farShip.getPosition()); + nearestStationPosition.add(farShip.getHullConfig().getForceBeaconPositions().get(0)); + } + } + } + waypointPosition = nearestStationPosition; + super.start(); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java new file mode 100644 index 000000000..3abf58a3d --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java @@ -0,0 +1,71 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.planet.Planet; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +import java.util.ArrayList; +import java.util.List; + +public class FlyToPlanetSellingMercenariesStep extends FlyToPlanetStep { + public FlyToPlanetSellingMercenariesStep(TutorialScreen tutorialScreen, SolGame game, String message) { + super(tutorialScreen, game, null, message); + } + + @Override + public void start() { + List planetsWithMercenaries = new ArrayList<>(); + + Vector2 heroPosition = game.getHero().getPosition(); + for (Planet planet : game.getPlanetManager().getNearestSystem(heroPosition).getPlanets()) { + if (planet.getConfig().tradeConfig.mercs.groupCount() > 0) { + planetsWithMercenaries.add(planet); + } + } + + if (planetsWithMercenaries.size() == 0) { + tutorialScreen.setTutorialText("ERROR: Failed to find suitable planet."); + return; + } + + Planet closestPlanet = planetsWithMercenaries.get(0); + float closestDistance = Float.MAX_VALUE; + + for (Planet planet : planetsWithMercenaries) { + float distance = planet.getPosition().dst(heroPosition); + if (distance < closestDistance) { + closestPlanet = planet; + closestDistance = distance; + } + } + + planet = closestPlanet; + super.start(); + } + + @Override + public boolean checkComplete(float timeStep) { + boolean nearPlanet = super.checkComplete(timeStep); + if (nearPlanet && planet.areObjectsCreated()) { + return game.getScreens().talkScreen.isTargetFar(game.getHero()); + } + return false; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java new file mode 100644 index 000000000..ceb947115 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java @@ -0,0 +1,51 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.planet.Planet; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class FlyToPlanetStep extends FlyToWaypointStep { + protected Planet planet; + + public FlyToPlanetStep(TutorialScreen tutorialScreen, SolGame game, Planet planet, String message) { + super(tutorialScreen, game, planet != null ? planet.getPosition() : Vector2.Zero, message); + this.planet = planet; + } + + @Override + public void start() { + waypointPosition = planet.getPosition(); + super.start(); + } + + @Override + public boolean checkComplete(float timeStep) { + Hero hero = game.getHero(); + if (planet.isNearGround(hero.getPosition())) { + game.getObjectManager().removeObjDelayed(waypoint); + // Force an object manager update to remove the waypoint. + game.getObjectManager().update(game, timeStep); + hero.removeWaypoint(waypoint); + return true; + } + return super.checkComplete(timeStep); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java new file mode 100644 index 000000000..c13d4c984 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java @@ -0,0 +1,49 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.common.SolRandom; +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class FlyToRandomWaypointAroundHeroStep extends FlyToWaypointStep { + private final float minDistance; + private final float radius; + + public FlyToRandomWaypointAroundHeroStep(TutorialScreen tutorialScreen, SolGame game, + float minDistance, float radius, String message) { + super(tutorialScreen, game, Vector2.Zero, message); + this.minDistance = minDistance; + this.radius = radius; + } + + @Override + public void start() { + Hero hero = game.getHero(); + waypointPosition = hero.getPosition().cpy(); + while (!game.isPlaceEmpty(waypointPosition, true)) { + waypointPosition.set( + hero.getPosition().x + SolRandom.randomFloat(minDistance, minDistance + radius), + hero.getPosition().y + SolRandom.randomFloat(minDistance, minDistance + radius) + ); + } + + super.start(); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java new file mode 100644 index 000000000..fd2da7e29 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.assets.Assets; +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.Waypoint; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class FlyToWaypointStep extends TutorialStep { + private static final float MIN_WAYPOINT_DISTANCE = 0.2f; + protected final TutorialScreen tutorialScreen; + protected final SolGame game; + protected final String message; + protected Vector2 waypointPosition; + protected Waypoint waypoint; + + public FlyToWaypointStep(TutorialScreen tutorialScreen, SolGame game, Vector2 waypointPosition, String message) { + this.tutorialScreen = tutorialScreen; + this.game = game; + this.waypointPosition = waypointPosition; + this.message = message; + } + + @Override + public void start() { + waypoint = new Waypoint(waypointPosition, Color.WHITE, Assets.getAtlasRegion("engine:mapObjects/waypoint")); + + Hero hero = game.getHero(); + hero.addWaypoint(waypoint); + game.getObjectManager().addObjDelayed(waypoint); + tutorialScreen.setTutorialText(message); + } + + @Override + public boolean checkComplete(float timeStep) { + Hero hero = game.getHero(); + if (hero.getPosition().dst(waypoint.getPosition()) < MIN_WAYPOINT_DISTANCE) { + hero.removeWaypoint(waypoint); + game.getObjectManager().removeObjDelayed(waypoint); + return true; + } + return false; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java new file mode 100644 index 000000000..1be19a888 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java @@ -0,0 +1,97 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.screens.ChooseMercenaryScreen; +import org.destinationsol.game.screens.GiveItemsScreen; +import org.destinationsol.game.screens.ShowInventory; +import org.destinationsol.game.screens.TakeItems; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.NUIManager; +import org.destinationsol.ui.nui.screens.InventoryScreen; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class ManageMercenariesGuidanceStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final NUIManager nuiManager; + private final InventoryScreen inventoryScreen; + private final String chooseMessage; + private final String giveMessage; + private final String takeMessage; + private final String equipMessage; + private boolean giveItemsPressed; + private boolean takeItemsPressed; + private boolean equipItemsPressed; + + public ManageMercenariesGuidanceStep(TutorialScreen tutorialScreen, NUIManager nuiManager, InventoryScreen inventoryScreen, + String chooseMessage, String giveMessage, + String takeMessage, String equipMessage) { + this.tutorialScreen = tutorialScreen; + this.nuiManager = nuiManager; + this.inventoryScreen = inventoryScreen; + + this.chooseMessage = chooseMessage; + this.giveMessage = giveMessage; + this.takeMessage = takeMessage; + this.equipMessage = equipMessage; + } + + @Override + public void start() { + tutorialScreen.setTutorialText(chooseMessage); + + ChooseMercenaryScreen chooseMercenaryScreen = inventoryScreen.getChooseMercenaryScreen(); + chooseMercenaryScreen.getGiveItemsButton().subscribe(button -> { + giveItemsPressed = true; + }); + chooseMercenaryScreen.getTakeItemsButton().subscribe(button -> { + takeItemsPressed = true; + }); + chooseMercenaryScreen.getEquipItemsButton().subscribe(button -> { + equipItemsPressed = true; + }); + } + + @Override + public boolean checkComplete(float timeStep) { + ChooseMercenaryScreen chooseMercenaryScreen = inventoryScreen.getChooseMercenaryScreen(); + GiveItemsScreen giveItemsScreen = inventoryScreen.getGiveItems(); + TakeItems takeItemsScreen = inventoryScreen.getTakeItems(); + ShowInventory equipItemsScreen = inventoryScreen.getShowInventory(); + + if (inventoryScreen.getOperations() == chooseMercenaryScreen) { + tutorialScreen.setTutorialText(chooseMessage); + if (!giveItemsPressed) { + chooseMercenaryScreen.getGiveItemsButton().enableWarn(); + } + if (!takeItemsPressed) { + chooseMercenaryScreen.getTakeItemsButton().enableWarn(); + } + if (!equipItemsPressed) { + chooseMercenaryScreen.getEquipItemsButton().enableWarn(); + } + } else if (inventoryScreen.getOperations() == giveItemsScreen) { + tutorialScreen.setTutorialText(giveMessage); + } else if (inventoryScreen.getOperations() == takeItemsScreen) { + tutorialScreen.setTutorialText(takeMessage); + } else if (inventoryScreen.getOperations() == equipItemsScreen && equipItemsScreen.getTarget().isMerc()) { + tutorialScreen.setTutorialText(equipMessage); + } + + return !nuiManager.hasScreen(inventoryScreen); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java new file mode 100644 index 000000000..4af2c77f7 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java @@ -0,0 +1,43 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.game.MapDrawer; +import org.destinationsol.game.SolGame; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class MapDragStep extends MessageStep { + private final MapDrawer mapDrawer; + private Vector2 originalMapPosition; + + public MapDragStep(TutorialScreen tutorialScreen, SolGame game, MapDrawer mapDrawer, String message) { + super(tutorialScreen, game, message); + this.mapDrawer = mapDrawer; + } + + @Override + public void start() { + originalMapPosition = game.getMapDrawer().getMapDrawPositionAdditive().cpy(); + super.start(); + } + + @Override + public boolean checkComplete(float timeStep) { + return !mapDrawer.getMapDrawPositionAdditive().equals(originalMapPosition); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java new file mode 100644 index 000000000..a547a13c8 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.SolGame; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class MessageStep extends TutorialStep { + protected static final float MIN_STEP_DURATION = 0.5f; + protected final TutorialScreen tutorialScreen; + protected final SolGame game; + protected final String message; + protected float stepTimer; + + public MessageStep(TutorialScreen tutorialScreen, SolGame game, String message) { + this.tutorialScreen = tutorialScreen; + this.game = game; + this.message = message; + this.stepTimer = 0.0f; + } + + @Override + public void start() { + tutorialScreen.setTutorialText(message); + } + + @Override + public boolean checkComplete(float timeStep) { + stepTimer += timeStep; + return stepTimer >= MIN_STEP_DURATION && game.getHero().getPilot().isShoot(); + } +} \ No newline at end of file diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java new file mode 100644 index 000000000..133773a7c --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.common.Nullable; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.NUIManager; +import org.destinationsol.ui.nui.NUIScreenLayer; +import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; + +public class OpenScreenStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final NUIManager nuiManager; + private final UIWarnButton openButton; + private final NUIScreenLayer screen; + private final String message; + + public OpenScreenStep(TutorialScreen tutorialScreen, NUIManager nuiManager, @Nullable UIWarnButton openButton, + NUIScreenLayer screen, String message) { + this.tutorialScreen = tutorialScreen; + this.nuiManager = nuiManager; + this.openButton = openButton; + this.screen = screen; + this.message = message; + } + + @Override + public void start() { + if (openButton != null) { + openButton.setVisible(true); + } + tutorialScreen.setTutorialText(message); + } + + @Override + public boolean checkComplete(float timeStep) { + if (openButton != null) { + openButton.enableWarn(); + } + return nuiManager.hasScreen(screen); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java new file mode 100644 index 000000000..dc0826714 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.InventoryScreen; +import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; + +public class SelectEquippedWeaponStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final InventoryScreen inventoryScreen; + private final String message; + + public SelectEquippedWeaponStep(TutorialScreen tutorialScreen, InventoryScreen inventoryScreen, String message) { + this.tutorialScreen = tutorialScreen; + this.inventoryScreen = inventoryScreen; + this.message = message; + } + + @Override + public void start() { + tutorialScreen.setTutorialText(message); + } + + @Override + public boolean checkComplete(float timeStep) { + for (UIWarnButton button : inventoryScreen.getEquippedItemUIControlsForTutorial()) { + button.enableWarn(); + } + return inventoryScreen.getSelectedItem().isEquipped() > 0; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java new file mode 100644 index 000000000..c01a41a33 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class SlowVelocityStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final SolGame game; + private final float threshold; + private final String message; + + public SlowVelocityStep(TutorialScreen tutorialScreen, SolGame game, float threshold, String message) { + this.tutorialScreen = tutorialScreen; + this.game = game; + this.threshold = threshold; + this.message = message; + } + + @Override + public void start() { + tutorialScreen.setTutorialText(message); + } + + @Override + public boolean checkComplete(float timeStep) { + Hero hero = game.getHero(); + return hero.getShip().getVelocity().len() < threshold; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java new file mode 100644 index 000000000..b6061fbc5 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.input.Pilot; +import org.destinationsol.game.screens.ShipUiControl; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.ui.nui.screens.UIShipControlsScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; + +public class ThrustForwardsStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final SolGame game; + private final String message; + private final UIWarnButton thrustButton; + private boolean didThrust = false; + + public ThrustForwardsStep(TutorialScreen tutorialScreen, SolGame game, String message) { + this.tutorialScreen = tutorialScreen; + this.game = game; + this.message = message; + ShipUiControl shipUiControl = game.getScreens().oldMainGameScreen.getShipControl(); + if (shipUiControl instanceof UIShipControlsScreen) { + thrustButton = ((UIShipControlsScreen) shipUiControl).getForwardButton(); + } else { + thrustButton = null; + } + } + + public void start() { + tutorialScreen.setTutorialText(message); + } + + public boolean checkComplete(float timeStep) { + if (thrustButton != null) { + thrustButton.enableWarn(); + } + + Hero hero = game.getHero(); + Pilot playerPilot = hero.getPilot(); + if (playerPilot.isUp()) { + didThrust = true; + } + return (didThrust && hero.getShip().getVelocity().len() > 0.1f); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java new file mode 100644 index 000000000..06f1d8deb --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java @@ -0,0 +1,56 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.input.Pilot; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class TurnLeftRightStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final SolGame game; + private final String message; + private float leftSeconds = 0; + private float rightSeconds = 0; + + public TurnLeftRightStep(TutorialScreen tutorialScreen, SolGame game, String message) { + this.tutorialScreen = tutorialScreen; + this.game = game; + this.message = message; + } + + public void start() { + tutorialScreen.setTutorialText(message); + } + + public boolean checkComplete(float timeStep) { + Hero hero = game.getHero(); + Pilot playerPilot = hero.getShip().getPilot(); + + if (playerPilot.isLeft()) { + leftSeconds += timeStep; + } + + if (playerPilot.isRight()) { + rightSeconds += timeStep; + } + + return (leftSeconds > 1.0f && rightSeconds > 1.0f); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java new file mode 100644 index 000000000..efe8443ee --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.GameOptions; +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.screens.ShipUiControl; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.ui.nui.screens.UIShipControlsScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; + +public class UseAbilityStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final SolGame game; + private final GameOptions gameOptions; + private final boolean isMobile; + private final String message; + private UIWarnButton abilityButton; + + public UseAbilityStep(TutorialScreen tutorialScreen, SolGame game, GameOptions gameOptions, boolean isMobile, String message) { + this.tutorialScreen = tutorialScreen; + this.game = game; + this.gameOptions = gameOptions; + this.isMobile = isMobile; + this.message = message; + } + + public void start() { + ShipUiControl shipUiControl = game.getScreens().oldMainGameScreen.getShipControl(); + if (shipUiControl instanceof UIShipControlsScreen) { + abilityButton = ((UIShipControlsScreen) shipUiControl).getAbilityButton(); + } + if (!isMobile) { + tutorialScreen.setTutorialText(message + " (" + gameOptions.getKeyAbilityName() + ")."); + } else { + tutorialScreen.setTutorialText(message + "."); + } + } + public boolean checkComplete(float timeStep) { + if (abilityButton != null) { + abilityButton.enableWarn(); + } + + Hero hero = game.getHero(); + return hero.getAbilityAwait() > 0.0f; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java new file mode 100644 index 000000000..e9527a75e --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java @@ -0,0 +1,45 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class WaitUntilFullyRepairedStep extends TutorialStep { + private final TutorialScreen tutorialScreen; + private final SolGame game; + private final String message; + + public WaitUntilFullyRepairedStep(TutorialScreen tutorialScreen, SolGame game, String message) { + this.tutorialScreen = tutorialScreen; + this.game = game; + this.message = message; + } + + @Override + public void start() { + tutorialScreen.setTutorialText(message); + } + + @Override + public boolean checkComplete(float timeStep) { + Hero hero = game.getHero(); + return !hero.isTranscendent() && hero.getHull().life >= hero.getHull().config.getMaxLife(); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/TrackedFarObjectWrapper.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/TrackedFarObjectWrapper.java new file mode 100644 index 000000000..e133fd090 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/TrackedFarObjectWrapper.java @@ -0,0 +1,76 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps.wrapper; + +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.game.FarObject; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.SolObject; + +/** + * This is the {@link FarObject} equivalent of {@link TrackedSolObjectWrapper}. + * @see TrackedSolObjectWrapper + */ +public class TrackedFarObjectWrapper implements FarObject { + private final TrackedSolObjectWrapper trackedSolObjectWrapper; + private FarObject trackedFarObject; + + public TrackedFarObjectWrapper(TrackedSolObjectWrapper trackedSolObjectWrapper, FarObject trackedFarObject) { + this.trackedSolObjectWrapper = trackedSolObjectWrapper; + this.trackedFarObject = trackedFarObject; + } + + @Override + public boolean shouldBeRemoved(SolGame game) { + return trackedFarObject.shouldBeRemoved(game); + } + + @Override + public SolObject toObject(SolGame game) { + trackedSolObjectWrapper.setTrackedSolObject(trackedFarObject.toObject(game)); + return trackedSolObjectWrapper; + } + + @Override + public void update(SolGame game) { + trackedFarObject.update(game); + } + + @Override + public float getRadius() { + return trackedFarObject.getRadius(); + } + + @Override + public Vector2 getPosition() { + return trackedFarObject.getPosition(); + } + + @Override + public String toDebugString() { + return trackedFarObject.toDebugString(); + } + + @Override + public boolean hasBody() { + return trackedFarObject.hasBody(); + } + + public void setTrackedFarObject(FarObject trackedFarObject) { + this.trackedFarObject = trackedFarObject; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/TrackedSolObjectWrapper.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/TrackedSolObjectWrapper.java new file mode 100644 index 000000000..ac51cb88b --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/TrackedSolObjectWrapper.java @@ -0,0 +1,119 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps.wrapper; + +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.game.DmgType; +import org.destinationsol.game.FarObject; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.SolObject; +import org.destinationsol.game.drawables.Drawable; + +import java.util.List; + +/** + * When you obtain a {@link SolObject} instance, it only remains valid until it transitions to a {@link FarObject}. + * When the {@link FarObject} transitions back into a {@link SolObject}, it will not use the same instance as before. + * This makes it difficult to track objects off-screen. + * + * This class attempts to work around this issue by keeping track of the most recent {@link SolObject} and {@link FarObject} + * instances in-use from a given {@link SolObject}. + */ +public class TrackedSolObjectWrapper implements SolObject { + private final TrackedFarObjectWrapper trackedFarObjectWrapper; + private SolObject trackedSolObject; + + public TrackedSolObjectWrapper(SolObject trackedSolObject) { + this.trackedFarObjectWrapper = new TrackedFarObjectWrapper(this, null); + this.trackedSolObject = trackedSolObject; + } + + @Override + public void update(SolGame game) { + trackedSolObject.update(game); + } + + @Override + public boolean shouldBeRemoved(SolGame game) { + return trackedSolObject.shouldBeRemoved(game); + } + + @Override + public void onRemove(SolGame game) { + trackedSolObject.onRemove(game); + } + + @Override + public void receiveDmg(float dmg, SolGame game, Vector2 position, DmgType dmgType) { + trackedSolObject.receiveDmg(dmg, game, position, dmgType); + } + + @Override + public boolean receivesGravity() { + return trackedSolObject.receivesGravity(); + } + + @Override + public void receiveForce(Vector2 force, SolGame game, boolean acc) { + trackedSolObject.receiveForce(force, game, acc); + } + + @Override + public Vector2 getPosition() { + return trackedSolObject.getPosition(); + } + + @Override + public FarObject toFarObject() { + trackedFarObjectWrapper.setTrackedFarObject(trackedSolObject.toFarObject()); + return trackedFarObjectWrapper; + } + + @Override + public List getDrawables() { + return trackedSolObject.getDrawables(); + } + + @Override + public float getAngle() { + return trackedSolObject.getAngle(); + } + + @Override + public Vector2 getVelocity() { + return trackedSolObject.getVelocity(); + } + + @Override + public void handleContact(SolObject other, float absImpulse, SolGame game, Vector2 collPos) { + trackedSolObject.handleContact(other, absImpulse, game, collPos); + } + + @Override + public Boolean isMetal() { + return trackedSolObject.isMetal(); + } + + @Override + public boolean hasBody() { + return trackedSolObject.hasBody(); + } + + public void setTrackedSolObject(SolObject trackedSolObject) { + this.trackedSolObject = trackedSolObject; + } +} diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/MapScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/MapScreen.java index c7ff9ee85..1ec9208c9 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/MapScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/MapScreen.java @@ -67,8 +67,8 @@ private enum WaypointOperation { private UIWarnButton closeButton; private UIWarnButton zoomInButton; private UIWarnButton zoomOutButton; - private KeyActivatedButton addWaypointButton; - private KeyActivatedButton removeWaypointButton; + private UIWarnButton addWaypointButton; + private UIWarnButton removeWaypointButton; private WaypointOperation waypointOperation; private final InteractionListener dragListener = new BaseInteractionListener() { @Override @@ -193,7 +193,7 @@ public void initialise() { zoomOutButton.setEnabled(mapZoom != MapDrawer.MAX_ZOOM); }); - addWaypointButton = find("addWaypointButton", KeyActivatedButton.class); + addWaypointButton = find("addWaypointButton", UIWarnButton.class); addWaypointButton.subscribe(button -> { if (waypointOperation == WaypointOperation.ADDING) { // Cancel add @@ -206,7 +206,7 @@ public void initialise() { addWaypointButton.setText(CANCEL_TEXT); } }); - removeWaypointButton = find("removeWaypointButton", KeyActivatedButton.class); + removeWaypointButton = find("removeWaypointButton", UIWarnButton.class); removeWaypointButton.subscribe(button -> { if (waypointOperation == WaypointOperation.REMOVING) { // Cancel remove @@ -316,6 +316,24 @@ public UIWarnButton getZoomOutButton() { return zoomOutButton; } + /** + * Returns the button used to add a waypoint. + * This is mostly exposed for use in the tutorial. + * @return the button used to add a waypoint. + */ + public UIWarnButton getAddWaypointButton() { + return addWaypointButton; + } + + /** + * Returns the button used to remove a waypoint. + * This is mostly exposed for use in the tutorial. + * @return the button used to remove a waypoint. + */ + public UIWarnButton getRemoveWaypointButton() { + return removeWaypointButton; + } + /** * Returns the button used to close the map. * This is mostly exposed for use in the tutorial. diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/TalkScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/TalkScreen.java index f315077ae..26ebaf160 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/TalkScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/TalkScreen.java @@ -34,9 +34,10 @@ public class TalkScreen extends NUIScreenLayer { public static final float MAX_TALK_DIST = 1f; private final SolApplication solApplication; + private UIWarnButton sellButton; private UIWarnButton buyButton; private KeyActivatedButton changeShipButton; - private KeyActivatedButton hireButton; + private UIWarnButton hireButton; private SolShip target; @Inject @@ -46,7 +47,7 @@ public TalkScreen(SolApplication solApplication) { @Override public void initialise() { - KeyActivatedButton sellButton = find("sellButton", KeyActivatedButton.class); + sellButton = find("sellButton", UIWarnButton.class); sellButton.setKey(GDXInputUtil.GDXToNuiKey(solApplication.getOptions().getKeySellMenu())); sellButton.subscribe(button -> { InventoryScreen inventoryScreen = solApplication.getGame().getScreens().inventoryScreen; @@ -73,7 +74,7 @@ public void initialise() { nuiManager.pushScreen(inventoryScreen); }); - hireButton = find("hireButton", KeyActivatedButton.class); + hireButton = find("hireButton", UIWarnButton.class); hireButton.setKey(GDXInputUtil.GDXToNuiKey(solApplication.getOptions().getKeyHireShipMenu())); hireButton.subscribe(button -> { InventoryScreen inventoryScreen = solApplication.getGame().getScreens().inventoryScreen; @@ -115,6 +116,20 @@ public UIWarnButton getBuyButton() { return buyButton; } + /** + * Returns the button pressed to open the sell items screen. + * + * This is exposed directly for use in the tutorial. + * @return the buy items button + */ + public UIWarnButton getSellButton() { + return sellButton; + } + + public UIWarnButton getHireButton() { + return hireButton; + } + /** * Returns the current ship being talked to. * @return the current ship being talked to diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java index f1a012a65..afb96c735 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java @@ -18,6 +18,8 @@ import org.destinationsol.game.SolGame; import org.destinationsol.ui.nui.NUIScreenLayer; +import org.terasology.nui.HorizontalAlign; +import org.terasology.nui.widgets.UIBox; import org.terasology.nui.widgets.UILabel; import javax.inject.Inject; @@ -28,7 +30,10 @@ * See {@link #moveToTop()} and {@link org.destinationsol.ui.TutorialManager#update(SolGame, float)} for how this is done. */ public class TutorialScreen extends NUIScreenLayer { - private UILabel tutorialText; + private UIBox tutorialBoxLeft; + private UILabel tutorialTextLeft; + private UIBox tutorialBoxCentre; + private UILabel tutorialTextCentre; private boolean isReplaceRemove; @Inject @@ -37,15 +42,32 @@ public TutorialScreen() { @Override public void initialise() { - tutorialText = find("tutorialText", UILabel.class); + tutorialBoxLeft = find("tutorialBoxLeft", UIBox.class); + tutorialTextLeft = find("tutorialTextLeft", UILabel.class); + tutorialBoxCentre = find("tutorialBoxCentre", UIBox.class); + tutorialTextCentre = find("tutorialTextCentre", UILabel.class); } public String getTutorialText() { - return tutorialText.getText(); + return getTutorialText(HorizontalAlign.CENTER); + } + + public String getTutorialText(HorizontalAlign horizontalAlign) { + return getTutorialTextLabel(horizontalAlign).getText(); } public void setTutorialText(String text) { - tutorialText.setText(text); + setTutorialText(text, HorizontalAlign.CENTER); + } + + public void setTutorialText(String text, HorizontalAlign horizontalAlign) { + getTutorialTextLabel(horizontalAlign).setText(text); + getTutorialBox(horizontalAlign).setVisible(!text.isEmpty()); + } + + public void clearAllTutorialBoxes() { + tutorialBoxLeft.setVisible(false); + tutorialBoxCentre.setVisible(false); } @Override @@ -53,6 +75,28 @@ public boolean isBlockingInput() { return false; } + protected UILabel getTutorialTextLabel(HorizontalAlign horizontalAlign) { + switch (horizontalAlign) { + case LEFT: + return tutorialTextLeft; + case CENTER: + return tutorialTextCentre; + default: + return tutorialTextCentre; + } + } + + protected UIBox getTutorialBox(HorizontalAlign horizontalAlign) { + switch (horizontalAlign) { + case LEFT: + return tutorialBoxLeft; + case CENTER: + return tutorialBoxCentre; + default: + return tutorialBoxLeft; + } + } + @Override protected boolean escapeCloses() { return false; diff --git a/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin index 70394417a..232fabb3a 100644 --- a/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin +++ b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin @@ -7,7 +7,18 @@ "min-height": 128, "elements": { "UIBox": { - "background": "engine:background", + "background": "engine:box", + "text-align-horizontal": "middle" + } + } + }, + "tutorialBoxLeft": { + "font": "engine:main#0.8", + "max-width": 160, + "min-height": 192, + "elements": { + "UIBox": { + "background": "engine:box", "text-align-horizontal": "middle" } } diff --git a/engine/src/main/resources/org/destinationsol/assets/ui/talkScreen.ui b/engine/src/main/resources/org/destinationsol/assets/ui/talkScreen.ui index 6d7d8dcb5..650a5ce75 100644 --- a/engine/src/main/resources/org/destinationsol/assets/ui/talkScreen.ui +++ b/engine/src/main/resources/org/destinationsol/assets/ui/talkScreen.ui @@ -31,7 +31,7 @@ "verticalSpacing": 10, "contents": [ { - "type": "KeyActivatedButton", + "type": "UIWarnButton", "id": "sellButton", "text": "Sell" }, @@ -46,7 +46,7 @@ "text": "Change Ship" }, { - "type": "KeyActivatedButton", + "type": "UIWarnButton", "id": "hireButton", "text": "Hire" }, diff --git a/engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui b/engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui index eabee7d77..a05d5e490 100644 --- a/engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui +++ b/engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui @@ -6,11 +6,11 @@ "contents": [ { "type": "UIBox", - "id": "tutorialBox", + "id": "tutorialBoxCentre", "family": "tutorialBox", "content": { "type": "UILabel", - "id": "tutorialText", + "id": "tutorialTextCentre", "text": "Tutorial content goes here..." }, "layoutInfo": { @@ -23,6 +23,26 @@ "position-bottom": {}, "use-content-height": true } + }, + { + "type": "UIBox", + "id": "tutorialBoxLeft", + "family": "tutorialBoxLeft", + "content": { + "type": "UILabel", + "id": "tutorialTextLeft", + "text": "Tutorial content goes here..." + }, + "layoutInfo": { + "position-left": { + "offset": 16 + }, + "position-bottom": { + "offset": 64 + }, + "use-content-height": true, + "use-content-width": true + } } ] } From 384f8db2c59230c0dc44e0fcb5dc8085b971e888 Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Sat, 25 Feb 2023 21:57:54 +0000 Subject: [PATCH 02/18] feat(tutorial): add message continue indicator. Also removed some incorrect information regarding weapon mounts. This may have fixed a crash regarding flying to planetary station in the station purchasing step. --- .../game/tutorial/NewTutorialManager.java | 69 +++++++------- .../game/tutorial/steps/FireGunStep.java | 13 +-- .../steps/FlyToNearestStationStep.java | 5 +- .../game/tutorial/steps/MessageStep.java | 3 + .../game/tutorial/steps/UseAbilityStep.java | 13 +-- .../ui/nui/screens/TutorialScreen.java | 89 +++++++++++++----- .../ui/nui/widgets/InteractHint.java | 81 ++++++++++++++++ .../assets/skins/tutorialScreen.skin | 38 +++++++- .../assets/textures/ui/keyboardKey.png | Bin 0 -> 213 bytes .../assets/textures/ui/mouseClickLeft.png | Bin 0 -> 231 bytes .../assets/textures/ui/mouseClickRight.png | Bin 0 -> 229 bytes .../assets/ui/tutorialScreen.ui | 34 +++++-- 12 files changed, 253 insertions(+), 92 deletions(-) create mode 100644 engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java create mode 100644 engine/src/main/resources/org/destinationsol/assets/textures/ui/keyboardKey.png create mode 100644 engine/src/main/resources/org/destinationsol/assets/textures/ui/mouseClickLeft.png create mode 100644 engine/src/main/resources/org/destinationsol/assets/textures/ui/mouseClickRight.png diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java index 33b24a232..b2b63360f 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java @@ -95,56 +95,38 @@ public void start() { "Thrust forwards (" + gameOptions.getKeyUpName() + ")." : "Thrust forwards."), new SlowVelocityStep(tutorialScreen, solGame.get(), 0.05f, "Turn around and slow to a stop."), - new FlyToRandomWaypointAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 2.0f, "Fly to the waypoint."), + new FlyToRandomWaypointAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 4.0f, "Fly to the waypoint."), new MessageStep(tutorialScreen, solGame.get(), "Section 2 - Weapons"), - new FireGunStep(tutorialScreen, solGame.get(),gameOptions, isMobile, "Fire your gun"), + new FireGunStep(tutorialScreen, solGame.get(), isMobile ? "Fire your gun." : "Fire your gun (" + gameOptions.getKeyShootName() + ")."), new CheckGunReloadStep(tutorialScreen, solGame.get(), false, true, "Firing weapons drains your ammunition. Keep on firing."), new CheckGunReloadStep(tutorialScreen, solGame.get(), false, false, "When your weapon is depleted, it automatically reloads.\n" + "You can't fire when your weapon is reloading."), - new UseAbilityStep(tutorialScreen, solGame.get(), gameOptions, isMobile, "Use your ability"), + new UseAbilityStep(tutorialScreen, solGame.get(), isMobile ? "Use your ability." : "Use your ability (" + gameOptions.getKeyAbilityName() + ")."), new MessageStep(tutorialScreen, solGame.get(), "Abilities consume ability charges."), - new MessageStep(tutorialScreen, solGame.get(), "Section 3 - Map"), - new OpenScreenStep(tutorialScreen, nuiManager, - solGame.get().getScreens().mainGameScreen.getMapButton(), - solGame.get().getScreens().mapScreen, - "Open the map."), - new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomInButton(), "Zoom In"), - new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomOutButton(), "Zoom Out"), - new MapDragStep(tutorialScreen, solGame.get(), solGame.get().getMapDrawer(), "You can move around the map by clicking/tapping and dragging."), - new CreateWaypointStep(tutorialScreen, solGame.get(), - solGame.get().getScreens().mapScreen.getAddWaypointButton(), - "Create a waypoint."), - new CloseScreenStep(tutorialScreen, - nuiManager, - solGame.get().getScreens().mapScreen.getCloseButton(), - solGame.get().getScreens().mapScreen, - "Close the map."), - new FlyToHeroFirstWaypointStep(tutorialScreen, solGame.get(), "Fly to your waypoint."), - new MessageStep(tutorialScreen, solGame.get(), "Section 4 - Money"), + new MessageStep(tutorialScreen, solGame.get(), "Section 3 - Money"), new DestroySpawnedAsteroidAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 2.0f, "Fire at the asteroid."), new MessageStep(tutorialScreen, solGame.get(), "Asteroids drop loot - money in this case."), - new MessageStep(tutorialScreen, solGame.get(), "Section 5 - Items"), + new MessageStep(tutorialScreen, solGame.get(), "Section 4 - Items"), new OpenScreenStep(tutorialScreen, nuiManager, solGame.get().getScreens().mainGameScreen.getInventoryButton(), solGame.get().getScreens().inventoryScreen, "Open your inventory."), new SelectEquippedWeaponStep(tutorialScreen, solGame.get().getScreens().inventoryScreen, - "Select an equipped weapon."), + "Select an equipped item."), new CheckItemEquippedStep(tutorialScreen, solGame.get().getScreens().inventoryScreen, false, "Un-equip an item."), new CheckItemEquippedStep(tutorialScreen, solGame.get().getScreens().inventoryScreen, false, "Equip an item."), - new MessageStep(tutorialScreen, solGame.get(), "Section 6 - Weapon Mounts"), - new MessageStep(tutorialScreen, solGame.get(), "All ships are equipped with up to two weapon mounts."), - new MessageStep(tutorialScreen, solGame.get(), "Weapon mounts vary in size and socket."), - new MessageStep(tutorialScreen, solGame.get(), "Weapon sizes are small or large."), - new MessageStep(tutorialScreen, solGame.get(), "Weapon sockets are fixed or rotating."), - new MessageStep(tutorialScreen, solGame.get(), "A weapon can only be equipped if both its size and socket match its mount."), - new MessageStep(tutorialScreen, solGame.get(), "Section 7 - Shops"), + new MessageStep(tutorialScreen, solGame.get(), "Section 5 - Weapon Mounts"), + new MessageStep(tutorialScreen, solGame.get(), "All ships may come with with up to two weapon mounts."), + new MessageStep(tutorialScreen, solGame.get(), "Weapon mounts vary by their socket type."), + new MessageStep(tutorialScreen, solGame.get(), "Weapon sockets are either fixed or rotating."), + new MessageStep(tutorialScreen, solGame.get(), "A weapon can only be equipped if its socket type matches its mount."), + new MessageStep(tutorialScreen, solGame.get(), "Section 6 - Shops"), new FlyToNearestStationStep(tutorialScreen, solGame.get(), "Fly to the station."), new OpenScreenStep(tutorialScreen, nuiManager, solGame.get().getScreens().mainGameScreen.getTalkButton(), @@ -154,14 +136,31 @@ public void start() { solGame.get().getScreens().talkScreen.getBuyButton(), solGame.get().getScreens().inventoryScreen.getBuyItemsScreen().getBuyControl(), "Buy an item."), - new MessageStep(tutorialScreen, solGame.get(), "Section 8 - Combat"), - new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:pirateSmall", - "core:blaster+core:gun", "Destroy all targets.", + new MessageStep(tutorialScreen, solGame.get(), "Section 7 - Combat"), + new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:minerSmall", + "core:fixedBlaster", "Destroy all targets.", "Enemy ships can be tough.\nOpen the pause menu and select Respawn."), new MessageStep(tutorialScreen, solGame.get(), "Destroyed ships drop valuable loot."), - new MessageStep(tutorialScreen, solGame.get(), "Section 9 - Repair Kits"), + new MessageStep(tutorialScreen, solGame.get(), "Section 8 - Repair Kits"), new WaitUntilFullyRepairedStep(tutorialScreen, solGame.get(), "Stay still and wait until the repair kits have repaired your hull fully."), + new MessageStep(tutorialScreen, solGame.get(), "Section 9 - Map"), + new OpenScreenStep(tutorialScreen, nuiManager, + solGame.get().getScreens().mainGameScreen.getMapButton(), + solGame.get().getScreens().mapScreen, + "Open the map."), + new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomInButton(), "Zoom In"), + new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomOutButton(), "Zoom Out"), + new MapDragStep(tutorialScreen, solGame.get(), solGame.get().getMapDrawer(), "You can move around the map by clicking/tapping and dragging."), + new CreateWaypointStep(tutorialScreen, solGame.get(), + solGame.get().getScreens().mapScreen.getAddWaypointButton(), + "Create a waypoint."), + new CloseScreenStep(tutorialScreen, + nuiManager, + solGame.get().getScreens().mapScreen.getCloseButton(), + solGame.get().getScreens().mapScreen, + "Close the map."), + new FlyToHeroFirstWaypointStep(tutorialScreen, solGame.get(), "Fly to your waypoint."), new MessageStep(tutorialScreen, solGame.get(), "Section 10 - Hiring Mercenaries"), new FlyToPlanetSellingMercenariesStep(tutorialScreen, solGame.get(), "Fly to a planetary station providing mercenaries."), new MessageStep(tutorialScreen, solGame.get(), "When flying around planets, you'll be affected by gravity."), @@ -172,7 +171,7 @@ public void start() { new BuyMercenaryStep(tutorialScreen, solGame.get(), 1000, "Try hiring a mercenary."), new MessageStep(tutorialScreen, solGame.get(), "Let's see how your mercenary fights."), new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:pirateSmall", - "core:blaster+core:gun 0.5|core:smallShield+core:shield", "Destroy all targets.", + "core:blaster core:smallShield", "Destroy all targets.", "Enemy ships can be tough.\nOpen the pause menu and select Respawn."), new MessageStep(tutorialScreen, solGame.get(), "Mercenaries will keep any money they collect as part of their payment."), new MessageStep(tutorialScreen, solGame.get(), "Section 11 - Managing Mercenaries"), diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java index d47334145..cd0e4270e 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java @@ -16,7 +16,6 @@ package org.destinationsol.game.tutorial.steps; -import org.destinationsol.GameOptions; import org.destinationsol.game.SolGame; import org.destinationsol.game.screens.ShipUiControl; import org.destinationsol.ui.nui.screens.TutorialScreen; @@ -24,22 +23,14 @@ import org.destinationsol.ui.nui.widgets.UIWarnButton; public class FireGunStep extends MessageStep { - private final GameOptions gameOptions; - private final boolean isMobile; private UIWarnButton fireButton; - public FireGunStep(TutorialScreen tutorialScreen, SolGame game, GameOptions gameOptions, boolean isMobile, String message) { + public FireGunStep(TutorialScreen tutorialScreen, SolGame game, String message) { super(tutorialScreen, game, message); - this.gameOptions = gameOptions; - this.isMobile = isMobile; } public void start() { - if (!isMobile) { - tutorialScreen.setTutorialText(message + " (" + gameOptions.getKeyShootName() + ")."); - } else { - tutorialScreen.setTutorialText(message + "."); - } + tutorialScreen.setTutorialText(message); ShipUiControl shipUiControl = game.getScreens().oldMainGameScreen.getShipControl(); if (shipUiControl instanceof UIShipControlsScreen) { diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java index 981912a0f..e2110b7c4 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java @@ -38,11 +38,14 @@ public void start() { for (SolObject solObject : game.getObjectManager().getObjects()) { if (solObject instanceof SolShip && ((SolShip) solObject).getHull().getHullConfig().getType() == HullConfig.Type.STATION) { + SolShip station = (SolShip) solObject; float stationDistance = solObject.getPosition().dst(heroPosition); if (stationDistance < nearestStationDistance) { nearestStationDistance = stationDistance; nearestStationPosition.set(solObject.getPosition()); - nearestStationPosition.add(((SolShip) solObject).getHull().getHullConfig().getForceBeaconPositions().get(0)); + if (station.getHull().getHullConfig().getForceBeaconPositions().size() > 0) { + nearestStationPosition.add(station.getHull().getHullConfig().getForceBeaconPositions().get(0)); + } } } } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java index a547a13c8..f12cc2fa5 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java @@ -19,6 +19,7 @@ import org.destinationsol.game.SolGame; import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.terasology.input.MouseInput; public class MessageStep extends TutorialStep { protected static final float MIN_STEP_DURATION = 0.5f; @@ -37,6 +38,8 @@ public MessageStep(TutorialScreen tutorialScreen, SolGame game, String message) @Override public void start() { tutorialScreen.setTutorialText(message); + // TODO: Choose this input dynamically. + tutorialScreen.setInteractHintInput(MouseInput.MOUSE_LEFT); } @Override diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java index efe8443ee..ce007d6ab 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java @@ -16,7 +16,6 @@ package org.destinationsol.game.tutorial.steps; -import org.destinationsol.GameOptions; import org.destinationsol.game.Hero; import org.destinationsol.game.SolGame; import org.destinationsol.game.screens.ShipUiControl; @@ -28,16 +27,12 @@ public class UseAbilityStep extends TutorialStep { private final TutorialScreen tutorialScreen; private final SolGame game; - private final GameOptions gameOptions; - private final boolean isMobile; private final String message; private UIWarnButton abilityButton; - public UseAbilityStep(TutorialScreen tutorialScreen, SolGame game, GameOptions gameOptions, boolean isMobile, String message) { + public UseAbilityStep(TutorialScreen tutorialScreen, SolGame game, String message) { this.tutorialScreen = tutorialScreen; this.game = game; - this.gameOptions = gameOptions; - this.isMobile = isMobile; this.message = message; } @@ -46,11 +41,7 @@ public void start() { if (shipUiControl instanceof UIShipControlsScreen) { abilityButton = ((UIShipControlsScreen) shipUiControl).getAbilityButton(); } - if (!isMobile) { - tutorialScreen.setTutorialText(message + " (" + gameOptions.getKeyAbilityName() + ")."); - } else { - tutorialScreen.setTutorialText(message + "."); - } + tutorialScreen.setTutorialText(message); } public boolean checkComplete(float timeStep) { if (abilityButton != null) { diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java index afb96c735..ba66314de 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java @@ -18,11 +18,14 @@ import org.destinationsol.game.SolGame; import org.destinationsol.ui.nui.NUIScreenLayer; +import org.destinationsol.ui.nui.widgets.InteractHint; +import org.terasology.input.Input; import org.terasology.nui.HorizontalAlign; import org.terasology.nui.widgets.UIBox; import org.terasology.nui.widgets.UILabel; import javax.inject.Inject; +import java.util.EnumMap; /** * This screen displays the message box shown during the tutorial to instruct the user. @@ -30,22 +33,35 @@ * See {@link #moveToTop()} and {@link org.destinationsol.ui.TutorialManager#update(SolGame, float)} for how this is done. */ public class TutorialScreen extends NUIScreenLayer { - private UIBox tutorialBoxLeft; - private UILabel tutorialTextLeft; - private UIBox tutorialBoxCentre; - private UILabel tutorialTextCentre; + private static final class TutorialBox { + public final UIBox box; + public final UILabel text; + public final InteractHint interactHint; + + public TutorialBox(UIBox box, UILabel text, InteractHint interactHint) { + this.box = box; + this.text = text; + this.interactHint = interactHint; + } + } + + private final EnumMap tutorialBoxes; private boolean isReplaceRemove; @Inject public TutorialScreen() { + tutorialBoxes = new EnumMap<>(HorizontalAlign.class); } @Override public void initialise() { - tutorialBoxLeft = find("tutorialBoxLeft", UIBox.class); - tutorialTextLeft = find("tutorialTextLeft", UILabel.class); - tutorialBoxCentre = find("tutorialBoxCentre", UIBox.class); - tutorialTextCentre = find("tutorialTextCentre", UILabel.class); + for (HorizontalAlign horizontalAlign : HorizontalAlign.values()) { + tutorialBoxes.put(horizontalAlign, new TutorialBox( + find("tutorialBox" + horizontalAlign.toString(), UIBox.class), + find("tutorialText" + horizontalAlign.toString(), UILabel.class), + find("interactHint" + horizontalAlign.toString(), InteractHint.class) + )); + } } public String getTutorialText() { @@ -65,9 +81,40 @@ public void setTutorialText(String text, HorizontalAlign horizontalAlign) { getTutorialBox(horizontalAlign).setVisible(!text.isEmpty()); } + public Input getInteractHintInput() { + return getInteractHintInput(HorizontalAlign.CENTER); + } + + public Input getInteractHintInput(HorizontalAlign horizontalAlign) { + return getInteractHint(horizontalAlign).getInput(); + } + + public void setInteractHintInput(Input input) { + setInteractHintInput(HorizontalAlign.CENTER, input); + } + + public void setInteractHintInput(HorizontalAlign horizontalAlign, Input input) { + InteractHint interactHint = getInteractHint(horizontalAlign); + if (input != null) { + getInteractHint(horizontalAlign).setInput(input); + interactHint.setVisible(true); + } else { + interactHint.setVisible(false); + } + } + public void clearAllTutorialBoxes() { - tutorialBoxLeft.setVisible(false); - tutorialBoxCentre.setVisible(false); + for (TutorialBox tutorialBox : tutorialBoxes.values()) { + if (tutorialBox.box != null) { + tutorialBox.box.setVisible(false); + } + if (tutorialBox.text != null) { + tutorialBox.text.setText(""); + } + if (tutorialBox.interactHint != null) { + tutorialBox.interactHint.setVisible(false); + } + } } @Override @@ -76,25 +123,15 @@ public boolean isBlockingInput() { } protected UILabel getTutorialTextLabel(HorizontalAlign horizontalAlign) { - switch (horizontalAlign) { - case LEFT: - return tutorialTextLeft; - case CENTER: - return tutorialTextCentre; - default: - return tutorialTextCentre; - } + return tutorialBoxes.get(horizontalAlign).text; } protected UIBox getTutorialBox(HorizontalAlign horizontalAlign) { - switch (horizontalAlign) { - case LEFT: - return tutorialBoxLeft; - case CENTER: - return tutorialBoxCentre; - default: - return tutorialBoxLeft; - } + return tutorialBoxes.get(horizontalAlign).box; + } + + protected InteractHint getInteractHint(HorizontalAlign horizontalAlign) { + return tutorialBoxes.get(horizontalAlign).interactHint; } @Override diff --git a/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java b/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java new file mode 100644 index 000000000..cd4d68dca --- /dev/null +++ b/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java @@ -0,0 +1,81 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.ui.nui.widgets; + +import org.joml.Vector2i; +import org.terasology.input.Input; +import org.terasology.input.InputType; +import org.terasology.input.Keyboard; +import org.terasology.nui.Canvas; +import org.terasology.nui.CoreWidget; +import org.terasology.nui.LayoutConfig; +import org.terasology.nui.databinding.Binding; +import org.terasology.nui.databinding.DefaultBinding; + +public class InteractHint extends CoreWidget { + private Binding input = new DefaultBinding<>(Keyboard.Key.NONE); + + public InteractHint() { + } + + public InteractHint(String id, Input input) { + this.input.set(input); + } + + public Input getInput() { + return input.get(); + } + + public void setInput(Input input) { + this.input.set(input); + } + + public void bindInput(Binding input) { + this.input = input; + } + + @Override + public void onDraw(Canvas canvas) { + canvas.setPart(input.get().getType().toString().toLowerCase()); + canvas.drawBackground(); + if (input.get().getType() == InputType.KEY) { + canvas.drawText(input.get().getDisplayName(), canvas.getCurrentStyle().getBackground().getPixelRegion()); + } + } + + @Override + public Vector2i getPreferredContentSize(Canvas canvas, Vector2i sizeHint) { + canvas.setPart(input.get().getType().toString().toLowerCase()); + return canvas.getCurrentStyle().getBackground().size(); + } + + @Override + public String getMode() { + if (!isEnabled()) { + return DISABLED_MODE; + } + if (input.get() == null) { + return DEFAULT_MODE; + } + return input.get().getName().toLowerCase(); + } + + @Override + public boolean isSkinAppliedByCanvas() { + return false; + } +} diff --git a/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin index 232fabb3a..8711a4434 100644 --- a/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin +++ b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin @@ -12,7 +12,7 @@ } } }, - "tutorialBoxLeft": { + "tutorialBoxLEFT": { "font": "engine:main#0.8", "max-width": 160, "min-height": 192, @@ -23,5 +23,41 @@ } } } + }, + "elements": { + "InteractHint": { + "background": "engine:uiWhiteTex", + "background-scale-mode": "scale fit", + "font": "engine:main#0.36", + "parts": { + "key": { + "modes": { + "": { + "background": "engine:keyboardKey" + } + } + }, + "mouse_button": { + "modes": { + "mouse_left": { + "background": "engine:mouseClickLeft" + }, + "mouse_right": { + "background": "engine:mouseClickRight" + } + } + }, + "button": { + "modes": { + "": { + "background": "engine:uiPlanetProximityIndicator" + } + } + }, + "axis": { + "background": "engine:uiWhiteTex" + } + } + } } } \ No newline at end of file diff --git a/engine/src/main/resources/org/destinationsol/assets/textures/ui/keyboardKey.png b/engine/src/main/resources/org/destinationsol/assets/textures/ui/keyboardKey.png new file mode 100644 index 0000000000000000000000000000000000000000..5bf2204617ca7ccccf7b5ac5551910aa67d82003 GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}{hlt4ArY-_ zr=R6(P~d5fe*Am?r#%*n4Gh!_Y__=n`<3gk>Ean41`namqWf!QPMEMWgfUMx4y-bq zbz(xb{i3-uJFnf0-g!aYJ#ESN3*R%CJ#VcH6jglrCoavy_E{Js^B}>4P1; zQ|3BsU-&s7_~XW7!et0nB{&6d?IaX02ZB7OGI;mjR= Yf<|5oCY*Tg2Xrihr>mdKI;Vst06sZYQ2+n{ literal 0 HcmV?d00001 diff --git a/engine/src/main/resources/org/destinationsol/assets/textures/ui/mouseClickRight.png b/engine/src/main/resources/org/destinationsol/assets/textures/ui/mouseClickRight.png new file mode 100644 index 0000000000000000000000000000000000000000..fd2b041ebc31f3d523845afabde2ee13b12a306a GIT binary patch literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}^F3W0Ln2z= zUJvAJ0IEvJxx4gt>7%?)-zS|m{n2{%3+p`QeWJ6vbozvR85p8AObIw^duzwqc-`xj z8RvH@#5P=;;d-<}QG|ivp!q5%iKO3)e~KS^c8ujr7mE)2jNogx3l8=6bS^txa70U> z(p*P(T746XiUK32hXa$4L;VkBzhew9?k Date: Mon, 27 Feb 2023 23:11:01 +0000 Subject: [PATCH 03/18] feat(ui): use input method dependant icon in interact hint --- .../game/tutorial/steps/MessageStep.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java index f12cc2fa5..04e5ce850 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java @@ -16,10 +16,14 @@ package org.destinationsol.game.tutorial.steps; +import org.destinationsol.GameOptions; import org.destinationsol.game.SolGame; import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.terasology.input.ControllerInput; +import org.terasology.input.InputType; import org.terasology.input.MouseInput; +import org.terasology.nui.backends.libgdx.GDXInputUtil; public class MessageStep extends TutorialStep { protected static final float MIN_STEP_DURATION = 0.5f; @@ -38,8 +42,19 @@ public MessageStep(TutorialScreen tutorialScreen, SolGame game, String message) @Override public void start() { tutorialScreen.setTutorialText(message); - // TODO: Choose this input dynamically. - tutorialScreen.setInteractHintInput(MouseInput.MOUSE_LEFT); + GameOptions gameOptions = game.getSolApplication().getOptions(); + switch (gameOptions.controlType) { + case KEYBOARD: + tutorialScreen.setInteractHintInput(GDXInputUtil.GDXToNuiKey(gameOptions.getKeyShoot())); + break; + case MOUSE: + case MIXED: + tutorialScreen.setInteractHintInput(MouseInput.MOUSE_LEFT); + break; + case CONTROLLER: + tutorialScreen.setInteractHintInput(ControllerInput.find(InputType.CONTROLLER_BUTTON, gameOptions.getControllerButtonUp())); + break; + } } @Override From 27e0536dc104b5df55a430f97d904c57187a7e63 Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Mon, 27 Feb 2023 23:11:34 +0000 Subject: [PATCH 04/18] fix(tutorial): add more keyboard hints to tutorial text --- .../game/tutorial/NewTutorialManager.java | 38 +++++++++++++------ .../tutorial/steps/CheckItemEquippedStep.java | 8 +++- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java index b2b63360f..1e3c78285 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java @@ -81,6 +81,8 @@ public void start() { GameOptions gameOptions = solApplication.getOptions(); boolean isMobile = solApplication.isMobile(); + boolean usesKeyboard = !isMobile && + (gameOptions.controlType == GameOptions.ControlType.KEYBOARD || gameOptions.controlType == GameOptions.ControlType.MIXED); steps = new TutorialStep[] { new MessageStep(tutorialScreen, solGame.get(), "Section 1 - Movement"), @@ -97,30 +99,44 @@ public void start() { new SlowVelocityStep(tutorialScreen, solGame.get(), 0.05f, "Turn around and slow to a stop."), new FlyToRandomWaypointAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 4.0f, "Fly to the waypoint."), new MessageStep(tutorialScreen, solGame.get(), "Section 2 - Weapons"), - new FireGunStep(tutorialScreen, solGame.get(), isMobile ? "Fire your gun." : "Fire your gun (" + gameOptions.getKeyShootName() + ")."), + new FireGunStep(tutorialScreen, solGame.get(), isMobile ? + "Fire your gun." : + "Fire your gun (" + (gameOptions.controlType == GameOptions.ControlType.MIXED ? + "Left mouse button" : // TODO: Why isn't there a setting for this control? + gameOptions.getKeyShootName()) + ")."), new CheckGunReloadStep(tutorialScreen, solGame.get(), false, true, "Firing weapons drains your ammunition. Keep on firing."), new CheckGunReloadStep(tutorialScreen, solGame.get(), false, false, "When your weapon is depleted, it automatically reloads.\n" + "You can't fire when your weapon is reloading."), - new UseAbilityStep(tutorialScreen, solGame.get(), isMobile ? "Use your ability." : "Use your ability (" + gameOptions.getKeyAbilityName() + ")."), + new UseAbilityStep(tutorialScreen, solGame.get(), isMobile ? + "Use your ability." : + "Use your ability (" + (gameOptions.controlType == GameOptions.ControlType.MIXED ? + "Middle mouse button" : // TODO: Why isn't there a setting for this control? + gameOptions.getKeyAbilityName()) + ")."), new MessageStep(tutorialScreen, solGame.get(), "Abilities consume ability charges."), new MessageStep(tutorialScreen, solGame.get(), "Section 3 - Money"), - new DestroySpawnedAsteroidAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 2.0f, "Fire at the asteroid."), + new DestroySpawnedAsteroidAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 4.0f, "Fire at the asteroid."), new MessageStep(tutorialScreen, solGame.get(), "Asteroids drop loot - money in this case."), new MessageStep(tutorialScreen, solGame.get(), "Section 4 - Items"), new OpenScreenStep(tutorialScreen, nuiManager, solGame.get().getScreens().mainGameScreen.getInventoryButton(), solGame.get().getScreens().inventoryScreen, + usesKeyboard ? + "Open your inventory (" + gameOptions.getKeyInventoryName() + ")." : "Open your inventory."), new SelectEquippedWeaponStep(tutorialScreen, solGame.get().getScreens().inventoryScreen, - "Select an equipped item."), + usesKeyboard ? + "Select an equipped item (Move with " + gameOptions.getKeyUpName() + " and " + gameOptions.getKeyDownName() + ")" : + "Select an equipped item."), new CheckItemEquippedStep(tutorialScreen, solGame.get().getScreens().inventoryScreen, - false, "Un-equip an item."), + false, + usesKeyboard ? "Un-equip an item (" + gameOptions.getKeyEquipName() + ")." : "Un-equip an item."), new CheckItemEquippedStep(tutorialScreen, solGame.get().getScreens().inventoryScreen, - false, "Equip an item."), + false, + usesKeyboard ? "Re-equip that item (" + gameOptions.getKeyEquipName() + ")." : "Un-equip an item."), new MessageStep(tutorialScreen, solGame.get(), "Section 5 - Weapon Mounts"), new MessageStep(tutorialScreen, solGame.get(), "All ships may come with with up to two weapon mounts."), new MessageStep(tutorialScreen, solGame.get(), "Weapon mounts vary by their socket type."), @@ -131,11 +147,11 @@ public void start() { new OpenScreenStep(tutorialScreen, nuiManager, solGame.get().getScreens().mainGameScreen.getTalkButton(), solGame.get().getScreens().talkScreen, - "Talk to the station."), + isMobile ? "Talk to the station." : "Talk to the station (" + gameOptions.getKeyTalkName() + ")."), new BuyItemStep(tutorialScreen, solGame.get().getScreens().talkScreen.getBuyButton(), solGame.get().getScreens().inventoryScreen.getBuyItemsScreen().getBuyControl(), - "Buy an item."), + isMobile ? "Buy an item." : "Buy an item (" + gameOptions.getKeyBuyItemName() + ")."), new MessageStep(tutorialScreen, solGame.get(), "Section 7 - Combat"), new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:minerSmall", "core:fixedBlaster", "Destroy all targets.", @@ -148,7 +164,7 @@ public void start() { new OpenScreenStep(tutorialScreen, nuiManager, solGame.get().getScreens().mainGameScreen.getMapButton(), solGame.get().getScreens().mapScreen, - "Open the map."), + isMobile ? "Open the map." : "Open the map (" + gameOptions.getKeyMapName() + ")."), new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomInButton(), "Zoom In"), new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomOutButton(), "Zoom Out"), new MapDragStep(tutorialScreen, solGame.get(), solGame.get().getMapDrawer(), "You can move around the map by clicking/tapping and dragging."), @@ -178,10 +194,10 @@ public void start() { new OpenScreenStep(tutorialScreen, nuiManager, solGame.get().getScreens().mainGameScreen.getMercsButton(), solGame.get().getScreens().inventoryScreen, - "Open the mercenaries menu."), + isMobile ? "Open the mercenaries menu." : "Open the mercenaries menu (" + gameOptions.getKeyMercenaryInterationName() + ")."), new ManageMercenariesGuidanceStep(tutorialScreen, nuiManager, solGame.get().getScreens().inventoryScreen, - "Here you can manage your mercenaries.", + "Here you can manage your mercenaries. When you're done here, close the menu.", "Here you can give items to your mercenary.", "Here you can take items back from your mercenary.", "Here you can manage your mercenary's equipment.") diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java index 0730454d7..23c6bdb49 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java @@ -16,6 +16,7 @@ package org.destinationsol.game.tutorial.steps; +import org.destinationsol.game.item.SolItem; import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.nui.screens.InventoryScreen; import org.destinationsol.ui.nui.screens.TutorialScreen; @@ -25,6 +26,7 @@ public class CheckItemEquippedStep extends TutorialStep { private final InventoryScreen inventoryScreen; private final boolean equipped; private final String message; + private SolItem itemToCheck; public CheckItemEquippedStep(TutorialScreen tutorialScreen, InventoryScreen inventoryScreen, boolean equipped, String message) { @@ -36,10 +38,14 @@ public CheckItemEquippedStep(TutorialScreen tutorialScreen, InventoryScreen inve public void start() { tutorialScreen.setTutorialText(message); + if (equipped) { + itemToCheck = inventoryScreen.getSelectedItem(); + } } + public boolean checkComplete(float timeStep) { if (equipped) { - return inventoryScreen.getSelectedItem().isEquipped() > 0; + return itemToCheck.isEquipped() > 0; } else { return inventoryScreen.getSelectedItem().isEquipped() == 0; } From ffe526b69161158d1b98873c7c9f4bf7eacaa1fa Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Sat, 4 Mar 2023 13:59:02 +0000 Subject: [PATCH 05/18] fix(tutorial): reduce issues with enemy spawning in tutorial --- .../org/destinationsol/game/ship/FarShip.java | 4 + .../game/tutorial/NewTutorialManager.java | 12 ++- .../steps/DestroySpawnedShipsStep.java | 20 ++++- .../steps/FlyToNearestStarPortStep.java | 86 +++++++++++++++++++ .../FlyToPlanetSellingMercenariesStep.java | 45 +++++++++- .../game/tutorial/steps/FlyToPlanetStep.java | 3 - .../tutorial/steps/FlyToWaypointStep.java | 5 ++ 7 files changed, 166 insertions(+), 9 deletions(-) create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java diff --git a/engine/src/main/java/org/destinationsol/game/ship/FarShip.java b/engine/src/main/java/org/destinationsol/game/ship/FarShip.java index ae516697a..21f01136e 100644 --- a/engine/src/main/java/org/destinationsol/game/ship/FarShip.java +++ b/engine/src/main/java/org/destinationsol/game/ship/FarShip.java @@ -147,6 +147,10 @@ public HullConfig getHullConfig() { return hullConfig; } + public TradeContainer getTradeContainer() { + return tradeContainer; + } + public float getAngle() { return angle; } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java index 1e3c78285..c13c16031 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java @@ -31,6 +31,7 @@ import org.destinationsol.game.tutorial.steps.DestroySpawnedShipsStep; import org.destinationsol.game.tutorial.steps.FireGunStep; import org.destinationsol.game.tutorial.steps.FlyToHeroFirstWaypointStep; +import org.destinationsol.game.tutorial.steps.FlyToNearestStarPortStep; import org.destinationsol.game.tutorial.steps.FlyToNearestStationStep; import org.destinationsol.game.tutorial.steps.FlyToPlanetSellingMercenariesStep; import org.destinationsol.game.tutorial.steps.FlyToRandomWaypointAroundHeroStep; @@ -135,7 +136,7 @@ public void start() { usesKeyboard ? "Un-equip an item (" + gameOptions.getKeyEquipName() + ")." : "Un-equip an item."), new CheckItemEquippedStep(tutorialScreen, solGame.get().getScreens().inventoryScreen, - false, + true, usesKeyboard ? "Re-equip that item (" + gameOptions.getKeyEquipName() + ")." : "Un-equip an item."), new MessageStep(tutorialScreen, solGame.get(), "Section 5 - Weapon Mounts"), new MessageStep(tutorialScreen, solGame.get(), "All ships may come with with up to two weapon mounts."), @@ -185,6 +186,10 @@ public void start() { solGame.get().getScreens().talkScreen, "Talk to the station."), new BuyMercenaryStep(tutorialScreen, solGame.get(), 1000, "Try hiring a mercenary."), + new CloseScreenStep(tutorialScreen, nuiManager, + solGame.get().getScreens().inventoryScreen.getCloseButton(), + solGame.get().getScreens().inventoryScreen, + "Close the buy screen."), new MessageStep(tutorialScreen, solGame.get(), "Let's see how your mercenary fights."), new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:pirateSmall", "core:blaster core:smallShield", "Destroy all targets.", @@ -200,7 +205,10 @@ public void start() { "Here you can manage your mercenaries. When you're done here, close the menu.", "Here you can give items to your mercenary.", "Here you can take items back from your mercenary.", - "Here you can manage your mercenary's equipment.") + "Here you can manage your mercenary's equipment."), + new FlyToNearestStarPortStep(tutorialScreen, solGame.get(), "Fly to the marked star lane."), + new MessageStep(tutorialScreen, solGame.get(), + "Star lanes allow you to travel quickly between planets.\nThey cost money to use.") }; stepNo = 0; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java index 4cc909c7d..f2b2b6dcd 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java @@ -18,6 +18,7 @@ import com.badlogic.gdx.math.Vector2; import org.destinationsol.Const; +import org.destinationsol.common.SolMath; import org.destinationsol.common.SolRandom; import org.destinationsol.game.Faction; import org.destinationsol.game.Hero; @@ -26,6 +27,7 @@ import org.destinationsol.game.input.AiPilot; import org.destinationsol.game.input.Guardian; import org.destinationsol.game.input.Pilot; +import org.destinationsol.game.planet.Planet; import org.destinationsol.game.ship.FarShip; import org.destinationsol.game.ship.hulls.HullConfig; import org.destinationsol.game.tutorial.steps.wrapper.TrackedSolObjectWrapper; @@ -53,8 +55,22 @@ public void start() { HullConfig enemyConfig = game.getHullConfigManager().getConfig(hullConfig); for (int enemyNo = 0; enemyNo < shipCount; enemyNo++) { Vector2 enemyPosition = hero.getPosition().cpy(); - while (!game.isPlaceEmpty(enemyPosition, false)) { - enemyPosition.set(hero.getPosition().x + SolRandom.randomFloat(2, 10), hero.getPosition().y + SolRandom.randomFloat(2, 10)); + + Planet nearestPlanet = game.getPlanetManager().getNearestPlanet(hero.getPosition()); + if (nearestPlanet.isNearGround(hero.getPosition())) { + Vector2 enemyOffset = SolMath.fromAl(game.getCam().getAngle() + SolRandom.randomFloat(-8, 8), SolRandom.randomFloat(4, 12)); + enemyPosition.add(enemyOffset); + SolMath.free(enemyOffset); + while (!game.isPlaceEmpty(enemyPosition, false)) { + enemyPosition.set(hero.getPosition()); + enemyOffset = SolMath.fromAl(game.getCam().getAngle() + SolRandom.randomFloat(-8, 8), SolRandom.randomFloat(8, 20)); + enemyPosition.add(enemyOffset); + SolMath.free(enemyOffset); + } + } else { + while (!game.isPlaceEmpty(enemyPosition, false)) { + enemyPosition.set(hero.getPosition().x + SolRandom.randomFloat(2, 10), hero.getPosition().y + SolRandom.randomFloat(2, 10)); + } } Guardian dp = new Guardian(game, enemyConfig, hero.getPilot(), hero.getPosition(), hero.getHull().getHullConfig(), 0); diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java new file mode 100644 index 000000000..46f455ea3 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java @@ -0,0 +1,86 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import com.badlogic.gdx.math.Vector2; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.SolObject; +import org.destinationsol.game.StarPort; +import org.destinationsol.game.planet.Planet; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class FlyToNearestStarPortStep extends FlyToWaypointStep { + private Planet fromPlanet; + private Planet toPlanet; + + public FlyToNearestStarPortStep(TutorialScreen tutorialScreen, SolGame game, String message) { + super(tutorialScreen, game, Vector2.Zero, message); + } + + private Vector2 findNearestStarPort() { + Planet nearestPlanet = game.getPlanetManager().getNearestPlanet(); + Vector2 nearestStarPortPosition = null; + + for (SolObject solObject : game.getObjectManager().getObjects()) { + if (solObject instanceof StarPort && ((StarPort)solObject).getFromPlanet() == nearestPlanet) { + StarPort starPort = (StarPort) solObject; + fromPlanet = starPort.getFromPlanet(); + toPlanet = starPort.getToPlanet(); + nearestStarPortPosition = solObject.getPosition(); + break; + } + } + for (StarPort.FarStarPort farStarPort : game.getObjectManager().getFarPorts()) { + if (farStarPort.getFrom() == nearestPlanet) { + fromPlanet = farStarPort.getFrom(); + toPlanet = farStarPort.getTo(); + nearestStarPortPosition = farStarPort.getPosition(); + break; + } + } + + return nearestStarPortPosition; + } + + @Override + public void start() { + Vector2 nearestStarPortPosition = findNearestStarPort(); + if (nearestStarPortPosition == null) { + throw new RuntimeException("Unable to find nearby star port!"); + } + waypointPosition = nearestStarPortPosition; + super.start(); + } + + @Override + public boolean checkComplete(float timeStep) { + for (SolObject solObject : game.getObjectManager().getObjects()) { + if (solObject instanceof StarPort && ((StarPort)solObject).getFromPlanet() == fromPlanet && + ((StarPort) solObject).getToPlanet() == toPlanet) { + if (game.getHero().getPosition().dst(solObject.getPosition()) < game.getObjectManager().getRadius(solObject)) { + game.getHero().removeWaypoint(waypoint); + game.getObjectManager().removeObjDelayed(waypoint); + return true; + } else { + waypoint.position.set(solObject.getPosition()); + } + break; + } + } + return super.checkComplete(timeStep); + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java index 3abf58a3d..5993faa17 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java @@ -18,7 +18,11 @@ import com.badlogic.gdx.math.Vector2; import org.destinationsol.game.SolGame; +import org.destinationsol.game.SolObject; import org.destinationsol.game.planet.Planet; +import org.destinationsol.game.ship.FarShip; +import org.destinationsol.game.ship.SolShip; +import org.destinationsol.game.ship.hulls.HullConfig; import org.destinationsol.ui.nui.screens.TutorialScreen; import java.util.ArrayList; @@ -35,7 +39,7 @@ public void start() { Vector2 heroPosition = game.getHero().getPosition(); for (Planet planet : game.getPlanetManager().getNearestSystem(heroPosition).getPlanets()) { - if (planet.getConfig().tradeConfig.mercs.groupCount() > 0) { + if (planet.getConfig().easyOnly && planet.getConfig().tradeConfig.mercs.groupCount() > 0) { planetsWithMercenaries.add(planet); } } @@ -60,11 +64,48 @@ public void start() { super.start(); } + private void setPlanetStationWaypoint() { + for (FarShip farShip : game.getObjectManager().getFarShips()) { + if (farShip.getHullConfig().getType() == HullConfig.Type.STATION && + farShip.getTradeContainer() != null && + farShip.getTradeContainer().getMercs().groupCount() > 0 && + planet.isNearGround(farShip.getPosition())) { + waypointPosition = farShip.getPosition(); + waypoint.position.set(waypointPosition); + if (!game.getHero().getWaypoints().contains(waypoint)) { + game.getHero().addWaypoint(waypoint); + game.getObjectManager().addObjNow(game, waypoint); + } + return; + } + } + for (SolObject solObject : game.getObjectManager().getObjects()) { + if (solObject instanceof SolShip && + ((SolShip) solObject).getHull().getHullConfig().getType() == HullConfig.Type.STATION && + ((SolShip) solObject).getTradeContainer() != null && + ((SolShip) solObject).getTradeContainer().getMercs().groupCount() > 0 && + !planet.isNearGround(solObject.getPosition())) { + waypointPosition = solObject.getPosition(); + waypoint.position.set(waypointPosition); + if (!game.getHero().getWaypoints().contains(waypoint)) { + game.getHero().addWaypoint(waypoint); + game.getObjectManager().addObjNow(game, waypoint); + } + return; + } + } + } + @Override public boolean checkComplete(float timeStep) { boolean nearPlanet = super.checkComplete(timeStep); if (nearPlanet && planet.areObjectsCreated()) { - return game.getScreens().talkScreen.isTargetFar(game.getHero()); + setPlanetStationWaypoint(); + if (game.getMainGameScreen().getTalkButton().isEnabled()) { + game.getHero().getWaypoints().remove(waypoint); + game.getObjectManager().removeObjDelayed(waypoint); + return true; + } } return false; } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java index ceb947115..b0981fae3 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java @@ -40,9 +40,6 @@ public void start() { public boolean checkComplete(float timeStep) { Hero hero = game.getHero(); if (planet.isNearGround(hero.getPosition())) { - game.getObjectManager().removeObjDelayed(waypoint); - // Force an object manager update to remove the waypoint. - game.getObjectManager().update(game, timeStep); hero.removeWaypoint(waypoint); return true; } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java index fd2da7e29..ced2684f3 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java @@ -53,6 +53,11 @@ public void start() { @Override public boolean checkComplete(float timeStep) { Hero hero = game.getHero(); + if (!hero.getWaypoints().contains(waypoint)) { + hero.getWaypoints().add(waypoint); + game.getObjectManager().addObjDelayed(waypoint); + } + if (hero.getPosition().dst(waypoint.getPosition()) < MIN_WAYPOINT_DISTANCE) { hero.removeWaypoint(waypoint); game.getObjectManager().removeObjDelayed(waypoint); From 48c0831f2281ff1ac7af6dd23cb6fc8c07c32f87 Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Sun, 5 Mar 2023 19:58:52 +0000 Subject: [PATCH 06/18] fix(tutorial): vary hints based on input method --- .../game/tutorial/NewTutorialManager.java | 66 +++++++++++++------ .../game/tutorial/steps/MessageStep.java | 7 +- .../nui/screens/mainMenu/MainMenuScreen.java | 2 - .../ui/nui/widgets/InteractHint.java | 4 ++ .../assets/skins/tutorialScreen.skin | 11 ++-- 5 files changed, 64 insertions(+), 26 deletions(-) diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java index c13c16031..a3ef94c53 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java @@ -81,39 +81,67 @@ public void start() { tutorialScreen.clearAllTutorialBoxes(); GameOptions gameOptions = solApplication.getOptions(); + GameOptions.ControlType controlType = gameOptions.controlType; boolean isMobile = solApplication.isMobile(); boolean usesKeyboard = !isMobile && - (gameOptions.controlType == GameOptions.ControlType.KEYBOARD || gameOptions.controlType == GameOptions.ControlType.MIXED); + (controlType != GameOptions.ControlType.MOUSE); + + String turnControlHint = ""; + String thrustForwardControlHint = ""; + String shootControlHint = ""; + String abilityControlHint = ""; + switch (controlType) { + case KEYBOARD: + turnControlHint = gameOptions.getKeyLeftName() + " and " + gameOptions.getKeyRightName(); + thrustForwardControlHint = gameOptions.getKeyUpName(); + shootControlHint = gameOptions.getKeyShootName(); + abilityControlHint = gameOptions.getKeyAbilityName(); + break; + case MIXED: + case MOUSE: + turnControlHint = "Move Mouse"; + thrustForwardControlHint = gameOptions.getKeyUpMouseName(); + shootControlHint = "Left Mouse Button"; + abilityControlHint = "Middle Mouse Button"; + break; + case CONTROLLER: + if (gameOptions.getControllerAxisLeftRight() > 0) { + turnControlHint = "Axis " + gameOptions.getControllerAxisLeftRight(); + } else { + turnControlHint = "Button " + gameOptions.getControllerButtonLeft() + " and Button " + gameOptions.getControllerButtonRight(); + } + if (gameOptions.getControllerAxisUpDown() > 0) { + thrustForwardControlHint = "Axis " + gameOptions.getControllerAxisUpDown(); + } else { + thrustForwardControlHint = "Button " + gameOptions.getControllerButtonUp(); + } + if (gameOptions.getControllerAxisShoot() > 0) { + shootControlHint = "Axis " + gameOptions.getControllerAxisShoot(); + } else { + shootControlHint = "Button " + gameOptions.getControllerButtonShoot(); + } + if (gameOptions.getControllerAxisAbility() > 0) { + abilityControlHint = "Axis " + gameOptions.getControllerAxisAbility(); + } else { + abilityControlHint = "Button " + gameOptions.getControllerButtonAbility(); + } + } steps = new TutorialStep[] { new MessageStep(tutorialScreen, solGame.get(), "Section 1 - Movement"), - new TurnLeftRightStep(tutorialScreen, solGame.get(), "Turn left and right (" + - ((gameOptions.controlType == GameOptions.ControlType.MIXED) ? "Move Mouse" : - gameOptions.getKeyLeftName() + " and " + gameOptions.getKeyRightName()) - + ")."), - new ThrustForwardsStep(tutorialScreen, solGame.get(), - (gameOptions.controlType == GameOptions.ControlType.MOUSE || gameOptions.controlType == GameOptions.ControlType.MIXED) ? - "Thrust forwards (" + gameOptions.getKeyUpMouseName() + ")." : - !isMobile ? - "Thrust forwards (" + gameOptions.getKeyUpName() + ")." : - "Thrust forwards."), + new TurnLeftRightStep(tutorialScreen, solGame.get(), isMobile ? "Turn left and right." : "Turn left and right (" + turnControlHint + ")."), + new ThrustForwardsStep(tutorialScreen, solGame.get(), isMobile ? "Thrust forwards." : "Thrust forwards (" + thrustForwardControlHint + ")."), new SlowVelocityStep(tutorialScreen, solGame.get(), 0.05f, "Turn around and slow to a stop."), new FlyToRandomWaypointAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 4.0f, "Fly to the waypoint."), new MessageStep(tutorialScreen, solGame.get(), "Section 2 - Weapons"), - new FireGunStep(tutorialScreen, solGame.get(), isMobile ? - "Fire your gun." : - "Fire your gun (" + (gameOptions.controlType == GameOptions.ControlType.MIXED ? - "Left mouse button" : // TODO: Why isn't there a setting for this control? - gameOptions.getKeyShootName()) + ")."), + new FireGunStep(tutorialScreen, solGame.get(), isMobile ? "Fire your gun." : "Fire your gun (" + shootControlHint + ")."), new CheckGunReloadStep(tutorialScreen, solGame.get(), false, true, "Firing weapons drains your ammunition. Keep on firing."), new CheckGunReloadStep(tutorialScreen, solGame.get(), false, false, "When your weapon is depleted, it automatically reloads.\n" + "You can't fire when your weapon is reloading."), new UseAbilityStep(tutorialScreen, solGame.get(), isMobile ? "Use your ability." : - "Use your ability (" + (gameOptions.controlType == GameOptions.ControlType.MIXED ? - "Middle mouse button" : // TODO: Why isn't there a setting for this control? - gameOptions.getKeyAbilityName()) + ")."), + "Use your ability (" + abilityControlHint + ")."), new MessageStep(tutorialScreen, solGame.get(), "Abilities consume ability charges."), new MessageStep(tutorialScreen, solGame.get(), "Section 3 - Money"), new DestroySpawnedAsteroidAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 4.0f, "Fire at the asteroid."), diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java index 04e5ce850..6c50b3656 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java @@ -52,7 +52,12 @@ public void start() { tutorialScreen.setInteractHintInput(MouseInput.MOUSE_LEFT); break; case CONTROLLER: - tutorialScreen.setInteractHintInput(ControllerInput.find(InputType.CONTROLLER_BUTTON, gameOptions.getControllerButtonUp())); + if (gameOptions.getControllerAxisShoot() > 0) { + // Ideally this would use CONTROLLER_AXIS but the ids do not quite match-up. + tutorialScreen.setInteractHintInput(ControllerInput.find(InputType.CONTROLLER_BUTTON, gameOptions.getControllerAxisShoot())); + } else { + tutorialScreen.setInteractHintInput(ControllerInput.find(InputType.CONTROLLER_BUTTON, gameOptions.getControllerButtonShoot())); + } break; } } diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/MainMenuScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/MainMenuScreen.java index 48a03a7d7..34ecb7759 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/MainMenuScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/mainMenu/MainMenuScreen.java @@ -80,8 +80,6 @@ public void onAdded() { if (!musicManager.getCurrentMusicSet().equals(OggMusicManager.MENU_MUSIC_SET)) { musicManager.playMusic(OggMusicManager.MENU_MUSIC_SET, solApplication.getOptions()); } - - tutorialButton.setEnabled(solApplication.getOptions().controlType != GameOptions.ControlType.CONTROLLER); } @Override diff --git a/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java b/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java index cd4d68dca..d3d095293 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java @@ -55,6 +55,10 @@ public void onDraw(Canvas canvas) { if (input.get().getType() == InputType.KEY) { canvas.drawText(input.get().getDisplayName(), canvas.getCurrentStyle().getBackground().getPixelRegion()); } + + if (input.get().getType() == InputType.CONTROLLER_BUTTON || input.get().getType() == InputType.CONTROLLER_AXIS) { + canvas.drawText(String.valueOf(input.get().getId()), canvas.getCurrentStyle().getBackground().getPixelRegion()); + } } @Override diff --git a/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin index 8711a4434..93f759ebb 100644 --- a/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin +++ b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin @@ -28,7 +28,7 @@ "InteractHint": { "background": "engine:uiWhiteTex", "background-scale-mode": "scale fit", - "font": "engine:main#0.36", + "font": "engine:main#0.30", "parts": { "key": { "modes": { @@ -47,14 +47,17 @@ } } }, - "button": { + "controller_button": { "modes": { "": { - "background": "engine:uiPlanetProximityIndicator" + "background": "engine:uiPlanetProximityIndicator", + "text-shadowed": false, + "text-color": "000000FF", + "font": "engine:main#0.40" } } }, - "axis": { + "controller_axis": { "background": "engine:uiWhiteTex" } } From f09622440470e51fad879d5251d0432cf64cd9af Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Tue, 7 Mar 2023 23:06:06 +0000 Subject: [PATCH 07/18] fix(tutorial): item types explanation and various tutorial fixes Fixed item equipped check. In the waypoint creation step, only waypoints placed near the player are accepted. --- .../game/tutorial/NewTutorialManager.java | 20 +++-- .../game/tutorial/steps/BuyItemStep.java | 13 ++- .../game/tutorial/steps/BuyMercenaryStep.java | 7 +- .../tutorial/steps/CheckItemEquippedStep.java | 18 ++-- .../tutorial/steps/CreateWaypointStep.java | 14 +++- .../game/tutorial/steps/FireGunStep.java | 3 +- .../steps/ItemTypesExplanationStep.java | 82 +++++++++++++++++++ .../game/tutorial/steps/MessageStep.java | 7 +- .../game/tutorial/steps/SlowVelocityStep.java | 7 +- .../ui/nui/screens/InventoryScreen.java | 36 ++++++++ .../ui/nui/screens/TutorialScreen.java | 63 ++++++++++++++ 11 files changed, 250 insertions(+), 20 deletions(-) create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java index a3ef94c53..18888e19c 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java @@ -35,6 +35,7 @@ import org.destinationsol.game.tutorial.steps.FlyToNearestStationStep; import org.destinationsol.game.tutorial.steps.FlyToPlanetSellingMercenariesStep; import org.destinationsol.game.tutorial.steps.FlyToRandomWaypointAroundHeroStep; +import org.destinationsol.game.tutorial.steps.ItemTypesExplanationStep; import org.destinationsol.game.tutorial.steps.ManageMercenariesGuidanceStep; import org.destinationsol.game.tutorial.steps.MapDragStep; import org.destinationsol.game.tutorial.steps.MessageStep; @@ -131,7 +132,7 @@ public void start() { new MessageStep(tutorialScreen, solGame.get(), "Section 1 - Movement"), new TurnLeftRightStep(tutorialScreen, solGame.get(), isMobile ? "Turn left and right." : "Turn left and right (" + turnControlHint + ")."), new ThrustForwardsStep(tutorialScreen, solGame.get(), isMobile ? "Thrust forwards." : "Thrust forwards (" + thrustForwardControlHint + ")."), - new SlowVelocityStep(tutorialScreen, solGame.get(), 0.05f, "Turn around and slow to a stop."), + new SlowVelocityStep(tutorialScreen, solGame.get(), 0.1f, "Turn around and thrust again to slow down.\n\nTry slowing to a stop."), new FlyToRandomWaypointAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 4.0f, "Fly to the waypoint."), new MessageStep(tutorialScreen, solGame.get(), "Section 2 - Weapons"), new FireGunStep(tutorialScreen, solGame.get(), isMobile ? "Fire your gun." : "Fire your gun (" + shootControlHint + ")."), @@ -153,6 +154,7 @@ public void start() { usesKeyboard ? "Open your inventory (" + gameOptions.getKeyInventoryName() + ")." : "Open your inventory."), + new ItemTypesExplanationStep(tutorialScreen, solGame.get()), new SelectEquippedWeaponStep(tutorialScreen, solGame.get().getScreens().inventoryScreen, usesKeyboard ? @@ -166,8 +168,12 @@ public void start() { solGame.get().getScreens().inventoryScreen, true, usesKeyboard ? "Re-equip that item (" + gameOptions.getKeyEquipName() + ")." : "Un-equip an item."), + new CloseScreenStep(tutorialScreen, nuiManager, + solGame.get().getScreens().inventoryScreen.getCloseButton(), + solGame.get().getScreens().inventoryScreen, + isMobile ? "Close your inventory (tap outside of your inventory)." : "Close your inventory."), new MessageStep(tutorialScreen, solGame.get(), "Section 5 - Weapon Mounts"), - new MessageStep(tutorialScreen, solGame.get(), "All ships may come with with up to two weapon mounts."), + new MessageStep(tutorialScreen, solGame.get(), "All ships may come with up to two weapon mounts."), new MessageStep(tutorialScreen, solGame.get(), "Weapon mounts vary by their socket type."), new MessageStep(tutorialScreen, solGame.get(), "Weapon sockets are either fixed or rotating."), new MessageStep(tutorialScreen, solGame.get(), "A weapon can only be equipped if its socket type matches its mount."), @@ -177,11 +183,13 @@ public void start() { solGame.get().getScreens().mainGameScreen.getTalkButton(), solGame.get().getScreens().talkScreen, isMobile ? "Talk to the station." : "Talk to the station (" + gameOptions.getKeyTalkName() + ")."), - new BuyItemStep(tutorialScreen, + new BuyItemStep(tutorialScreen, nuiManager, solGame.get().getScreens().talkScreen.getBuyButton(), solGame.get().getScreens().inventoryScreen.getBuyItemsScreen().getBuyControl(), isMobile ? "Buy an item." : "Buy an item (" + gameOptions.getKeyBuyItemName() + ")."), new MessageStep(tutorialScreen, solGame.get(), "Section 7 - Combat"), + new MessageStep(tutorialScreen, solGame.get(), "You'll encounter hostile ships whilst travelling. " + + "Shoot at the ships to destroy them.\nHere's one coming now."), new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:minerSmall", "core:fixedBlaster", "Destroy all targets.", "Enemy ships can be tough.\nOpen the pause menu and select Respawn."), @@ -199,7 +207,7 @@ public void start() { new MapDragStep(tutorialScreen, solGame.get(), solGame.get().getMapDrawer(), "You can move around the map by clicking/tapping and dragging."), new CreateWaypointStep(tutorialScreen, solGame.get(), solGame.get().getScreens().mapScreen.getAddWaypointButton(), - "Create a waypoint."), + "Create a waypoint near your ship."), new CloseScreenStep(tutorialScreen, nuiManager, solGame.get().getScreens().mapScreen.getCloseButton(), @@ -214,10 +222,6 @@ public void start() { solGame.get().getScreens().talkScreen, "Talk to the station."), new BuyMercenaryStep(tutorialScreen, solGame.get(), 1000, "Try hiring a mercenary."), - new CloseScreenStep(tutorialScreen, nuiManager, - solGame.get().getScreens().inventoryScreen.getCloseButton(), - solGame.get().getScreens().inventoryScreen, - "Close the buy screen."), new MessageStep(tutorialScreen, solGame.get(), "Let's see how your mercenary fights."), new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:pirateSmall", "core:blaster core:smallShield", "Destroy all targets.", diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java index 4154b38e4..6e001566d 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java @@ -17,20 +17,24 @@ package org.destinationsol.game.tutorial.steps; import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.NUIManager; import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; import org.terasology.nui.HorizontalAlign; public class BuyItemStep extends TutorialStep { private final TutorialScreen tutorialScreen; + private final NUIManager nuiManager; private final UIWarnButton buyButton; private final UIWarnButton purchaseButton; private final String message; private boolean buyButtonPressed = false; private boolean purchaseButtonPressed = false; - public BuyItemStep(TutorialScreen tutorialScreen, UIWarnButton buyButton, UIWarnButton purchaseButton, String message) { + public BuyItemStep(TutorialScreen tutorialScreen, NUIManager nuiManager, + UIWarnButton buyButton, UIWarnButton purchaseButton, String message) { this.tutorialScreen = tutorialScreen; + this.nuiManager = nuiManager; this.buyButton = buyButton; this.purchaseButton = purchaseButton; this.message = message; @@ -51,6 +55,11 @@ public boolean checkComplete(float timeStep) { } else { purchaseButton.enableWarn(); } - return buyButtonPressed && purchaseButtonPressed; + + if (buyButtonPressed && purchaseButtonPressed) { + nuiManager.popScreen(); + return true; + } + return false; } } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java index 92cca3c5b..92ae3d994 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java @@ -65,6 +65,11 @@ public boolean checkComplete(float timeStep) { } else { hireMercenaryButton.enableWarn(); } - return hireButtonPressed && hireMercenaryButtonPressed; + + if (hireButtonPressed && hireMercenaryButtonPressed) { + game.getSolApplication().getNuiManager().popScreen(); + return true; + } + return false; } } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java index 23c6bdb49..5c3036018 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java @@ -20,6 +20,7 @@ import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.nui.screens.InventoryScreen; import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; public class CheckItemEquippedStep extends TutorialStep { private final TutorialScreen tutorialScreen; @@ -27,6 +28,8 @@ public class CheckItemEquippedStep extends TutorialStep { private final boolean equipped; private final String message; private SolItem itemToCheck; + private UIWarnButton equipButton; + private boolean actionPerformed; public CheckItemEquippedStep(TutorialScreen tutorialScreen, InventoryScreen inventoryScreen, boolean equipped, String message) { @@ -41,13 +44,18 @@ public void start() { if (equipped) { itemToCheck = inventoryScreen.getSelectedItem(); } + equipButton = inventoryScreen.getShowInventory().getEq1Control(); + equipButton.subscribe(button -> { + if (equipped && inventoryScreen.getSelectedItem() != itemToCheck) { + return; + } + actionPerformed = true; + }); + actionPerformed = false; } public boolean checkComplete(float timeStep) { - if (equipped) { - return itemToCheck.isEquipped() > 0; - } else { - return inventoryScreen.getSelectedItem().isEquipped() == 0; - } + equipButton.enableWarn(); + return actionPerformed; } } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java index ea128ad71..ee447e928 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java @@ -19,6 +19,7 @@ import org.destinationsol.game.Hero; import org.destinationsol.game.SolGame; import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.Waypoint; import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; import org.terasology.nui.HorizontalAlign; @@ -29,6 +30,7 @@ public class CreateWaypointStep extends TutorialStep { private final UIWarnButton addWaypointButton; private final String message; private boolean buttonPressed = false; + private int lastWaypointCount; public CreateWaypointStep(TutorialScreen tutorialScreen, SolGame game, UIWarnButton addWaypointButton, String message) { this.tutorialScreen = tutorialScreen; @@ -43,6 +45,7 @@ public void start() { addWaypointButton.enableWarn(); buttonPressed = true; }); + lastWaypointCount = game.getHero().getWaypoints().size(); } public boolean checkComplete(float timeStep) { if (!buttonPressed) { @@ -50,6 +53,15 @@ public boolean checkComplete(float timeStep) { } Hero hero = game.getHero(); - return hero.getWaypoints().size() > 0; + if (hero.getWaypoints().size() > lastWaypointCount) { + Waypoint waypoint = hero.getWaypoints().get(hero.getWaypoints().size()-1); + if (waypoint.getPosition().dst(hero.getPosition()) < 100.0f) { + return true; + } else { + hero.removeWaypoint(waypoint); + } + } + lastWaypointCount = game.getHero().getWaypoints().size(); + return false; } } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java index cd0e4270e..413d39cea 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java @@ -43,6 +43,7 @@ public boolean checkComplete(float timeStep) { fireButton.enableWarn(); } - return super.checkComplete(timeStep) && game.getHero().getPilot().isShoot(); + stepTimer += timeStep; + return stepTimer > MIN_STEP_DURATION && game.getHero().getPilot().isShoot(); } } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java new file mode 100644 index 000000000..5d2e8dd11 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java @@ -0,0 +1,82 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.SolGame; +import org.destinationsol.game.item.Armor; +import org.destinationsol.game.item.Gun; +import org.destinationsol.game.item.Shield; +import org.destinationsol.game.item.SolItem; +import org.destinationsol.ui.nui.screens.TutorialScreen; + +public class ItemTypesExplanationStep extends MessageStep { + private enum ExplanationType { + // TODO: Pass these in as constructor parameters instead. + WEAPONS(Gun.class, "You can mine asteroids and attack enemies with guns."), + ARMOUR(Armor.class, "Armour makes attacks less effective against you."), + SHIELDS(Shield.class, "Shields absorb energy-based projectiles until depleted."), + LAST(null, ""); + + private final Class itemClass; + private final String explanation; + + ExplanationType(Class itemClass, String explanation) { + this.itemClass = itemClass; + this.explanation = explanation; + } + + public Class getItemClass() { + return itemClass; + } + + public String getExplanation() { + return explanation; + } + + public ExplanationType next() { + return ExplanationType.values()[this.ordinal()+1]; + } + }; + private ExplanationType explanationType; + + public ItemTypesExplanationStep(TutorialScreen tutorialScreen, SolGame game) { + super(tutorialScreen, game, ""); + } + + @Override + public void start() { + super.start(); + explanationType = ExplanationType.WEAPONS; + tutorialScreen.setTutorialText(explanationType.getExplanation()); + } + + @Override + public boolean checkComplete(float timeStep) { + game.getScreens().inventoryScreen.getItemUIControlsForTutorialByType(explanationType.getItemClass()).get(0).enableWarn(); + boolean complete = super.checkComplete(timeStep); + if (complete) { + explanationType = explanationType.next(); + if (explanationType == ExplanationType.LAST) { + return true; + } + stepTimer = 0.0f; + interactComplete = false; + tutorialScreen.setTutorialText(explanationType.getExplanation()); + } + return false; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java index 6c50b3656..e5478b74e 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java @@ -31,6 +31,7 @@ public class MessageStep extends TutorialStep { protected final SolGame game; protected final String message; protected float stepTimer; + protected boolean interactComplete; public MessageStep(TutorialScreen tutorialScreen, SolGame game, String message) { this.tutorialScreen = tutorialScreen; @@ -60,11 +61,15 @@ public void start() { } break; } + interactComplete = false; + tutorialScreen.setInteractEvent(input -> { + interactComplete = true; + }); } @Override public boolean checkComplete(float timeStep) { stepTimer += timeStep; - return stepTimer >= MIN_STEP_DURATION && game.getHero().getPilot().isShoot(); + return stepTimer >= MIN_STEP_DURATION && interactComplete; } } \ No newline at end of file diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java index c01a41a33..ecc9914c5 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java @@ -22,10 +22,12 @@ import org.destinationsol.ui.nui.screens.TutorialScreen; public class SlowVelocityStep extends TutorialStep { + private static final float MAX_ACCELERATION = 0.8f; private final TutorialScreen tutorialScreen; private final SolGame game; private final float threshold; private final String message; + private float lastSpeed; public SlowVelocityStep(TutorialScreen tutorialScreen, SolGame game, float threshold, String message) { this.tutorialScreen = tutorialScreen; @@ -37,11 +39,14 @@ public SlowVelocityStep(TutorialScreen tutorialScreen, SolGame game, float thres @Override public void start() { tutorialScreen.setTutorialText(message); + lastSpeed = game.getHero().getVelocity().len(); } @Override public boolean checkComplete(float timeStep) { Hero hero = game.getHero(); - return hero.getShip().getVelocity().len() < threshold; + float speed = hero.getVelocity().len(); + float acceleration = (speed - lastSpeed)/timeStep; + return acceleration < MAX_ACCELERATION && speed < threshold; } } diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/InventoryScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/InventoryScreen.java index 9f1534ac2..3ca548f90 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/InventoryScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/InventoryScreen.java @@ -379,6 +379,42 @@ public List getEquippedItemUIControlsForTutorial() { return controls; } + /** + * This is an internal API used by the tutorial. It just returns the buttons representing items of a specified type. + * @return The buttons representing items of a specified type. + */ + public List getItemUIControlsForTutorialByType(Class itemClass) { + List controls = new ArrayList<>(); + + Iterator rowsIterator = inventoryRows.iterator(); + rowsIterator.next(); // Skip header + UIWidget row = rowsIterator.next(); + int startIndex = page * Const.ITEM_GROUPS_PER_PAGE; + ItemContainer items = inventoryOperations.getItems(solApplication.getGame()); + for (int rowNo = 0; rowNo < Const.ITEM_GROUPS_PER_PAGE; rowNo++) { + int groupNo = startIndex + rowNo; + boolean emptyRow = groupNo >= items.groupCount(); + + UIWarnButton itemButton = row.find("itemButton", UIWarnButton.class); + if (emptyRow) { + break; + } else { + List itemGroup = items.getGroup(groupNo); + SolItem sample = itemGroup.get(0); + + if (itemClass.isInstance(sample)) { + controls.add(itemButton); + } + } + + if (rowsIterator.hasNext()) { + row = rowsIterator.next(); + } + } + + return controls; + } + /** * @return the next button - used in the tutorial */ diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java index ba66314de..3accce567 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java @@ -20,12 +20,18 @@ import org.destinationsol.ui.nui.NUIScreenLayer; import org.destinationsol.ui.nui.widgets.InteractHint; import org.terasology.input.Input; +import org.terasology.nui.BaseInteractionListener; +import org.terasology.nui.Canvas; import org.terasology.nui.HorizontalAlign; +import org.terasology.nui.InteractionListener; +import org.terasology.nui.events.NUIKeyEvent; +import org.terasology.nui.events.NUIMouseClickEvent; import org.terasology.nui.widgets.UIBox; import org.terasology.nui.widgets.UILabel; import javax.inject.Inject; import java.util.EnumMap; +import java.util.function.Consumer; /** * This screen displays the message box shown during the tutorial to instruct the user. @@ -37,6 +43,7 @@ private static final class TutorialBox { public final UIBox box; public final UILabel text; public final InteractHint interactHint; + public Consumer inputEventListener; public TutorialBox(UIBox box, UILabel text, InteractHint interactHint) { this.box = box; @@ -46,6 +53,22 @@ public TutorialBox(UIBox box, UILabel text, InteractHint interactHint) { } private final EnumMap tutorialBoxes; + private final InteractionListener mouseInputListener = new BaseInteractionListener() { + @Override + public boolean onMouseClick(NUIMouseClickEvent event) { + for (TutorialBox tutorialBox : tutorialBoxes.values()) { + if (tutorialBox.interactHint != null && tutorialBox.interactHint.isVisible() && + tutorialBox.interactHint.getInput().equals(event.getMouseButton())) { + if (tutorialBox.inputEventListener != null) { + tutorialBox.inputEventListener.accept(event.getMouseButton()); + } + return true; + } + } + return super.onMouseClick(event); + } + }; + private boolean isReplaceRemove; @Inject @@ -103,6 +126,14 @@ public void setInteractHintInput(HorizontalAlign horizontalAlign, Input input) { } } + public void setInteractEvent(Consumer interactEvent) { + setInteractEvent(HorizontalAlign.CENTER, interactEvent); + } + + public void setInteractEvent(HorizontalAlign horizontalAlign, Consumer interactEvent) { + tutorialBoxes.get(horizontalAlign).inputEventListener = interactEvent; + } + public void clearAllTutorialBoxes() { for (TutorialBox tutorialBox : tutorialBoxes.values()) { if (tutorialBox.box != null) { @@ -114,6 +145,7 @@ public void clearAllTutorialBoxes() { if (tutorialBox.interactHint != null) { tutorialBox.interactHint.setVisible(false); } + tutorialBox.inputEventListener = null; } } @@ -139,6 +171,37 @@ protected boolean escapeCloses() { return false; } + @Override + public boolean onKeyEvent(NUIKeyEvent event) { + if (event.isDown()) { + return super.onKeyEvent(event); + } + + for (TutorialBox tutorialBox : tutorialBoxes.values()) { + if (tutorialBox.interactHint != null && tutorialBox.interactHint.isVisible() && + tutorialBox.interactHint.getInput().equals(event.getKey())) { + if (tutorialBox.inputEventListener != null) { + tutorialBox.inputEventListener.accept(event.getKey()); + } + return true; + } + } + return super.onKeyEvent(event); + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + InteractHint leftInteractHint = getInteractHint(HorizontalAlign.LEFT); + InteractHint centreInteractHint = getInteractHint(HorizontalAlign.CENTER); + InteractHint rightInteractHint = getInteractHint(HorizontalAlign.RIGHT); + if ((leftInteractHint != null && leftInteractHint.isVisible()) || + (centreInteractHint != null && centreInteractHint.isVisible()) || + (rightInteractHint != null && rightInteractHint.isVisible())) { + canvas.addInteractionRegion(mouseInputListener, canvas.getRegion()); + } + } + public void moveToTop() { isReplaceRemove = true; nuiManager.removeScreen(this); From a25b9d84c92d0250f95be58e8b026a9240bcfdf2 Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Thu, 9 Mar 2023 20:21:04 +0000 Subject: [PATCH 08/18] fix(tutorial): reduce crashes when failing to find a star port --- .../tutorial/steps/FlyToNearestStarPortStep.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java index 46f455ea3..054193f62 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java @@ -32,24 +32,27 @@ public FlyToNearestStarPortStep(TutorialScreen tutorialScreen, SolGame game, Str } private Vector2 findNearestStarPort() { - Planet nearestPlanet = game.getPlanetManager().getNearestPlanet(); + Vector2 heroPosition = game.getHero().getPosition(); + float nearestStarPortDistance = Float.MAX_VALUE; Vector2 nearestStarPortPosition = null; for (SolObject solObject : game.getObjectManager().getObjects()) { - if (solObject instanceof StarPort && ((StarPort)solObject).getFromPlanet() == nearestPlanet) { + float distance = solObject.getPosition().dst(heroPosition); + if (solObject instanceof StarPort && distance < nearestStarPortDistance) { StarPort starPort = (StarPort) solObject; fromPlanet = starPort.getFromPlanet(); toPlanet = starPort.getToPlanet(); nearestStarPortPosition = solObject.getPosition(); - break; + nearestStarPortDistance = distance; } } for (StarPort.FarStarPort farStarPort : game.getObjectManager().getFarPorts()) { - if (farStarPort.getFrom() == nearestPlanet) { + float distance = farStarPort.getPosition().dst(heroPosition); + if (distance < nearestStarPortDistance) { fromPlanet = farStarPort.getFrom(); toPlanet = farStarPort.getTo(); nearestStarPortPosition = farStarPort.getPosition(); - break; + nearestStarPortDistance = distance; } } From b3389b8f3dcbf107b480faf5de32a654c700b809 Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Thu, 9 Mar 2023 20:23:10 +0000 Subject: [PATCH 09/18] fix(tutorial): adjusted tutorial text for conciseness --- .../game/tutorial/NewTutorialManager.java | 39 +++++++++----- .../steps/ItemTypesExplanationStep.java | 52 ++++++------------- .../game/tutorial/steps/MapDragStep.java | 17 +++--- .../game/tutorial/steps/MessageStep.java | 3 ++ .../ui/nui/widgets/InteractHint.java | 2 +- 5 files changed, 58 insertions(+), 55 deletions(-) diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java index 18888e19c..0aa7f7202 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java @@ -20,6 +20,10 @@ import org.destinationsol.SolApplication; import org.destinationsol.game.SolGame; import org.destinationsol.game.UpdateAwareSystem; +import org.destinationsol.game.item.Armor; +import org.destinationsol.game.item.Gun; +import org.destinationsol.game.item.Shield; +import org.destinationsol.game.item.SolItem; import org.destinationsol.game.tutorial.steps.ButtonPressStep; import org.destinationsol.game.tutorial.steps.BuyItemStep; import org.destinationsol.game.tutorial.steps.BuyMercenaryStep; @@ -54,6 +58,8 @@ import javax.inject.Inject; import javax.inject.Provider; +import java.util.HashMap; +import java.util.Map; public class NewTutorialManager implements UpdateAwareSystem { private static final Logger logger = LoggerFactory.getLogger(NewTutorialManager.class); @@ -128,6 +134,11 @@ public void start() { } } + Map, String> itemTypesExplanations = new HashMap<>(); + itemTypesExplanations.put(Gun.class, "You can mine asteroids and attack enemies with guns."); + itemTypesExplanations.put(Armor.class, "Armour makes attacks less effective against you."); + itemTypesExplanations.put(Shield.class, "Shields absorb energy-based projectiles until depleted."); + steps = new TutorialStep[] { new MessageStep(tutorialScreen, solGame.get(), "Section 1 - Movement"), new TurnLeftRightStep(tutorialScreen, solGame.get(), isMobile ? "Turn left and right." : "Turn left and right (" + turnControlHint + ")."), @@ -138,8 +149,8 @@ public void start() { new FireGunStep(tutorialScreen, solGame.get(), isMobile ? "Fire your gun." : "Fire your gun (" + shootControlHint + ")."), new CheckGunReloadStep(tutorialScreen, solGame.get(), false, true, "Firing weapons drains your ammunition. Keep on firing."), new CheckGunReloadStep(tutorialScreen, solGame.get(), false, false, - "When your weapon is depleted, it automatically reloads.\n" + - "You can't fire when your weapon is reloading."), + "Your weapon reloads when depleted.\n" + + "You can't fire when reloading."), new UseAbilityStep(tutorialScreen, solGame.get(), isMobile ? "Use your ability." : "Use your ability (" + abilityControlHint + ")."), @@ -154,7 +165,11 @@ public void start() { usesKeyboard ? "Open your inventory (" + gameOptions.getKeyInventoryName() + ")." : "Open your inventory."), - new ItemTypesExplanationStep(tutorialScreen, solGame.get()), + new ItemTypesExplanationStep(tutorialScreen, solGame.get(), itemTypesExplanations, new Class[] { + Gun.class, + Armor.class, + Shield.class + }), new SelectEquippedWeaponStep(tutorialScreen, solGame.get().getScreens().inventoryScreen, usesKeyboard ? @@ -171,12 +186,11 @@ public void start() { new CloseScreenStep(tutorialScreen, nuiManager, solGame.get().getScreens().inventoryScreen.getCloseButton(), solGame.get().getScreens().inventoryScreen, - isMobile ? "Close your inventory (tap outside of your inventory)." : "Close your inventory."), + isMobile ? "Close your inventory (tap outside of the inventory)." : "Close your inventory."), new MessageStep(tutorialScreen, solGame.get(), "Section 5 - Weapon Mounts"), new MessageStep(tutorialScreen, solGame.get(), "All ships may come with up to two weapon mounts."), - new MessageStep(tutorialScreen, solGame.get(), "Weapon mounts vary by their socket type."), - new MessageStep(tutorialScreen, solGame.get(), "Weapon sockets are either fixed or rotating."), - new MessageStep(tutorialScreen, solGame.get(), "A weapon can only be equipped if its socket type matches its mount."), + new MessageStep(tutorialScreen, solGame.get(), "Weapon mounts are either fixed or rotating."), + new MessageStep(tutorialScreen, solGame.get(), "You can only equip weapons on matching mounts."), new MessageStep(tutorialScreen, solGame.get(), "Section 6 - Shops"), new FlyToNearestStationStep(tutorialScreen, solGame.get(), "Fly to the station."), new OpenScreenStep(tutorialScreen, nuiManager, @@ -188,10 +202,9 @@ public void start() { solGame.get().getScreens().inventoryScreen.getBuyItemsScreen().getBuyControl(), isMobile ? "Buy an item." : "Buy an item (" + gameOptions.getKeyBuyItemName() + ")."), new MessageStep(tutorialScreen, solGame.get(), "Section 7 - Combat"), - new MessageStep(tutorialScreen, solGame.get(), "You'll encounter hostile ships whilst travelling. " + - "Shoot at the ships to destroy them.\nHere's one coming now."), + new MessageStep(tutorialScreen, solGame.get(), "Shoot at ships to destroy them.\n"), new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:minerSmall", - "core:fixedBlaster", "Destroy all targets.", + "core:fixedBlaster", "Destroy the targeted ship.", "Enemy ships can be tough.\nOpen the pause menu and select Respawn."), new MessageStep(tutorialScreen, solGame.get(), "Destroyed ships drop valuable loot."), new MessageStep(tutorialScreen, solGame.get(), "Section 8 - Repair Kits"), @@ -204,7 +217,7 @@ public void start() { isMobile ? "Open the map." : "Open the map (" + gameOptions.getKeyMapName() + ")."), new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomInButton(), "Zoom In"), new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomOutButton(), "Zoom Out"), - new MapDragStep(tutorialScreen, solGame.get(), solGame.get().getMapDrawer(), "You can move around the map by clicking/tapping and dragging."), + new MapDragStep(tutorialScreen, solGame.get().getMapDrawer(), "You can move around the map by clicking/tapping and dragging."), new CreateWaypointStep(tutorialScreen, solGame.get(), solGame.get().getScreens().mapScreen.getAddWaypointButton(), "Create a waypoint near your ship."), @@ -224,7 +237,7 @@ public void start() { new BuyMercenaryStep(tutorialScreen, solGame.get(), 1000, "Try hiring a mercenary."), new MessageStep(tutorialScreen, solGame.get(), "Let's see how your mercenary fights."), new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:pirateSmall", - "core:blaster core:smallShield", "Destroy all targets.", + "core:blaster core:smallShield", "Destroy the targeted ship.", "Enemy ships can be tough.\nOpen the pause menu and select Respawn."), new MessageStep(tutorialScreen, solGame.get(), "Mercenaries will keep any money they collect as part of their payment."), new MessageStep(tutorialScreen, solGame.get(), "Section 11 - Managing Mercenaries"), @@ -240,7 +253,7 @@ public void start() { "Here you can manage your mercenary's equipment."), new FlyToNearestStarPortStep(tutorialScreen, solGame.get(), "Fly to the marked star lane."), new MessageStep(tutorialScreen, solGame.get(), - "Star lanes allow you to travel quickly between planets.\nThey cost money to use.") + "For a small fee, star lanes allow you to travel quickly between planets.") }; stepNo = 0; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java index 5d2e8dd11..93ba11490 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java @@ -23,59 +23,41 @@ import org.destinationsol.game.item.SolItem; import org.destinationsol.ui.nui.screens.TutorialScreen; -public class ItemTypesExplanationStep extends MessageStep { - private enum ExplanationType { - // TODO: Pass these in as constructor parameters instead. - WEAPONS(Gun.class, "You can mine asteroids and attack enemies with guns."), - ARMOUR(Armor.class, "Armour makes attacks less effective against you."), - SHIELDS(Shield.class, "Shields absorb energy-based projectiles until depleted."), - LAST(null, ""); - - private final Class itemClass; - private final String explanation; - - ExplanationType(Class itemClass, String explanation) { - this.itemClass = itemClass; - this.explanation = explanation; - } - - public Class getItemClass() { - return itemClass; - } - - public String getExplanation() { - return explanation; - } +import java.util.Map; - public ExplanationType next() { - return ExplanationType.values()[this.ordinal()+1]; - } - }; - private ExplanationType explanationType; +public class ItemTypesExplanationStep extends MessageStep { + private final Map, String> itemExplanations; + private final Class[] itemTypes; + private int itemTypeNo; - public ItemTypesExplanationStep(TutorialScreen tutorialScreen, SolGame game) { + public ItemTypesExplanationStep(TutorialScreen tutorialScreen, SolGame game, + Map, String> itemExplanations, + Class[] itemTypes) { super(tutorialScreen, game, ""); + this.itemExplanations = itemExplanations; + this.itemTypes = itemTypes; + this.itemTypeNo = 0; } @Override public void start() { super.start(); - explanationType = ExplanationType.WEAPONS; - tutorialScreen.setTutorialText(explanationType.getExplanation()); + itemTypeNo = 0; + tutorialScreen.setTutorialText(itemExplanations.get(itemTypes[itemTypeNo])); } @Override public boolean checkComplete(float timeStep) { - game.getScreens().inventoryScreen.getItemUIControlsForTutorialByType(explanationType.getItemClass()).get(0).enableWarn(); + game.getScreens().inventoryScreen.getItemUIControlsForTutorialByType(itemTypes[itemTypeNo]).get(0).enableWarn(); boolean complete = super.checkComplete(timeStep); if (complete) { - explanationType = explanationType.next(); - if (explanationType == ExplanationType.LAST) { + itemTypeNo++; + if (itemTypeNo >= itemTypes.length) { return true; } stepTimer = 0.0f; interactComplete = false; - tutorialScreen.setTutorialText(explanationType.getExplanation()); + tutorialScreen.setTutorialText(itemExplanations.get(itemTypes[itemTypeNo])); } return false; } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java index 4af2c77f7..b92524e55 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java @@ -18,22 +18,27 @@ import com.badlogic.gdx.math.Vector2; import org.destinationsol.game.MapDrawer; -import org.destinationsol.game.SolGame; +import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.terasology.input.MouseInput; -public class MapDragStep extends MessageStep { +public class MapDragStep extends TutorialStep { + private final TutorialScreen tutorialScreen; private final MapDrawer mapDrawer; + private final String message; private Vector2 originalMapPosition; - public MapDragStep(TutorialScreen tutorialScreen, SolGame game, MapDrawer mapDrawer, String message) { - super(tutorialScreen, game, message); + public MapDragStep(TutorialScreen tutorialScreen, MapDrawer mapDrawer, String message) { + this.tutorialScreen = tutorialScreen; this.mapDrawer = mapDrawer; + this.message = message; } @Override public void start() { - originalMapPosition = game.getMapDrawer().getMapDrawPositionAdditive().cpy(); - super.start(); + tutorialScreen.setTutorialText(message); + tutorialScreen.setInteractHintInput(MouseInput.MOUSE_LEFT); + originalMapPosition = mapDrawer.getMapDrawPositionAdditive().cpy(); } @Override diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java index e5478b74e..367343f57 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java @@ -61,6 +61,9 @@ public void start() { } break; } + if (game.getSolApplication().isMobile()) { + tutorialScreen.setInteractHintInput(MouseInput.MOUSE_LEFT); + } interactComplete = false; tutorialScreen.setInteractEvent(input -> { interactComplete = true; diff --git a/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java b/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java index d3d095293..ff4861a33 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java @@ -22,7 +22,6 @@ import org.terasology.input.Keyboard; import org.terasology.nui.Canvas; import org.terasology.nui.CoreWidget; -import org.terasology.nui.LayoutConfig; import org.terasology.nui.databinding.Binding; import org.terasology.nui.databinding.DefaultBinding; @@ -33,6 +32,7 @@ public InteractHint() { } public InteractHint(String id, Input input) { + setId(id); this.input.set(input); } From 7cfdf9e1f62812285022bc0a9f6ea173134e30e9 Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Sat, 11 Mar 2023 12:29:55 +0000 Subject: [PATCH 10/18] feat(tutorial): injectable tutorial steps --- .../destinationsol/game/PlayerCreator.java | 14 -- .../java/org/destinationsol/game/SolGame.java | 4 +- .../game/tutorial/NewTutorialManager.java | 198 ++++++++++-------- .../game/tutorial/TutorialStep.java | 43 ++++ .../game/tutorial/steps/ButtonPressStep.java | 14 +- .../game/tutorial/steps/BuyItemStep.java | 40 ++-- .../game/tutorial/steps/BuyMercenaryStep.java | 19 +- .../tutorial/steps/CheckGunReloadStep.java | 19 +- .../tutorial/steps/CheckItemEquippedStep.java | 21 +- .../game/tutorial/steps/CloseScreenStep.java | 18 +- .../tutorial/steps/CreateWaypointStep.java | 27 ++- .../tutorial/steps/DestroyObjectsStep.java | 18 +- .../DestroySpawnedAsteroidAroundHeroStep.java | 14 +- .../steps/DestroySpawnedShipsStep.java | 18 +- .../game/tutorial/steps/FireGunStep.java | 21 +- .../steps/FlyToHeroFirstWaypointStep.java | 15 +- .../steps/FlyToNearestStarPortStep.java | 13 +- .../steps/FlyToNearestStationStep.java | 13 +- .../FlyToPlanetSellingMercenariesStep.java | 14 +- .../game/tutorial/steps/FlyToPlanetStep.java | 13 +- .../FlyToRandomWaypointAroundHeroStep.java | 14 +- .../tutorial/steps/FlyToWaypointStep.java | 18 +- .../steps/ItemTypesExplanationStep.java | 27 ++- .../steps/ManageMercenariesGuidanceStep.java | 36 ++-- .../game/tutorial/steps/MapDragStep.java | 20 +- .../game/tutorial/steps/MessageStep.java | 36 ++-- .../game/tutorial/steps/OpenScreenStep.java | 18 +- .../steps/SelectEquippedWeaponStep.java | 24 ++- .../game/tutorial/steps/SlowVelocityStep.java | 18 +- .../tutorial/steps/ThrustForwardsStep.java | 33 +-- .../tutorial/steps/TurnLeftRightStep.java | 18 +- .../game/tutorial/steps/UseAbilityStep.java | 23 +- .../steps/WaitUntilFullyRepairedStep.java | 18 +- .../ui/nui/screens/TutorialScreen.java | 2 +- 34 files changed, 534 insertions(+), 327 deletions(-) diff --git a/engine/src/main/java/org/destinationsol/game/PlayerCreator.java b/engine/src/main/java/org/destinationsol/game/PlayerCreator.java index 0203ed8b8..6be256014 100644 --- a/engine/src/main/java/org/destinationsol/game/PlayerCreator.java +++ b/engine/src/main/java/org/destinationsol/game/PlayerCreator.java @@ -67,8 +67,6 @@ private void addAndEquipItems(Hero hero, RespawnState respawnState, SolGame game ItemContainer itemContainer = hero.getItemContainer(); if (!respawnState.getRespawnItems().isEmpty()) { addAndEquipRespawnItems(hero, respawnState, itemContainer, game); - } else if (game.isTutorial()) { - addRandomTutorialItems(game, itemContainer); } itemContainer.markAllAsSeen(); } @@ -96,18 +94,6 @@ private void addWaypoints(Hero hero, String waypoints, RespawnState respawnState } } - private void addRandomTutorialItems(SolGame game, ItemContainer itemContainer) { - for (int i = 0; i < NUMBER_OF_TUTORIAL_ITEM_ADD_ATTEMPTS; i++) { - if (itemContainer.groupCount() > MAX_NUMBER_OF_TUTORIAL_ITEM_GROUPS) { - return; - } - SolItem item = game.getItemMan().random(); - if (isNoGunAndHasIcon(item, game) && itemContainer.canAdd(item)) { - itemContainer.add(item.copy()); - } - } - } - private boolean isNoGunAndHasIcon(SolItem it, SolGame game) { return !(it instanceof Gun) && it.getIcon(game) != null; } diff --git a/engine/src/main/java/org/destinationsol/game/SolGame.java b/engine/src/main/java/org/destinationsol/game/SolGame.java index 7c08d96ef..1bc4368b2 100644 --- a/engine/src/main/java/org/destinationsol/game/SolGame.java +++ b/engine/src/main/java/org/destinationsol/game/SolGame.java @@ -342,6 +342,8 @@ public void onGameEnd(Context context) { } catch (Exception e) { e.printStackTrace(); } + } else { + newTutorialManager.ifPresent(NewTutorialManager::onGameEnd); } // TODO: Remove this when context is reset after each game @@ -637,7 +639,7 @@ public void setRespawnState() { } public boolean isTutorial() { - return tutorialManager.isPresent(); + return tutorialManager.isPresent() || newTutorialManager.isPresent(); } public SolApplication getSolApplication() { diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java index 0aa7f7202..6ab59c9bc 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java @@ -55,10 +55,15 @@ import org.destinationsol.ui.nui.screens.TutorialScreen; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.terasology.context.exception.BeanNotFoundException; +import org.terasology.gestalt.di.BeanContext; import javax.inject.Inject; import javax.inject.Provider; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; public class NewTutorialManager implements UpdateAwareSystem { @@ -67,15 +72,17 @@ public class NewTutorialManager implements UpdateAwareSystem { private final TutorialScreen tutorialScreen; private final SolApplication solApplication; private final Provider solGame; - private TutorialStep[] steps; + private final BeanContext beanContext; + private List steps; private int stepNo; @Inject - public NewTutorialManager(NUIManager nuiManager, SolApplication solApplication, Provider game) { + public NewTutorialManager(NUIManager nuiManager, SolApplication solApplication, Provider game, BeanContext beanContext) { this.nuiManager = nuiManager; this.tutorialScreen = (TutorialScreen) nuiManager.createScreen("engine:tutorialScreen"); this.solApplication = solApplication; this.solGame = game; + this.beanContext = beanContext; } public void start() { @@ -139,125 +146,128 @@ public void start() { itemTypesExplanations.put(Armor.class, "Armour makes attacks less effective against you."); itemTypesExplanations.put(Shield.class, "Shields absorb energy-based projectiles until depleted."); - steps = new TutorialStep[] { - new MessageStep(tutorialScreen, solGame.get(), "Section 1 - Movement"), - new TurnLeftRightStep(tutorialScreen, solGame.get(), isMobile ? "Turn left and right." : "Turn left and right (" + turnControlHint + ")."), - new ThrustForwardsStep(tutorialScreen, solGame.get(), isMobile ? "Thrust forwards." : "Thrust forwards (" + thrustForwardControlHint + ")."), - new SlowVelocityStep(tutorialScreen, solGame.get(), 0.1f, "Turn around and thrust again to slow down.\n\nTry slowing to a stop."), - new FlyToRandomWaypointAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 4.0f, "Fly to the waypoint."), - new MessageStep(tutorialScreen, solGame.get(), "Section 2 - Weapons"), - new FireGunStep(tutorialScreen, solGame.get(), isMobile ? "Fire your gun." : "Fire your gun (" + shootControlHint + ")."), - new CheckGunReloadStep(tutorialScreen, solGame.get(), false, true, "Firing weapons drains your ammunition. Keep on firing."), - new CheckGunReloadStep(tutorialScreen, solGame.get(), false, false, + steps = new ArrayList<>(Arrays.asList( + new MessageStep("Section 1 - Movement"), + new TurnLeftRightStep(isMobile ? "Turn left and right." : "Turn left and right (" + turnControlHint + ")."), + new ThrustForwardsStep(isMobile ? "Thrust forwards." : "Thrust forwards (" + thrustForwardControlHint + ")."), + new SlowVelocityStep(0.1f, "Turn around and thrust again to slow down.\n\nTry slowing to a stop."), + new FlyToRandomWaypointAroundHeroStep(1.0f, 4.0f, "Fly to the waypoint."), + new MessageStep("Section 2 - Weapons"), + new FireGunStep(isMobile ? "Fire your gun." : "Fire your gun (" + shootControlHint + ")."), + new CheckGunReloadStep(false, true, "Firing weapons drains your ammunition. Keep on firing."), + new CheckGunReloadStep(false, false, "Your weapon reloads when depleted.\n" + "You can't fire when reloading."), - new UseAbilityStep(tutorialScreen, solGame.get(), isMobile ? - "Use your ability." : - "Use your ability (" + abilityControlHint + ")."), - new MessageStep(tutorialScreen, solGame.get(), "Abilities consume ability charges."), - new MessageStep(tutorialScreen, solGame.get(), "Section 3 - Money"), - new DestroySpawnedAsteroidAroundHeroStep(tutorialScreen, solGame.get(), 1.0f, 4.0f, "Fire at the asteroid."), - new MessageStep(tutorialScreen, solGame.get(), "Asteroids drop loot - money in this case."), - new MessageStep(tutorialScreen, solGame.get(), "Section 4 - Items"), - new OpenScreenStep(tutorialScreen, nuiManager, + new UseAbilityStep(isMobile ? "Use your ability." : "Use your ability (" + abilityControlHint + ")."), + new MessageStep("Abilities consume ability charges."), + new MessageStep("Section 3 - Money"), + new DestroySpawnedAsteroidAroundHeroStep(1.0f, 4.0f, "Fire at the asteroid."), + new MessageStep("Asteroids drop loot - money in this case."), + new MessageStep("Section 4 - Items"), + new OpenScreenStep( solGame.get().getScreens().mainGameScreen.getInventoryButton(), solGame.get().getScreens().inventoryScreen, usesKeyboard ? "Open your inventory (" + gameOptions.getKeyInventoryName() + ")." : "Open your inventory."), - new ItemTypesExplanationStep(tutorialScreen, solGame.get(), itemTypesExplanations, new Class[] { + new ItemTypesExplanationStep(itemTypesExplanations, new Class[] { Gun.class, Armor.class, Shield.class }), - new SelectEquippedWeaponStep(tutorialScreen, - solGame.get().getScreens().inventoryScreen, + new SelectEquippedWeaponStep( usesKeyboard ? "Select an equipped item (Move with " + gameOptions.getKeyUpName() + " and " + gameOptions.getKeyDownName() + ")" : "Select an equipped item."), - new CheckItemEquippedStep(tutorialScreen, - solGame.get().getScreens().inventoryScreen, - false, + new CheckItemEquippedStep(false, usesKeyboard ? "Un-equip an item (" + gameOptions.getKeyEquipName() + ")." : "Un-equip an item."), - new CheckItemEquippedStep(tutorialScreen, - solGame.get().getScreens().inventoryScreen, - true, + new CheckItemEquippedStep(true, usesKeyboard ? "Re-equip that item (" + gameOptions.getKeyEquipName() + ")." : "Un-equip an item."), - new CloseScreenStep(tutorialScreen, nuiManager, + new CloseScreenStep( solGame.get().getScreens().inventoryScreen.getCloseButton(), solGame.get().getScreens().inventoryScreen, isMobile ? "Close your inventory (tap outside of the inventory)." : "Close your inventory."), - new MessageStep(tutorialScreen, solGame.get(), "Section 5 - Weapon Mounts"), - new MessageStep(tutorialScreen, solGame.get(), "All ships may come with up to two weapon mounts."), - new MessageStep(tutorialScreen, solGame.get(), "Weapon mounts are either fixed or rotating."), - new MessageStep(tutorialScreen, solGame.get(), "You can only equip weapons on matching mounts."), - new MessageStep(tutorialScreen, solGame.get(), "Section 6 - Shops"), - new FlyToNearestStationStep(tutorialScreen, solGame.get(), "Fly to the station."), - new OpenScreenStep(tutorialScreen, nuiManager, + new MessageStep("Section 5 - Weapon Mounts"), + new MessageStep("All ships may come with up to two weapon mounts."), + new MessageStep("Weapon mounts are either fixed or rotating."), + new MessageStep("You can only equip weapons on matching mounts."), + new MessageStep("Section 6 - Shops"), + new FlyToNearestStationStep("Fly to the station."), + new OpenScreenStep( solGame.get().getScreens().mainGameScreen.getTalkButton(), solGame.get().getScreens().talkScreen, isMobile ? "Talk to the station." : "Talk to the station (" + gameOptions.getKeyTalkName() + ")."), - new BuyItemStep(tutorialScreen, nuiManager, - solGame.get().getScreens().talkScreen.getBuyButton(), - solGame.get().getScreens().inventoryScreen.getBuyItemsScreen().getBuyControl(), + new BuyItemStep(usesKeyboard ? "Select Buy (" + gameOptions.getKeyBuyMenuName() + ")." : "Select Buy.", isMobile ? "Buy an item." : "Buy an item (" + gameOptions.getKeyBuyItemName() + ")."), - new MessageStep(tutorialScreen, solGame.get(), "Section 7 - Combat"), - new MessageStep(tutorialScreen, solGame.get(), "Shoot at ships to destroy them.\n"), - new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:minerSmall", + new MessageStep("Section 7 - Combat"), + new MessageStep("Shoot at ships to destroy them.\n"), + new DestroySpawnedShipsStep(1, "core:minerSmall", "core:fixedBlaster", "Destroy the targeted ship.", "Enemy ships can be tough.\nOpen the pause menu and select Respawn."), - new MessageStep(tutorialScreen, solGame.get(), "Destroyed ships drop valuable loot."), - new MessageStep(tutorialScreen, solGame.get(), "Section 8 - Repair Kits"), - new WaitUntilFullyRepairedStep(tutorialScreen, solGame.get(), - "Stay still and wait until the repair kits have repaired your hull fully."), - new MessageStep(tutorialScreen, solGame.get(), "Section 9 - Map"), - new OpenScreenStep(tutorialScreen, nuiManager, + new MessageStep("Destroyed ships drop valuable loot."), + new MessageStep("Section 8 - Repair Kits"), + new WaitUntilFullyRepairedStep("Stay still and wait until the repair kits have repaired your hull fully."), + new MessageStep("Section 9 - Map"), + new OpenScreenStep( solGame.get().getScreens().mainGameScreen.getMapButton(), solGame.get().getScreens().mapScreen, isMobile ? "Open the map." : "Open the map (" + gameOptions.getKeyMapName() + ")."), - new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomInButton(), "Zoom In"), - new ButtonPressStep(tutorialScreen, solGame.get().getScreens().mapScreen.getZoomOutButton(), "Zoom Out"), - new MapDragStep(tutorialScreen, solGame.get().getMapDrawer(), "You can move around the map by clicking/tapping and dragging."), - new CreateWaypointStep(tutorialScreen, solGame.get(), - solGame.get().getScreens().mapScreen.getAddWaypointButton(), - "Create a waypoint near your ship."), - new CloseScreenStep(tutorialScreen, - nuiManager, + new ButtonPressStep(solGame.get().getScreens().mapScreen.getZoomInButton(), "Zoom In"), + new ButtonPressStep(solGame.get().getScreens().mapScreen.getZoomOutButton(), "Zoom Out"), + new MapDragStep("You can move around the map by clicking/tapping and dragging."), + new CreateWaypointStep("Create a waypoint near your ship."), + new CloseScreenStep( solGame.get().getScreens().mapScreen.getCloseButton(), solGame.get().getScreens().mapScreen, "Close the map."), - new FlyToHeroFirstWaypointStep(tutorialScreen, solGame.get(), "Fly to your waypoint."), - new MessageStep(tutorialScreen, solGame.get(), "Section 10 - Hiring Mercenaries"), - new FlyToPlanetSellingMercenariesStep(tutorialScreen, solGame.get(), "Fly to a planetary station providing mercenaries."), - new MessageStep(tutorialScreen, solGame.get(), "When flying around planets, you'll be affected by gravity."), - new OpenScreenStep(tutorialScreen, nuiManager, + new FlyToHeroFirstWaypointStep("Fly to your waypoint."), + new MessageStep("Section 10 - Hiring Mercenaries"), + new FlyToPlanetSellingMercenariesStep("Fly to a planetary station providing mercenaries."), + new MessageStep("When flying around planets, you'll be affected by gravity."), + new OpenScreenStep( solGame.get().getScreens().mainGameScreen.getTalkButton(), solGame.get().getScreens().talkScreen, "Talk to the station."), - new BuyMercenaryStep(tutorialScreen, solGame.get(), 1000, "Try hiring a mercenary."), - new MessageStep(tutorialScreen, solGame.get(), "Let's see how your mercenary fights."), - new DestroySpawnedShipsStep(tutorialScreen, solGame.get(), 1, "core:pirateSmall", + new BuyMercenaryStep(1000, "Try hiring a mercenary."), + new MessageStep("Let's see how your mercenary fights."), + new DestroySpawnedShipsStep(1, "core:pirateSmall", "core:blaster core:smallShield", "Destroy the targeted ship.", "Enemy ships can be tough.\nOpen the pause menu and select Respawn."), - new MessageStep(tutorialScreen, solGame.get(), "Mercenaries will keep any money they collect as part of their payment."), - new MessageStep(tutorialScreen, solGame.get(), "Section 11 - Managing Mercenaries"), - new OpenScreenStep(tutorialScreen, nuiManager, + new MessageStep("Mercenaries will keep any money they collect as part of their payment."), + new MessageStep("Section 11 - Managing Mercenaries"), + new OpenScreenStep( solGame.get().getScreens().mainGameScreen.getMercsButton(), solGame.get().getScreens().inventoryScreen, isMobile ? "Open the mercenaries menu." : "Open the mercenaries menu (" + gameOptions.getKeyMercenaryInterationName() + ")."), - new ManageMercenariesGuidanceStep(tutorialScreen, nuiManager, - solGame.get().getScreens().inventoryScreen, + new ManageMercenariesGuidanceStep( "Here you can manage your mercenaries. When you're done here, close the menu.", "Here you can give items to your mercenary.", "Here you can take items back from your mercenary.", "Here you can manage your mercenary's equipment."), - new FlyToNearestStarPortStep(tutorialScreen, solGame.get(), "Fly to the marked star lane."), - new MessageStep(tutorialScreen, solGame.get(), - "For a small fee, star lanes allow you to travel quickly between planets.") - }; - + new FlyToNearestStarPortStep("Fly to the marked star lane."), + new MessageStep("For a small fee, star lanes allow you to travel quickly between planets."), + new MessageStep("The tutorial is finished. You will be returned to the main menu.") + )); + + for (TutorialStep step : steps) { + try { + beanContext.inject(step); + } catch (BeanNotFoundException ignore) { + } + } + stepNo = 0; - steps[stepNo].start(); + TutorialStep firstStep = steps.get(stepNo); + firstStep.start(); + tutorialScreen.setTutorialText(firstStep.getTutorialText(), firstStep.getTutorialBoxPosition()); + } + + public boolean hasStep(TutorialStep step) { + return steps.contains(step); + } + + public void addStep(TutorialStep step) { + steps.add(step); } @Override @@ -270,20 +280,34 @@ public void update(SolGame game, float timeStep) { } } - if (stepNo >= steps.length) { - tutorialScreen.setTutorialText("The tutorial is finished. Shoot to return to the main menu."); - if (game.getHero().getShip().getPilot().isShoot()) { - solApplication.finishGame(); - } - return; + TutorialStep currentStep = steps.get(stepNo); + tutorialScreen.setTutorialText(currentStep.getTutorialText(), currentStep.getTutorialBoxPosition()); + if (currentStep.getRequiredInput() != null) { + tutorialScreen.setInteractHintInput(currentStep.getTutorialBoxPosition(), currentStep.getRequiredInput()); + tutorialScreen.setInteractEvent(currentStep.getTutorialBoxPosition(), currentStep.getInputHandler()); } - - if (steps[stepNo].checkComplete(timeStep)) { + if (currentStep.checkComplete(timeStep)) { stepNo++; tutorialScreen.clearAllTutorialBoxes(); - if (stepNo < steps.length) { - steps[stepNo].start(); + if (stepNo < steps.size()) { + TutorialStep newStep = steps.get(stepNo); + newStep.start(); + tutorialScreen.setTutorialText(newStep.getTutorialText(), newStep.getTutorialBoxPosition()); + if (newStep.getRequiredInput() != null) { + tutorialScreen.setInteractHintInput(newStep.getTutorialBoxPosition(), newStep.getRequiredInput()); + tutorialScreen.setInteractEvent(newStep.getTutorialBoxPosition(), newStep.getInputHandler()); + } + } else { + solApplication.finishGame(); } } } + + public void onGameEnd() { + MainGameScreen mainGameScreen = solGame.get().getMainGameScreen(); + mainGameScreen.getTalkButton().setVisible(true); + mainGameScreen.getMapButton().setVisible(true); + mainGameScreen.getInventoryButton().setVisible(true); + mainGameScreen.getMercsButton().setVisible(true); + } } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java index 48139ba14..043f6b011 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java @@ -15,7 +15,50 @@ */ package org.destinationsol.game.tutorial; + +import org.terasology.input.Input; +import org.terasology.nui.HorizontalAlign; + +import java.util.function.Consumer; + public abstract class TutorialStep { + private String tutorialText; + private HorizontalAlign tutorialBoxPosition = HorizontalAlign.CENTER; + private Input requiredInput; + private Consumer inputHandler; + public abstract void start(); public abstract boolean checkComplete(float timeStep); + + public String getTutorialText() { + return tutorialText; + } + + public HorizontalAlign getTutorialBoxPosition() { + return tutorialBoxPosition; + } + + public Input getRequiredInput() { + return requiredInput; + } + + public Consumer getInputHandler() { + return inputHandler; + } + + protected void setTutorialText(String tutorialText) { + this.tutorialText = tutorialText; + } + + public void setTutorialBoxPosition(HorizontalAlign tutorialBoxPosition) { + this.tutorialBoxPosition = tutorialBoxPosition; + } + + protected void setRequiredInput(Input requiredInput) { + this.requiredInput = requiredInput; + } + + protected void setInputHandler(Consumer inputHandler) { + this.inputHandler = inputHandler; + } } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java index ee7cce421..75cca02ea 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java @@ -17,24 +17,28 @@ package org.destinationsol.game.tutorial.steps; import org.destinationsol.game.tutorial.TutorialStep; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; +import javax.inject.Inject; + public class ButtonPressStep extends TutorialStep { - private final TutorialScreen tutorialScreen; private final UIWarnButton button; private final String message; private boolean buttonPressed; - public ButtonPressStep(TutorialScreen tutorialScreen, UIWarnButton button, String message) { - this.tutorialScreen = tutorialScreen; + @Inject + protected ButtonPressStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public ButtonPressStep(UIWarnButton button, String message) { this.button = button; this.message = message; } @Override public void start() { - tutorialScreen.setTutorialText(message); + setTutorialText(message); button.subscribe(button -> { buttonPressed = true; }); diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java index 6e001566d..91c2605e7 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java @@ -16,32 +16,42 @@ package org.destinationsol.game.tutorial.steps; +import org.destinationsol.game.screens.GameScreens; import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.nui.NUIManager; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; import org.terasology.nui.HorizontalAlign; +import javax.inject.Inject; + public class BuyItemStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final NUIManager nuiManager; - private final UIWarnButton buyButton; - private final UIWarnButton purchaseButton; - private final String message; + @Inject + protected NUIManager nuiManager; + @Inject + protected GameScreens gameScreens; + private final String buyButtonMessage; + private final String purchaseButtonMessage; + private UIWarnButton buyButton; + private UIWarnButton purchaseButton; private boolean buyButtonPressed = false; private boolean purchaseButtonPressed = false; - public BuyItemStep(TutorialScreen tutorialScreen, NUIManager nuiManager, - UIWarnButton buyButton, UIWarnButton purchaseButton, String message) { - this.tutorialScreen = tutorialScreen; - this.nuiManager = nuiManager; - this.buyButton = buyButton; - this.purchaseButton = purchaseButton; - this.message = message; + @Inject + protected BuyItemStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public BuyItemStep(String buyButtonMessage, String purchaseButtonMessage) { + this.buyButtonMessage = buyButtonMessage; + this.purchaseButtonMessage = purchaseButtonMessage; } public void start() { - tutorialScreen.setTutorialText(message, HorizontalAlign.LEFT); + buyButton = gameScreens.talkScreen.getBuyButton();; + purchaseButton = gameScreens.inventoryScreen.getBuyItemsScreen().getBuyControl(); + + setTutorialBoxPosition(HorizontalAlign.LEFT); + setTutorialText(buyButtonMessage); buyButton.subscribe(button -> { buyButtonPressed = true; }); @@ -53,6 +63,8 @@ public boolean checkComplete(float timeStep) { if (!buyButtonPressed) { buyButton.enableWarn(); } else { + setTutorialBoxPosition(HorizontalAlign.LEFT); + setTutorialText(purchaseButtonMessage); purchaseButton.enableWarn(); } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java index 92ae3d994..0dab41701 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java @@ -19,13 +19,14 @@ import org.destinationsol.game.Hero; import org.destinationsol.game.SolGame; import org.destinationsol.game.tutorial.TutorialStep; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; import org.terasology.nui.HorizontalAlign; +import javax.inject.Inject; + public class BuyMercenaryStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final SolGame game; + @Inject + protected SolGame game; private final int giftMoney; private final String message; private boolean hireButtonPressed = false; @@ -33,9 +34,12 @@ public class BuyMercenaryStep extends TutorialStep { private UIWarnButton hireButton; private UIWarnButton hireMercenaryButton; - public BuyMercenaryStep(TutorialScreen tutorialScreen, SolGame game, int giftMoney, String message) { - this.tutorialScreen = tutorialScreen; - this.game = game; + @Inject + protected BuyMercenaryStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public BuyMercenaryStep(int giftMoney, String message) { this.giftMoney = giftMoney; this.message = message; } @@ -43,7 +47,8 @@ public BuyMercenaryStep(TutorialScreen tutorialScreen, SolGame game, int giftMon public void start() { Hero hero = game.getHero(); hero.setMoney(hero.getMoney() + giftMoney); - tutorialScreen.setTutorialText(message, HorizontalAlign.LEFT); + setTutorialBoxPosition(HorizontalAlign.LEFT); + setTutorialText(message); hireButton = game.getScreens().talkScreen.getHireButton(); hireButton.subscribe(button -> { hireButtonPressed = true; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java index 23ab8e1a9..c94fa8be9 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java @@ -19,17 +19,22 @@ import org.destinationsol.game.Hero; import org.destinationsol.game.SolGame; import org.destinationsol.game.tutorial.TutorialStep; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; + public class CheckGunReloadStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final SolGame game; + @Inject + protected SolGame game; private final boolean isSecondary; private final boolean isReloading; private final String message; - public CheckGunReloadStep(TutorialScreen tutorialScreen, SolGame game, boolean isSecondary, boolean isReloading, String message) { - this.tutorialScreen = tutorialScreen; - this.game = game; + @Inject + protected CheckGunReloadStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public CheckGunReloadStep(boolean isSecondary, boolean isReloading, String message) { this.isSecondary = isSecondary; this.isReloading = isReloading; this.message = message; @@ -37,7 +42,7 @@ public CheckGunReloadStep(TutorialScreen tutorialScreen, SolGame game, boolean i @Override public void start() { - tutorialScreen.setTutorialText(message); + setTutorialText(message); } @Override diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java index 5c3036018..62615683d 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java @@ -17,30 +17,35 @@ package org.destinationsol.game.tutorial.steps; import org.destinationsol.game.item.SolItem; +import org.destinationsol.game.screens.GameScreens; import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.nui.screens.InventoryScreen; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; +import javax.inject.Inject; + public class CheckItemEquippedStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final InventoryScreen inventoryScreen; + @Inject + protected GameScreens gameScreens; private final boolean equipped; private final String message; private SolItem itemToCheck; private UIWarnButton equipButton; private boolean actionPerformed; - public CheckItemEquippedStep(TutorialScreen tutorialScreen, InventoryScreen inventoryScreen, - boolean equipped, String message) { - this.tutorialScreen = tutorialScreen; - this.inventoryScreen = inventoryScreen; + @Inject + protected CheckItemEquippedStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public CheckItemEquippedStep(boolean equipped, String message) { this.equipped = equipped; this.message = message; } public void start() { - tutorialScreen.setTutorialText(message); + InventoryScreen inventoryScreen = gameScreens.inventoryScreen; + setTutorialText(message); if (equipped) { itemToCheck = inventoryScreen.getSelectedItem(); } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java index 86de9502f..8b3123822 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java @@ -20,20 +20,24 @@ import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.nui.NUIManager; import org.destinationsol.ui.nui.NUIScreenLayer; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; +import javax.inject.Inject; + public class CloseScreenStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final NUIManager nuiManager; + @Inject + protected NUIManager nuiManager; private final UIWarnButton closeButton; private final NUIScreenLayer screen; private final String message; - public CloseScreenStep(TutorialScreen tutorialScreen, NUIManager nuiManager, @Nullable UIWarnButton closeButton, + @Inject + protected CloseScreenStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public CloseScreenStep(@Nullable UIWarnButton closeButton, NUIScreenLayer screen, String message) { - this.tutorialScreen = tutorialScreen; - this.nuiManager = nuiManager; this.closeButton = closeButton; this.screen = screen; this.message = message; @@ -44,7 +48,7 @@ public void start() { if (closeButton != null) { closeButton.setVisible(true); } - tutorialScreen.setTutorialText(message); + setTutorialText(message); } @Override diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java index ee447e928..b5cfd9780 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java @@ -18,29 +18,38 @@ import org.destinationsol.game.Hero; import org.destinationsol.game.SolGame; +import org.destinationsol.game.screens.GameScreens; import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.Waypoint; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; import org.terasology.nui.HorizontalAlign; +import javax.inject.Inject; + public class CreateWaypointStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final SolGame game; - private final UIWarnButton addWaypointButton; + @Inject + protected SolGame game; + @Inject + protected GameScreens gameScreens; private final String message; + private UIWarnButton addWaypointButton; private boolean buttonPressed = false; private int lastWaypointCount; - public CreateWaypointStep(TutorialScreen tutorialScreen, SolGame game, UIWarnButton addWaypointButton, String message) { - this.tutorialScreen = tutorialScreen; - this.game = game; - this.addWaypointButton = addWaypointButton; + @Inject + protected CreateWaypointStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public CreateWaypointStep(String message) { this.message = message; } public void start() { - tutorialScreen.setTutorialText(message, HorizontalAlign.LEFT); + addWaypointButton = gameScreens.mapScreen.getAddWaypointButton(); + + setTutorialBoxPosition(HorizontalAlign.LEFT); + setTutorialText(message); addWaypointButton.subscribe(button -> { addWaypointButton.enableWarn(); buttonPressed = true; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java index 0001e8270..91e9f8c17 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java @@ -25,18 +25,22 @@ import org.destinationsol.game.SolObject; import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.Waypoint; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class DestroyObjectsStep extends TutorialStep { - protected final TutorialScreen tutorialScreen; - protected final SolGame game; + @Inject + protected SolGame game; protected final SolObject[] objects; protected final String message; protected final Waypoint[] objectWaypoints; - public DestroyObjectsStep(TutorialScreen tutorialScreen, SolGame game, SolObject[] objects, String message) { - this.tutorialScreen = tutorialScreen; - this.game = game; + @Inject + protected DestroyObjectsStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public DestroyObjectsStep(SolObject[] objects, String message) { this.objects = objects; this.message = message; this.objectWaypoints = new Waypoint[objects.length]; @@ -44,7 +48,7 @@ public DestroyObjectsStep(TutorialScreen tutorialScreen, SolGame game, SolObject @Override public void start() { - tutorialScreen.setTutorialText(message); + setTutorialText(message); Hero hero = game.getHero(); ObjectManager objectManager = game.getObjectManager(); diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java index 55a8709f2..38ae5258e 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java @@ -19,19 +19,23 @@ import com.badlogic.gdx.math.Vector2; import org.destinationsol.common.SolRandom; import org.destinationsol.game.Hero; -import org.destinationsol.game.SolGame; import org.destinationsol.game.SolObject; import org.destinationsol.game.asteroid.Asteroid; import org.destinationsol.game.tutorial.steps.wrapper.TrackedSolObjectWrapper; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class DestroySpawnedAsteroidAroundHeroStep extends DestroyObjectsStep { private final float minDistance; private final float spawnRadius; - public DestroySpawnedAsteroidAroundHeroStep(TutorialScreen tutorialScreen, SolGame game, - float minDistance, float spawnRadius, String message) { - super(tutorialScreen, game, new SolObject[1], message); + @Inject + protected DestroySpawnedAsteroidAroundHeroStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public DestroySpawnedAsteroidAroundHeroStep(float minDistance, float spawnRadius, String message) { + super(new SolObject[1], message); this.minDistance = minDistance; this.spawnRadius = spawnRadius; } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java index f2b2b6dcd..9659b06cf 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java @@ -22,7 +22,6 @@ import org.destinationsol.common.SolRandom; import org.destinationsol.game.Faction; import org.destinationsol.game.Hero; -import org.destinationsol.game.SolGame; import org.destinationsol.game.SolObject; import org.destinationsol.game.input.AiPilot; import org.destinationsol.game.input.Guardian; @@ -31,7 +30,8 @@ import org.destinationsol.game.ship.FarShip; import org.destinationsol.game.ship.hulls.HullConfig; import org.destinationsol.game.tutorial.steps.wrapper.TrackedSolObjectWrapper; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class DestroySpawnedShipsStep extends DestroyObjectsStep { private final int shipCount; @@ -39,9 +39,13 @@ public class DestroySpawnedShipsStep extends DestroyObjectsStep { private final String items; private final String respawnMessage; - public DestroySpawnedShipsStep(TutorialScreen tutorialScreen, SolGame game, int shipCount, - String hullConfig, String items, String attackMessage, String respawnMessage) { - super(tutorialScreen, game, new SolObject[shipCount], attackMessage); + @Inject + protected DestroySpawnedShipsStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public DestroySpawnedShipsStep(int shipCount, String hullConfig, String items, String attackMessage, String respawnMessage) { + super(new SolObject[shipCount], attackMessage); this.shipCount = shipCount; this.hullConfig = hullConfig; this.items = items; @@ -91,9 +95,9 @@ public void start() { public boolean checkComplete(float timeStep) { Hero hero = game.getHero(); if (hero.isDead()) { - tutorialScreen.setTutorialText(respawnMessage); + setTutorialText(respawnMessage); } else { - tutorialScreen.setTutorialText(message); + setTutorialText(message); } return super.checkComplete(timeStep); diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java index 413d39cea..88345638b 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java @@ -17,22 +17,33 @@ package org.destinationsol.game.tutorial.steps; import org.destinationsol.game.SolGame; +import org.destinationsol.game.screens.GameScreens; import org.destinationsol.game.screens.ShipUiControl; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.screens.UIShipControlsScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; +import javax.inject.Inject; + public class FireGunStep extends MessageStep { + @Inject + protected SolGame game; + @Inject + protected GameScreens gameScreens; private UIWarnButton fireButton; - public FireGunStep(TutorialScreen tutorialScreen, SolGame game, String message) { - super(tutorialScreen, game, message); + @Inject + protected FireGunStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public FireGunStep(String message) { + super(message); } public void start() { - tutorialScreen.setTutorialText(message); + setTutorialText(message); - ShipUiControl shipUiControl = game.getScreens().oldMainGameScreen.getShipControl(); + ShipUiControl shipUiControl = gameScreens.oldMainGameScreen.getShipControl(); if (shipUiControl instanceof UIShipControlsScreen) { fireButton = ((UIShipControlsScreen) shipUiControl).getGun1Button(); } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java index 690511884..a4182a0d6 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java @@ -18,18 +18,23 @@ import com.badlogic.gdx.math.Vector2; import org.destinationsol.game.Hero; -import org.destinationsol.game.SolGame; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class FlyToHeroFirstWaypointStep extends FlyToWaypointStep { - public FlyToHeroFirstWaypointStep(TutorialScreen tutorialScreen, SolGame game, String message) { - super(tutorialScreen, game, Vector2.Zero, message); + @Inject + protected FlyToHeroFirstWaypointStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public FlyToHeroFirstWaypointStep(String message) { + super(Vector2.Zero, message); } @Override public void start() { waypoint = game.getHero().getWaypoints().get(0); - tutorialScreen.setTutorialText(message); + setTutorialText(message); } @Override diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java index 054193f62..5996b14e9 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java @@ -17,18 +17,23 @@ package org.destinationsol.game.tutorial.steps; import com.badlogic.gdx.math.Vector2; -import org.destinationsol.game.SolGame; import org.destinationsol.game.SolObject; import org.destinationsol.game.StarPort; import org.destinationsol.game.planet.Planet; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class FlyToNearestStarPortStep extends FlyToWaypointStep { private Planet fromPlanet; private Planet toPlanet; - public FlyToNearestStarPortStep(TutorialScreen tutorialScreen, SolGame game, String message) { - super(tutorialScreen, game, Vector2.Zero, message); + @Inject + protected FlyToNearestStarPortStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public FlyToNearestStarPortStep(String message) { + super(Vector2.Zero, message); } private Vector2 findNearestStarPort() { diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java index e2110b7c4..e29304edf 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java @@ -17,16 +17,21 @@ package org.destinationsol.game.tutorial.steps; import com.badlogic.gdx.math.Vector2; -import org.destinationsol.game.SolGame; import org.destinationsol.game.SolObject; import org.destinationsol.game.ship.FarShip; import org.destinationsol.game.ship.SolShip; import org.destinationsol.game.ship.hulls.HullConfig; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class FlyToNearestStationStep extends FlyToWaypointStep { - public FlyToNearestStationStep(TutorialScreen tutorialScreen, SolGame game, String message) { - super(tutorialScreen, game, Vector2.Zero, message); + @Inject + protected FlyToNearestStationStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public FlyToNearestStationStep(String message) { + super(Vector2.Zero, message); } @Override diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java index 5993faa17..8749c2878 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java @@ -17,20 +17,24 @@ package org.destinationsol.game.tutorial.steps; import com.badlogic.gdx.math.Vector2; -import org.destinationsol.game.SolGame; import org.destinationsol.game.SolObject; import org.destinationsol.game.planet.Planet; import org.destinationsol.game.ship.FarShip; import org.destinationsol.game.ship.SolShip; import org.destinationsol.game.ship.hulls.HullConfig; -import org.destinationsol.ui.nui.screens.TutorialScreen; +import javax.inject.Inject; import java.util.ArrayList; import java.util.List; public class FlyToPlanetSellingMercenariesStep extends FlyToPlanetStep { - public FlyToPlanetSellingMercenariesStep(TutorialScreen tutorialScreen, SolGame game, String message) { - super(tutorialScreen, game, null, message); + @Inject + protected FlyToPlanetSellingMercenariesStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public FlyToPlanetSellingMercenariesStep(String message) { + super(null, message); } @Override @@ -45,7 +49,7 @@ public void start() { } if (planetsWithMercenaries.size() == 0) { - tutorialScreen.setTutorialText("ERROR: Failed to find suitable planet."); + setTutorialText("ERROR: Failed to find suitable planet."); return; } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java index b0981fae3..ee3435463 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java @@ -18,15 +18,20 @@ import com.badlogic.gdx.math.Vector2; import org.destinationsol.game.Hero; -import org.destinationsol.game.SolGame; import org.destinationsol.game.planet.Planet; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class FlyToPlanetStep extends FlyToWaypointStep { protected Planet planet; - public FlyToPlanetStep(TutorialScreen tutorialScreen, SolGame game, Planet planet, String message) { - super(tutorialScreen, game, planet != null ? planet.getPosition() : Vector2.Zero, message); + @Inject + protected FlyToPlanetStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public FlyToPlanetStep(Planet planet, String message) { + super(planet != null ? planet.getPosition() : Vector2.Zero, message); this.planet = planet; } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java index c13d4c984..ed8956dc0 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java @@ -19,16 +19,20 @@ import com.badlogic.gdx.math.Vector2; import org.destinationsol.common.SolRandom; import org.destinationsol.game.Hero; -import org.destinationsol.game.SolGame; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class FlyToRandomWaypointAroundHeroStep extends FlyToWaypointStep { private final float minDistance; private final float radius; - public FlyToRandomWaypointAroundHeroStep(TutorialScreen tutorialScreen, SolGame game, - float minDistance, float radius, String message) { - super(tutorialScreen, game, Vector2.Zero, message); + @Inject + protected FlyToRandomWaypointAroundHeroStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public FlyToRandomWaypointAroundHeroStep(float minDistance, float radius, String message) { + super(Vector2.Zero, message); this.minDistance = minDistance; this.radius = radius; } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java index ced2684f3..ff41f3d6a 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java @@ -23,19 +23,23 @@ import org.destinationsol.game.SolGame; import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.Waypoint; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class FlyToWaypointStep extends TutorialStep { private static final float MIN_WAYPOINT_DISTANCE = 0.2f; - protected final TutorialScreen tutorialScreen; - protected final SolGame game; + @Inject + protected SolGame game; protected final String message; protected Vector2 waypointPosition; protected Waypoint waypoint; - public FlyToWaypointStep(TutorialScreen tutorialScreen, SolGame game, Vector2 waypointPosition, String message) { - this.tutorialScreen = tutorialScreen; - this.game = game; + @Inject + protected FlyToWaypointStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public FlyToWaypointStep(Vector2 waypointPosition, String message) { this.waypointPosition = waypointPosition; this.message = message; } @@ -47,7 +51,7 @@ public void start() { Hero hero = game.getHero(); hero.addWaypoint(waypoint); game.getObjectManager().addObjDelayed(waypoint); - tutorialScreen.setTutorialText(message); + setTutorialText(message); } @Override diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java index 93ba11490..109504afe 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java @@ -16,24 +16,30 @@ package org.destinationsol.game.tutorial.steps; -import org.destinationsol.game.SolGame; -import org.destinationsol.game.item.Armor; -import org.destinationsol.game.item.Gun; -import org.destinationsol.game.item.Shield; import org.destinationsol.game.item.SolItem; -import org.destinationsol.ui.nui.screens.TutorialScreen; +import org.destinationsol.game.screens.GameScreens; +import org.destinationsol.ui.nui.widgets.UIWarnButton; +import javax.inject.Inject; +import java.util.List; import java.util.Map; public class ItemTypesExplanationStep extends MessageStep { + @Inject + protected GameScreens gameScreens; private final Map, String> itemExplanations; private final Class[] itemTypes; private int itemTypeNo; - public ItemTypesExplanationStep(TutorialScreen tutorialScreen, SolGame game, + @Inject + protected ItemTypesExplanationStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public ItemTypesExplanationStep( Map, String> itemExplanations, Class[] itemTypes) { - super(tutorialScreen, game, ""); + super(""); this.itemExplanations = itemExplanations; this.itemTypes = itemTypes; this.itemTypeNo = 0; @@ -43,12 +49,13 @@ public ItemTypesExplanationStep(TutorialScreen tutorialScreen, SolGame game, public void start() { super.start(); itemTypeNo = 0; - tutorialScreen.setTutorialText(itemExplanations.get(itemTypes[itemTypeNo])); + setTutorialText(itemExplanations.get(itemTypes[itemTypeNo])); } @Override public boolean checkComplete(float timeStep) { - game.getScreens().inventoryScreen.getItemUIControlsForTutorialByType(itemTypes[itemTypeNo]).get(0).enableWarn(); + List itemControls = gameScreens.inventoryScreen.getItemUIControlsForTutorialByType(itemTypes[itemTypeNo]); + itemControls.get(0).enableWarn(); boolean complete = super.checkComplete(timeStep); if (complete) { itemTypeNo++; @@ -57,7 +64,7 @@ public boolean checkComplete(float timeStep) { } stepTimer = 0.0f; interactComplete = false; - tutorialScreen.setTutorialText(itemExplanations.get(itemTypes[itemTypeNo])); + setTutorialText(itemExplanations.get(itemTypes[itemTypeNo])); } return false; } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java index 1be19a888..abae1d853 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java @@ -17,18 +17,21 @@ package org.destinationsol.game.tutorial.steps; import org.destinationsol.game.screens.ChooseMercenaryScreen; +import org.destinationsol.game.screens.GameScreens; import org.destinationsol.game.screens.GiveItemsScreen; import org.destinationsol.game.screens.ShowInventory; import org.destinationsol.game.screens.TakeItems; import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.nui.NUIManager; import org.destinationsol.ui.nui.screens.InventoryScreen; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class ManageMercenariesGuidanceStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final NUIManager nuiManager; - private final InventoryScreen inventoryScreen; + @Inject + protected NUIManager nuiManager; + @Inject + protected GameScreens gameScreens; private final String chooseMessage; private final String giveMessage; private final String takeMessage; @@ -37,13 +40,13 @@ public class ManageMercenariesGuidanceStep extends TutorialStep { private boolean takeItemsPressed; private boolean equipItemsPressed; - public ManageMercenariesGuidanceStep(TutorialScreen tutorialScreen, NUIManager nuiManager, InventoryScreen inventoryScreen, - String chooseMessage, String giveMessage, - String takeMessage, String equipMessage) { - this.tutorialScreen = tutorialScreen; - this.nuiManager = nuiManager; - this.inventoryScreen = inventoryScreen; + @Inject + protected ManageMercenariesGuidanceStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + public ManageMercenariesGuidanceStep(String chooseMessage, String giveMessage, + String takeMessage, String equipMessage) { this.chooseMessage = chooseMessage; this.giveMessage = giveMessage; this.takeMessage = takeMessage; @@ -52,9 +55,9 @@ public ManageMercenariesGuidanceStep(TutorialScreen tutorialScreen, NUIManager n @Override public void start() { - tutorialScreen.setTutorialText(chooseMessage); + setTutorialText(chooseMessage); - ChooseMercenaryScreen chooseMercenaryScreen = inventoryScreen.getChooseMercenaryScreen(); + ChooseMercenaryScreen chooseMercenaryScreen = gameScreens.inventoryScreen.getChooseMercenaryScreen(); chooseMercenaryScreen.getGiveItemsButton().subscribe(button -> { giveItemsPressed = true; }); @@ -68,13 +71,14 @@ public void start() { @Override public boolean checkComplete(float timeStep) { + InventoryScreen inventoryScreen = gameScreens.inventoryScreen; ChooseMercenaryScreen chooseMercenaryScreen = inventoryScreen.getChooseMercenaryScreen(); GiveItemsScreen giveItemsScreen = inventoryScreen.getGiveItems(); TakeItems takeItemsScreen = inventoryScreen.getTakeItems(); ShowInventory equipItemsScreen = inventoryScreen.getShowInventory(); if (inventoryScreen.getOperations() == chooseMercenaryScreen) { - tutorialScreen.setTutorialText(chooseMessage); + setTutorialText(chooseMessage); if (!giveItemsPressed) { chooseMercenaryScreen.getGiveItemsButton().enableWarn(); } @@ -85,11 +89,11 @@ public boolean checkComplete(float timeStep) { chooseMercenaryScreen.getEquipItemsButton().enableWarn(); } } else if (inventoryScreen.getOperations() == giveItemsScreen) { - tutorialScreen.setTutorialText(giveMessage); + setTutorialText(giveMessage); } else if (inventoryScreen.getOperations() == takeItemsScreen) { - tutorialScreen.setTutorialText(takeMessage); + setTutorialText(takeMessage); } else if (inventoryScreen.getOperations() == equipItemsScreen && equipItemsScreen.getTarget().isMerc()) { - tutorialScreen.setTutorialText(equipMessage); + setTutorialText(equipMessage); } return !nuiManager.hasScreen(inventoryScreen); diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java index b92524e55..0beb0fc93 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java @@ -19,25 +19,29 @@ import com.badlogic.gdx.math.Vector2; import org.destinationsol.game.MapDrawer; import org.destinationsol.game.tutorial.TutorialStep; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.terasology.input.MouseInput; +import javax.inject.Inject; + public class MapDragStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final MapDrawer mapDrawer; + @Inject + protected MapDrawer mapDrawer; private final String message; private Vector2 originalMapPosition; - public MapDragStep(TutorialScreen tutorialScreen, MapDrawer mapDrawer, String message) { - this.tutorialScreen = tutorialScreen; - this.mapDrawer = mapDrawer; + @Inject + protected MapDragStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public MapDragStep(String message) { this.message = message; } @Override public void start() { - tutorialScreen.setTutorialText(message); - tutorialScreen.setInteractHintInput(MouseInput.MOUSE_LEFT); + setTutorialText(message); + setRequiredInput(MouseInput.MOUSE_LEFT); originalMapPosition = mapDrawer.getMapDrawPositionAdditive().cpy(); } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java index 367343f57..8a4a98d1c 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java @@ -17,55 +17,59 @@ package org.destinationsol.game.tutorial.steps; import org.destinationsol.GameOptions; -import org.destinationsol.game.SolGame; +import org.destinationsol.SolApplication; import org.destinationsol.game.tutorial.TutorialStep; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.terasology.input.ControllerInput; import org.terasology.input.InputType; import org.terasology.input.MouseInput; import org.terasology.nui.backends.libgdx.GDXInputUtil; +import javax.inject.Inject; + public class MessageStep extends TutorialStep { protected static final float MIN_STEP_DURATION = 0.5f; - protected final TutorialScreen tutorialScreen; - protected final SolGame game; + @Inject + protected SolApplication solApplication; protected final String message; protected float stepTimer; protected boolean interactComplete; - public MessageStep(TutorialScreen tutorialScreen, SolGame game, String message) { - this.tutorialScreen = tutorialScreen; - this.game = game; + @Inject + protected MessageStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public MessageStep(String message) { this.message = message; this.stepTimer = 0.0f; } @Override public void start() { - tutorialScreen.setTutorialText(message); - GameOptions gameOptions = game.getSolApplication().getOptions(); + setTutorialText(message); + GameOptions gameOptions = solApplication.getOptions(); switch (gameOptions.controlType) { case KEYBOARD: - tutorialScreen.setInteractHintInput(GDXInputUtil.GDXToNuiKey(gameOptions.getKeyShoot())); + setRequiredInput(GDXInputUtil.GDXToNuiKey(gameOptions.getKeyShoot())); break; case MOUSE: case MIXED: - tutorialScreen.setInteractHintInput(MouseInput.MOUSE_LEFT); + setRequiredInput(MouseInput.MOUSE_LEFT); break; case CONTROLLER: if (gameOptions.getControllerAxisShoot() > 0) { // Ideally this would use CONTROLLER_AXIS but the ids do not quite match-up. - tutorialScreen.setInteractHintInput(ControllerInput.find(InputType.CONTROLLER_BUTTON, gameOptions.getControllerAxisShoot())); + setRequiredInput(ControllerInput.find(InputType.CONTROLLER_BUTTON, gameOptions.getControllerAxisShoot())); } else { - tutorialScreen.setInteractHintInput(ControllerInput.find(InputType.CONTROLLER_BUTTON, gameOptions.getControllerButtonShoot())); + setRequiredInput(ControllerInput.find(InputType.CONTROLLER_BUTTON, gameOptions.getControllerButtonShoot())); } break; } - if (game.getSolApplication().isMobile()) { - tutorialScreen.setInteractHintInput(MouseInput.MOUSE_LEFT); + if (solApplication.isMobile()) { + setRequiredInput(MouseInput.MOUSE_LEFT); } interactComplete = false; - tutorialScreen.setInteractEvent(input -> { + setInputHandler(input -> { interactComplete = true; }); } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java index 133773a7c..fc912f65a 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java @@ -20,20 +20,24 @@ import org.destinationsol.game.tutorial.TutorialStep; import org.destinationsol.ui.nui.NUIManager; import org.destinationsol.ui.nui.NUIScreenLayer; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; +import javax.inject.Inject; + public class OpenScreenStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final NUIManager nuiManager; + @Inject + protected NUIManager nuiManager; private final UIWarnButton openButton; private final NUIScreenLayer screen; private final String message; - public OpenScreenStep(TutorialScreen tutorialScreen, NUIManager nuiManager, @Nullable UIWarnButton openButton, + @Inject + protected OpenScreenStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public OpenScreenStep(@Nullable UIWarnButton openButton, NUIScreenLayer screen, String message) { - this.tutorialScreen = tutorialScreen; - this.nuiManager = nuiManager; this.openButton = openButton; this.screen = screen; this.message = message; @@ -44,7 +48,7 @@ public void start() { if (openButton != null) { openButton.setVisible(true); } - tutorialScreen.setTutorialText(message); + setTutorialText(message); } @Override diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java index dc0826714..4ed8be01c 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java @@ -16,32 +16,36 @@ package org.destinationsol.game.tutorial.steps; +import org.destinationsol.game.screens.GameScreens; import org.destinationsol.game.tutorial.TutorialStep; -import org.destinationsol.ui.nui.screens.InventoryScreen; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; +import javax.inject.Inject; + public class SelectEquippedWeaponStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final InventoryScreen inventoryScreen; + @Inject + protected GameScreens gameScreens; private final String message; - public SelectEquippedWeaponStep(TutorialScreen tutorialScreen, InventoryScreen inventoryScreen, String message) { - this.tutorialScreen = tutorialScreen; - this.inventoryScreen = inventoryScreen; + @Inject + protected SelectEquippedWeaponStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public SelectEquippedWeaponStep(String message) { this.message = message; } @Override public void start() { - tutorialScreen.setTutorialText(message); + setTutorialText(message); } @Override public boolean checkComplete(float timeStep) { - for (UIWarnButton button : inventoryScreen.getEquippedItemUIControlsForTutorial()) { + for (UIWarnButton button : gameScreens.inventoryScreen.getEquippedItemUIControlsForTutorial()) { button.enableWarn(); } - return inventoryScreen.getSelectedItem().isEquipped() > 0; + return gameScreens.inventoryScreen.getSelectedItem().isEquipped() > 0; } } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java index ecc9914c5..dc6184808 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java @@ -19,26 +19,30 @@ import org.destinationsol.game.Hero; import org.destinationsol.game.SolGame; import org.destinationsol.game.tutorial.TutorialStep; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class SlowVelocityStep extends TutorialStep { private static final float MAX_ACCELERATION = 0.8f; - private final TutorialScreen tutorialScreen; - private final SolGame game; + @Inject + protected SolGame game; private final float threshold; private final String message; private float lastSpeed; - public SlowVelocityStep(TutorialScreen tutorialScreen, SolGame game, float threshold, String message) { - this.tutorialScreen = tutorialScreen; - this.game = game; + @Inject + protected SlowVelocityStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public SlowVelocityStep(float threshold, String message) { this.threshold = threshold; this.message = message; } @Override public void start() { - tutorialScreen.setTutorialText(message); + setTutorialText(message); lastSpeed = game.getHero().getVelocity().len(); } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java index b6061fbc5..d7a70834e 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java @@ -19,33 +19,40 @@ import org.destinationsol.game.Hero; import org.destinationsol.game.SolGame; import org.destinationsol.game.input.Pilot; +import org.destinationsol.game.screens.GameScreens; import org.destinationsol.game.screens.ShipUiControl; import org.destinationsol.game.tutorial.TutorialStep; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.screens.UIShipControlsScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; +import javax.inject.Inject; + public class ThrustForwardsStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final SolGame game; + @Inject + protected SolGame game; + @Inject + protected GameScreens gameScreens; private final String message; - private final UIWarnButton thrustButton; + private UIWarnButton thrustButton; private boolean didThrust = false; - public ThrustForwardsStep(TutorialScreen tutorialScreen, SolGame game, String message) { - this.tutorialScreen = tutorialScreen; - this.game = game; + @Inject + protected ThrustForwardsStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public ThrustForwardsStep( String message) { this.message = message; - ShipUiControl shipUiControl = game.getScreens().oldMainGameScreen.getShipControl(); + } + + public void start() { + ShipUiControl shipUiControl = gameScreens.oldMainGameScreen.getShipControl(); if (shipUiControl instanceof UIShipControlsScreen) { thrustButton = ((UIShipControlsScreen) shipUiControl).getForwardButton(); } else { thrustButton = null; } - } - - public void start() { - tutorialScreen.setTutorialText(message); + setTutorialText(message); } public boolean checkComplete(float timeStep) { @@ -58,6 +65,6 @@ public boolean checkComplete(float timeStep) { if (playerPilot.isUp()) { didThrust = true; } - return (didThrust && hero.getShip().getVelocity().len() > 0.1f); + return (didThrust && hero.getVelocity().len() > 0.1f); } } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java index 06f1d8deb..25336c5d0 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java @@ -20,23 +20,27 @@ import org.destinationsol.game.SolGame; import org.destinationsol.game.input.Pilot; import org.destinationsol.game.tutorial.TutorialStep; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class TurnLeftRightStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final SolGame game; + @Inject + protected SolGame game; private final String message; private float leftSeconds = 0; private float rightSeconds = 0; - public TurnLeftRightStep(TutorialScreen tutorialScreen, SolGame game, String message) { - this.tutorialScreen = tutorialScreen; - this.game = game; + @Inject + protected TurnLeftRightStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public TurnLeftRightStep(String message) { this.message = message; } public void start() { - tutorialScreen.setTutorialText(message); + setTutorialText(message); } public boolean checkComplete(float timeStep) { diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java index ce007d6ab..26f9e2309 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java @@ -18,30 +18,37 @@ import org.destinationsol.game.Hero; import org.destinationsol.game.SolGame; +import org.destinationsol.game.screens.GameScreens; import org.destinationsol.game.screens.ShipUiControl; import org.destinationsol.game.tutorial.TutorialStep; -import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.screens.UIShipControlsScreen; import org.destinationsol.ui.nui.widgets.UIWarnButton; +import javax.inject.Inject; + public class UseAbilityStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final SolGame game; + @Inject + protected SolGame game; + @Inject + protected GameScreens gameScreens; private final String message; private UIWarnButton abilityButton; - public UseAbilityStep(TutorialScreen tutorialScreen, SolGame game, String message) { - this.tutorialScreen = tutorialScreen; - this.game = game; + @Inject + protected UseAbilityStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public UseAbilityStep(String message) { this.message = message; } public void start() { - ShipUiControl shipUiControl = game.getScreens().oldMainGameScreen.getShipControl(); + ShipUiControl shipUiControl = gameScreens.oldMainGameScreen.getShipControl(); if (shipUiControl instanceof UIShipControlsScreen) { abilityButton = ((UIShipControlsScreen) shipUiControl).getAbilityButton(); } - tutorialScreen.setTutorialText(message); + setTutorialText(message); } public boolean checkComplete(float timeStep) { if (abilityButton != null) { diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java index e9527a75e..c895eb86e 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java @@ -19,22 +19,26 @@ import org.destinationsol.game.Hero; import org.destinationsol.game.SolGame; import org.destinationsol.game.tutorial.TutorialStep; -import org.destinationsol.ui.nui.screens.TutorialScreen; + +import javax.inject.Inject; public class WaitUntilFullyRepairedStep extends TutorialStep { - private final TutorialScreen tutorialScreen; - private final SolGame game; + @Inject + protected SolGame game; private final String message; - public WaitUntilFullyRepairedStep(TutorialScreen tutorialScreen, SolGame game, String message) { - this.tutorialScreen = tutorialScreen; - this.game = game; + @Inject + protected WaitUntilFullyRepairedStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public WaitUntilFullyRepairedStep(String message) { this.message = message; } @Override public void start() { - tutorialScreen.setTutorialText(message); + setTutorialText(message); } @Override diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java index 3accce567..7d3222a13 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java @@ -61,8 +61,8 @@ public boolean onMouseClick(NUIMouseClickEvent event) { tutorialBox.interactHint.getInput().equals(event.getMouseButton())) { if (tutorialBox.inputEventListener != null) { tutorialBox.inputEventListener.accept(event.getMouseButton()); + return true; } - return true; } } return super.onMouseClick(event); From 21f9006f29cb8351d36d051597c898ffedee623f Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Sat, 11 Mar 2023 21:39:20 +0000 Subject: [PATCH 11/18] fix(tutorial): make the tutorial easier to complete on mobile --- .../game/tutorial/NewTutorialManager.java | 4 +-- .../FlyToRandomWaypointAroundHeroStep.java | 2 +- .../tutorial/steps/TurnLeftRightStep.java | 25 ++++++++++++++++++- .../ui/nui/screens/TutorialScreen.java | 13 +++++++--- .../ui/nui/widgets/InteractHint.java | 16 ++++++++++++ .../assets/skins/tutorialScreen.skin | 3 +++ 6 files changed, 56 insertions(+), 7 deletions(-) diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java index 6ab59c9bc..33050cb34 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java @@ -151,7 +151,7 @@ public void start() { new TurnLeftRightStep(isMobile ? "Turn left and right." : "Turn left and right (" + turnControlHint + ")."), new ThrustForwardsStep(isMobile ? "Thrust forwards." : "Thrust forwards (" + thrustForwardControlHint + ")."), new SlowVelocityStep(0.1f, "Turn around and thrust again to slow down.\n\nTry slowing to a stop."), - new FlyToRandomWaypointAroundHeroStep(1.0f, 4.0f, "Fly to the waypoint."), + new FlyToRandomWaypointAroundHeroStep(1.0f, 2.5f, "Fly to the waypoint."), new MessageStep("Section 2 - Weapons"), new FireGunStep(isMobile ? "Fire your gun." : "Fire your gun (" + shootControlHint + ")."), new CheckGunReloadStep(false, true, "Firing weapons drains your ammunition. Keep on firing."), @@ -161,7 +161,7 @@ public void start() { new UseAbilityStep(isMobile ? "Use your ability." : "Use your ability (" + abilityControlHint + ")."), new MessageStep("Abilities consume ability charges."), new MessageStep("Section 3 - Money"), - new DestroySpawnedAsteroidAroundHeroStep(1.0f, 4.0f, "Fire at the asteroid."), + new DestroySpawnedAsteroidAroundHeroStep(1.0f, 2.5f, "Fire at the asteroid."), new MessageStep("Asteroids drop loot - money in this case."), new MessageStep("Section 4 - Items"), new OpenScreenStep( diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java index ed8956dc0..2b740d58e 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java @@ -43,7 +43,7 @@ public void start() { waypointPosition = hero.getPosition().cpy(); while (!game.isPlaceEmpty(waypointPosition, true)) { waypointPosition.set( - hero.getPosition().x + SolRandom.randomFloat(minDistance, minDistance + radius), + hero.getPosition().x + SolRandom.randomFloat(-(minDistance + radius), minDistance + radius), hero.getPosition().y + SolRandom.randomFloat(minDistance, minDistance + radius) ); } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java index 25336c5d0..20c19501f 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java @@ -19,14 +19,24 @@ import org.destinationsol.game.Hero; import org.destinationsol.game.SolGame; import org.destinationsol.game.input.Pilot; +import org.destinationsol.game.screens.GameScreens; +import org.destinationsol.game.screens.ShipUiControl; import org.destinationsol.game.tutorial.TutorialStep; +import org.destinationsol.ui.nui.screens.UIShipControlsScreen; +import org.destinationsol.ui.nui.widgets.UIWarnButton; import javax.inject.Inject; public class TurnLeftRightStep extends TutorialStep { + private static final float LEFT_TURN_DURATION = 0.75f; + private static final float RIGHT_TURN_DURATION = 0.75f; @Inject protected SolGame game; + @Inject + protected GameScreens gameScreens; private final String message; + private UIWarnButton leftButton; + private UIWarnButton rightButton; private float leftSeconds = 0; private float rightSeconds = 0; @@ -40,10 +50,23 @@ public TurnLeftRightStep(String message) { } public void start() { + ShipUiControl shipUiControl = gameScreens.oldMainGameScreen.getShipControl(); + if (shipUiControl instanceof UIShipControlsScreen) { + UIShipControlsScreen uiShipControlsScreen = (UIShipControlsScreen) shipUiControl; + this.leftButton = uiShipControlsScreen.getLeftButton(); + this.rightButton = uiShipControlsScreen.getRightButton(); + } setTutorialText(message); } public boolean checkComplete(float timeStep) { + if (this.leftButton != null && leftSeconds < LEFT_TURN_DURATION) { + this.leftButton.enableWarn(); + } + if (this.rightButton != null && rightSeconds < RIGHT_TURN_DURATION) { + this.rightButton.enableWarn(); + } + Hero hero = game.getHero(); Pilot playerPilot = hero.getShip().getPilot(); @@ -55,6 +78,6 @@ public boolean checkComplete(float timeStep) { rightSeconds += timeStep; } - return (leftSeconds > 1.0f && rightSeconds > 1.0f); + return (leftSeconds >= LEFT_TURN_DURATION && rightSeconds >= RIGHT_TURN_DURATION); } } diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java index 7d3222a13..6dc6fdd37 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java @@ -16,6 +16,7 @@ package org.destinationsol.ui.nui.screens; +import org.destinationsol.SolApplication; import org.destinationsol.game.SolGame; import org.destinationsol.ui.nui.NUIScreenLayer; import org.destinationsol.ui.nui.widgets.InteractHint; @@ -68,22 +69,28 @@ public boolean onMouseClick(NUIMouseClickEvent event) { return super.onMouseClick(event); } }; + private final SolApplication solApplication; private boolean isReplaceRemove; @Inject - public TutorialScreen() { + public TutorialScreen(SolApplication solApplication) { tutorialBoxes = new EnumMap<>(HorizontalAlign.class); + this.solApplication = solApplication; } @Override public void initialise() { for (HorizontalAlign horizontalAlign : HorizontalAlign.values()) { - tutorialBoxes.put(horizontalAlign, new TutorialBox( + TutorialBox tutorialBox = new TutorialBox( find("tutorialBox" + horizontalAlign.toString(), UIBox.class), find("tutorialText" + horizontalAlign.toString(), UILabel.class), find("interactHint" + horizontalAlign.toString(), InteractHint.class) - )); + ); + if (tutorialBox.interactHint != null) { + tutorialBox.interactHint.useMobileIcons(solApplication.isMobile()); + } + tutorialBoxes.put(horizontalAlign, tutorialBox); } } diff --git a/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java b/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java index ff4861a33..79415e4f9 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java @@ -20,13 +20,18 @@ import org.terasology.input.Input; import org.terasology.input.InputType; import org.terasology.input.Keyboard; +import org.terasology.input.MouseInput; import org.terasology.nui.Canvas; import org.terasology.nui.CoreWidget; +import org.terasology.nui.LayoutConfig; import org.terasology.nui.databinding.Binding; import org.terasology.nui.databinding.DefaultBinding; public class InteractHint extends CoreWidget { + public static String MOBILE_TAP_MODE = "mobile_tap"; private Binding input = new DefaultBinding<>(Keyboard.Key.NONE); + @LayoutConfig + private boolean useMobileIcons; public InteractHint() { } @@ -48,6 +53,14 @@ public void bindInput(Binding input) { this.input = input; } + public boolean isUsingMobileIcons() { + return useMobileIcons; + } + + public void useMobileIcons(boolean useMobileIcons) { + this.useMobileIcons = useMobileIcons; + } + @Override public void onDraw(Canvas canvas) { canvas.setPart(input.get().getType().toString().toLowerCase()); @@ -75,6 +88,9 @@ public String getMode() { if (input.get() == null) { return DEFAULT_MODE; } + if (useMobileIcons && input.get() == MouseInput.MOUSE_LEFT) { + return MOBILE_TAP_MODE; + } return input.get().getName().toLowerCase(); } diff --git a/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin index 93f759ebb..32460de37 100644 --- a/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin +++ b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin @@ -44,6 +44,9 @@ }, "mouse_right": { "background": "engine:mouseClickRight" + }, + "mobile_tap": { + "background": "engine:uiPlanetProximityIndicator" } } }, From c2151b6cd3c47f3e8b0cbf2d6989f57463f082f8 Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Sat, 11 Mar 2023 22:52:34 +0000 Subject: [PATCH 12/18] doc(tutorial): added comments and JavaDoc to tutorial classes --- .../game/tutorial/NewTutorialManager.java | 25 +++++++++- .../game/tutorial/TutorialStep.java | 47 ++++++++++++++++++ .../game/tutorial/steps/ButtonPressStep.java | 3 ++ .../game/tutorial/steps/BuyItemStep.java | 4 ++ .../game/tutorial/steps/BuyMercenaryStep.java | 15 ++++-- .../tutorial/steps/CheckGunReloadStep.java | 5 ++ .../tutorial/steps/CheckItemEquippedStep.java | 5 ++ .../game/tutorial/steps/CloseScreenStep.java | 3 ++ .../tutorial/steps/CreateWaypointStep.java | 4 ++ .../tutorial/steps/DestroyObjectsStep.java | 4 ++ .../DestroySpawnedAsteroidAroundHeroStep.java | 4 ++ .../steps/DestroySpawnedShipsStep.java | 5 ++ .../game/tutorial/steps/FireGunStep.java | 3 ++ .../steps/FlyToHeroFirstWaypointStep.java | 3 ++ .../steps/FlyToNearestStarPortStep.java | 4 ++ .../steps/FlyToNearestStationStep.java | 3 ++ .../FlyToPlanetSellingMercenariesStep.java | 4 ++ .../game/tutorial/steps/FlyToPlanetStep.java | 3 ++ .../FlyToRandomWaypointAroundHeroStep.java | 4 ++ .../tutorial/steps/FlyToWaypointStep.java | 4 ++ .../steps/ItemTypesExplanationStep.java | 5 ++ .../steps/ManageMercenariesGuidanceStep.java | 5 ++ .../game/tutorial/steps/MapDragStep.java | 3 ++ .../game/tutorial/steps/MessageStep.java | 5 ++ .../game/tutorial/steps/OpenScreenStep.java | 3 ++ .../steps/SelectEquippedWeaponStep.java | 4 ++ .../game/tutorial/steps/SlowVelocityStep.java | 3 ++ .../tutorial/steps/ThrustForwardsStep.java | 3 ++ .../tutorial/steps/TurnLeftRightStep.java | 3 ++ .../game/tutorial/steps/UseAbilityStep.java | 3 ++ .../steps/WaitUntilFullyRepairedStep.java | 3 ++ .../game/tutorial/steps/package-info.java | 19 +++++++ .../tutorial/steps/wrapper/package-info.java | 20 ++++++++ .../ui/nui/screens/TutorialScreen.java | 49 +++++++++++++++++++ .../ui/nui/widgets/InteractHint.java | 28 +++++++++++ 35 files changed, 302 insertions(+), 6 deletions(-) create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/package-info.java create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/package-info.java diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java index 33050cb34..694ab300e 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java @@ -66,8 +66,12 @@ import java.util.List; import java.util.Map; +/** + * This class is responsible for running the in-game tutorial. It delegates the individual tasks to {@link TutorialStep} + * instances. + * @see TutorialStep + */ public class NewTutorialManager implements UpdateAwareSystem { - private static final Logger logger = LoggerFactory.getLogger(NewTutorialManager.class); private final NUIManager nuiManager; private final TutorialScreen tutorialScreen; private final SolApplication solApplication; @@ -100,6 +104,8 @@ public void start() { boolean usesKeyboard = !isMobile && (controlType != GameOptions.ControlType.MOUSE); + // A lot of input names will vary by control type. In some control types, the inputs are also hard-coded. + // Rather than a lot of messy conditional statements in-line with the step definitions, we'll set things up here. String turnControlHint = ""; String thrustForwardControlHint = ""; String shootControlHint = ""; @@ -228,7 +234,9 @@ public void start() { solGame.get().getScreens().mainGameScreen.getTalkButton(), solGame.get().getScreens().talkScreen, "Talk to the station."), - new BuyMercenaryStep(1000, "Try hiring a mercenary."), + new BuyMercenaryStep(1000, + usesKeyboard ? "Select Hire (" + gameOptions.getKeyHireShipMenuName() + ")." : "Select Hire.", + "Try hiring a mercenary."), new MessageStep("Let's see how your mercenary fights."), new DestroySpawnedShipsStep(1, "core:pirateSmall", "core:blaster core:smallShield", "Destroy the targeted ship.", @@ -262,16 +270,26 @@ public void start() { tutorialScreen.setTutorialText(firstStep.getTutorialText(), firstStep.getTutorialBoxPosition()); } + /** + * Checks if a step instance is already present in the tutorial. + * @param step the step to check for + * @return true, if the step exists, otherwise false + */ public boolean hasStep(TutorialStep step) { return steps.contains(step); } + /** + * Appends a step to the end of the tutorial list. + * @param step the step to add + */ public void addStep(TutorialStep step) { steps.add(step); } @Override public void update(SolGame game, float timeStep) { + // Move the tutorial overlay back to the front of the UI stack, if needed. if (nuiManager.getTopScreen() != tutorialScreen) { if (nuiManager.hasScreen(tutorialScreen)) { tutorialScreen.moveToTop(); @@ -303,6 +321,9 @@ public void update(SolGame game, float timeStep) { } } + /** + * This method should be called when either the game or the tutorial ends. It cleans-up the UI changes made for the tutorial. + */ public void onGameEnd() { MainGameScreen mainGameScreen = solGame.get().getMainGameScreen(); mainGameScreen.getTalkButton().setVisible(true); diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java index 043f6b011..afde22272 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java @@ -21,43 +21,90 @@ import java.util.function.Consumer; +/** + * A tutorial step is an action (or connected sequence of actions) that demonstrates to the player a portion of the game's + * functionality. Tutorial steps are queried for completion every frame. They should provide some explanatory text to + * show to the player, as well as optionally an input hint. The step is allowed to decide the tutorial box it will + * populate. + */ public abstract class TutorialStep { private String tutorialText; private HorizontalAlign tutorialBoxPosition = HorizontalAlign.CENTER; private Input requiredInput; private Consumer inputHandler; + /** + * This is called only once, when the step is started. It usually occurs after the previous step has been completed. + */ public abstract void start(); + + /** + * This should return true if the step has been completed. + * @param timeStep the time passed since the last query + * @return true, if the step has been completed, otherwise false + */ public abstract boolean checkComplete(float timeStep); + /** + * Returns the explanatory text to the shown to the player. + * @return the explanatory text to the shown to the player + */ public String getTutorialText() { return tutorialText; } + /** + * Returns the position of the text box to show the explanatory text in. + * @return the position of the text box to show the explanatory text in. + */ public HorizontalAlign getTutorialBoxPosition() { return tutorialBoxPosition; } + /** + * Returns the desired input that should be hinted at by the tutorial box. + * @return the desired input that should be hinted at by the tutorial box. + */ public Input getRequiredInput() { return requiredInput; } + /** + * Returns the callback to be invoked if the hinted input is performed. + * @return the callback to be invoked if the hinted input is performed. + */ public Consumer getInputHandler() { return inputHandler; } + /** + * Specifies the explanatory text to display in the tutorial box. + * @param tutorialText the explanatory text to display + */ protected void setTutorialText(String tutorialText) { this.tutorialText = tutorialText; } + /** + * Specifies the position the tutorial box should use. + * @param tutorialBoxPosition the position of the tutorial box + */ public void setTutorialBoxPosition(HorizontalAlign tutorialBoxPosition) { this.tutorialBoxPosition = tutorialBoxPosition; } + /** + * Specifies the desired input to be hinted by the tutorial box. + * @param requiredInput the desired input to be hinted + */ protected void setRequiredInput(Input requiredInput) { this.requiredInput = requiredInput; } + /** + * Specifies the callback to the invoked in the desired input is performed. + * @param inputHandler the callback to be invoked + */ protected void setInputHandler(Consumer inputHandler) { this.inputHandler = inputHandler; } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java index 75cca02ea..06657cc9b 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ButtonPressStep.java @@ -21,6 +21,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the user presses the specified {@link UIWarnButton}. + */ public class ButtonPressStep extends TutorialStep { private final UIWarnButton button; private final String message; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java index 91c2605e7..d205d53d2 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyItemStep.java @@ -24,6 +24,10 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the user has navigated to the {@link org.destinationsol.game.screens.BuyItemsScreen} + * and purchased an item. + */ public class BuyItemStep extends TutorialStep { @Inject protected NUIManager nuiManager; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java index 0dab41701..5b9b8b792 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/BuyMercenaryStep.java @@ -24,11 +24,16 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the user has navigated to the {@link org.destinationsol.game.screens.HireShipsScreen} + * and hired a mercenary. The player is gifted some money to allow them to afford this. + */ public class BuyMercenaryStep extends TutorialStep { @Inject protected SolGame game; private final int giftMoney; - private final String message; + private final String hireMessage; + private final String hireMercenaryMessage; private boolean hireButtonPressed = false; private boolean hireMercenaryButtonPressed = false; private UIWarnButton hireButton; @@ -39,16 +44,17 @@ protected BuyMercenaryStep() { throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); } - public BuyMercenaryStep(int giftMoney, String message) { + public BuyMercenaryStep(int giftMoney, String hireMessage, String hireMercenaryMessage) { this.giftMoney = giftMoney; - this.message = message; + this.hireMessage = hireMessage; + this.hireMercenaryMessage = hireMercenaryMessage; } public void start() { Hero hero = game.getHero(); hero.setMoney(hero.getMoney() + giftMoney); setTutorialBoxPosition(HorizontalAlign.LEFT); - setTutorialText(message); + setTutorialText(hireMessage); hireButton = game.getScreens().talkScreen.getHireButton(); hireButton.subscribe(button -> { hireButtonPressed = true; @@ -68,6 +74,7 @@ public boolean checkComplete(float timeStep) { if (!hireButtonPressed) { hireButton.enableWarn(); } else { + setTutorialText(hireMercenaryMessage); hireMercenaryButton.enableWarn(); } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java index c94fa8be9..e2239d454 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckGunReloadStep.java @@ -22,6 +22,11 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player gun has either started or stopped reloading. + * If {@link #isReloading} is true, then the step completes when the player gun starts reloading. + * If {@link #isReloading} is false, then the step completes when the player gun has finished reloading. + */ public class CheckGunReloadStep extends TutorialStep { @Inject protected SolGame game; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java index 62615683d..37bff7547 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CheckItemEquippedStep.java @@ -24,6 +24,11 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player either equips or un-equips an item. + * If {@link #equipped} is true, then the step completes when the player equips an item. + * If {@link #equipped} is false, then the step completes when the player un-equips the item they had selected. + */ public class CheckItemEquippedStep extends TutorialStep { @Inject protected GameScreens gameScreens; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java index 8b3123822..395f943db 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CloseScreenStep.java @@ -24,6 +24,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the specified screen is closed. It hints at a button that can be used to do this. + */ public class CloseScreenStep extends TutorialStep { @Inject protected NUIManager nuiManager; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java index b5cfd9780..4f23b107a 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java @@ -26,6 +26,10 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player has created a waypoint. + * The waypoint will only be accepted if it is less than 100 units away from the player ship. + */ public class CreateWaypointStep extends TutorialStep { @Inject protected SolGame game; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java index 91e9f8c17..8df88a945 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroyObjectsStep.java @@ -28,6 +28,10 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the specified object is destroyed. + * It marks the object with a waypoint to guide the player towards it. + */ public class DestroyObjectsStep extends TutorialStep { @Inject protected SolGame game; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java index 38ae5258e..8ecb3fa03 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedAsteroidAroundHeroStep.java @@ -25,6 +25,10 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player destroys a spawned asteroid. + * The asteroid is spawned somewhere around the player ship at the start of this step. + */ public class DestroySpawnedAsteroidAroundHeroStep extends DestroyObjectsStep { private final float minDistance; private final float spawnRadius; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java index 9659b06cf..4e1eeabf3 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/DestroySpawnedShipsStep.java @@ -33,6 +33,11 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when all spawned ships have been destroyed. If the player dies, they are prompted to respawn. + * The spawned ships are marked by waypoints, which guide the player towards them. + * All ships are spawned at the start of the ships from a specified hull configuration. + */ public class DestroySpawnedShipsStep extends DestroyObjectsStep { private final int shipCount; private final String hullConfig; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java index 88345638b..3241183f6 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FireGunStep.java @@ -24,6 +24,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player ship fires its gun. + */ public class FireGunStep extends MessageStep { @Inject protected SolGame game; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java index a4182a0d6..f218c9b0a 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToHeroFirstWaypointStep.java @@ -21,6 +21,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player ship reaches a nearby spawned waypoint. + */ public class FlyToHeroFirstWaypointStep extends FlyToWaypointStep { @Inject protected FlyToHeroFirstWaypointStep() { diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java index 5996b14e9..5ce7ff72d 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStarPortStep.java @@ -23,6 +23,10 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player reaches the nearest star port (star lane) to their location at + * the start of the step. + */ public class FlyToNearestStarPortStep extends FlyToWaypointStep { private Planet fromPlanet; private Planet toPlanet; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java index e29304edf..d6b0fca30 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToNearestStationStep.java @@ -24,6 +24,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player reaches the nearest station to their location at the start of the step. + */ public class FlyToNearestStationStep extends FlyToWaypointStep { @Inject protected FlyToNearestStationStep() { diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java index 8749c2878..c92b0c2bb 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java @@ -27,6 +27,10 @@ import java.util.ArrayList; import java.util.List; +/** + * A tutorial step that completes when the player reaches a planet-based store selling mercenaries. + * It guides the player towards the planet first with a waypoint, then towards the shop itself. + */ public class FlyToPlanetSellingMercenariesStep extends FlyToPlanetStep { @Inject protected FlyToPlanetSellingMercenariesStep() { diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java index ee3435463..d1eb94b5a 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetStep.java @@ -22,6 +22,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player reaches the specified planet. + */ public class FlyToPlanetStep extends FlyToWaypointStep { protected Planet planet; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java index 2b740d58e..4478b0c3b 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToRandomWaypointAroundHeroStep.java @@ -22,6 +22,10 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player reaches a waypoint randomly spawned around them. + * You can configure how close or far away from the player's starting position the waypoint should spawn. + */ public class FlyToRandomWaypointAroundHeroStep extends FlyToWaypointStep { private final float minDistance; private final float radius; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java index ff41f3d6a..f05663b84 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToWaypointStep.java @@ -26,6 +26,10 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player reaches the specified position. + * A waypoint is automatically created at the specified position to guide the player. + */ public class FlyToWaypointStep extends TutorialStep { private static final float MIN_WAYPOINT_DISTANCE = 0.2f; @Inject diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java index 109504afe..aab430e10 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ItemTypesExplanationStep.java @@ -24,6 +24,11 @@ import java.util.List; import java.util.Map; +/** + * A tutorial step that explains the purposes of specified item types to the player. + * It completes when the explanations have been advanced. + * @see MessageStep + */ public class ItemTypesExplanationStep extends MessageStep { @Inject protected GameScreens gameScreens; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java index abae1d853..97fd99a95 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ManageMercenariesGuidanceStep.java @@ -27,6 +27,11 @@ import javax.inject.Inject; +/** + * A tutorial step that guides the player through the {@link ChooseMercenaryScreen}. + * It explains what the various sub-screens do as well. + * It completes when the player closes the screen. + */ public class ManageMercenariesGuidanceStep extends TutorialStep { @Inject protected NUIManager nuiManager; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java index 0beb0fc93..19a3c6afc 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MapDragStep.java @@ -23,6 +23,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player pans the map. + */ public class MapDragStep extends TutorialStep { @Inject protected MapDrawer mapDrawer; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java index 8a4a98d1c..3e8eb4699 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java @@ -26,6 +26,11 @@ import javax.inject.Inject; +/** + * A tutorial step that displays a message to the player and waits for input before completing. + * The input is hinted-at in the corner of the message box. + * There is a cooldown to prevent continuously skipping these messages by holding down the advance input. + */ public class MessageStep extends TutorialStep { protected static final float MIN_STEP_DURATION = 0.5f; @Inject diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java index fc912f65a..c839bc235 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/OpenScreenStep.java @@ -24,6 +24,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the specified screen is opened. + */ public class OpenScreenStep extends TutorialStep { @Inject protected NUIManager nuiManager; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java index 4ed8be01c..56f8a82b7 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SelectEquippedWeaponStep.java @@ -22,6 +22,10 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player selects an equipped weapon. + * The step hints to the player those items that are already equipped. + */ public class SelectEquippedWeaponStep extends TutorialStep { @Inject protected GameScreens gameScreens; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java index dc6184808..3b435b9ac 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/SlowVelocityStep.java @@ -22,6 +22,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player ship reduces its velocity to below a specified threshold. + */ public class SlowVelocityStep extends TutorialStep { private static final float MAX_ACCELERATION = 0.8f; @Inject diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java index d7a70834e..427e8bea1 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ThrustForwardsStep.java @@ -27,6 +27,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the play ship thrusts forwards. + */ public class ThrustForwardsStep extends TutorialStep { @Inject protected SolGame game; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java index 20c19501f..c967099b2 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/TurnLeftRightStep.java @@ -27,6 +27,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes once the player ship has attempted turning both left and right. + */ public class TurnLeftRightStep extends TutorialStep { private static final float LEFT_TURN_DURATION = 0.75f; private static final float RIGHT_TURN_DURATION = 0.75f; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java index 26f9e2309..e60a8f43d 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/UseAbilityStep.java @@ -26,6 +26,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player ship uses its ability. + */ public class UseAbilityStep extends TutorialStep { @Inject protected SolGame game; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java index c895eb86e..dbf89608e 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/WaitUntilFullyRepairedStep.java @@ -22,6 +22,9 @@ import javax.inject.Inject; +/** + * A tutorial step that completes when the player ship's hull is at full health. + */ public class WaitUntilFullyRepairedStep extends TutorialStep { @Inject protected SolGame game; diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/package-info.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/package-info.java new file mode 100644 index 000000000..7498daf10 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@API +package org.destinationsol.game.tutorial.steps; + +import org.terasology.context.annotation.API; \ No newline at end of file diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/package-info.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/package-info.java new file mode 100644 index 000000000..c0c56e82f --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/wrapper/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@API +package org.destinationsol.game.tutorial.steps.wrapper; + +import org.terasology.context.annotation.API; \ No newline at end of file diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java index 6dc6fdd37..016364c63 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java @@ -81,6 +81,7 @@ public TutorialScreen(SolApplication solApplication) { @Override public void initialise() { + // TODO: The right tutorial box doesn't exist yet. It will return null values. for (HorizontalAlign horizontalAlign : HorizontalAlign.values()) { TutorialBox tutorialBox = new TutorialBox( find("tutorialBox" + horizontalAlign.toString(), UIBox.class), @@ -94,35 +95,71 @@ public void initialise() { } } + /** + * Returns the text displayed in the centre tutorial box. + * @return the text displayed in the centre tutorial box + */ public String getTutorialText() { return getTutorialText(HorizontalAlign.CENTER); } + /** + * Returns the text displayed in the specified tutorial box. + * @param horizontalAlign the tutorial box to select + * @return the text displayed in the specified tutorial box. + */ public String getTutorialText(HorizontalAlign horizontalAlign) { return getTutorialTextLabel(horizontalAlign).getText(); } + /** + * Specifies the text to be displayed in the centre tutorial box. + * @param text the text to be displayed in the centre tutorial box + */ public void setTutorialText(String text) { setTutorialText(text, HorizontalAlign.CENTER); } + /** + * Specifies the text to be displayed in the specified tutorial box. + * @param text the text to be displayed + * @param horizontalAlign the tutorial box to select + */ public void setTutorialText(String text, HorizontalAlign horizontalAlign) { getTutorialTextLabel(horizontalAlign).setText(text); getTutorialBox(horizontalAlign).setVisible(!text.isEmpty()); } + /** + * Returns the input hinted at by the centre tutorial box. This can be null. + * @return the input hinted at by the centre tutorial box + */ public Input getInteractHintInput() { return getInteractHintInput(HorizontalAlign.CENTER); } + /** + * Returns the input hinted at by the specified tutorial box. This can be null. + * @param horizontalAlign the tutorial box to select + * @return the input hinted at by the specified tutorial box + */ public Input getInteractHintInput(HorizontalAlign horizontalAlign) { return getInteractHint(horizontalAlign).getInput(); } + /** + * Specifies the input hinted at by the centre tutorial box. + * @param input the input hinted at by the centre tutorial box + */ public void setInteractHintInput(Input input) { setInteractHintInput(HorizontalAlign.CENTER, input); } + /** + * Specifies the input hinted at by the specified tutorial box. + * @param horizontalAlign the tutorial box to select + * @param input the input to hint at + */ public void setInteractHintInput(HorizontalAlign horizontalAlign, Input input) { InteractHint interactHint = getInteractHint(horizontalAlign); if (input != null) { @@ -133,14 +170,26 @@ public void setInteractHintInput(HorizontalAlign horizontalAlign, Input input) { } } + /** + * Specifies a callback involved when the hinted-at input is activated. + * @param interactEvent the interaction callback + */ public void setInteractEvent(Consumer interactEvent) { setInteractEvent(HorizontalAlign.CENTER, interactEvent); } + /** + * Specifies a callback involved when the hinted-at input is activated. + * @param horizontalAlign the tutorial box to select + * @param interactEvent the interaction callback + */ public void setInteractEvent(HorizontalAlign horizontalAlign, Consumer interactEvent) { tutorialBoxes.get(horizontalAlign).inputEventListener = interactEvent; } + /** + * This clears the contents of all the tutorial boxes and hides them. + */ public void clearAllTutorialBoxes() { for (TutorialBox tutorialBox : tutorialBoxes.values()) { if (tutorialBox.box != null) { diff --git a/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java b/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java index 79415e4f9..9a2328bc4 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/widgets/InteractHint.java @@ -27,9 +27,17 @@ import org.terasology.nui.databinding.Binding; import org.terasology.nui.databinding.DefaultBinding; +/** + * This widget displays an icon representing the desired input interaction. + * The icons used are determined by the UI style assigned to the widget. + * The widget's part is set to the input type and its mode set to the exact input name (lowercase). + */ public class InteractHint extends CoreWidget { public static String MOBILE_TAP_MODE = "mobile_tap"; private Binding input = new DefaultBinding<>(Keyboard.Key.NONE); + /** + * Determines if mouse left-click actions should use the {@link #MOBILE_TAP_MODE} mode instead. + */ @LayoutConfig private boolean useMobileIcons; @@ -41,22 +49,42 @@ public InteractHint(String id, Input input) { this.input.set(input); } + /** + * Returns the input being hinted at. + * @return the hinted input + */ public Input getInput() { return input.get(); } + /** + * Sets the input to be hinted. + * @param input the input to be hinted + */ public void setInput(Input input) { this.input.set(input); } + /** + * Binds the input to be hinted to the specified binding. + * @param input the input binding + */ public void bindInput(Binding input) { this.input = input; } + /** + * States if the hint uses a mobile icon for the mouse left-click input. + * @return true, if using a mobile icon for left-click, otherwise false + */ public boolean isUsingMobileIcons() { return useMobileIcons; } + /** + * Specifies if a mobile icon should be used for the mouse left-click hint. + * @param useMobileIcons determines if the mobile icon should be used + */ public void useMobileIcons(boolean useMobileIcons) { this.useMobileIcons = useMobileIcons; } From e5aba8b2919803cc8904e024411137dae5f74b73 Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Sat, 11 Mar 2023 23:16:44 +0000 Subject: [PATCH 13/18] fix: remove obsolete failing test --- .../org/destinationsol/game/PlayerCreatorTest.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/engine/src/test/java/org/destinationsol/game/PlayerCreatorTest.java b/engine/src/test/java/org/destinationsol/game/PlayerCreatorTest.java index 7f0939da9..1887ab4c9 100644 --- a/engine/src/test/java/org/destinationsol/game/PlayerCreatorTest.java +++ b/engine/src/test/java/org/destinationsol/game/PlayerCreatorTest.java @@ -265,16 +265,6 @@ public void testReEquipRespawnItemGunsSecondarySlot() { verify(solShip).maybeEquip(any(), eq(gun), eq(true), eq(true)); } - @Test - public void testTutorialModeAddsSeenItemsIfRespawnItemsAreEmpty() { - respawnState.getRespawnItems().clear(); - when(solGame.isTutorial()).thenReturn(true); - int groupCountBefore = shipItemContainer.groupCount(); - playerCreator.createPlayer(shipConfig, false, respawnState, solGame, false, false); - assertThat(shipItemContainer.groupCount()).isGreaterThan(groupCountBefore); - assertThat(shipItemContainer.hasNew()).isFalse(); - } - @Test public void testAddShipDelayed() { playerCreator.createPlayer(shipConfig, false, respawnState, solGame, false, false); From 00cf6fac017a334cbb240b46c7b1d0951d3957d7 Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Sat, 11 Mar 2023 23:22:30 +0000 Subject: [PATCH 14/18] fix: fix tests after removing special tutorial item logic --- .../src/test/java/org/destinationsol/game/PlayerCreatorTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/src/test/java/org/destinationsol/game/PlayerCreatorTest.java b/engine/src/test/java/org/destinationsol/game/PlayerCreatorTest.java index 1887ab4c9..ee43ccb7d 100644 --- a/engine/src/test/java/org/destinationsol/game/PlayerCreatorTest.java +++ b/engine/src/test/java/org/destinationsol/game/PlayerCreatorTest.java @@ -142,6 +142,7 @@ public void testNoMouseControlCreatesUiControlledPilot() { @Test public void testUseRespawnMoneyIfNotZero() { + solGame.isTutorial(); // This is here purely for the side effects, as this is the only test that doesn't call isTutorial. float respawnMoney = 42f; respawnState.setRespawnMoney(respawnMoney); playerCreator.createPlayer(shipConfig, false, respawnState, solGame, false, false); From 1b749273ee0ed2de8350bbb0a6387ddda5668d35 Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Sun, 12 Mar 2023 18:21:12 +0000 Subject: [PATCH 15/18] fix(tutorial): refined tutorial text and fixed controller input --- .../SolGameServiceRegistry.java | 6 +- .../java/org/destinationsol/game/SolGame.java | 11 +- ...orialManager.java => TutorialManager.java} | 41 +- .../game/tutorial/steps/MessageStep.java | 8 + .../steps/TravelThroughStarPortStep.java | 57 +++ .../destinationsol/ui/TutorialManager.java | 436 ------------------ .../ui/nui/screens/TutorialScreen.java | 8 +- 7 files changed, 90 insertions(+), 477 deletions(-) rename engine/src/main/java/org/destinationsol/game/tutorial/{NewTutorialManager.java => TutorialManager.java} (90%) create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/TravelThroughStarPortStep.java delete mode 100644 engine/src/main/java/org/destinationsol/ui/TutorialManager.java diff --git a/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java b/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java index c65875601..0e6485e74 100644 --- a/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java +++ b/engine/src/main/java/org/destinationsol/SolGameServiceRegistry.java @@ -46,8 +46,7 @@ import org.destinationsol.game.planet.PlanetManager; import org.destinationsol.game.screens.GameScreens; import org.destinationsol.game.ship.ShipBuilder; -import org.destinationsol.game.tutorial.NewTutorialManager; -import org.destinationsol.ui.TutorialManager; +import org.destinationsol.game.tutorial.TutorialManager; import org.terasology.context.Lifetime; import org.terasology.gestalt.di.ServiceRegistry; @@ -55,8 +54,7 @@ public class SolGameServiceRegistry extends ServiceRegistry { public SolGameServiceRegistry(boolean isTutorial) { this.with(SolGame.class).lifetime(Lifetime.Singleton); if (isTutorial) { -// this.with(TutorialManager.class).lifetime(Lifetime.Singleton); - this.with(NewTutorialManager.class).lifetime(Lifetime.Singleton); + this.with(TutorialManager.class).lifetime(Lifetime.Singleton); } this.with(EntitySystemManager.class); diff --git a/engine/src/main/java/org/destinationsol/game/SolGame.java b/engine/src/main/java/org/destinationsol/game/SolGame.java index 1bc4368b2..9cf9518f6 100644 --- a/engine/src/main/java/org/destinationsol/game/SolGame.java +++ b/engine/src/main/java/org/destinationsol/game/SolGame.java @@ -58,11 +58,10 @@ import org.destinationsol.game.ship.ShipBuilder; import org.destinationsol.game.ship.SloMo; import org.destinationsol.game.ship.hulls.HullConfig; -import org.destinationsol.game.tutorial.NewTutorialManager; +import org.destinationsol.game.tutorial.TutorialManager; import org.destinationsol.mercenary.MercenaryUtils; import org.destinationsol.modules.ModuleManager; import org.destinationsol.ui.DebugCollector; -import org.destinationsol.ui.TutorialManager; import org.destinationsol.ui.UiDrawer; import org.destinationsol.ui.Waypoint; import org.destinationsol.ui.nui.screens.MainGameScreen; @@ -140,8 +139,6 @@ public class SolGame { @Inject protected Optional tutorialManager; @Inject - protected Optional newTutorialManager; - @Inject protected BeanContext beanContext; @Inject protected GalaxyBuilder galaxyBuilder; @@ -194,7 +191,6 @@ public void createUpdateSystems() { updateSystems = new TreeMap<>(); List defaultSystems = new ArrayList<>(Arrays.asList(planetManager, solCam, chunkManager, mountDetectDrawer, objectManager, mapDrawer, soundManager, beaconHandler, drawableDebugger)); tutorialManager.ifPresent(defaultSystems::add); - newTutorialManager.ifPresent(defaultSystems::add); updateSystems.put(0, defaultSystems); List defaultPausedSystems = new ArrayList(); @@ -269,7 +265,6 @@ public void run() { gameScreens.consoleScreen.init(this); solApplication.getNuiManager().pushScreen(gameScreens.mainGameScreen); tutorialManager.ifPresent(TutorialManager::start); - newTutorialManager.ifPresent(NewTutorialManager::start); } private void addObjectsToPlanetManager() { @@ -343,7 +338,7 @@ public void onGameEnd(Context context) { e.printStackTrace(); } } else { - newTutorialManager.ifPresent(NewTutorialManager::onGameEnd); + tutorialManager.ifPresent(TutorialManager::onGameEnd); } // TODO: Remove this when context is reset after each game @@ -639,7 +634,7 @@ public void setRespawnState() { } public boolean isTutorial() { - return tutorialManager.isPresent() || newTutorialManager.isPresent(); + return tutorialManager.isPresent(); } public SolApplication getSolApplication() { diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java similarity index 90% rename from engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java rename to engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java index 694ab300e..41153dcf7 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/NewTutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java @@ -47,14 +47,13 @@ import org.destinationsol.game.tutorial.steps.SelectEquippedWeaponStep; import org.destinationsol.game.tutorial.steps.SlowVelocityStep; import org.destinationsol.game.tutorial.steps.ThrustForwardsStep; +import org.destinationsol.game.tutorial.steps.TravelThroughStarPortStep; import org.destinationsol.game.tutorial.steps.TurnLeftRightStep; import org.destinationsol.game.tutorial.steps.UseAbilityStep; import org.destinationsol.game.tutorial.steps.WaitUntilFullyRepairedStep; import org.destinationsol.ui.nui.NUIManager; import org.destinationsol.ui.nui.screens.MainGameScreen; import org.destinationsol.ui.nui.screens.TutorialScreen; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.terasology.context.exception.BeanNotFoundException; import org.terasology.gestalt.di.BeanContext; @@ -71,7 +70,7 @@ * instances. * @see TutorialStep */ -public class NewTutorialManager implements UpdateAwareSystem { +public class TutorialManager implements UpdateAwareSystem { private final NUIManager nuiManager; private final TutorialScreen tutorialScreen; private final SolApplication solApplication; @@ -81,7 +80,7 @@ public class NewTutorialManager implements UpdateAwareSystem { private int stepNo; @Inject - public NewTutorialManager(NUIManager nuiManager, SolApplication solApplication, Provider game, BeanContext beanContext) { + public TutorialManager(NUIManager nuiManager, SolApplication solApplication, Provider game, BeanContext beanContext) { this.nuiManager = nuiManager; this.tutorialScreen = (TutorialScreen) nuiManager.createScreen("engine:tutorialScreen"); this.solApplication = solApplication; @@ -101,8 +100,7 @@ public void start() { GameOptions gameOptions = solApplication.getOptions(); GameOptions.ControlType controlType = gameOptions.controlType; boolean isMobile = solApplication.isMobile(); - boolean usesKeyboard = !isMobile && - (controlType != GameOptions.ControlType.MOUSE); + boolean usesKeyboard = !isMobile && (controlType != GameOptions.ControlType.MOUSE); // A lot of input names will vary by control type. In some control types, the inputs are also hard-coded. // Rather than a lot of messy conditional statements in-line with the step definitions, we'll set things up here. @@ -220,7 +218,7 @@ public void start() { isMobile ? "Open the map." : "Open the map (" + gameOptions.getKeyMapName() + ")."), new ButtonPressStep(solGame.get().getScreens().mapScreen.getZoomInButton(), "Zoom In"), new ButtonPressStep(solGame.get().getScreens().mapScreen.getZoomOutButton(), "Zoom Out"), - new MapDragStep("You can move around the map by clicking/tapping and dragging."), + new MapDragStep("You can drag the map to move around."), new CreateWaypointStep("Create a waypoint near your ship."), new CloseScreenStep( solGame.get().getScreens().mapScreen.getCloseButton(), @@ -237,11 +235,7 @@ public void start() { new BuyMercenaryStep(1000, usesKeyboard ? "Select Hire (" + gameOptions.getKeyHireShipMenuName() + ")." : "Select Hire.", "Try hiring a mercenary."), - new MessageStep("Let's see how your mercenary fights."), - new DestroySpawnedShipsStep(1, "core:pirateSmall", - "core:blaster core:smallShield", "Destroy the targeted ship.", - "Enemy ships can be tough.\nOpen the pause menu and select Respawn."), - new MessageStep("Mercenaries will keep any money they collect as part of their payment."), + new MessageStep("Mercenaries will fight for you. They keep any money they collect as part of their payment."), new MessageStep("Section 11 - Managing Mercenaries"), new OpenScreenStep( solGame.get().getScreens().mainGameScreen.getMercsButton(), @@ -254,7 +248,8 @@ public void start() { "Here you can manage your mercenary's equipment."), new FlyToNearestStarPortStep("Fly to the marked star lane."), new MessageStep("For a small fee, star lanes allow you to travel quickly between planets."), - new MessageStep("The tutorial is finished. You will be returned to the main menu.") + new TravelThroughStarPortStep("Fly into the centre to travel across the star lane."), + new MessageStep("That's it! The tutorial is finished. You will be returned to the main menu.") )); for (TutorialStep step : steps) { @@ -299,28 +294,28 @@ public void update(SolGame game, float timeStep) { } TutorialStep currentStep = steps.get(stepNo); - tutorialScreen.setTutorialText(currentStep.getTutorialText(), currentStep.getTutorialBoxPosition()); - if (currentStep.getRequiredInput() != null) { - tutorialScreen.setInteractHintInput(currentStep.getTutorialBoxPosition(), currentStep.getRequiredInput()); - tutorialScreen.setInteractEvent(currentStep.getTutorialBoxPosition(), currentStep.getInputHandler()); - } + setUpTutorialBox(currentStep); if (currentStep.checkComplete(timeStep)) { stepNo++; tutorialScreen.clearAllTutorialBoxes(); if (stepNo < steps.size()) { TutorialStep newStep = steps.get(stepNo); newStep.start(); - tutorialScreen.setTutorialText(newStep.getTutorialText(), newStep.getTutorialBoxPosition()); - if (newStep.getRequiredInput() != null) { - tutorialScreen.setInteractHintInput(newStep.getTutorialBoxPosition(), newStep.getRequiredInput()); - tutorialScreen.setInteractEvent(newStep.getTutorialBoxPosition(), newStep.getInputHandler()); - } + setUpTutorialBox(newStep); } else { solApplication.finishGame(); } } } + private void setUpTutorialBox(TutorialStep tutorialStep) { + tutorialScreen.setTutorialText(tutorialStep.getTutorialText(), tutorialStep.getTutorialBoxPosition()); + if (tutorialStep.getRequiredInput() != null) { + tutorialScreen.setInteractHintInput(tutorialStep.getTutorialBoxPosition(), tutorialStep.getRequiredInput()); + tutorialScreen.setInteractEvent(tutorialStep.getTutorialBoxPosition(), tutorialStep.getInputHandler()); + } + } + /** * This method should be called when either the game or the tutorial ends. It cleans-up the UI changes made for the tutorial. */ diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java index 3e8eb4699..7021716aa 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/MessageStep.java @@ -18,6 +18,7 @@ import org.destinationsol.GameOptions; import org.destinationsol.SolApplication; +import org.destinationsol.game.SolGame; import org.destinationsol.game.tutorial.TutorialStep; import org.terasology.input.ControllerInput; import org.terasology.input.InputType; @@ -35,6 +36,8 @@ public class MessageStep extends TutorialStep { protected static final float MIN_STEP_DURATION = 0.5f; @Inject protected SolApplication solApplication; + @Inject + protected SolGame game; protected final String message; protected float stepTimer; protected boolean interactComplete; @@ -81,6 +84,11 @@ public void start() { @Override public boolean checkComplete(float timeStep) { + if (solApplication.getOptions().controlType == GameOptions.ControlType.CONTROLLER && game.getHero().getPilot().isShoot()) { + // TODO: NUI doesn't support controller input at the moment, so we detect completion here. + interactComplete = true; + } + stepTimer += timeStep; return stepTimer >= MIN_STEP_DURATION && interactComplete; } diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/TravelThroughStarPortStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/TravelThroughStarPortStep.java new file mode 100644 index 000000000..8ac383a8e --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/TravelThroughStarPortStep.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.Hero; +import org.destinationsol.game.SolGame; +import org.destinationsol.game.tutorial.TutorialStep; + +import javax.inject.Inject; + +/** + * A tutorial step that completes once the player has travelled across a star lane. + */ +public class TravelThroughStarPortStep extends TutorialStep { + @Inject + protected SolGame game; + private final String message; + private boolean wasTranscendent; + + @Inject + protected TravelThroughStarPortStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public TravelThroughStarPortStep(String message) { + this.message = message; + } + + @Override + public void start() { + setTutorialText(message); + } + + @Override + public boolean checkComplete(float timeStep) { + Hero hero = game.getHero(); + if (wasTranscendent && !hero.isTranscendent()) { + return true; + } + wasTranscendent = hero.isTranscendent(); + return false; + } +} diff --git a/engine/src/main/java/org/destinationsol/ui/TutorialManager.java b/engine/src/main/java/org/destinationsol/ui/TutorialManager.java deleted file mode 100644 index 6c913df3a..000000000 --- a/engine/src/main/java/org/destinationsol/ui/TutorialManager.java +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright 2018 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.destinationsol.ui; - -import com.badlogic.gdx.Gdx; -import org.destinationsol.GameOptions; -import org.destinationsol.game.SolGame; -import org.destinationsol.game.UpdateAwareSystem; -import org.destinationsol.game.item.SolItem; -import org.destinationsol.game.screens.GameScreens; -import org.destinationsol.game.screens.MainGameScreen; -import org.destinationsol.game.screens.ShipMixedControl; -import org.destinationsol.ui.nui.NUIManager; -import org.destinationsol.ui.nui.NUIScreenLayer; -import org.destinationsol.ui.nui.screens.TutorialScreen; -import org.destinationsol.ui.nui.screens.UIShipControlsScreen; -import org.destinationsol.ui.nui.widgets.UIWarnButton; - -import javax.inject.Inject; -import javax.inject.Provider; -import java.util.ArrayList; -import java.util.List; - -public class TutorialManager implements UpdateAwareSystem { - private final NUIManager nuiManager; - private final TutorialScreen tutorialScreen; - private final ArrayList steps; - private final GameScreens screens; - private final GameOptions gameOptions; - private final Provider game; - - private int stepIndex; - - @Inject - public TutorialManager(GameScreens screens, GameOptions gameOptions, Provider game, NUIManager nuiManager) { - this.screens = screens; - this.gameOptions = gameOptions; - this.game = game; - this.nuiManager = nuiManager; - this.tutorialScreen = (TutorialScreen) nuiManager.createScreen("engine:tutorialScreen"); - - steps = new ArrayList<>(); - stepIndex = 0; - } - - public void start() { - boolean mobile = game.get().getSolApplication().isMobile(); - MainGameScreen main = screens.oldMainGameScreen; - boolean mouseCtrl = main.getShipControl() instanceof ShipMixedControl; - org.destinationsol.ui.nui.screens.MainGameScreen nuiMain = game.get().getMainGameScreen(); - SolUiControl shootCtrl = null; - String shootKey; - String shootKey2; - SolUiControl upCtrl = null; - SolUiControl abilityCtrl = null; - UIWarnButton nuiShootCtrl = null; - UIWarnButton nuiUpCtrl = null; - UIWarnButton nuiLeftCtrl = null; - UIWarnButton nuiAbilityCtrl = null; - if (mouseCtrl) { - ShipMixedControl mixedControl = (ShipMixedControl) main.getShipControl(); - shootCtrl = mixedControl.shootCtrl; - shootKey = "(LEFT mouse button)"; - shootKey2 = "(Click LEFT mouse button)"; - upCtrl = mixedControl.upCtrl; - abilityCtrl = mixedControl.abilityCtrl; - } else { - UIShipControlsScreen kbControl = (UIShipControlsScreen) main.getShipControl(); - nuiShootCtrl = kbControl.getGun1Button(); - nuiUpCtrl = kbControl.getForwardButton(); - nuiLeftCtrl = kbControl.getLeftButton(); - nuiAbilityCtrl = kbControl.getAbilityButton(); - if (mobile) { - shootKey = "(GUN 1 button)"; - shootKey2 = "(Press GUN 1 button)"; - } else { - shootKey = "(" + gameOptions.getKeyShootName() + " key)"; - shootKey2 = "(Press " + gameOptions.getKeyShootName() + " key)"; - } - } - - if (mouseCtrl) { - addStep("Hi! Shoot your main gun\n" + shootKey, shootCtrl); - } else { - addStep("Hi! Shoot your main gun\n" + shootKey, nuiShootCtrl); - } - - if (nuiLeftCtrl != null) { - if (mobile) { - addStep("Great! Turn left.\nDon't fly away yet!", nuiLeftCtrl); - } else { - addStep("Great! Turn left (" + gameOptions.getKeyLeftName() + " key). \nDon't fly away yet!", nuiLeftCtrl); - } - } - - UIWarnButton mapButton = nuiMain.getMapButton(); - if (mobile) { - addStep("Have a look at the map", mapButton, true); - } else { - addStep("Have a look at the map\n(" + gameOptions.getKeyMapName() + " key)", mapButton, true); - } - - if (mouseCtrl) { - addStep("Zoom in the map\n(mouse wheel UP)", screens.mapScreen.getZoomInButton()); - } else if (mobile) { - addStep("Zoom in the map", screens.mapScreen.getZoomInButton()); - } else { - addStep("Zoom in the map\n(" + gameOptions.getKeyZoomInName() + " key)", screens.mapScreen.getZoomInButton()); - } - - if (mobile) { - addScreenCloseStep("Close the map", screens.mapScreen.getCloseButton(), screens.mapScreen); - } else { - addScreenCloseStep("Close the map\n(" + gameOptions.getKeyMapName() + " or " + gameOptions.getKeyCloseName() + " keys)", - screens.mapScreen.getCloseButton(), screens.mapScreen); - } - - UIWarnButton inventoryButton = nuiMain.getInventoryButton(); - if (mouseCtrl || mobile) { - addStep("Have a look\nat your inventory", inventoryButton, true); - } else { - addStep("Have a look\nat your inventory (" + gameOptions.getKeyInventoryName() + " key)", inventoryButton, true); - } - - if (mouseCtrl || mobile) { - addStep("In the inventory,\nselect the second row", screens.inventoryScreen.getRowButton(1)); - } else { - addStep("In the inventory,\nselect the next item (" + gameOptions.getKeyDownName() + " key)", gameOptions.getKeyDown()); - } - - if (mouseCtrl || mobile) { - addStep("Go to the next page", screens.inventoryScreen.getNextButton(), true); - } else { - addStep("Go to the next page\n(" + gameOptions.getKeyRightName() + " key)", screens.inventoryScreen.getNextButton(), true); - } - - if (mouseCtrl || mobile) { - addStep("Throw away some item\nyou don't use", screens.inventoryScreen.getShowInventory().getDropControl()); - } else { - addStep("Throw away some item\nyou don't use (" + gameOptions.getKeyDropName() + " key)", - screens.inventoryScreen.getShowInventory().getDropControl()); - } - - // Extra step to make sure an equipped item is selected before asking player to unequip - if (screens.inventoryScreen.getSelectedItem() == null || - (screens.inventoryScreen.getSelectedItem() != null && screens.inventoryScreen.getSelectedItem().isEquipped() == 0)) { - addStep(new SelectEquippedItemStep( - "Select an equipped item\n(note the text \"using\")", screens.inventoryScreen, game.get())); - } - - if (mobile) { - addStep("Unequip the item\nthat is used now", screens.inventoryScreen.getShowInventory().getEq1Control()); - } else { - addStep("Unequip the item\nthat is used now (" + gameOptions.getKeyEquipName() + " key)", - screens.inventoryScreen.getShowInventory().getEq1Control()); - } - - if (mobile) { - addStep("Now equip it again", screens.inventoryScreen.getShowInventory().getEq1Control()); - } else { - addStep("Now equip it again\n(" + gameOptions.getKeyEquipName() + " key)", screens.inventoryScreen.getShowInventory().getEq1Control()); - } - - if (mobile) { - addScreenCloseStep("Close the inventory\n(Touch the screen outside inventory)", screens.inventoryScreen.getCloseButton(), screens.inventoryScreen); - } else { - addScreenCloseStep("Close the inventory (" + gameOptions.getKeyCloseName() + " key)", screens.inventoryScreen.getCloseButton(), screens.inventoryScreen); - } - - if (mouseCtrl) { - addStep("Move forward (" + gameOptions.getKeyUpMouseName() + " key).\nThere's no stop!", upCtrl); - } else if (mobile) { - addStep("Move forward.\nThere's no stop!", nuiUpCtrl); - } else { - addStep("Move forward (" + gameOptions.getKeyUpName() + " key).\nThere's no stop!", nuiUpCtrl); - } - - UIWarnButton talkButton = nuiMain.getTalkButton(); - if (mobile) { - addStep("Fly closer to the station\nand talk with it", talkButton, true); - } else { - addStep("Fly closer to the station\nand talk with it (" + gameOptions.getKeyTalkName() + " key)", talkButton, true); - } - - if (mouseCtrl || mobile) { - addStep("See what there is to buy", screens.talkScreen.getBuyButton(), true); - } else { - addStep("See what there is to buy\n(" + gameOptions.getKeyBuyMenuName() + " key)", screens.talkScreen.getBuyButton(), true); - } - - if (mobile) { - addStep("Buy some item", screens.inventoryScreen.getBuyItemsScreen().getBuyControl()); - } else { - addStep("Buy some item\n(" + gameOptions.getKeyBuyItemName() + " key)", screens.inventoryScreen.getBuyItemsScreen().getBuyControl()); - } - - if (mobile) { - addScreenCloseStep("Close the Buy screen\n(Touch the screen outside inventory)", screens.inventoryScreen.getCloseButton(), screens.inventoryScreen); - } else { - addScreenCloseStep("Close the Buy screen\n(" + gameOptions.getKeyCloseName() + " key)", screens.inventoryScreen.getCloseButton(), screens.inventoryScreen); - } - - if (mouseCtrl) { - addStep("Use the ability of your ship\n(MIDDLE mouse button or " + gameOptions.getKeyAbilityName() + " key)", - abilityCtrl, true); - } else if (mobile) { - addStep("Use the ability of your ship", nuiAbilityCtrl, true); - } else { - addStep("Use the ability of your ship\n(" + gameOptions.getKeyAbilityName() + " key)", nuiAbilityCtrl, true); - } - - if (mouseCtrl) { - addStep("Here's a couple of hints...\n" + shootKey2, shootCtrl); - addStep("Enemies are orange icons, allies are blue\n" + shootKey2, shootCtrl); - addStep("Avoid enemies with skull icon\n" + shootKey2, shootCtrl); - addStep("To repair, have repair kits and just stay idle\n" + shootKey2, shootCtrl); - addStep("Destroy asteroids to find money\n" + shootKey2, shootCtrl); - addStep("Find or buy shields, armor, guns; equip them\n" + shootKey2, shootCtrl); - addStep("Buy new ships, hire mercenaries\n" + shootKey2, shootCtrl); - addStep("Tutorial is complete and will exit now!\n" + shootKey2, shootCtrl); - } else { - addStep("Here's a couple of hints...\n" + shootKey2, nuiShootCtrl); - addStep("Enemies are orange icons, allies are blue\n" + shootKey2, nuiShootCtrl); - addStep("Avoid enemies with skull icon\n" + shootKey2, nuiShootCtrl); - addStep("To repair, have repair kits and just stay idle\n" + shootKey2, nuiShootCtrl); - addStep("Destroy asteroids to find money\n" + shootKey2, nuiShootCtrl); - addStep("Find or buy shields, armor, guns; equip them\n" + shootKey2, nuiShootCtrl); - addStep("Buy new ships, hire mercenaries\n" + shootKey2, nuiShootCtrl); - addStep("Tutorial is complete and will exit now!\n" + shootKey2, nuiShootCtrl); - } - - steps.get(0).start(); - tutorialScreen.setTutorialText(steps.get(0).text); - } - - private void addStep(String text, SolUiControl ctrl) { - addStep(text, ctrl, false); - } - - private void addStep(String text, int key) { - steps.add(new KeyPressedStep(text, key)); - } - - private void addStep(String text, UIWarnButton ctrl) { - addStep(text, ctrl, false); - } - - private void addStep(String text, SolUiControl ctrl, boolean checkOn) { - steps.add(new Step(text, ctrl, checkOn)); - } - - private void addStep(String text, UIWarnButton ctrl, boolean checkOn) { - steps.add(new NuiStep(text, ctrl, checkOn)); - } - - private void addStep(Step step) { - steps.add(step); - } - - private void addScreenCloseStep(String text, UIWarnButton ctrl, NUIScreenLayer uiScreen) { - steps.add(new NuiScreenCloseStep(text, ctrl, nuiManager, uiScreen)); - } - - @Override - public void update(SolGame game, float timeStep) { - if (nuiManager.getTopScreen() != tutorialScreen) { - if (nuiManager.hasScreen(tutorialScreen)) { - tutorialScreen.moveToTop(); - } else { - nuiManager.pushScreen(tutorialScreen); - } - } - - Step step = steps.get(stepIndex); - step.highlight(); - if (step.canProgressToNextStep()) { - stepIndex++; - if (stepIndex < steps.size()) { - steps.get(stepIndex).start(); - tutorialScreen.setTutorialText(steps.get(stepIndex).text); - } - - if (isFinished()) { - game.getSolApplication().finishGame(); - } - } - } - - public boolean isFinished() { - return stepIndex == steps.size(); - } - - public static class Step { - public final String text; - public final SolUiControl ctrl; - public final boolean checkOn; - - public Step(String text, SolUiControl ctrl, boolean checkOn) { - this.text = text; - this.ctrl = ctrl; - this.checkOn = checkOn; - } - - public void start() { - // Empty, as it is only used in NuiStep - } - - // highlight control that needs to be pressed - public void highlight() { - if (ctrl != null) { - ctrl.enableWarn(); - } - } - - public boolean canProgressToNextStep() { - if (checkOn) { - return ctrl.isOn(); - } else { - return ctrl.isJustOff(); - } - } - } - - public static class NuiStep extends Step { - public final UIWarnButton nuiCtrl; - private boolean buttonPressed; - - public NuiStep(String text, UIWarnButton ctrl, boolean checkOn) { - super(text, null, checkOn); - nuiCtrl = ctrl; - } - - @Override - public void start() { - nuiCtrl.subscribe(widget -> { - buttonPressed = true; - }); - } - - // highlight control that needs to be pressed - @Override - public void highlight() { - if (nuiCtrl != null) { - nuiCtrl.enableWarn(); - } - } - - @Override - public boolean canProgressToNextStep() { - boolean pressed = buttonPressed; - buttonPressed = false; - if (checkOn) { - // TODO: The following line should work but doesn't currently due a nui-libgdx issue - //return nuiCtrl.getMode().equals(UIButton.DOWN_MODE); - return pressed; - } else { - return pressed; - } - } - } - - public static class NuiScreenCloseStep extends NuiStep { - private final NUIManager nuiManager; - private final NUIScreenLayer uiScreen; - - public NuiScreenCloseStep(String text, UIWarnButton closeButton, NUIManager nuiManager, NUIScreenLayer uiScreen) { - super(text, closeButton, true); - this.nuiManager = nuiManager; - this.uiScreen = uiScreen; - } - - @Override - public boolean canProgressToNextStep() { - if (super.canProgressToNextStep()) { - return true; - } - return !nuiManager.hasScreen(uiScreen); - } - } - - public static class SelectEquippedItemStep extends Step { - org.destinationsol.ui.nui.screens.InventoryScreen inventoryScreen; - SolGame game; - - public SelectEquippedItemStep(String text, org.destinationsol.ui.nui.screens.InventoryScreen inventoryScreen, SolGame game) { - super(text, null, true); - this.inventoryScreen = inventoryScreen; - this.game = game; - } - - @Override - public boolean canProgressToNextStep() { - SolItem selected = inventoryScreen.getSelectedItem(); - if (selected != null && selected.isEquipped() != 0) { - return true; - } - return false; - } - - // Highlight all equipped items on opened inventory page - @Override - public void highlight() { - List equippedItemControls = inventoryScreen.getEquippedItemUIControlsForTutorial(); - for (UIWarnButton control : equippedItemControls) { - control.enableWarn(); - } - } - } - - public static class KeyPressedStep extends Step { - private final int key; - - public KeyPressedStep(String text, int key) { - super(text, null, false); - this.key = key; - } - - @Override - public boolean canProgressToNextStep() { - return Gdx.input.isKeyJustPressed(key); - } - } -} diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java index 016364c63..c428bd745 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java @@ -37,7 +37,7 @@ /** * This screen displays the message box shown during the tutorial to instruct the user. * It is unusual in that it should always be rendered on-top of all other UI screens. - * See {@link #moveToTop()} and {@link org.destinationsol.ui.TutorialManager#update(SolGame, float)} for how this is done. + * See {@link #moveToTop()} and {@link org.destinationsol.game.tutorial.TutorialManager#update(SolGame, float)} for how this is done. */ public class TutorialScreen extends NUIScreenLayer { private static final class TutorialBox { @@ -229,14 +229,10 @@ protected boolean escapeCloses() { @Override public boolean onKeyEvent(NUIKeyEvent event) { - if (event.isDown()) { - return super.onKeyEvent(event); - } - for (TutorialBox tutorialBox : tutorialBoxes.values()) { if (tutorialBox.interactHint != null && tutorialBox.interactHint.isVisible() && tutorialBox.interactHint.getInput().equals(event.getKey())) { - if (tutorialBox.inputEventListener != null) { + if (!event.isDown() && tutorialBox.inputEventListener != null) { tutorialBox.inputEventListener.accept(event.getKey()); } return true; From 2e5e04a248282c34f9e975218772abb1ec54e57d Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Tue, 28 Mar 2023 21:09:23 +0100 Subject: [PATCH 16/18] feat(tutorial): move tutorial sections into headings --- .../game/tutorial/TutorialManager.java | 35 +++++++++----- .../game/tutorial/TutorialStep.java | 21 +++++++- .../steps/ChangeTutorialSectionStep.java | 48 +++++++++++++++++++ .../FlyToPlanetSellingMercenariesStep.java | 20 +++++--- .../ui/nui/screens/TutorialScreen.java | 44 ++++++++++++++++- .../assets/skins/tutorialScreen.skin | 5 +- .../assets/ui/tutorialScreen.ui | 44 +++++++++++++++-- 7 files changed, 190 insertions(+), 27 deletions(-) create mode 100644 engine/src/main/java/org/destinationsol/game/tutorial/steps/ChangeTutorialSectionStep.java diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java index 41153dcf7..765290ee0 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java @@ -27,6 +27,7 @@ import org.destinationsol.game.tutorial.steps.ButtonPressStep; import org.destinationsol.game.tutorial.steps.BuyItemStep; import org.destinationsol.game.tutorial.steps.BuyMercenaryStep; +import org.destinationsol.game.tutorial.steps.ChangeTutorialSectionStep; import org.destinationsol.game.tutorial.steps.CheckGunReloadStep; import org.destinationsol.game.tutorial.steps.CheckItemEquippedStep; import org.destinationsol.game.tutorial.steps.CloseScreenStep; @@ -78,6 +79,7 @@ public class TutorialManager implements UpdateAwareSystem { private final BeanContext beanContext; private List steps; private int stepNo; + private String currentTutorialHeading; @Inject public TutorialManager(NUIManager nuiManager, SolApplication solApplication, Provider game, BeanContext beanContext) { @@ -151,12 +153,12 @@ public void start() { itemTypesExplanations.put(Shield.class, "Shields absorb energy-based projectiles until depleted."); steps = new ArrayList<>(Arrays.asList( - new MessageStep("Section 1 - Movement"), + new ChangeTutorialSectionStep("Movement"), new TurnLeftRightStep(isMobile ? "Turn left and right." : "Turn left and right (" + turnControlHint + ")."), new ThrustForwardsStep(isMobile ? "Thrust forwards." : "Thrust forwards (" + thrustForwardControlHint + ")."), new SlowVelocityStep(0.1f, "Turn around and thrust again to slow down.\n\nTry slowing to a stop."), new FlyToRandomWaypointAroundHeroStep(1.0f, 2.5f, "Fly to the waypoint."), - new MessageStep("Section 2 - Weapons"), + new ChangeTutorialSectionStep("Weapons"), new FireGunStep(isMobile ? "Fire your gun." : "Fire your gun (" + shootControlHint + ")."), new CheckGunReloadStep(false, true, "Firing weapons drains your ammunition. Keep on firing."), new CheckGunReloadStep(false, false, @@ -164,10 +166,10 @@ public void start() { "You can't fire when reloading."), new UseAbilityStep(isMobile ? "Use your ability." : "Use your ability (" + abilityControlHint + ")."), new MessageStep("Abilities consume ability charges."), - new MessageStep("Section 3 - Money"), + new ChangeTutorialSectionStep("Money"), new DestroySpawnedAsteroidAroundHeroStep(1.0f, 2.5f, "Fire at the asteroid."), - new MessageStep("Asteroids drop loot - money in this case."), - new MessageStep("Section 4 - Items"), + new MessageStep("Asteroids drop money. You can fly into it to collect it."), + new ChangeTutorialSectionStep("Items"), new OpenScreenStep( solGame.get().getScreens().mainGameScreen.getInventoryButton(), solGame.get().getScreens().inventoryScreen, @@ -191,11 +193,11 @@ public void start() { solGame.get().getScreens().inventoryScreen.getCloseButton(), solGame.get().getScreens().inventoryScreen, isMobile ? "Close your inventory (tap outside of the inventory)." : "Close your inventory."), - new MessageStep("Section 5 - Weapon Mounts"), + new ChangeTutorialSectionStep("Weapon Mounts"), new MessageStep("All ships may come with up to two weapon mounts."), new MessageStep("Weapon mounts are either fixed or rotating."), new MessageStep("You can only equip weapons on matching mounts."), - new MessageStep("Section 6 - Shops"), + new ChangeTutorialSectionStep("Shops"), new FlyToNearestStationStep("Fly to the station."), new OpenScreenStep( solGame.get().getScreens().mainGameScreen.getTalkButton(), @@ -203,15 +205,15 @@ public void start() { isMobile ? "Talk to the station." : "Talk to the station (" + gameOptions.getKeyTalkName() + ")."), new BuyItemStep(usesKeyboard ? "Select Buy (" + gameOptions.getKeyBuyMenuName() + ")." : "Select Buy.", isMobile ? "Buy an item." : "Buy an item (" + gameOptions.getKeyBuyItemName() + ")."), - new MessageStep("Section 7 - Combat"), + new ChangeTutorialSectionStep("Combat"), new MessageStep("Shoot at ships to destroy them.\n"), new DestroySpawnedShipsStep(1, "core:minerSmall", "core:fixedBlaster", "Destroy the targeted ship.", "Enemy ships can be tough.\nOpen the pause menu and select Respawn."), new MessageStep("Destroyed ships drop valuable loot."), - new MessageStep("Section 8 - Repair Kits"), + new ChangeTutorialSectionStep("Repair Kits"), new WaitUntilFullyRepairedStep("Stay still and wait until the repair kits have repaired your hull fully."), - new MessageStep("Section 9 - Map"), + new ChangeTutorialSectionStep("Map"), new OpenScreenStep( solGame.get().getScreens().mainGameScreen.getMapButton(), solGame.get().getScreens().mapScreen, @@ -225,8 +227,9 @@ public void start() { solGame.get().getScreens().mapScreen, "Close the map."), new FlyToHeroFirstWaypointStep("Fly to your waypoint."), - new MessageStep("Section 10 - Hiring Mercenaries"), - new FlyToPlanetSellingMercenariesStep("Fly to a planetary station providing mercenaries."), + new ChangeTutorialSectionStep("Planets"), + new FlyToPlanetSellingMercenariesStep("Head towards a planet.", "Look for the planetary station."), + new ChangeTutorialSectionStep("Mercenaries"), new MessageStep("When flying around planets, you'll be affected by gravity."), new OpenScreenStep( solGame.get().getScreens().mainGameScreen.getTalkButton(), @@ -236,7 +239,6 @@ public void start() { usesKeyboard ? "Select Hire (" + gameOptions.getKeyHireShipMenuName() + ")." : "Select Hire.", "Try hiring a mercenary."), new MessageStep("Mercenaries will fight for you. They keep any money they collect as part of their payment."), - new MessageStep("Section 11 - Managing Mercenaries"), new OpenScreenStep( solGame.get().getScreens().mainGameScreen.getMercsButton(), solGame.get().getScreens().inventoryScreen, @@ -246,9 +248,11 @@ public void start() { "Here you can give items to your mercenary.", "Here you can take items back from your mercenary.", "Here you can manage your mercenary's equipment."), + new ChangeTutorialSectionStep("Star Lanes"), new FlyToNearestStarPortStep("Fly to the marked star lane."), new MessageStep("For a small fee, star lanes allow you to travel quickly between planets."), new TravelThroughStarPortStep("Fly into the centre to travel across the star lane."), + new ChangeTutorialSectionStep("Finish"), new MessageStep("That's it! The tutorial is finished. You will be returned to the main menu.") )); @@ -309,7 +313,12 @@ public void update(SolGame game, float timeStep) { } private void setUpTutorialBox(TutorialStep tutorialStep) { + if (tutorialStep.getTutorialHeading() != null) { + currentTutorialHeading = tutorialStep.getTutorialHeading(); + } + tutorialScreen.setTutorialText(tutorialStep.getTutorialText(), tutorialStep.getTutorialBoxPosition()); + tutorialScreen.setTutorialHeading(currentTutorialHeading, tutorialStep.getTutorialBoxPosition()); if (tutorialStep.getRequiredInput() != null) { tutorialScreen.setInteractHintInput(tutorialStep.getTutorialBoxPosition(), tutorialStep.getRequiredInput()); tutorialScreen.setInteractEvent(tutorialStep.getTutorialBoxPosition(), tutorialStep.getInputHandler()); diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java index afde22272..eb632e1a4 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialStep.java @@ -28,6 +28,7 @@ * populate. */ public abstract class TutorialStep { + private String tutorialHeading; private String tutorialText; private HorizontalAlign tutorialBoxPosition = HorizontalAlign.CENTER; private Input requiredInput; @@ -46,8 +47,16 @@ public abstract class TutorialStep { public abstract boolean checkComplete(float timeStep); /** - * Returns the explanatory text to the shown to the player. - * @return the explanatory text to the shown to the player + * Returns the heading text shown to the player. This can be null, to preserve the existing heading. + * @return the heading text shown to the player This can be null to preserve the existing heading. + */ + public String getTutorialHeading() { + return tutorialHeading; + } + + /** + * Returns the explanatory text to be the shown to the player. + * @return the explanatory text to be the shown to the player */ public String getTutorialText() { return tutorialText; @@ -77,6 +86,14 @@ public Consumer getInputHandler() { return inputHandler; } + /** + * Specifies the heading to display above the tutorial box. This can be null to preserve the existing heading. + * @param tutorialHeading the heading to display above the tutorial box, or null to preserve the existing heading. + */ + protected void setTutorialHeading(String tutorialHeading) { + this.tutorialHeading = tutorialHeading; + } + /** * Specifies the explanatory text to display in the tutorial box. * @param tutorialText the explanatory text to display diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/ChangeTutorialSectionStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ChangeTutorialSectionStep.java new file mode 100644 index 000000000..d5b4a2057 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/ChangeTutorialSectionStep.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 The Terasology Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.destinationsol.game.tutorial.steps; + +import org.destinationsol.game.tutorial.TutorialStep; + +import javax.inject.Inject; + +/** + * This tutorial step simply changes the displayed heading above the tutorial box. + */ +public class ChangeTutorialSectionStep extends TutorialStep { + private final String sectionHeading; + + @Inject + protected ChangeTutorialSectionStep() { + throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); + } + + public ChangeTutorialSectionStep(String sectionHeading) { + this.sectionHeading = sectionHeading; + } + + @Override + public void start() { + setTutorialText(sectionHeading); + setTutorialHeading(sectionHeading); + } + + @Override + public boolean checkComplete(float timeStep) { + return true; + } +} diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java index c92b0c2bb..2c9235a44 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/FlyToPlanetSellingMercenariesStep.java @@ -32,13 +32,16 @@ * It guides the player towards the planet first with a waypoint, then towards the shop itself. */ public class FlyToPlanetSellingMercenariesStep extends FlyToPlanetStep { + private final String onPlanetMessage; + @Inject protected FlyToPlanetSellingMercenariesStep() { throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); } - public FlyToPlanetSellingMercenariesStep(String message) { + public FlyToPlanetSellingMercenariesStep(String message, String onPlanetMessage) { super(null, message); + this.onPlanetMessage = onPlanetMessage; } @Override @@ -107,12 +110,15 @@ private void setPlanetStationWaypoint() { @Override public boolean checkComplete(float timeStep) { boolean nearPlanet = super.checkComplete(timeStep); - if (nearPlanet && planet.areObjectsCreated()) { - setPlanetStationWaypoint(); - if (game.getMainGameScreen().getTalkButton().isEnabled()) { - game.getHero().getWaypoints().remove(waypoint); - game.getObjectManager().removeObjDelayed(waypoint); - return true; + if (nearPlanet) { + setTutorialText(onPlanetMessage); + if (planet.areObjectsCreated()) { + setPlanetStationWaypoint(); + if (game.getMainGameScreen().getTalkButton().isEnabled()) { + game.getHero().getWaypoints().remove(waypoint); + game.getObjectManager().removeObjDelayed(waypoint); + return true; + } } } return false; diff --git a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java index c428bd745..7aa6e459c 100644 --- a/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java @@ -43,12 +43,14 @@ public class TutorialScreen extends NUIScreenLayer { private static final class TutorialBox { public final UIBox box; public final UILabel text; + public final UILabel heading; public final InteractHint interactHint; public Consumer inputEventListener; - public TutorialBox(UIBox box, UILabel text, InteractHint interactHint) { + public TutorialBox(UIBox box, UILabel text, UILabel heading, InteractHint interactHint) { this.box = box; this.text = text; + this.heading = heading; this.interactHint = interactHint; } } @@ -86,6 +88,7 @@ public void initialise() { TutorialBox tutorialBox = new TutorialBox( find("tutorialBox" + horizontalAlign.toString(), UIBox.class), find("tutorialText" + horizontalAlign.toString(), UILabel.class), + find("tutorialHeading" + horizontalAlign.toString(), UILabel.class), find("interactHint" + horizontalAlign.toString(), InteractHint.class) ); if (tutorialBox.interactHint != null) { @@ -130,6 +133,41 @@ public void setTutorialText(String text, HorizontalAlign horizontalAlign) { getTutorialBox(horizontalAlign).setVisible(!text.isEmpty()); } + /** + * Returns the heading displayed above the centre tutorial box. + * @return the heading displayed above the centre tutorial box. + */ + public String getTutorialHeading() { + return getTutorialHeading(HorizontalAlign.CENTER); + } + + /** + * Returns the heading displayed above the specified tutorial box. + * @param horizontalAlign the tutorial box to select + * @return the heading displayed above the specified tutorial box. + */ + public String getTutorialHeading(HorizontalAlign horizontalAlign) { + return getTutorialHeadingLabel(horizontalAlign).getText(); + } + + /** + * Specifies the heading to be displayed above the specified tutorial box. + * @param heading the heading to be displayed + */ + public void setTutorialHeading(String heading) { + setTutorialHeading(heading, HorizontalAlign.CENTER); + } + + /** + * Specifies the heading to be displayed above the specified tutorial box. + * @param heading the heading to be displayed + * @param horizontalAlign the tutorial box to select + */ + public void setTutorialHeading(String heading, HorizontalAlign horizontalAlign) { + getTutorialHeadingLabel(horizontalAlign).setText(heading); + getTutorialHeadingLabel(horizontalAlign).setVisible(!heading.isEmpty()); + } + /** * Returns the input hinted at by the centre tutorial box. This can be null. * @return the input hinted at by the centre tutorial box @@ -214,6 +252,10 @@ protected UILabel getTutorialTextLabel(HorizontalAlign horizontalAlign) { return tutorialBoxes.get(horizontalAlign).text; } + protected UILabel getTutorialHeadingLabel(HorizontalAlign horizontalAlign) { + return tutorialBoxes.get(horizontalAlign).heading; + } + protected UIBox getTutorialBox(HorizontalAlign horizontalAlign) { return tutorialBoxes.get(horizontalAlign).box; } diff --git a/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin index 32460de37..f7344773e 100644 --- a/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin +++ b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin @@ -14,7 +14,7 @@ }, "tutorialBoxLEFT": { "font": "engine:main#0.8", - "max-width": 160, + "min-width": 178, "min-height": 192, "elements": { "UIBox": { @@ -22,6 +22,9 @@ "text-align-horizontal": "middle" } } + }, + "tutorialHeading": { + "font": "engine:main#0.4" } }, "elements": { diff --git a/engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui b/engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui index 9906b4de3..52b3ecedd 100644 --- a/engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui +++ b/engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui @@ -11,6 +11,22 @@ "content": { "type": "RelativeLayout", "contents": [ + { + "type": "UILabel", + "family": "tutorialHeading", + "id": "tutorialHeadingCENTER", + "text": "Heading", + "layoutInfo": { + "position-top": { + "offset": -16 + }, + "position-left": { + "offset": 8 + }, + "use-content-width": true, + "use-content-height": true + } + }, { "type": "UILabel", "id": "tutorialTextCENTER", @@ -49,9 +65,31 @@ "id": "tutorialBoxLEFT", "family": "tutorialBoxLEFT", "content": { - "type": "UILabel", - "id": "tutorialTextLEFT", - "text": "Tutorial content goes here..." + "type": "RelativeLayout", + "contents": [ + { + "type": "UILabel", + "family": "tutorialHeading", + "id": "tutorialHeadingLEFT", + "text": "Heading", + "layoutInfo": { + "position-top": { + "offset": -16 + }, + "position-left": { + "offset": 8 + }, + "use-content-width": true, + "use-content-height": true + } + }, + { + "type": "UILabel", + "id": "tutorialTextLEFT", + "text": "Tutorial content goes here...", + "layoutInfo": {} + } + ] }, "layoutInfo": { "position-left": { From 6285bb18d6d37d70a74c155f772ef580c0925e6e Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Thu, 30 Mar 2023 22:12:23 +0100 Subject: [PATCH 17/18] fix(knockback): fix excessive knockback force This fixes #676. --- .../src/main/java/org/destinationsol/game/ship/KnockBack.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/src/main/java/org/destinationsol/game/ship/KnockBack.java b/engine/src/main/java/org/destinationsol/game/ship/KnockBack.java index 410604ea1..810f35d3d 100644 --- a/engine/src/main/java/org/destinationsol/game/ship/KnockBack.java +++ b/engine/src/main/java/org/destinationsol/game/ship/KnockBack.java @@ -83,7 +83,7 @@ public boolean update(SolGame game, SolShip owner, boolean tryToUse) { Vector2 toO = SolMath.distVec(ownerPos, oPos); float accLen = config.force * perc; toO.scl(accLen / dst); - o.receiveForce(toO, game, false); + o.receiveForce(toO, game, true); SolMath.free(toO); } DSParticleEmitter src = new DSParticleEmitter(config.cc.effect, MAX_RADIUS, DrawableLevel.PART_BG_0, new Vector2(), true, game, ownerPos, Vector2.Zero, 0); From 670b073ed64d729420a9487e0fd11173ca795fd8 Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Thu, 30 Mar 2023 23:26:20 +0100 Subject: [PATCH 18/18] fix(tutorial): notify player if their waypoint is too far away --- .../destinationsol/game/tutorial/TutorialManager.java | 4 +++- .../game/tutorial/steps/CreateWaypointStep.java | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java index 765290ee0..488f5f362 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/TutorialManager.java @@ -221,7 +221,9 @@ public void start() { new ButtonPressStep(solGame.get().getScreens().mapScreen.getZoomInButton(), "Zoom In"), new ButtonPressStep(solGame.get().getScreens().mapScreen.getZoomOutButton(), "Zoom Out"), new MapDragStep("You can drag the map to move around."), - new CreateWaypointStep("Create a waypoint near your ship."), + new CreateWaypointStep("Create a waypoint near your ship.", + "That's too far away.\n\n" + + "Remove it and place one closer to your ship."), new CloseScreenStep( solGame.get().getScreens().mapScreen.getCloseButton(), solGame.get().getScreens().mapScreen, diff --git a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java index 4f23b107a..7d57ffbaf 100644 --- a/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java +++ b/engine/src/main/java/org/destinationsol/game/tutorial/steps/CreateWaypointStep.java @@ -36,6 +36,7 @@ public class CreateWaypointStep extends TutorialStep { @Inject protected GameScreens gameScreens; private final String message; + private final String waypointTooFarMessage; private UIWarnButton addWaypointButton; private boolean buttonPressed = false; private int lastWaypointCount; @@ -45,8 +46,9 @@ protected CreateWaypointStep() { throw new RuntimeException("Attempted to instantiate TutorialStep via DI. This is not supported."); } - public CreateWaypointStep(String message) { + public CreateWaypointStep(String message, String waypointTooFarMessage) { this.message = message; + this.waypointTooFarMessage = waypointTooFarMessage; } public void start() { @@ -66,12 +68,16 @@ public boolean checkComplete(float timeStep) { } Hero hero = game.getHero(); + if (hero.getWaypoints().size() == 0) { + setTutorialText(message); + } + if (hero.getWaypoints().size() > lastWaypointCount) { Waypoint waypoint = hero.getWaypoints().get(hero.getWaypoints().size()-1); if (waypoint.getPosition().dst(hero.getPosition()) < 100.0f) { return true; } else { - hero.removeWaypoint(waypoint); + setTutorialText(waypointTooFarMessage); } } lastWaypointCount = game.getHero().getWaypoints().size();