From 221e36caa38157a0c46f1f78d2078796b4419c6d Mon Sep 17 00:00:00 2001 From: Benjamin Amos Date: Wed, 12 Oct 2022 19:25:30 +0100 Subject: [PATCH] feat(nui): convert the tutorial screen to NUI --- .../java/org/destinationsol/game/SolGame.java | 7 +- .../game/screens/MainGameScreen.java | 4 +- .../destinationsol/ui/SolInputManager.java | 12 --- .../destinationsol/ui/TutorialManager.java | 84 ++++++++++++------- .../ui/nui/screens/TutorialScreen.java | 82 ++++++++++++++++++ .../assets/skins/tutorialScreen.skin | 16 ++++ .../assets/ui/tutorialScreen.ui | 29 +++++++ 7 files changed, 188 insertions(+), 46 deletions(-) create mode 100644 engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java create mode 100644 engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin create mode 100644 engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui diff --git a/engine/src/main/java/org/destinationsol/game/SolGame.java b/engine/src/main/java/org/destinationsol/game/SolGame.java index 3b99edfd0..36ddaf03e 100644 --- a/engine/src/main/java/org/destinationsol/game/SolGame.java +++ b/engine/src/main/java/org/destinationsol/game/SolGame.java @@ -337,10 +337,11 @@ public void onGameEnd(Context context) { } catch (Exception e) { e.printStackTrace(); } - - // TODO: Remove this when context is reset after each game - context.get(EntitySystemManager.class).getEntityManager().allEntities().forEach(EntityRef::delete); } + + // TODO: Remove this when context is reset after each game + context.get(EntitySystemManager.class).getEntityManager().allEntities().forEach(EntityRef::delete); + FactionInfo.clearValues(); try { objectManager.close(); diff --git a/engine/src/main/java/org/destinationsol/game/screens/MainGameScreen.java b/engine/src/main/java/org/destinationsol/game/screens/MainGameScreen.java index 87030ace3..855dbdfa5 100644 --- a/engine/src/main/java/org/destinationsol/game/screens/MainGameScreen.java +++ b/engine/src/main/java/org/destinationsol/game/screens/MainGameScreen.java @@ -29,6 +29,7 @@ import org.destinationsol.ui.nui.NUIManager; import org.destinationsol.ui.nui.NUIScreenLayer; import org.destinationsol.ui.nui.screens.ConsoleScreen; +import org.destinationsol.ui.nui.screens.TutorialScreen; import org.destinationsol.ui.nui.screens.UIShipControlsScreen; import java.util.ArrayList; @@ -117,7 +118,8 @@ public void updateCustom(SolApplication solApplication, SolInputManager.InputPoi NUIScreenLayer topScreen = nuiManager.getTopScreen(); boolean controlsEnabled = inputMan.getTopScreen() == this && (topScreen instanceof org.destinationsol.ui.nui.screens.MainGameScreen || - topScreen instanceof UIShipControlsScreen); + topScreen instanceof UIShipControlsScreen || + topScreen instanceof TutorialScreen); shipControl.update(solApplication, controlsEnabled); if (solApplication.getNuiManager().hasScreenOfType(ConsoleScreen.class)) { diff --git a/engine/src/main/java/org/destinationsol/ui/SolInputManager.java b/engine/src/main/java/org/destinationsol/ui/SolInputManager.java index 813ad01d2..c520f8d94 100644 --- a/engine/src/main/java/org/destinationsol/ui/SolInputManager.java +++ b/engine/src/main/java/org/destinationsol/ui/SolInputManager.java @@ -255,11 +255,6 @@ public void update(SolApplication solApplication) { screen.updateCustom(solApplication, inputPointers, clickedOutside); } - TutorialManager tutorialManager = game == null ? null : game.getTutMan(); - if (tutorialManager != null && tutorialManager.isFinished()) { - solApplication.finishGame(); - } - updateCursor(solApplication); addRemoveScreens(); updateWarnPerc(); @@ -366,13 +361,6 @@ public void draw(UiDrawer uiDrawer, SolApplication solApplication) { } } uiDrawer.setTextMode(null); - - SolGame game = solApplication.getGame(); - TutorialManager tutorialManager = game == null ? null : game.getTutMan(); - if (tutorialManager != null && - solApplication.getNuiManager().getTopScreen() != game.getScreens().menuScreen) { - tutorialManager.draw(uiDrawer); - } } public void drawCursor(UiDrawer uiDrawer) { diff --git a/engine/src/main/java/org/destinationsol/ui/TutorialManager.java b/engine/src/main/java/org/destinationsol/ui/TutorialManager.java index 5b70fb20e..6c913df3a 100644 --- a/engine/src/main/java/org/destinationsol/ui/TutorialManager.java +++ b/engine/src/main/java/org/destinationsol/ui/TutorialManager.java @@ -16,16 +16,16 @@ package org.destinationsol.ui; import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.math.Rectangle; import org.destinationsol.GameOptions; -import org.destinationsol.common.SolColor; 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.screens.InventoryScreen; +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; @@ -35,25 +35,23 @@ import java.util.List; public class TutorialManager implements UpdateAwareSystem { - private final Rectangle background; + 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 final DisplayDimensions displayDimensions; private int stepIndex; @Inject - public TutorialManager(GameScreens screens, GameOptions gameOptions, Provider game, DisplayDimensions displayDimensions) { + public TutorialManager(GameScreens screens, GameOptions gameOptions, Provider game, NUIManager nuiManager) { this.screens = screens; this.gameOptions = gameOptions; this.game = game; - this.displayDimensions = displayDimensions; + this.nuiManager = nuiManager; + this.tutorialScreen = (TutorialScreen) nuiManager.createScreen("engine:tutorialScreen"); - float backgroundW = displayDimensions.getRatio() * .5f; - float backgroundH = .2f; - background = new Rectangle(displayDimensions.getRatio() / 2 - backgroundW / 2, 1 - backgroundH, backgroundW, backgroundH); steps = new ArrayList<>(); stepIndex = 0; } @@ -124,10 +122,10 @@ public void start() { } if (mobile) { - addStep("Close the map", screens.mapScreen.getCloseButton(), true); + addScreenCloseStep("Close the map", screens.mapScreen.getCloseButton(), screens.mapScreen); } else { - addStep("Close the map\n(" + gameOptions.getKeyMapName() + " or " + gameOptions.getKeyCloseName() + " keys)", - screens.mapScreen.getCloseButton(), true); + addScreenCloseStep("Close the map\n(" + gameOptions.getKeyMapName() + " or " + gameOptions.getKeyCloseName() + " keys)", + screens.mapScreen.getCloseButton(), screens.mapScreen); } UIWarnButton inventoryButton = nuiMain.getInventoryButton(); @@ -177,9 +175,9 @@ public void start() { } if (mobile) { - addStep("Close the inventory\n(Touch the screen outside inventory)", screens.inventoryScreen.getCloseButton(), true); + addScreenCloseStep("Close the inventory\n(Touch the screen outside inventory)", screens.inventoryScreen.getCloseButton(), screens.inventoryScreen); } else { - addStep("Close the inventory (" + gameOptions.getKeyCloseName() + " key)", screens.inventoryScreen.getCloseButton(), true); + addScreenCloseStep("Close the inventory (" + gameOptions.getKeyCloseName() + " key)", screens.inventoryScreen.getCloseButton(), screens.inventoryScreen); } if (mouseCtrl) { @@ -210,9 +208,9 @@ public void start() { } if (mobile) { - addStep("Close the Buy screen\n(Touch the screen outside inventory)", screens.inventoryScreen.getCloseButton(), true); + addScreenCloseStep("Close the Buy screen\n(Touch the screen outside inventory)", screens.inventoryScreen.getCloseButton(), screens.inventoryScreen); } else { - addStep("Close the Buy screen\n(" + gameOptions.getKeyCloseName() + " key)", screens.inventoryScreen.getCloseButton(), true); + addScreenCloseStep("Close the Buy screen\n(" + gameOptions.getKeyCloseName() + " key)", screens.inventoryScreen.getCloseButton(), screens.inventoryScreen); } if (mouseCtrl) { @@ -243,7 +241,9 @@ public void start() { 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) { @@ -270,28 +270,33 @@ 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); } - } - } - public void draw(UiDrawer uiDrawer) { - if (isFinished()) { - return; + if (isFinished()) { + game.getSolApplication().finishGame(); + } } - Step step = steps.get(stepIndex); - uiDrawer.draw(background, SolColor.UI_BG_LIGHT); - uiDrawer.drawLine(background.x, background.y, 0, background.width, SolColor.WHITE); - uiDrawer.drawLine(background.x + background.width, background.y, 90, background.height, SolColor.WHITE); - uiDrawer.drawLine(background.x, background.y, 90, background.height, SolColor.WHITE); - uiDrawer.drawString(step.text, displayDimensions.getRatio() / 2, background.y + background.height / 2, FontSize.TUT, true, SolColor.WHITE); } public boolean isFinished() { @@ -367,11 +372,30 @@ public boolean canProgressToNextStep() { } } + 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 { - InventoryScreen inventoryScreen; + org.destinationsol.ui.nui.screens.InventoryScreen inventoryScreen; SolGame game; - public SelectEquippedItemStep(String text, 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; 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 new file mode 100644 index 000000000..f1a012a65 --- /dev/null +++ b/engine/src/main/java/org/destinationsol/ui/nui/screens/TutorialScreen.java @@ -0,0 +1,82 @@ +/* + * Copyright 2022 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.screens; + +import org.destinationsol.game.SolGame; +import org.destinationsol.ui.nui.NUIScreenLayer; +import org.terasology.nui.widgets.UILabel; + +import javax.inject.Inject; + +/** + * 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. + */ +public class TutorialScreen extends NUIScreenLayer { + private UILabel tutorialText; + private boolean isReplaceRemove; + + @Inject + public TutorialScreen() { + } + + @Override + public void initialise() { + tutorialText = find("tutorialText", UILabel.class); + } + + public String getTutorialText() { + return tutorialText.getText(); + } + + public void setTutorialText(String text) { + tutorialText.setText(text); + } + + @Override + public boolean isBlockingInput() { + return false; + } + + @Override + protected boolean escapeCloses() { + return false; + } + + public void moveToTop() { + isReplaceRemove = true; + nuiManager.removeScreen(this); + isReplaceRemove = false; + nuiManager.pushScreen(this); + } + + @Override + public void onRemoved() { + if (isReplaceRemove) { + return; + } + + // This screen is always on-top, so when other screens call popScreen, + // we should remove the screen underneath us, since this was likely the intended behaviour. + if (nuiManager.getScreens().size() > 1 && + !(nuiManager.getTopScreen() instanceof MainGameScreen) && + !(nuiManager.getTopScreen() instanceof UIShipControlsScreen)) { + nuiManager.popScreen(); + } + } +} diff --git a/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin new file mode 100644 index 000000000..70394417a --- /dev/null +++ b/engine/src/main/resources/org/destinationsol/assets/skins/tutorialScreen.skin @@ -0,0 +1,16 @@ +{ + "inherit": "engine:mainGameScreen", + "families": { + "tutorialBox": { + "font": "engine:main#0.8", + "max-width": 512, + "min-height": 128, + "elements": { + "UIBox": { + "background": "engine:background", + "text-align-horizontal": "middle" + } + } + } + } +} \ No newline at end of file diff --git a/engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui b/engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui new file mode 100644 index 000000000..eabee7d77 --- /dev/null +++ b/engine/src/main/resources/org/destinationsol/assets/ui/tutorialScreen.ui @@ -0,0 +1,29 @@ +{ + "type": "TutorialScreen", + "skin": "engine:tutorialScreen", + "contents": { + "type": "RelativeLayout", + "contents": [ + { + "type": "UIBox", + "id": "tutorialBox", + "family": "tutorialBox", + "content": { + "type": "UILabel", + "id": "tutorialText", + "text": "Tutorial content goes here..." + }, + "layoutInfo": { + "position-left": { + "offset": 128 + }, + "position-right": { + "offset": 128 + }, + "position-bottom": {}, + "use-content-height": true + } + } + ] + } +} \ No newline at end of file