From 8a103f9533e064625a0b58a5d272c2c96a0f0853 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Thu, 9 Feb 2017 20:34:23 +0100 Subject: [PATCH] Save the game state on pause (closes #1) --- android/assets/.klooni.sav | Bin 0 -> 427 bytes .../lonamiwebs/klooni/screens/GameScreen.java | 93 +++++++++++++++++- .../klooni/screens/PauseMenuStage.java | 3 +- .../klooni/serializer/BinSerializer.java | 29 +++--- 4 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 android/assets/.klooni.sav diff --git a/android/assets/.klooni.sav b/android/assets/.klooni.sav new file mode 100644 index 0000000000000000000000000000000000000000..63dbd9c1b00410848677ac6984be6d573e6c32a0 GIT binary patch literal 427 zcmZQz00XZ7XaFRNCWMs>RK^IxSS67JNl?cER13yP+A)|wHLS=OLjhC(sD=$4<5D}A T#DQ*R28S#Mki{Sf4B@)~@Dxl7 literal 0 HcmV?d00001 diff --git a/core/src/io/github/lonamiwebs/klooni/screens/GameScreen.java b/core/src/io/github/lonamiwebs/klooni/screens/GameScreen.java index 7d2de5c..5dea04e 100644 --- a/core/src/io/github/lonamiwebs/klooni/screens/GameScreen.java +++ b/core/src/io/github/lonamiwebs/klooni/screens/GameScreen.java @@ -5,9 +5,14 @@ import com.badlogic.gdx.Input; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.Screen; import com.badlogic.gdx.audio.Sound; +import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + import io.github.lonamiwebs.klooni.Klooni; import io.github.lonamiwebs.klooni.game.BaseScorer; import io.github.lonamiwebs.klooni.game.Board; @@ -16,9 +21,11 @@ import io.github.lonamiwebs.klooni.game.Piece; import io.github.lonamiwebs.klooni.game.PieceHolder; import io.github.lonamiwebs.klooni.game.Scorer; import io.github.lonamiwebs.klooni.game.TimeScorer; +import io.github.lonamiwebs.klooni.serializer.BinSerializable; +import io.github.lonamiwebs.klooni.serializer.BinSerializer; // Main game screen. Here the board, piece holder and score are shown -class GameScreen implements Screen, InputProcessor { +class GameScreen implements Screen, InputProcessor, BinSerializable { //region Members @@ -48,11 +55,18 @@ class GameScreen implements Screen, InputProcessor { final static int GAME_MODE_SCORE = 0; final static int GAME_MODE_TIME = 1; + private final static String SAVE_DAT_FILENAME = ".klooni.sav"; + //endregion //region Constructor + // Load any previously saved file by default GameScreen(final Klooni game, final int gameMode) { + this(game, gameMode, true); + } + + GameScreen(final Klooni game, final int gameMode, final boolean loadSave) { batch = new SpriteBatch(); this.gameMode = gameMode; @@ -73,6 +87,15 @@ class GameScreen implements Screen, InputProcessor { pauseMenu = new PauseMenuStage(layout, game, scorer, gameMode); gameOverSound = Gdx.audio.newSound(Gdx.files.internal("sound/game_over.mp3")); + + if (loadSave) { + // The user might have a previous game. If this is the case, load it + tryLoad(); + } + else { + // Ensure that there is no old save, we don't want to load it, thus delete it + deleteSave(); + } } //endregion @@ -90,12 +113,15 @@ class GameScreen implements Screen, InputProcessor { private void doGameOver() { if (!gameOverDone) { + gameOverDone = true; + holder.enabled = false; pauseMenu.show(true); if (Klooni.soundsEnabled()) gameOverSound.play(); - gameOverDone = true; + // The user should not be able to return to the game if its game over + deleteSave(); } } @@ -111,6 +137,11 @@ class GameScreen implements Screen, InputProcessor { Gdx.input.setInputProcessor(this); } + private void showPauseMenu() { + pauseMenu.show(false); + save(); // Save the state, the user might leave the game + } + @Override public void render(float delta) { Klooni.theme.glClearBackground(); @@ -147,7 +178,7 @@ class GameScreen implements Screen, InputProcessor { @Override public boolean keyUp(int keycode) { if (keycode == Input.Keys.P || keycode == Input.Keys.BACK) // Pause - pauseMenu.show(false); + showPauseMenu(); return false; } @@ -218,4 +249,60 @@ class GameScreen implements Screen, InputProcessor { } //endregion + + //region Saving and loading + + private void save() { + // Only save if the game is not over + if (gameOverDone) + return; + + final FileHandle handle = Gdx.files.local(SAVE_DAT_FILENAME); + try { + BinSerializer.serialize(this, handle.write(false)); + } catch (IOException e) { + // Should never happen but what else could be done if the game wasn't saved? + e.printStackTrace(); + } + } + + static void deleteSave() { + final FileHandle handle = Gdx.files.local(SAVE_DAT_FILENAME); + if (handle.exists()) + handle.delete(); + } + + private boolean tryLoad() { + // Load will fail if the game modes differ, but that's okay + final FileHandle handle = Gdx.files.local(SAVE_DAT_FILENAME); + if (handle.exists()) { + try { + BinSerializer.deserialize(this, handle.read()); + return true; + } catch (IOException ignored) { } + } + return false; + } + + @Override + public void write(DataOutputStream out) throws IOException { + // gameMode, board, holder, scorer + out.writeInt(gameMode); + board.write(out); + holder.write(out); + scorer.write(out); + } + + @Override + public void read(DataInputStream in) throws IOException { + int savedGameMode = in.readInt(); + if (savedGameMode != gameMode) + throw new IOException("A different game mode was saved. Cannot load the save data."); + + board.read(in); + holder.read(in); + scorer.read(in); + } + + //endregion } diff --git a/core/src/io/github/lonamiwebs/klooni/screens/PauseMenuStage.java b/core/src/io/github/lonamiwebs/klooni/screens/PauseMenuStage.java index 754e824..afb56d7 100644 --- a/core/src/io/github/lonamiwebs/klooni/screens/PauseMenuStage.java +++ b/core/src/io/github/lonamiwebs/klooni/screens/PauseMenuStage.java @@ -71,7 +71,8 @@ class PauseMenuStage extends Stage { replayButton.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { - game.setScreen(new GameScreen(game, gameMode)); + // false, don't load the saved game state; we do want to replay + game.setScreen(new GameScreen(game, gameMode, false)); dispose(); } }); diff --git a/core/src/io/github/lonamiwebs/klooni/serializer/BinSerializer.java b/core/src/io/github/lonamiwebs/klooni/serializer/BinSerializer.java index 68c241c..d58b665 100644 --- a/core/src/io/github/lonamiwebs/klooni/serializer/BinSerializer.java +++ b/core/src/io/github/lonamiwebs/klooni/serializer/BinSerializer.java @@ -1,7 +1,9 @@ package io.github.lonamiwebs.klooni.serializer; +import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; public class BinSerializer { @@ -15,24 +17,17 @@ public class BinSerializer { out.close(); } catch (IOException ignored) { } } + } - // todo uhm maybe make the classes serializable? like telethon, kinda, idk, bye. - - - /* - DataInputStream d = new DataInputStream(new FileInputStream("test.txt")); - DataOutputStream out = new DataOutputStream(new FileOutputStream("test1.txt")); - String count; - d.readFully(); - while((count = d.readLine()) != null){ - String u = count.toUpperCase(); - System.out.println(u); - out.writeBytes(u + " ,"); + public static void deserialize(final BinSerializable serializable, final InputStream input) + throws IOException { + DataInputStream in = new DataInputStream(input); + try { + serializable.read(in); + } finally { + try { + in.close(); + } catch (IOException ignored) { } } - d.close(); - out.close(); - */ - - } }