From 12469315f72896774ed2d298f9a924a1a3436975 Mon Sep 17 00:00:00 2001 From: Srikavin Ramkumar Date: Wed, 12 Feb 2020 21:44:39 -0500 Subject: [PATCH] Add save support --- core/src/me/srikavin/fbla/game/FBLAGame.kt | 19 +++-- .../src/me/srikavin/fbla/game/award/Awards.kt | 2 +- .../srikavin/fbla/game/ecs/system/UISystem.kt | 2 +- .../srikavin/fbla/game/minigame/Minigame.kt | 3 + .../me/srikavin/fbla/game/state/GameState.kt | 2 +- core/src/me/srikavin/fbla/game/ui/DeadUI.kt | 5 +- .../src/me/srikavin/fbla/game/ui/GameHudUI.kt | 41 +++++++---- core/src/me/srikavin/fbla/game/ui/MainMenu.kt | 36 ++++++++-- .../me/srikavin/fbla/game/util/SaveUtils.kt | 69 ++++++++++++++++--- .../fbla/game/desktop/DesktopLauncher.java | 4 +- 10 files changed, 133 insertions(+), 50 deletions(-) diff --git a/core/src/me/srikavin/fbla/game/FBLAGame.kt b/core/src/me/srikavin/fbla/game/FBLAGame.kt index 4f63305..cae6724 100644 --- a/core/src/me/srikavin/fbla/game/FBLAGame.kt +++ b/core/src/me/srikavin/fbla/game/FBLAGame.kt @@ -25,6 +25,8 @@ import com.badlogic.gdx.scenes.scene2d.ui.Skin import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.utils.ObjectMap import com.badlogic.gdx.utils.viewport.ExtendViewport +import com.badlogic.gdx.utils.viewport.FitViewport +import com.badlogic.gdx.utils.viewport.Viewport import com.strongjoshua.console.CommandExecutor import com.strongjoshua.console.GUIConsole import ktx.assets.disposeSafely @@ -39,7 +41,6 @@ import me.srikavin.fbla.game.physics.ContactListenerManager import me.srikavin.fbla.game.state.GameState import me.srikavin.fbla.game.ui.MainMenu import me.srikavin.fbla.game.util.GameFonts -import me.srikavin.fbla.game.util.SaveUtils import me.srikavin.fbla.game.util.registerInputHandler const val cameraScale = 45f @@ -52,6 +53,7 @@ enum class Scene { class FBLAGame : ApplicationAdapter() { private lateinit var camera: OrthographicCamera + private lateinit var viewport: Viewport private lateinit var world: World private lateinit var batch: SpriteBatch private lateinit var skin: Skin @@ -65,18 +67,16 @@ class FBLAGame : ApplicationAdapter() { override fun resize(width: Int, height: Int) { if (scene == PLAYING) { - camera.viewportHeight = cameraScale * (height.toFloat() / width) - camera.viewportWidth = cameraScale - - camera.zoom = 1f + viewport.update(width, height) camera.update() } else if (scene == TITLE) { mainMenuUI.build() } } - private fun startGame() { + private fun startGame(gameState: GameState) { camera = OrthographicCamera(cameraScale, cameraScale * (9f / 16f)) + viewport = FitViewport(cameraScale, cameraScale * (9f / 16f), camera) camera.position.x = 0f camera.position.y = cameraScale * (9f / 16f) * 0.75f @@ -91,8 +91,6 @@ class FBLAGame : ApplicationAdapter() { root.setFillParent(true) root.top().right() - val gameState = GameState(0) - val listenerManager = ContactListenerManager() val mapLoader = MapLoader() @@ -125,7 +123,7 @@ class FBLAGame : ApplicationAdapter() { .register(listenerManager) world = World(config) - mapLoader.loadMap(world, "assets/maps/level1.tmx") + mapLoader.loadMap(world, gameState.currentLevelPath) console = GUIConsole() @@ -195,14 +193,13 @@ class FBLAGame : ApplicationAdapter() { fun afterLoad() { skin = assetManager.get("assets/skin/skin.json") mainMenuUI = MainMenu(skin) { - startGame() + startGame(it) mainMenuUI.disposeSafely() scene = PLAYING } mainMenuUI.build() scene = TITLE - SaveUtils.saveGame(GameState(), "") } override fun render() { diff --git a/core/src/me/srikavin/fbla/game/award/Awards.kt b/core/src/me/srikavin/fbla/game/award/Awards.kt index b067d32..b9242a2 100644 --- a/core/src/me/srikavin/fbla/game/award/Awards.kt +++ b/core/src/me/srikavin/fbla/game/award/Awards.kt @@ -23,6 +23,6 @@ object Awards { } fun getAward(name: String): Award { - return nameMap.getValue(name) + return nameMap.getValue(name.toLowerCase()) } } \ No newline at end of file diff --git a/core/src/me/srikavin/fbla/game/ecs/system/UISystem.kt b/core/src/me/srikavin/fbla/game/ecs/system/UISystem.kt index dd8542a..51c91b3 100644 --- a/core/src/me/srikavin/fbla/game/ecs/system/UISystem.kt +++ b/core/src/me/srikavin/fbla/game/ecs/system/UISystem.kt @@ -32,7 +32,7 @@ class UISystem : BaseSystem() { TypingConfig.DEFAULT_SPEED_PER_CHAR = 0.05f gameHudUI = GameHudUI(world.getRegistered(Skin::class.java), world.getRegistered(GameState::class.java)) - deadUI = DeadUI(world.getRegistered(Skin::class.java)) + deadUI = DeadUI(world.getRegistered(Skin::class.java), world.getRegistered(GameState::class.java)) gameHudUI.build() } diff --git a/core/src/me/srikavin/fbla/game/minigame/Minigame.kt b/core/src/me/srikavin/fbla/game/minigame/Minigame.kt index 2b95f28..cafd03e 100644 --- a/core/src/me/srikavin/fbla/game/minigame/Minigame.kt +++ b/core/src/me/srikavin/fbla/game/minigame/Minigame.kt @@ -10,6 +10,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Skin import me.srikavin.fbla.game.map.MapLoader import me.srikavin.fbla.game.state.GameState import me.srikavin.fbla.game.util.MapTriggerDelegate +import me.srikavin.fbla.game.util.SaveUtils /** * The baseclass that all minigames inherit from. This class handles level transitions and communications with outside @@ -99,6 +100,8 @@ abstract class Minigame { awardActive = false active = false + SaveUtils.saveGame(gameState) + Gdx.app.postRunnable { mapLoader.loadMap(world, "assets/maps/$nextLevel") } diff --git a/core/src/me/srikavin/fbla/game/state/GameState.kt b/core/src/me/srikavin/fbla/game/state/GameState.kt index f4b7f92..afea68f 100644 --- a/core/src/me/srikavin/fbla/game/state/GameState.kt +++ b/core/src/me/srikavin/fbla/game/state/GameState.kt @@ -8,7 +8,7 @@ data class GameState( var lives: Int = 3, var gameRules: GameRules = GameRules(), val awards: LinkedHashSet = LinkedHashSet(4), - var currentLevelPath: String = "level1.tmx") { + var currentLevelPath: String = "assets/maps/level1.tmx") { @Transient var lastAddedAward: Award? = null diff --git a/core/src/me/srikavin/fbla/game/ui/DeadUI.kt b/core/src/me/srikavin/fbla/game/ui/DeadUI.kt index 0641bc1..6c7577f 100644 --- a/core/src/me/srikavin/fbla/game/ui/DeadUI.kt +++ b/core/src/me/srikavin/fbla/game/ui/DeadUI.kt @@ -10,10 +10,12 @@ import com.badlogic.gdx.utils.viewport.ExtendViewport import me.srikavin.fbla.game.ecs.component.Dead import me.srikavin.fbla.game.ext.addImageTextButton import me.srikavin.fbla.game.ext.table +import me.srikavin.fbla.game.state.GameState +import me.srikavin.fbla.game.util.SaveUtils import me.srikavin.fbla.game.util.registerInputHandler import me.srikavin.fbla.game.util.unregisterInputHandler -class DeadUI(private val skin: Skin) : GameUI() { +class DeadUI(private val skin: Skin, private val gameState: GameState) : GameUI() { private val stage = Stage(ExtendViewport(1920f, 1080f, 1920f, 1080f)) private val container = Table(skin) @@ -53,6 +55,7 @@ class DeadUI(private val skin: Skin) : GameUI() { }, "menu") inner.add().width(45f) inner.addImageTextButton("[accent]Save and Quit[]", null, Runnable { + SaveUtils.saveGame(gameState) Gdx.app.exit() }, "menu") } diff --git a/core/src/me/srikavin/fbla/game/ui/GameHudUI.kt b/core/src/me/srikavin/fbla/game/ui/GameHudUI.kt index 4336f2c..08a1396 100644 --- a/core/src/me/srikavin/fbla/game/ui/GameHudUI.kt +++ b/core/src/me/srikavin/fbla/game/ui/GameHudUI.kt @@ -21,13 +21,13 @@ class GameHudUI(private val skin: Skin, private val gameState: GameState) : Game private lateinit var scoreLabel: Label private lateinit var awardsTable: Table + private var awardsHashCode = -1 + init { registerInputHandler(stage) } fun build() { - stage.viewport.update(Gdx.graphics.width, Gdx.graphics.height) - newAwardContainer.clear() newAwardContainer.setFillParent(true) newAwardContainer.setSize(Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat()) @@ -57,7 +57,26 @@ class GameHudUI(private val skin: Skin, private val gameState: GameState) : Game stage.addActor(container) } + fun renderAwards() { + awardsHashCode = gameState.awards.hashCode() + gameState.awards.forEach { award -> + val drawable = award.getDrawable() + awardsTable.add(Image(drawable).apply { + val manager = TooltipManager.getInstance() + manager.maxWidth = 350f + manager.instant() + manager.initialTime = 0.35f + addTextTooltip(award.getDescription(), skin = skin, tooltipManager = manager) + }) + .height(100f) + .width(100f * 28 / 43f) + .padLeft(10f) + } + } + override fun render() { + stage.viewport.update(Gdx.graphics.width, Gdx.graphics.height) + scoreLabel.setText(gameState.score) livesLabel.setText(gameState.lives) @@ -81,23 +100,15 @@ class GameHudUI(private val skin: Skin, private val gameState: GameState) : Game Actions.fadeOut(0.75f), Actions.removeActor() ) - } - gameState.awards.forEach { award -> - val drawable = award.getDrawable() - awardsTable.add(Image(drawable).apply { - val manager = TooltipManager.getInstance() - manager.maxWidth = 350f - manager.instant() - manager.initialTime = 0.35f - addTextTooltip(award.getDescription(), skin = skin, tooltipManager = manager) - }) - .height(100f) - .width(100f * 28 / 43f) - .padLeft(10f) + renderAwards() } } + if (awardsHashCode != gameState.awards.hashCode()) { + renderAwards() + } + stage.act() stage.draw() } diff --git a/core/src/me/srikavin/fbla/game/ui/MainMenu.kt b/core/src/me/srikavin/fbla/game/ui/MainMenu.kt index 185d443..c4ac542 100644 --- a/core/src/me/srikavin/fbla/game/ui/MainMenu.kt +++ b/core/src/me/srikavin/fbla/game/ui/MainMenu.kt @@ -17,13 +17,15 @@ import ktx.actors.alpha import me.srikavin.fbla.game.ext.addImageTextButton import me.srikavin.fbla.game.ext.sequence import me.srikavin.fbla.game.ext.table +import me.srikavin.fbla.game.state.GameState import me.srikavin.fbla.game.util.GdxArray +import me.srikavin.fbla.game.util.SaveUtils import me.srikavin.fbla.game.util.registerInputHandler import me.srikavin.fbla.game.util.unregisterInputHandler private const val style_name = "menu" -class MainMenu(private val skin: Skin, private val playRunnable: () -> Unit) : GameUI() { +class MainMenu(private val skin: Skin, private val playRunnable: (GameState) -> Unit) : GameUI() { private val stage = Stage(ExtendViewport(1920f, 1080f, 1920f, 1080f)) private val container = Table(skin) private val cloudContainer = Table(skin) @@ -54,7 +56,6 @@ class MainMenu(private val skin: Skin, private val playRunnable: () -> Unit) : G bgStack.add(Image( TextureRegionDrawable(Texture(Gdx.files.internal("assets/graphics/backgrounds/backgroundColorGrassTiled.png"))), Scaling.fill)) -// filterStack.add(Image(NinePatchDrawable(skin.getPatch("dark-filter")))) stage.addActor(bgStack) stage.addActor(cloudContainer) @@ -63,8 +64,9 @@ class MainMenu(private val skin: Skin, private val playRunnable: () -> Unit) : G registerInputHandler(stage) } + var clickAgain = false + fun build() { -// stage.isDebugAll = true stage.viewport.update(Gdx.graphics.width, Gdx.graphics.height) currentPanel = null @@ -93,7 +95,6 @@ class MainMenu(private val skin: Skin, private val playRunnable: () -> Unit) : G Actions.moveTo(-700f, 700f + vOffset, duration) )) ) - } animateCloud(cloud, 25f, 300f, 100f) @@ -111,9 +112,30 @@ class MainMenu(private val skin: Skin, private val playRunnable: () -> Unit) : G buttons(t, Buttoni("Play", null, - Buttoni("New Game", null, Runnable { playRunnable() }), - Buttoni("Load Game", null, Runnable { }), - Buttoni("Tutorial", null, Runnable { }) + Buttoni("New Game", null, Runnable { + if (!clickAgain && SaveUtils.saveGameExists()) { + infoPanel.clearChildren() + infoPanel.add(Label("[accent]Warning[]", skin, "default")) + infoPanel.row() + infoPanel.add("This will overwrite your previous save game!\nClick again to confirm.", style_name) + clickAgain = true + } else { + playRunnable(GameState()) + } + }), + Buttoni("Load Game", null, Runnable { + if (SaveUtils.saveGameExists()) { + val gameState = GameState() + SaveUtils.loadGame(gameState) + playRunnable(gameState) + } else { + infoPanel.clearChildren() + infoPanel.add(Label("[accent]Warning[]", skin, "default")) + infoPanel.row() + infoPanel.add("There are no found save games!", style_name) + clickAgain = true + } + }) ), Buttoni("Instructions", null, Runnable { infoPanel.clearChildren() diff --git a/core/src/me/srikavin/fbla/game/util/SaveUtils.kt b/core/src/me/srikavin/fbla/game/util/SaveUtils.kt index 2fcb805..d3c6717 100644 --- a/core/src/me/srikavin/fbla/game/util/SaveUtils.kt +++ b/core/src/me/srikavin/fbla/game/util/SaveUtils.kt @@ -4,30 +4,79 @@ import com.badlogic.gdx.Gdx import ktx.log.error import me.srikavin.fbla.game.state.GameState -const val SAVE_PATH = "save.dat" +const val SAVE_PATH = "saves/save.dat" object SaveUtils { /** * Load the game state from the given file into the given [GameState] instance */ fun loadGame(state: GameState) { - if (Gdx.files.isLocalStorageAvailable) { - val file = Gdx.files.local(SAVE_PATH) -// file. - } else { - error { "Local storage is not available on this platform" } + try { + if (Gdx.files.isLocalStorageAvailable) { + val file = Gdx.files.local(SAVE_PATH) + val reader = file.reader() + + state.lives = reader.read() + state.score = reader.read() + + val levelPathBuf = CharArray(reader.read()) + reader.read(levelPathBuf) + + state.currentLevelPath = String(levelPathBuf) + + state.awards.clear() + + for (i in 0 until reader.read()) { + val nameBuf = CharArray(reader.read()) + reader.read(nameBuf) + + state.addAward(String(nameBuf)) + } + state.lastAddedAward = null + reader.close() + + } else { + error { "Local storage is not available on this platform" } + } + } catch (e: Exception) { + error(e) } } /** * Saves the given game state into the given file */ - fun saveGame(state: GameState, name: String) { + fun saveGame(state: GameState) { + try { + if (Gdx.files.isLocalStorageAvailable) { + val file = Gdx.files.local(SAVE_PATH) + val writer = file.writer(false) + writer.write(state.lives) + writer.write(state.score) + writer.write(state.currentLevelPath.length) + writer.write(state.currentLevelPath.toCharArray()) + writer.write(state.awards.size) + state.awards.forEach { + writer.write(it.getName().length) + writer.write(it.getName().toCharArray()) + } + writer.close() + } else { + error { "Local storage is not available on this platform" } + } + } catch (e: Exception) { + error(e) + } + + } + + fun saveGameExists(): Boolean { if (Gdx.files.isLocalStorageAvailable) { val file = Gdx.files.local(SAVE_PATH) - file.writeBytes(ByteArray(5), true) - } else { - error { "Local storage is not available on this platform" } + + return file.exists() } + + return false } } \ No newline at end of file diff --git a/desktop/src/me/srikavin/fbla/game/desktop/DesktopLauncher.java b/desktop/src/me/srikavin/fbla/game/desktop/DesktopLauncher.java index 4c62959..fef5630 100644 --- a/desktop/src/me/srikavin/fbla/game/desktop/DesktopLauncher.java +++ b/desktop/src/me/srikavin/fbla/game/desktop/DesktopLauncher.java @@ -15,9 +15,7 @@ public static void main(String[] arg) { config.addIcon("assets/icon/QS-64.png", Files.FileType.Internal); config.addIcon("assets/icon/QS-32.png", Files.FileType.Internal); config.addIcon("assets/icon/QS-16.png", Files.FileType.Internal); -// config.foregroundFPS = 1000; -// config.vSyncEnabled = false; - config.fullscreen = true; +// config.fullscreen = true; new LwjglApplication(new FBLAGame(), config); } }