Save the game state on pause (closes #1)
This commit is contained in:
parent
67d74936d2
commit
8a103f9533
4 changed files with 104 additions and 21 deletions
BIN
android/assets/.klooni.sav
Normal file
BIN
android/assets/.klooni.sav
Normal file
Binary file not shown.
|
@ -5,9 +5,14 @@ import com.badlogic.gdx.Input;
|
||||||
import com.badlogic.gdx.InputProcessor;
|
import com.badlogic.gdx.InputProcessor;
|
||||||
import com.badlogic.gdx.Screen;
|
import com.badlogic.gdx.Screen;
|
||||||
import com.badlogic.gdx.audio.Sound;
|
import com.badlogic.gdx.audio.Sound;
|
||||||
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.graphics.GL20;
|
import com.badlogic.gdx.graphics.GL20;
|
||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
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.Klooni;
|
||||||
import io.github.lonamiwebs.klooni.game.BaseScorer;
|
import io.github.lonamiwebs.klooni.game.BaseScorer;
|
||||||
import io.github.lonamiwebs.klooni.game.Board;
|
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.PieceHolder;
|
||||||
import io.github.lonamiwebs.klooni.game.Scorer;
|
import io.github.lonamiwebs.klooni.game.Scorer;
|
||||||
import io.github.lonamiwebs.klooni.game.TimeScorer;
|
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
|
// 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
|
//region Members
|
||||||
|
|
||||||
|
@ -48,11 +55,18 @@ class GameScreen implements Screen, InputProcessor {
|
||||||
final static int GAME_MODE_SCORE = 0;
|
final static int GAME_MODE_SCORE = 0;
|
||||||
final static int GAME_MODE_TIME = 1;
|
final static int GAME_MODE_TIME = 1;
|
||||||
|
|
||||||
|
private final static String SAVE_DAT_FILENAME = ".klooni.sav";
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region Constructor
|
//region Constructor
|
||||||
|
|
||||||
|
// Load any previously saved file by default
|
||||||
GameScreen(final Klooni game, final int gameMode) {
|
GameScreen(final Klooni game, final int gameMode) {
|
||||||
|
this(game, gameMode, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameScreen(final Klooni game, final int gameMode, final boolean loadSave) {
|
||||||
batch = new SpriteBatch();
|
batch = new SpriteBatch();
|
||||||
this.gameMode = gameMode;
|
this.gameMode = gameMode;
|
||||||
|
|
||||||
|
@ -73,6 +87,15 @@ class GameScreen implements Screen, InputProcessor {
|
||||||
pauseMenu = new PauseMenuStage(layout, game, scorer, gameMode);
|
pauseMenu = new PauseMenuStage(layout, game, scorer, gameMode);
|
||||||
|
|
||||||
gameOverSound = Gdx.audio.newSound(Gdx.files.internal("sound/game_over.mp3"));
|
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
|
//endregion
|
||||||
|
@ -90,12 +113,15 @@ class GameScreen implements Screen, InputProcessor {
|
||||||
|
|
||||||
private void doGameOver() {
|
private void doGameOver() {
|
||||||
if (!gameOverDone) {
|
if (!gameOverDone) {
|
||||||
|
gameOverDone = true;
|
||||||
|
|
||||||
holder.enabled = false;
|
holder.enabled = false;
|
||||||
pauseMenu.show(true);
|
pauseMenu.show(true);
|
||||||
if (Klooni.soundsEnabled())
|
if (Klooni.soundsEnabled())
|
||||||
gameOverSound.play();
|
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);
|
Gdx.input.setInputProcessor(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showPauseMenu() {
|
||||||
|
pauseMenu.show(false);
|
||||||
|
save(); // Save the state, the user might leave the game
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(float delta) {
|
public void render(float delta) {
|
||||||
Klooni.theme.glClearBackground();
|
Klooni.theme.glClearBackground();
|
||||||
|
@ -147,7 +178,7 @@ class GameScreen implements Screen, InputProcessor {
|
||||||
@Override
|
@Override
|
||||||
public boolean keyUp(int keycode) {
|
public boolean keyUp(int keycode) {
|
||||||
if (keycode == Input.Keys.P || keycode == Input.Keys.BACK) // Pause
|
if (keycode == Input.Keys.P || keycode == Input.Keys.BACK) // Pause
|
||||||
pauseMenu.show(false);
|
showPauseMenu();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -218,4 +249,60 @@ class GameScreen implements Screen, InputProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//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
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,8 @@ class PauseMenuStage extends Stage {
|
||||||
replayButton.addListener(new ChangeListener() {
|
replayButton.addListener(new ChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void changed(ChangeEvent event, Actor actor) {
|
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();
|
dispose();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package io.github.lonamiwebs.klooni.serializer;
|
package io.github.lonamiwebs.klooni.serializer;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public class BinSerializer {
|
public class BinSerializer {
|
||||||
|
@ -15,24 +17,17 @@ public class BinSerializer {
|
||||||
out.close();
|
out.close();
|
||||||
} catch (IOException ignored) { }
|
} 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 + " ,");
|
|
||||||
}
|
}
|
||||||
d.close();
|
|
||||||
out.close();
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
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) { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue