Skip to content

Commit

Permalink
Add save support
Browse files Browse the repository at this point in the history
  • Loading branch information
srikavin committed Feb 13, 2020
1 parent f0317f2 commit 1246931
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 50 deletions.
19 changes: 8 additions & 11 deletions core/src/me/srikavin/fbla/game/FBLAGame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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

Expand All @@ -91,8 +91,6 @@ class FBLAGame : ApplicationAdapter() {
root.setFillParent(true)
root.top().right()

val gameState = GameState(0)

val listenerManager = ContactListenerManager()

val mapLoader = MapLoader()
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion core/src/me/srikavin/fbla/game/award/Awards.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ object Awards {
}

fun getAward(name: String): Award {
return nameMap.getValue(name)
return nameMap.getValue(name.toLowerCase())
}
}
2 changes: 1 addition & 1 deletion core/src/me/srikavin/fbla/game/ecs/system/UISystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
3 changes: 3 additions & 0 deletions core/src/me/srikavin/fbla/game/minigame/Minigame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -99,6 +100,8 @@ abstract class Minigame {
awardActive = false
active = false

SaveUtils.saveGame(gameState)

Gdx.app.postRunnable {
mapLoader.loadMap(world, "assets/maps/$nextLevel")
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/me/srikavin/fbla/game/state/GameState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ data class GameState(
var lives: Int = 3,
var gameRules: GameRules = GameRules(),
val awards: LinkedHashSet<Award> = LinkedHashSet(4),
var currentLevelPath: String = "level1.tmx") {
var currentLevelPath: String = "assets/maps/level1.tmx") {
@Transient
var lastAddedAward: Award? = null

Expand Down
5 changes: 4 additions & 1 deletion core/src/me/srikavin/fbla/game/ui/DeadUI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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")
}
Expand Down
41 changes: 26 additions & 15 deletions core/src/me/srikavin/fbla/game/ui/GameHudUI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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)

Expand All @@ -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()
}
Expand Down
36 changes: 29 additions & 7 deletions core/src/me/srikavin/fbla/game/ui/MainMenu.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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()
Expand Down
69 changes: 59 additions & 10 deletions core/src/me/srikavin/fbla/game/util/SaveUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

0 comments on commit 1246931

Please sign in to comment.