diff --git a/core/assets/audio/music/credits-attibution.txt b/core/assets/audio/music/credits-attibution.txt new file mode 100644 index 0000000..bb32ce1 --- /dev/null +++ b/core/assets/audio/music/credits-attibution.txt @@ -0,0 +1,5 @@ +Title: Wings Of Liberty +By: EvgenyBardyuzha +Website: https://pixabay.com/ + +Music was listed under Royalty Free \ No newline at end of file diff --git a/core/assets/audio/music/credits.mp3 b/core/assets/audio/music/credits.mp3 new file mode 100644 index 0000000..c12ba8b Binary files /dev/null and b/core/assets/audio/music/credits.mp3 differ diff --git a/core/assets/mario/shard-software-logo4.png b/core/assets/mario/shard-software-logo4.png new file mode 100644 index 0000000..be0e464 Binary files /dev/null and b/core/assets/mario/shard-software-logo4.png differ diff --git a/core/assets/ui/credits.txt b/core/assets/ui/credits.txt new file mode 100644 index 0000000..69f6d49 --- /dev/null +++ b/core/assets/ui/credits.txt @@ -0,0 +1,70 @@ +-= Developers =- + +-= Assessment 2 =- +-= Team 29 "Shard Software" =- +James Burnell +Hector Woods +Ben Faulkner + +-= Assessment 1 =- +-= Team 28 "Mario" =- +Annabeth Singleton +Joseph Frankland +Leif Kemp +Hugo Kwok +Saj Hoque +Lucy Li + + +-= Website =- +www.mario.shardsoftware.tk + +Jensen Bradshaw +James Burnell + + +-= Audio =- + +Powerup SoundFX by 15.ai +Based on characters from "TF2" + +Credits Music - "Wings Of Liberty" +By EvgenyBardyuzha +pixabay.com + + + +-= Graphics =- +Original Assets By + +Annabeth Singleton +Leif Kemp +James Burnell + + +-= Shard Software Logo =- + +James Burnell + + +-= Software Used =- + +libGDX - Java Game Engine +Blender - Shard Software Logo +Eclipse & IntelliJ - Code Development +Audacity - Audio Development + + + + + + + +Produced For The University of York +As Part of Engineering 1 + + + + + + diff --git a/core/src/main/java/io/github/annabeths/GameScreens/CreditScreen.java b/core/src/main/java/io/github/annabeths/GameScreens/CreditScreen.java new file mode 100644 index 0000000..ebbf1c4 --- /dev/null +++ b/core/src/main/java/io/github/annabeths/GameScreens/CreditScreen.java @@ -0,0 +1,149 @@ +package io.github.annabeths.GameScreens; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input.Keys; +import com.badlogic.gdx.Screen; +import com.badlogic.gdx.audio.Music; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.math.MathUtils; +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.Label.LabelStyle; +import com.badlogic.gdx.utils.Align; +import com.badlogic.gdx.utils.ScreenUtils; +import com.badlogic.gdx.utils.viewport.FillViewport; + +import io.github.annabeths.GeneralControl.ResourceManager; +import io.github.annabeths.GeneralControl.eng1game; + +/** + * A screen to display credits for the game + * + * @since Assessment 2 + * @author James Burnell + */ +public class CreditScreen implements Screen { + + private eng1game game; + private String creditText; + /** How fast the credits scroll in px/sec */ + private float scrollSpeed = 100; + + public Stage stage; + public LabelStyle lblStyle; + public Label label; + public Image shardLogo; + public Image marioLogo; + + public Music music; + /** How long the music will fade out in seconds */ + public static final float MUSIC_FADE_TIME = 4f; + + public CreditScreen(eng1game game) { + this.game = game; + + creditText = Gdx.files.internal("ui/credits.txt").readString(); + + lblStyle = new LabelStyle(); + lblStyle.font = ResourceManager.debugFont; + lblStyle.fontColor = Color.WHITE; + + music = Gdx.audio.newMusic(Gdx.files.internal("audio/music/credits.mp3")); + + label = new Label(creditText, lblStyle); + label.setAlignment(Align.center); + label.setFontScale(1f); + label.setHeight(label.getPrefHeight()); + label.setPosition(0, -label.getPrefHeight() - 100); + + shardLogo = new Image(ResourceManager.getTexture("mario/shard-software-logo4.png")); + shardLogo.setScale(0.5f); + + marioLogo = new Image(ResourceManager.getTexture("mario/full.png")); + } + + @Override + public void show() { + stage = new Stage(new FillViewport(1280, 720)); + + label.setWidth(stage.getWidth()); + stage.addActor(label); + + shardLogo.setPosition((stage.getWidth() - shardLogo.getWidth() * 0.5f) / 2, + label.getY() - shardLogo.getHeight() * 0.5f); + stage.addActor(shardLogo); + + marioLogo.setPosition((stage.getWidth() - marioLogo.getWidth()) / 2, + shardLogo.getY() - marioLogo.getHeight()); + stage.addActor(marioLogo); + + music.play(); + } + + @Override + public void render(float delta) { + ScreenUtils.clear(Color.BLACK); + + // skip credits + if (Gdx.input.isKeyJustPressed(Keys.ESCAPE)) { + returnToMenu(); + } + + // scroll text and images up + float scrollAmount = scrollSpeed * delta; + label.moveBy(0, scrollAmount); + shardLogo.moveBy(0, scrollAmount); + marioLogo.moveBy(0, scrollAmount); + + // go to the menu if the credits are over + if (marioLogo.getY(Align.bottom) > stage.getHeight()) { + returnToMenu(); + } + + music.setVolume(MathUtils.clamp(timeLeft() / MUSIC_FADE_TIME, 0, 1)); + + stage.draw(); + } + + public void returnToMenu() { + game.gotoScreen(Screens.menuScreen); + music.stop(); + music.dispose(); + } + + /** + * How long is left in the credits before they end + * + * @return the time left in seconds + */ + public float timeLeft() { + return (stage.getHeight() - marioLogo.getY(Align.bottom)) / scrollSpeed; + } + + @Override + public void resize(int width, int height) { + stage.getViewport().update(width, height, true); + } + + @Override + public void pause() { + + } + + @Override + public void resume() { + + } + + @Override + public void hide() { + + } + + @Override + public void dispose() { + + } + +} diff --git a/core/src/main/java/io/github/annabeths/GameScreens/Menu.java b/core/src/main/java/io/github/annabeths/GameScreens/Menu.java index 63d9ce8..8586f4d 100644 --- a/core/src/main/java/io/github/annabeths/GameScreens/Menu.java +++ b/core/src/main/java/io/github/annabeths/GameScreens/Menu.java @@ -45,15 +45,16 @@ public Menu(eng1game g) { */ @Override public void show() { - batch = new SpriteBatch(); // layouts can be used to manage text to allow it to be centered menuTextLayout = new GlyphLayout(); menuTextLayout.setText(font, - "press ENTER to PLAY\npress I to toggle INSTRUCTIONS\npress ESCAPE to QUIT"); + "press ENTER to PLAY\n" + + "press I to toggle INSTRUCTIONS\n" + + "press F for fullscreen\n" + + "press C for credits\n" + + "press ESCAPE to QUIT"); instructions = ResourceManager.getTexture("ui/instructions.png"); - // create example HUD - hud = new HUD(GameController.getMockForHUD()); - hud.Update(1f); + reset(); } /** @@ -75,8 +76,18 @@ public void render(float delta) { } else { toggleInstructions(); } - } else if (Gdx.input.isKeyJustPressed(Keys.ESCAPE)) { - Gdx.app.exit(); + } + if (Gdx.input.isKeyJustPressed(Keys.ESCAPE)) { + if (showInstructions) + showInstructions = false; + else + Gdx.app.exit(); + } + if (Gdx.input.isKeyJustPressed(Keys.C)) { + game.gotoScreen(Screens.credits); + } + if (Gdx.input.isKeyJustPressed(Keys.F)) { + game.setFullscreen(); } // do draws @@ -102,6 +113,14 @@ public void toggleInstructions() { instructionsBeenShown = true; } + /** Resets the menu graphics objects. Used for changing resolutions */ + public void reset() { + batch = new SpriteBatch(); + // create example HUD + hud = new HUD(GameController.getMockForHUD()); + hud.Update(1f); + } + /** * resize the window * @param width new width @@ -109,6 +128,7 @@ public void toggleInstructions() { */ @Override public void resize(int width, int height) { + reset(); } @Override @@ -125,7 +145,6 @@ public void hide() { @Override public void dispose() { - } } diff --git a/core/src/main/java/io/github/annabeths/GameScreens/Screens.java b/core/src/main/java/io/github/annabeths/GameScreens/Screens.java index 66766a2..968eca8 100644 --- a/core/src/main/java/io/github/annabeths/GameScreens/Screens.java +++ b/core/src/main/java/io/github/annabeths/GameScreens/Screens.java @@ -2,5 +2,13 @@ /** Enum that represents all the screens the game can switch to */ public enum Screens { - splashScreen, menuScreen, gameScreen, gameOverScreen, gameWinScreen, gameDifScreen, saveLoadScreen, loadedGameScreen + splashScreen, + menuScreen, + gameScreen, + gameOverScreen, + gameWinScreen, + gameDifScreen, + saveLoadScreen, + loadedGameScreen, + credits } \ No newline at end of file diff --git a/core/src/main/java/io/github/annabeths/GeneralControl/ResourceManager.java b/core/src/main/java/io/github/annabeths/GeneralControl/ResourceManager.java index f6cf01b..71e5197 100644 --- a/core/src/main/java/io/github/annabeths/GeneralControl/ResourceManager.java +++ b/core/src/main/java/io/github/annabeths/GeneralControl/ResourceManager.java @@ -80,6 +80,9 @@ public static void init(AssetManager assetMan) { for (PowerupType p : PowerupType.values()) { loadTexture(p.getTexture()); } + /* Other Textures */ + loadTexture("mario/shard-software-logo4.png"); + loadTexture("mario/full.png"); assets.finishLoading(); }); diff --git a/core/src/main/java/io/github/annabeths/GeneralControl/eng1game.java b/core/src/main/java/io/github/annabeths/GeneralControl/eng1game.java index 5142406..a63d41c 100644 --- a/core/src/main/java/io/github/annabeths/GeneralControl/eng1game.java +++ b/core/src/main/java/io/github/annabeths/GeneralControl/eng1game.java @@ -5,7 +5,15 @@ import com.badlogic.gdx.Graphics; import com.badlogic.gdx.assets.AssetManager; -import io.github.annabeths.GameScreens.*; +import io.github.annabeths.GameScreens.CreditScreen; +import io.github.annabeths.GameScreens.GameController; +import io.github.annabeths.GameScreens.GameDifScreen; +import io.github.annabeths.GameScreens.GameOverScreen; +import io.github.annabeths.GameScreens.GameWinScreen; +import io.github.annabeths.GameScreens.Menu; +import io.github.annabeths.GameScreens.SaveLoadScreen; +import io.github.annabeths.GameScreens.Screens; +import io.github.annabeths.GameScreens.Splash; /** @tt.updated Assessment 2 */ public class eng1game extends Game { @@ -30,6 +38,7 @@ public class eng1game extends Game { /** * Constructor for eng1game + * * @param debug whether Debug mode should be enabled or not */ public eng1game(boolean debug) { @@ -69,47 +78,52 @@ public void removeGameScreen() { * * @param s the screen to switch to */ + @SuppressWarnings("incomplete-switch") public void gotoScreen(Screens s) { switch (s) { - case splashScreen: // creates a new splash screen - Splash splashScreen = new Splash(this); - setScreen(splashScreen); - break; - case menuScreen: // switch back to the menu screen - setScreen(menuScreen); - break; - case gameScreen: // switch back to the game screen - gameScreen = new GameController(this, diff); - setScreen(gameScreen); - break; - case gameOverScreen: - removeGameScreen(); - GameOverScreen gameOverScreen = new GameOverScreen(this, - timeUp ? "Time Up! ENTER to go to menu, R to restart, or L to load from a save." - : "You Died! ENTER to go to menu, R to restart, or L to load from a save."); - setScreen(gameOverScreen); - break; - case gameWinScreen: - removeGameScreen(); - GameWinScreen gameWinScreen = new GameWinScreen(this); - setScreen(gameWinScreen); - break; - case gameDifScreen: - GameDifScreen gameDifScreen = new GameDifScreen(this); - setScreen(gameDifScreen); - break; - case saveLoadScreen: - SaveLoadScreen saveLoadScreen = new SaveLoadScreen(this, false); - setScreen(saveLoadScreen); - break; + case splashScreen: // creates a new splash screen + Splash splashScreen = new Splash(this); + setScreen(splashScreen); + break; + case menuScreen: // switch back to the menu screen + setScreen(menuScreen); + break; + case gameScreen: // switch back to the game screen + gameScreen = new GameController(this, diff); + setScreen(gameScreen); + break; + case gameOverScreen: + removeGameScreen(); + GameOverScreen gameOverScreen = new GameOverScreen(this, timeUp + ? "Time Up! ENTER to go to menu, R to restart, or L to load from a save." + : "You Died! ENTER to go to menu, R to restart, or L to load from a save."); + setScreen(gameOverScreen); + break; + case gameWinScreen: + removeGameScreen(); + GameWinScreen gameWinScreen = new GameWinScreen(this); + setScreen(gameWinScreen); + break; + case gameDifScreen: + GameDifScreen gameDifScreen = new GameDifScreen(this); + setScreen(gameDifScreen); + break; + case saveLoadScreen: + SaveLoadScreen saveLoadScreen = new SaveLoadScreen(this, false); + setScreen(saveLoadScreen); + break; + case credits: + setScreen(new CreditScreen(this)); + break; } } /** * load a saved game + * * @param saveFileName a saveFile referring to the saved game. */ - public void loadSaveGame(String saveFileName){ + public void loadSaveGame(String saveFileName) { gameScreen = new GameController(this, saveFileName); setScreen(gameScreen); } @@ -117,7 +131,7 @@ public void loadSaveGame(String saveFileName){ /** * Go to the load screen */ - public void goToLoadScreen(){ + public void goToLoadScreen() { SaveLoadScreen saveLoadScreen = new SaveLoadScreen(this, true); setScreen(saveLoadScreen); } @@ -133,6 +147,7 @@ public void setFullscreen() { /** * Set the game's difficulty + * * @param difficulty new difficulty */ public void setDifficulty(Difficulty difficulty) { diff --git a/core/src/test/java/io/github/annabeths/GameScreens/CreditScreenTest.java b/core/src/test/java/io/github/annabeths/GameScreens/CreditScreenTest.java new file mode 100644 index 0000000..b1fcd51 --- /dev/null +++ b/core/src/test/java/io/github/annabeths/GameScreens/CreditScreenTest.java @@ -0,0 +1,70 @@ +package io.github.annabeths.GameScreens; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.badlogic.gdx.Files; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input.Keys; +import com.badlogic.gdx.scenes.scene2d.Stage; +import com.badlogic.gdx.scenes.scene2d.ui.Image; + +import io.github.annabeths.GeneralControl.TestHelper; +import io.github.annabeths.GeneralControl.eng1game; + +public class CreditScreenTest { + + CreditScreen cs; + + @BeforeAll + public static void init() { + TestHelper.setupEnv(); + Gdx.files = mock(Files.class, withSettings().defaultAnswer(RETURNS_MOCKS)); + } + + @BeforeEach + public void setup() { + cs = mock(CreditScreen.class, withSettings().useConstructor(mock(eng1game.class)) + .defaultAnswer(CALLS_REAL_METHODS)); + cs.stage = mock(Stage.class, withSettings().defaultAnswer(RETURNS_DEEP_STUBS)); + when(cs.stage.getHeight()).thenReturn(720f); + } + + @Test + public void testRender() { + assertDoesNotThrow(() -> cs.render(1f)); + } + + @Test + public void testEscReturnToMenu() { + when(Gdx.input.isKeyJustPressed(Keys.ESCAPE)).thenReturn(false); + cs.render(1f); + verify(cs, never()).returnToMenu(); + + when(Gdx.input.isKeyJustPressed(Keys.ESCAPE)).thenReturn(true); + cs.render(1f); + verify(cs, times(1)).returnToMenu(); + } + + @Test + public void testEndReturnToMenu() { + cs.marioLogo = mock(Image.class); + when(cs.marioLogo.getY(anyInt())).thenReturn(725f); + + cs.render(1f); + verify(cs, times(1)).returnToMenu(); + } + + @Test + public void testResize() { + cs.resize(100, 100); + + verify(cs.stage.getViewport(), times(1)).update(eq(100), eq(100), eq(true)); + } + +} diff --git a/core/src/test/java/io/github/annabeths/GameScreens/MenuTest.java b/core/src/test/java/io/github/annabeths/GameScreens/MenuTest.java index f39c846..bea99fb 100644 --- a/core/src/test/java/io/github/annabeths/GameScreens/MenuTest.java +++ b/core/src/test/java/io/github/annabeths/GameScreens/MenuTest.java @@ -7,6 +7,7 @@ import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -115,4 +116,39 @@ public void testEscPressed() { verify(Gdx.app, times(1)).exit(); } + @Test + public void testEscPressedShowingInstructions() { + m.showInstructions = true; + when(Gdx.input.isKeyJustPressed(Keys.ESCAPE)).thenReturn(true); + m.render(1f); + assertFalse(m.showInstructions); + } + + @Test + public void testCPressed() { + when(Gdx.input.isKeyJustPressed(Keys.C)).thenReturn(true); + m.render(1f); + verify(game, times(1)).gotoScreen(eq(Screens.credits)); + } + + @Test + public void testFPressed() { + when(Gdx.input.isKeyJustPressed(Keys.F)).thenReturn(true); + m.render(1f); + verify(game, times(1)).setFullscreen(); + } + + @Test + public void testShow() { + doNothing().when(m).reset(); + assertDoesNotThrow(() -> m.show()); + } + + @Test + public void testResize() { + doNothing().when(m).reset(); + assertDoesNotThrow(() -> m.resize(1, 1)); + verify(m, times(1)).reset(); + } + }