diff --git a/source/core/src/main/com/csse3200/game/GdxGame.java b/source/core/src/main/com/csse3200/game/GdxGame.java index b7b88ce06..62926de15 100644 --- a/source/core/src/main/com/csse3200/game/GdxGame.java +++ b/source/core/src/main/com/csse3200/game/GdxGame.java @@ -127,8 +127,6 @@ private Screen newScreen(ScreenType screenType) { return new SettingsScreen(this); case LOAD_GAME: return new LoadGameScreen(this); - case CUTSCENE: - return new CutsceneScreen(this, CutsceneType.MORAL_2); case MORAL_SCENE_1: return new CutsceneScreen(this, CutsceneType.MORAL_1); case MORAL_SCENE_2: diff --git a/source/core/src/main/com/csse3200/game/components/maingame/MoralDisplayTemp.java b/source/core/src/main/com/csse3200/game/components/maingame/MoralDisplayTemp.java deleted file mode 100644 index 31ac8a708..000000000 --- a/source/core/src/main/com/csse3200/game/components/maingame/MoralDisplayTemp.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.csse3200.game.components.maingame; - - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.BitmapFont; -import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.scenes.scene2d.InputEvent; -import com.badlogic.gdx.scenes.scene2d.Stage; -import com.badlogic.gdx.scenes.scene2d.ui.Image; -import com.badlogic.gdx.scenes.scene2d.ui.Label; -import com.badlogic.gdx.scenes.scene2d.ui.List; -import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; -import com.badlogic.gdx.scenes.scene2d.ui.Table; -import com.badlogic.gdx.scenes.scene2d.ui.TextButton; -import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; -import com.badlogic.gdx.scenes.scene2d.utils.Drawable; -import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; -import com.csse3200.game.screens.MainGameScreen; -import com.csse3200.game.services.ServiceLocator; -import com.csse3200.game.ui.UIComponent; - - - -//From team 2, this is just a temporary file that serves as the moraldipslay -//when the proper file is implemented, the logic for the eventlisteners will be used in that -//and this file will be deleted - -public class MoralDisplayTemp extends UIComponent { - private Table layout; // Layout manager - private boolean isVisible; - private final MainGameScreen game; - private static final Logger logger = LoggerFactory.getLogger(EndDayDisplay.class); - - - public MoralDisplayTemp(MainGameScreen game) { - super(); - this.game = game; - isVisible = false; - } - - @Override - public void create() { - super.create(); - layout = new Table(); - layout.setFillParent(true); - layout.setVisible(isVisible); - stage.addActor(layout); - - // Create a white background - Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888); - pixmap.setColor(Color.RED); - pixmap.fill(); - Texture pixmapTex = new Texture(pixmap); - pixmap.dispose(); - Drawable whiteBackground = new TextureRegionDrawable(new TextureRegion(pixmapTex)); - layout.setBackground(whiteBackground); - - // Load the image - Texture imgTexture = ServiceLocator.getResourceService() - .getAsset("images/bird.png", Texture.class); - Drawable imgDrawable = new TextureRegionDrawable(imgTexture); - Image dayEndImage = new Image(imgDrawable); - layout.add(dayEndImage).center().padTop(10).row(); - - initializeUI(); - - //from team 2, added the listener for when game day ends to toggle visibility - ServiceLocator.getDayNightService().getEvents().addListener("TOMORAL", () -> { - logger.info("it is listened in moral"); - toggleVisibility();}); - - } - - private void initializeUI() { - Label titleLabel = new Label("MORAL DILEMMA", new Label.LabelStyle(new BitmapFont(), Color.BLACK)); - layout.add(titleLabel).pad(10).row(); - - // Example event labels - Label eventLabel = new Label("WHAT WILL YOU DO", new Label.LabelStyle(new BitmapFont(), Color.BLACK)); - layout.add(eventLabel).pad(10).row(); - - // Customer lists - List passedCustomers = new List<>(skin); - passedCustomers.setItems("Customer A", "Customer B", "Customer C"); - List failedCustomers = new List<>(skin); - failedCustomers.setItems("Customer X", "Customer Y"); - - Table listTable = new Table(); - listTable.add(new Label("Passed Customers", skin)).pad(10); - listTable.add(new Label("Failed Customers", skin)).pad(10).row(); - listTable.add(new ScrollPane(passedCustomers, skin)).pad(10); - listTable.add(new ScrollPane(failedCustomers, skin)).pad(10); - - layout.add(listTable).expand().fill().row(); - - TextButton closeBtn = new TextButton("Close", skin); - closeBtn.addListener(new ClickListener() { - @Override - public void clicked(InputEvent event, float x, float y) { - toggleVisibility(); - } - }); - layout.add(closeBtn).padTop(20).row(); - } - - - public void show() { - isVisible = true; - layout.setVisible(isVisible); - game.pause(); // Pause the game when the display is shown - } - - public void hide() { - isVisible = false; - layout.setVisible(isVisible); - game.resume(); // Resume the game when the display is hidden - } - - public void toggleVisibility() { - if (isVisible) { - hide(); - ServiceLocator.getDayNightService().getEvents().trigger("temp"); //new - - } else { - show(); - } - } - - @Override - public void draw(SpriteBatch batch) { - // draw is handled by the stage - - } - - @Override - public void setStage(Stage mock) { - // do nothing - } -} \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/components/maingame/TextDisplay.java b/source/core/src/main/com/csse3200/game/components/maingame/TextDisplay.java index fa5e06b26..68b70dbf1 100644 --- a/source/core/src/main/com/csse3200/game/components/maingame/TextDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/maingame/TextDisplay.java @@ -40,6 +40,8 @@ public class TextDisplay extends UIComponent { private long lastUpdate = 0L; private final long delay = 100L; + private InputListener inputListener; + // Displaying variables private boolean visible; public Label label; @@ -284,11 +286,9 @@ public void update() { * of the text or clear the textbox from the screen */ private void setupInputListener() { - logger.info(TextDisplay.this.screen); - stage.addListener(new InputListener() { + inputListener = new InputListener() { @Override public boolean keyDown(InputEvent event, int keycode) { - if (TextDisplay.this.screen.equals("cutscene")) { if (keycode == com.badlogic.gdx.Input.Keys.ENTER || keycode == com.badlogic.gdx.Input.Keys.SPACE) { logger.info("we've pressed enter"); @@ -299,7 +299,7 @@ public boolean keyDown(InputEvent event, int keycode) { return true; } else if (TextDisplay.this.screen.equals("moralDecision")) { Cutscene currentCutscene = ServiceLocator.getCurrentCutscene(); - Boolean atEnd = currentCutscene.isAtEnd(); + boolean atEnd = currentCutscene.isAtEnd(); if (keycode == com.badlogic.gdx.Input.Keys.ENTER || keycode == com.badlogic.gdx.Input.Keys.SPACE) { logger.info("at moral in textDisplay"); if (!atEnd) { @@ -308,13 +308,11 @@ public boolean keyDown(InputEvent event, int keycode) { label.setText(currentCutscene.currentText); } } else if (keycode == Input.Keys.Y && atEnd) { - logger.info("WE'RE ALMOST THERE"); currentCutscene = ServiceLocator.getCurrentCutscene(); currentCutscene.setTextForScene(currentCutscene.currentScene); ServiceLocator.getDayNightService().getEvents().trigger("YesAtMoralDecision"); } else if (keycode == Input.Keys.N && atEnd) { - logger.info("WE'RE ALMOST THERE NO"); currentCutscene = ServiceLocator.getCurrentCutscene(); currentCutscene.setTextForScene(currentCutscene.currentScene); @@ -324,7 +322,6 @@ public boolean keyDown(InputEvent event, int keycode) { } else if (TextDisplay.this.screen.equals("backstory")) { // Handling for backstory cutscenes Cutscene currentCutscene = ServiceLocator.getCurrentCutscene(); - Boolean atEnd = currentCutscene.isAtEnd(); // Check if the current text is "Press Enter to continue" if (label.getText().toString().equals("Press Enter to continue")) { @@ -339,11 +336,9 @@ public boolean keyDown(InputEvent event, int keycode) { // Continue through the backstory cutscene normally if (keycode == com.badlogic.gdx.Input.Keys.ENTER || keycode == com.badlogic.gdx.Input.Keys.SPACE) { logger.info("at backstory in textDisplay"); - if (!atEnd) { - logger.info("parsing through backstory"); - currentCutscene.setTextForScene(currentCutscene.currentScene); - label.setText(currentCutscene.currentText); - } + logger.info("parsing through backstory"); + currentCutscene.setTextForScene(currentCutscene.currentScene); + label.setText(currentCutscene.currentText); } } return true; @@ -367,10 +362,15 @@ public boolean keyDown(InputEvent event, int keycode) { } return false; } - }); + }; + stage.addListener(inputListener); } + + + + /** * Overrides the draw method from the Actor class. * This method is responsible for rendering the component. @@ -448,5 +448,16 @@ private void setUpBackstoryLayout() { // Add stack to the table table.add(stack).padTop(100).size((int) (Gdx.graphics.getWidth() * 0.6), (int) (Gdx.graphics.getHeight() * 0.15)); } + + /** + * Class used in unit tests to simulate keypress + * @param keycode: keycode realting to the key pressed + * @return whether the key was pressed + */ + public boolean simulateKeyPress(int keycode) { + InputEvent event = new InputEvent(); // Create a dummy InputEvent + event.setType(InputEvent.Type.keyDown); + return inputListener.keyDown(event, keycode); + } } diff --git a/source/core/src/main/com/csse3200/game/screens/MoralDecisionDisplay.java b/source/core/src/main/com/csse3200/game/screens/MoralDecisionDisplay.java deleted file mode 100644 index 328fbb68a..000000000 --- a/source/core/src/main/com/csse3200/game/screens/MoralDecisionDisplay.java +++ /dev/null @@ -1,256 +0,0 @@ -package com.csse3200.game.screens; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.scenes.scene2d.Actor; -import com.badlogic.gdx.scenes.scene2d.Stage; -import com.badlogic.gdx.scenes.scene2d.ui.*; -import com.badlogic.gdx.scenes.scene2d.utils.Drawable; -import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; -import com.csse3200.game.services.ServiceLocator; -import com.csse3200.game.ui.UIComponent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.graphics.g2d.BitmapFont; -import com.badlogic.gdx.utils.TimeUtils; - - -public class MoralDecisionDisplay extends UIComponent { - - private static final Logger logger = LoggerFactory.getLogger(MoralDecisionDisplay.class); - private Table layout; // Layout manager - private boolean isVisible; - private final MainGameScreen game; - private String question = "Set Question"; - private Label timerLabel; // Timer label - private long startTime; // Track the start time - - private static final long DEFAULT_TIMER = 10000; // 10 seconds (in milliseconds) - private long remainingTime; // Remaining time in milliseconds - -// public MoralDecisionDisplay(MainGameScreen game) { -// super(); -// this.game = game; -// isVisible = false; -// } - - /** - * Constructor for the MoralDecisionDisplay class. - * Initializes the display and sets its visibility to false. - * Retrieves the main game screen from the ServiceLocator. - */ - public MoralDecisionDisplay() { - super(); - this.game = ServiceLocator.getGameScreen(); - isVisible = false; - } - - /** - * Creates the moral decision screen. - */ - @Override - public void create() { - super.create(); - - // create a table layout - layout = new Table(); - layout.setFillParent(true); - layout.setVisible(isVisible); - layout.setSkin(skin); - stage.addActor(layout); - - // create gray background - Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888); - pixmap.setColor(Color.GRAY); - pixmap.fill(); - Texture pixmapTex = new Texture(pixmap); - pixmap.dispose(); - Drawable blackBackground = new TextureRegionDrawable(new TextureRegion(pixmapTex)); - layout.setBackground(blackBackground); - - // set up the label using table layout -// BitmapFont font = new BitmapFont(); - Label titleLabel = new Label("Moral Decision", skin); - layout.add(titleLabel).pad(10).row(); - - // load and position the raccoon image slightly to the left - Texture imgTexture = new Texture(Gdx.files.internal("images/racoon.png")); - Drawable imgDrawable = new TextureRegionDrawable(imgTexture); - Image characterImage = new Image(imgDrawable); // Declared as local variable to avoid unused field warning - - // add raccoon image to the table and shift it left by adjusting padding - layout.add(characterImage).left(); - - // Add a secondary table to the left side of the view. - Table questionSet = new Table(); - Label questionLabel = new Label("Do you want to save the raccoon?", skin); - questionSet.add(questionLabel).pad(10).row(); - - Label testingLabel = new Label("TestinvfHQ ETAAHWRVFBJWGEE HAET NVBbwegvkjER AHT QTHJTRAJ RTJRTg", skin); -// layout.add(testingLabel).padRight(100).right().row(); - - // add another table inside the existing table, in order to show the Decision question and buttons for yes/no - Table decisionTable = new Table(); - Skin btnSkin = skin; - btnSkin.setScale(2); - Label decisionLabel = new Label(question, btnSkin); - decisionTable.add(decisionLabel).pad(10).row(); - Actor button = new Actor(); - button.setHeight(50); - button.setWidth(100); - button.setColor(Color.GREEN); - Button.ButtonStyle buttonStyle = new Button.ButtonStyle(); - Button noButton = new Button(button, buttonStyle); -// decisionTable.add(yesButton).pad(10); - decisionTable.add(noButton).pad(10).row(); -// layout.add(decisionTable).center().row(); - - // add the secondary table to the main table - layout.add(questionSet).padRight(100).right().row(); -// setupInputListener(); - // Timer label setup - Initialize with default timer - timerLabel = new Label("Timer: " + (DEFAULT_TIMER / 1000) + "s", skin); - layout.add(timerLabel).padLeft(10f).row(); // Add timer label to the layout - - entity.getEvents().addListener("triggerMoralScreen", this::toggleVisibility); - - //from team 2, added the listener for when game day ends to toggle visibility - ServiceLocator.getDayNightService().getEvents().addListener("TOMORAL", () -> { - logger.info("TOMORAL event received in MoralDecisionDisplay"); - show(); - }); - } - - -// private void setupInputListener() { -// stage.addListener(new InputListener() { -// @Override -// public boolean keyDown(InputEvent event, int keycode) { -// if (keycode == com.badlogic.gdx.Input.Keys.M) { -// toggleVisibility(); -// return true; -// } -// return false; -// } -// }); -// } - - - /** - * Initialise the User Interface - */ - private void initialiseUI() { - Label titleLabel = new Label("moral deiciosn", new Label.LabelStyle(new BitmapFont(), Color.PINK)); - layout.add(titleLabel).pad(10).row(); - } - - /** - * Updates the timer label with the remaining time in seconds. - * @param timerLabel the label to update - * @param remainingTime the remaining time in milliseconds - */ - private void updateTimerLabel(Label timerLabel, long remainingTime) { - // Convert remaining time to seconds and update the label's text - String timeLeft = "Timer: " + (remainingTime / 1000) + "s"; - timerLabel.setText(timeLeft); - } - - /** - * Sets the question to be displayed on the moral decision screen. - * @param question the question to display - * @return true if the question was set successfully - */ - public boolean setQuestion(String question) { - this.question = question; - return true; - } - - /** - * Shows the moral decision screen. - */ - private void show() { - isVisible = true; - layout.setVisible(isVisible); - game.pause(); // Pause the game when the display is shown - startTime = TimeUtils.millis(); // Set the start time when shown - remainingTime = DEFAULT_TIMER; // Reset timer to the default value - - } - - /** - * Hides the moral decision screen. - */ - private void hide() { - isVisible = false; - layout.setVisible(isVisible); - game.resume(); // Resume the game when the display is hidden - logger.info("decisionDone about to be triggered"); - ServiceLocator.getDayNightService().getEvents().trigger("decisionDone"); - - } - - /** - * Toggles the visibility of the moral decision screen. - */ - private void toggleVisibility(int day) { - logger.debug(" Day - {}", day); -// this.update(); - if (isVisible) { - hide(); - } else { - show(); - } - } - - /** - * Returns the visibility of the moral decision screen. - * @return true if the screen is visible - */ - public boolean getVisible() { - return this.isVisible; - } - - /** - * Updates the moral decision screen. - */ - @Override - public void update() { -// super.update(); -// layout.clear(); -// this.create(); - - - // time stuff - if (isVisible) { - // Calculate the elapsed time since the moral decision screen was shown - long elapsedTime = TimeUtils.timeSinceMillis(startTime); - - // Calculate the remaining time by subtracting the elapsed time from the default timer - remainingTime = DEFAULT_TIMER - elapsedTime; - - // If there's still time left, update the label with the remaining time in seconds - if (remainingTime > 0) { - // Replace the timer text in the timer label - updateTimerLabel(timerLabel, remainingTime); - - } else { - // If the time has expired, hide the moral decision screen - hide(); - } - } - } - - @Override - public void draw(SpriteBatch batch) { - // draw is handled by the stage - } - - @Override - public void setStage(Stage stage) { - // Method intentionally left empty (to handle empty method warning) - } -} diff --git a/source/core/src/test/com/csse3200/game/components/maingame/TestTextDisplayMoral.java b/source/core/src/test/com/csse3200/game/components/maingame/TestTextDisplayMoral.java new file mode 100644 index 000000000..c1ce2dcd4 --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/maingame/TestTextDisplayMoral.java @@ -0,0 +1,82 @@ +package com.csse3200.game.components.maingame; + +import com.badlogic.gdx.ScreenAdapter; +import com.badlogic.gdx.scenes.scene2d.Stage; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; +import com.csse3200.game.components.cutscenes.Cutscene; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import static org.junit.jupiter.api.Assertions.assertTrue; +@ExtendWith(GameExtension.class) +public class TestTextDisplayMoral { + + @Mock + private ScreenAdapter game; + + @Mock + private Stage stage; + + @Mock + private Cutscene cutscene; + + + private TextDisplay textDisplay; + + + @BeforeEach + void setUp() { + // Mock the ServiceLocator and its methods + ServiceLocator.setCurrentCutscene(cutscene); + ServiceLocator.registerTimeSource(new GameTime()); + + + textDisplay = new TextDisplay(game); + textDisplay.setStage(stage); + } + + @Test + void testSetText_ShouldDivideTextIntoParts() { + String inputText = "This is a long piece of text that needs to be split into parts for display."; + textDisplay.setText(inputText); + + // Verify that the text is split correctly into parts + Assertions.assertEquals(2, textDisplay.getText().size()); + assertTrue(textDisplay.getText().get(0).contains("enter to continue")); + assertTrue(textDisplay.getText().get(1).contains("enter to continue")); + } + + @Test + void testSetText_ShouldMakeTextVisible() { + textDisplay.setText("Hello World"); + + assertTrue(textDisplay.getVisible()); + } + + @Test + void testUpdate_ShouldHideTextWhenFinished() { + textDisplay.setText("Hello World"); + + // Simulate that the text has been fully shown + textDisplay.setVisible(true); + textDisplay.update(); + + // Verify that setVisible is called when the text is finished + assertTrue(textDisplay.getVisible()); + } + + @Test + void testKeyDown_ShouldDisplayNextPartOrHide() { + textDisplay.setText("Hello World"); + textDisplay.setVisible(true); + + // Simulate the ENTER key press to move to the next part + textDisplay.setScreen("moralDecision"); + assertTrue(textDisplay.getVisible()); + } + +} diff --git a/source/core/src/test/com/csse3200/game/components/TextDisplayTest.java b/source/core/src/test/com/csse3200/game/components/maingame/TextDisplayTest.java similarity index 95% rename from source/core/src/test/com/csse3200/game/components/TextDisplayTest.java rename to source/core/src/test/com/csse3200/game/components/maingame/TextDisplayTest.java index 0710a39e3..371e9d478 100644 --- a/source/core/src/test/com/csse3200/game/components/TextDisplayTest.java +++ b/source/core/src/test/com/csse3200/game/components/maingame/TextDisplayTest.java @@ -2,15 +2,17 @@ import com.csse3200.game.extensions.GameExtension; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; + import static org.junit.jupiter.api.Assertions.*; + import java.util.List; import java.util.Collections; @ExtendWith(GameExtension.class) public class TextDisplayTest { + @Test public void testSetText() { TextDisplay test = new TextDisplay(); @@ -22,10 +24,10 @@ public void testSetText() { @Test public void testSettersAndGetters() { TextDisplay test = new TextDisplay(); - assertEquals(test.getVisible(), false); + assertFalse(test.getVisible()); assertEquals(test.getDelay(), 100L); test.setText("Testing the text string"); - assertEquals(test.getVisible(), true); + assertTrue(test.getVisible()); List expectedText = Collections.singletonList("Testing the text string (enter to continue)"); assertEquals(expectedText, test.getText()); }