summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 5ef6464)
raw | patch | inline | side by side (parent: 5ef6464)
author | fredboy <fredboy@protonmail.com> | |
Tue, 21 May 2024 19:42:50 +0000 (02:42 +0700) | ||
committer | fredboy <fredboy@protonmail.com> | |
Tue, 21 May 2024 19:42:50 +0000 (02:42 +0700) |
23 files changed:
diff --git a/core/build.gradle b/core/build.gradle
index 3b061a7b46e305818b0cc302409847fc593d016a..ac46757aece221bb9eea06e31a2a7c99b5d44c08 100644 (file)
--- a/core/build.gradle
+++ b/core/build.gradle
implementation 'org.jetbrains:annotations:23.1.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion"
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinSerializationVersion"
ksp "com.google.dagger:dagger-compiler:$daggerVersion"
-}
\ No newline at end of file
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameModule.java b/core/src/ru/deadsoftware/cavedroid/game/GameModule.java
index d57ab337e9e49c0cbc872ee037cb42b407908d24..29aac7df7e0f42be5364e1df21a5327fe3888697 100644 (file)
import ru.deadsoftware.cavedroid.game.model.block.Block;
import ru.deadsoftware.cavedroid.game.objects.drop.DropController;
import ru.deadsoftware.cavedroid.game.objects.container.ContainerController;
+import ru.deadsoftware.cavedroid.game.save.GameSaveData;
+import ru.deadsoftware.cavedroid.game.save.GameSaveLoader;
import ru.deadsoftware.cavedroid.game.ui.TooltipManager;
import ru.deadsoftware.cavedroid.game.world.GameWorld;
public class GameModule {
@CheckForNull
- private static GameSaver.Data data;
+ private static GameSaveData data;
public static boolean loaded = false;
- private static void load(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
+ private static void load(MainConfig mainConfig, GameItemsHolder gameItemsHolder, TooltipManager tooltipManager) {
if (loaded) {
return;
}
- data = GameSaver.load(mainConfig, gameItemsHolder);
+ data = GameSaveLoader.INSTANCE.load(mainConfig, gameItemsHolder, tooltipManager);
loaded = true;
}
@Provides
@GameScope
- public static DropController provideDropController(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
- load(mainConfig, gameItemsHolder);
+ public static DropController provideDropController(MainConfig mainConfig,
+ GameItemsHolder gameItemsHolder,
+ TooltipManager tooltipManager) {
+ load(mainConfig, gameItemsHolder, tooltipManager);
DropController controller = data != null ? data.retrieveDropController() : new DropController();
makeDataNullIfEmpty();
controller.initDrops(gameItemsHolder);
@Provides
@GameScope
- public static ContainerController provideFurnaceController(MainConfig mainConfig, DropController dropController, GameItemsHolder gameItemsHolder) {
- load(mainConfig, gameItemsHolder);
- ContainerController controller = data != null ? data.retrieveFurnaceController() : new ContainerController(dropController, gameItemsHolder);
+ public static ContainerController provideFurnaceController(MainConfig mainConfig,
+ DropController dropController,
+ GameItemsHolder gameItemsHolder,
+ TooltipManager tooltipManager) {
+ load(mainConfig, gameItemsHolder, tooltipManager);
+ ContainerController controller = data != null
+ ? data.retrieveContainerController()
+ : new ContainerController(dropController, gameItemsHolder);
makeDataNullIfEmpty();
controller.init(dropController, gameItemsHolder);
return controller;
public static MobsController provideMobsController(MainConfig mainConfig,
GameItemsHolder gameItemsHolder,
TooltipManager tooltipManager) {
- load(mainConfig, gameItemsHolder);
+ load(mainConfig, gameItemsHolder, tooltipManager);
MobsController controller = data != null
? data.retrieveMobsController()
: new MobsController(gameItemsHolder, tooltipManager);
DropController dropController,
MobsController mobsController,
GameItemsHolder gameItemsHolder,
- ContainerController containerController) {
- load(mainConfig, gameItemsHolder);
+ ContainerController containerController,
+ TooltipManager tooltipManager) {
+ load(mainConfig, gameItemsHolder, tooltipManager);
Block[][] fm = data != null ? data.retrieveForeMap() : null;
Block[][] bm = data != null ? data.retrieveBackMap() : null;
makeDataNullIfEmpty();
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameSaver.java b/core/src/ru/deadsoftware/cavedroid/game/GameSaver.java
+++ /dev/null
@@ -1,271 +0,0 @@
-package ru.deadsoftware.cavedroid.game;
-
-import com.badlogic.gdx.Gdx;
-import com.badlogic.gdx.files.FileHandle;
-import ru.deadsoftware.cavedroid.MainConfig;
-import ru.deadsoftware.cavedroid.game.mobs.MobsController;
-import ru.deadsoftware.cavedroid.game.model.block.Block;
-import ru.deadsoftware.cavedroid.game.objects.drop.DropController;
-import ru.deadsoftware.cavedroid.game.objects.container.ContainerController;
-import ru.deadsoftware.cavedroid.game.world.GameWorld;
-
-import javax.annotation.CheckForNull;
-import java.io.*;
-import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.Map;
-
-public class GameSaver {
-
- private static final String TAG = "GameSaver";
-
- public static class Data {
- @CheckForNull
- private MobsController mMobsController;
- @CheckForNull
- private DropController mDropController;
- @CheckForNull
- private ContainerController mContainerController;
- @CheckForNull
- private Block[][] mForeMap, mBackMap;
-
- public Data(MobsController mobsController,
- DropController dropController,
- ContainerController containerController,
- Block[][] foreMap,
- Block[][] backMap) {
- mMobsController = mobsController;
- mDropController = dropController;
- mContainerController = containerController;
- mForeMap = foreMap;
- mBackMap = backMap;
- }
-
- public MobsController retrieveMobsController() {
- assert mMobsController != null;
- MobsController mobsController = mMobsController;
- mMobsController = null;
- return mobsController;
- }
-
- public DropController retrieveDropController() {
- assert mDropController != null;
- DropController dropController = mDropController;
- mDropController = null;
- return dropController;
- }
-
- public ContainerController retrieveFurnaceController() {
- assert mContainerController != null;
- ContainerController containerController = mContainerController;
- mContainerController = null;
- return containerController;
- }
-
- public Block[][] retrieveForeMap() {
- assert mForeMap != null;
- Block[][] foreMap = mForeMap;
- mForeMap = null;
- return foreMap;
- }
-
- public Block[][] retrieveBackMap() {
- assert mBackMap != null;
- Block[][] backMap = mBackMap;
- mBackMap = null;
- return backMap;
- }
-
- public boolean isEmpty() {
- return mMobsController == null &&
- mDropController == null &&
- mContainerController == null &&
- mForeMap == null &&
- mBackMap == null;
- }
- }
-
- private static final int SAVE_VERSION = 1;
-
- private static byte[] intToBytes(int i) {
- return ByteBuffer.allocate(4).putInt(i).array();
- }
-
- private static Map<String, Integer> buildBlocksDictionary(Block[][] foreMap, Block[][] backMap) {
- final HashMap<String, Integer> dict = new HashMap<>();
-
- int id = 0;
- for (int i = 0; i < foreMap.length; i++) {
- for (int j = 0; j < foreMap[i].length; j++) {
- for (int k = 0; k < 2; k++) {
- final Block block = k == 0 ? foreMap[i][j] : backMap[i][j];
- final String key = block.getParams().getKey();
- if (!dict.containsKey(key)) {
- dict.put(key, id++);
- }
- }
- }
- }
-
- return dict;
- }
-
- private static void saveDict(FileHandle file, Map<String, Integer> dict) {
- final String[] arr = new String[dict.size()];
-
- for (Map.Entry<String, Integer> entry : dict.entrySet()) {
- arr[entry.getValue()] = entry.getKey();
- }
-
- final StringBuilder builder = new StringBuilder();
- for (String key : arr) {
- builder.append(key);
- builder.append('\n');
- }
-
- file.writeString(builder.toString(), false);
- }
-
- private static String[] loadDict(FileHandle file) {
- return file.readString().split("\n");
- }
-
- private static void saveMap(FileHandle file, Block[][] map, Map<String, Integer> dict) throws IOException {
- int run, block;
- int width = map.length;
- int height = map[0].length;
-
- BufferedOutputStream out = new BufferedOutputStream(file.write(false));
-
- out.write(SAVE_VERSION);
- out.write(intToBytes(width));
- out.write(intToBytes(height));
-
- for (int y = 0; y < height; y++) {
- block = dict.get(map[0][y].getParams().getKey());
- run = 0;
- for (Block[] blocks : map) {
- int newValue = dict.get(blocks[y].getParams().getKey());
- if (run >= 0xFF || newValue != block) {
- out.write(run);
- out.write(block);
- run = 0;
- block = dict.get(blocks[y].getParams().getKey());
- }
- run++;
- }
- out.write(run);
- out.write(block);
- }
-
- out.flush();
- out.close();
- }
-
- private static Block[][] loadMap(GameItemsHolder gameItemsHolder, FileHandle file, String[] dict) throws Exception {
- Block[][] map;
- int version, width, height;
- int run, block;
-
- DataInputStream in = new DataInputStream(file.read());
-
- version = in.readByte();
-
- if (SAVE_VERSION == version) {
- width = in.readInt();
- height = in.readInt();
- map = new Block[width][height];
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x += run) {
- run = in.readUnsignedByte();
- block = in.readUnsignedByte();
- for (int i = x; i < x + run; i++) {
- map[i][y] = gameItemsHolder.getBlock(dict[block]);
- }
- }
- }
- } else {
- throw new Exception("version mismatch");
- }
-
- in.close();
- return map;
- }
-
- @CheckForNull
- public static Data load(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
- String folder = mainConfig.getGameFolder();
- FileHandle file = Gdx.files.absolute(folder + "/saves/game.sav");
-
- try {
- ObjectInputStream in = new ObjectInputStream(file.read());
- int version = in.readInt();
- DropController dropController;
- MobsController mobsController;
- ContainerController containerController;
-
- if (SAVE_VERSION == version) {
- dropController = (DropController) in.readObject();
- mobsController = (MobsController) in.readObject();
- containerController = (ContainerController) in.readObject();
- } else {
- throw new Exception("version mismatch");
- }
-
- in.close();
-
- final String[] dict = loadDict(Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/dict"));
- Block[][] foreMap = loadMap(gameItemsHolder, Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/foremap.sav"), dict);
- Block[][] backMap = loadMap(gameItemsHolder, Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/backmap.sav"), dict);
-
- if (dropController == null || mobsController == null) {
- throw new Exception("couldn't load");
- }
-
- return new Data(mobsController, dropController, containerController, foreMap, backMap);
- } catch (Exception e) {
- Gdx.app.error("GameSaver", e.getMessage());
- }
-
- return null;
- }
-
- public static void save(MainConfig mainConfig,
- DropController dropController,
- MobsController mobsController,
- ContainerController containerController,
- GameWorld gameWorld) {
- String folder = mainConfig.getGameFolder();
- FileHandle file = Gdx.files.absolute(folder + "/saves/");
- file.mkdirs();
- file = Gdx.files.absolute(folder + "/saves/game.sav");
-
- final Block[][] foreMap, backMap;
- foreMap = gameWorld.getFullForeMap();
- backMap = gameWorld.getFullBackMap();
-
- final Map<String, Integer> dict = buildBlocksDictionary(foreMap, backMap);
-
- try {
- ObjectOutputStream out = new ObjectOutputStream(file.write(false));
- out.writeInt(SAVE_VERSION);
- out.writeObject(dropController);
- out.writeObject(mobsController);
- out.writeObject(containerController);
- out.close();
-
- saveDict(Gdx.files.absolute(folder + "/saves/dict"), dict);
- saveMap(Gdx.files.absolute(folder + "/saves/foremap.sav"), gameWorld.getFullForeMap(), dict);
- saveMap(Gdx.files.absolute(folder + "/saves/backmap.sav"), gameWorld.getFullBackMap(), dict);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public static boolean exists(MainConfig mainConfig) {
- String folder = mainConfig.getGameFolder();
- return (Gdx.files.absolute(folder + "/saves/game.sav").exists() &&
- Gdx.files.absolute(folder + "/saves/foremap.sav").exists() &&
- Gdx.files.absolute(folder + "/saves/backmap.sav").exists());
- }
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/PauseGameKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/PauseGameKeyboardInputHandler.kt
index cedc78a7fc128e95ed99757e6bd3243a2893e934..0311c8831fb22dd753d08db23564e9a8f5b19235 100644 (file)
--- a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/PauseGameKeyboardInputHandler.kt
+++ b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/PauseGameKeyboardInputHandler.kt
package ru.deadsoftware.cavedroid.game.input.handler.keyboard
import ru.deadsoftware.cavedroid.MainConfig
-import ru.deadsoftware.cavedroid.game.GameSaver
import ru.deadsoftware.cavedroid.game.GameScope
import ru.deadsoftware.cavedroid.game.GameUiWindow
import ru.deadsoftware.cavedroid.game.input.IKeyboardInputHandler
import ru.deadsoftware.cavedroid.game.mobs.MobsController
import ru.deadsoftware.cavedroid.game.objects.drop.DropController
import ru.deadsoftware.cavedroid.game.objects.container.ContainerController
+import ru.deadsoftware.cavedroid.game.save.GameSaveLoader
import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
import ru.deadsoftware.cavedroid.game.world.GameWorld
import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
return
}
- GameSaver.save(mainConfig, dropController, mobsController, containerController, gameWorld)
+ GameSaveLoader.save(mainConfig, dropController, mobsController, containerController, gameWorld)
mainConfig.caveGame.quitGame()
}
}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingBlock.kt b/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingBlock.kt
index f2735044aee27572ed2045f7f0978712aabeb4a9..77e51a1b189ecc2a4757a5b79f0be7dcff331117 100644 (file)
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import ru.deadsoftware.cavedroid.game.GameItemsHolder
import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
import ru.deadsoftware.cavedroid.game.world.GameWorld
import ru.deadsoftware.cavedroid.misc.utils.bl
import ru.deadsoftware.cavedroid.misc.utils.px
) {
_block?.draw(spriteBatch, x, y)
}
+
+ override fun getSaveData(): SaveDataDto.FallingBlockSaveData {
+ return SaveDataDto.FallingBlockSaveData(
+ version = SAVE_DATA_VERSION,
+ x = x,
+ y = y,
+ width = width,
+ height = height,
+ velocityX = velocity.x,
+ velocityY = velocity.y,
+ type = mType,
+ animDelta = mAnimDelta,
+ anim = mAnim,
+ direction = mDirection,
+ dead = mDead,
+ canJump = mCanJump,
+ flyMode = mFlyMode,
+ maxHealth = mMaxHealth,
+ health = mHealth,
+ blockKey = blockKey,
+ )
+ }
+
+ companion object {
+ private const val SAVE_DATA_VERSION = 1
+
+ fun fromSaveData(saveData: SaveDataDto.FallingBlockSaveData): FallingBlock {
+ saveData.verifyVersion(SAVE_DATA_VERSION)
+
+ return FallingBlock(saveData.blockKey, saveData.x, saveData.y).apply {
+ velocity.x = saveData.velocityX
+ velocity.y = saveData.velocityY
+ mAnimDelta = saveData.animDelta
+ mAnim = saveData.anim
+ mDirection = saveData.direction
+ mDead = saveData.dead
+ mCanJump = saveData.canJump
+ mFlyMode = saveData.flyMode
+ mMaxHealth = saveData.maxHealth
+ mHealth = saveData.health
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/Mob.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/Mob.java
index e4ca679cb43c5a68430d521889459c1958e6c439..0f3610b61c36d0d516c744d7158abfa348a5fbd5 100644 (file)
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Timer;
import ru.deadsoftware.cavedroid.game.GameItemsHolder;
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto;
import ru.deadsoftware.cavedroid.game.model.item.InventoryItem;
import ru.deadsoftware.cavedroid.game.model.item.Item;
import ru.deadsoftware.cavedroid.game.world.GameWorld;
+import ru.deadsoftware.cavedroid.misc.Saveable;
import javax.annotation.CheckForNull;
import java.io.Serializable;
/**
* Mob class.
*/
-public abstract class Mob extends Rectangle implements Serializable {
+public abstract class Mob extends Rectangle implements Serializable, Saveable {
private static final float DAMAGE_TINT_TIMEOUT_S = 0.5f;
private static final Color DAMAGE_TINT_COLOR = new Color(0xff8080ff);
protected int mAnimDelta = ANIMATION_SPEED;
protected float mAnim;
- private Direction mDirection;
+ protected Direction mDirection;
protected boolean mDead;
- private boolean mCanJump;
- private boolean mFlyMode;
+ protected boolean mCanJump;
+ protected boolean mFlyMode;
- private final int mMaxHealth;
- private int mHealth;
+ protected int mMaxHealth;
+ protected int mHealth;
private transient boolean mTakingDamage = false;
@CheckForNull private transient ResetTakeDamageTask mResetTakeDamageTask = null;
public abstract float getSpeed();
public abstract void jump();
+
+ @Override
+ public abstract SaveDataDto.MobSaveDataDto getSaveData();
+
+ public static Mob fromSaveData(SaveDataDto.MobSaveDataDto saveData) {
+ return MobSaveDataMapperKt.fromSaveData(saveData);
+ }
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/MobSaveDataMapper.kt b/core/src/ru/deadsoftware/cavedroid/game/mobs/MobSaveDataMapper.kt
--- /dev/null
@@ -0,0 +1,12 @@
+package ru.deadsoftware.cavedroid.game.mobs
+
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+
+fun fromSaveData(saveData: SaveDataDto.MobSaveDataDto): Mob {
+ return when (saveData) {
+ is SaveDataDto.PigSaveData -> Pig.fromSaveData(saveData)
+ is SaveDataDto.FallingBlockSaveData -> FallingBlock.fromSaveData(saveData)
+
+ is SaveDataDto.PlayerSaveData -> throw IllegalArgumentException("Cannot load player as regular Mob")
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/MobsController.kt b/core/src/ru/deadsoftware/cavedroid/game/mobs/MobsController.kt
index 158d22a9e55445ae1056e680c4fb3621f03a03e0..dc01e1e8e238db085a53a467edd52c6801bd634c 100644 (file)
import ru.deadsoftware.cavedroid.game.GameItemsHolder
import ru.deadsoftware.cavedroid.game.GameScope
import ru.deadsoftware.cavedroid.game.mobs.player.Player
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
import ru.deadsoftware.cavedroid.game.ui.TooltipManager
+import ru.deadsoftware.cavedroid.misc.Saveable
import java.io.Serializable
import java.util.*
import javax.inject.Inject
class MobsController @Inject constructor(
gameItemsHolder: GameItemsHolder,
tooltipManager: TooltipManager,
-) : Serializable {
+) : Serializable, Saveable {
private val _mobs = LinkedList<Mob>()
- val player: Player =
- Player(gameItemsHolder, tooltipManager)
+ var player: Player = Player(gameItemsHolder, tooltipManager)
+ private set
val mobs: List<Mob>
get() = _mobs
_mobs.add(mob)
}
+ override fun getSaveData(): SaveDataDto.MobsControllerSaveData {
+ return SaveDataDto.MobsControllerSaveData(
+ version = SAVE_DATA_VERSION,
+ mobs = _mobs.map(Mob::getSaveData),
+ player = player.getSaveData(),
+ )
+ }
+
companion object {
+ private const val SAVE_DATA_VERSION = 1
+
private const val TAG = "MobsController"
+
+ fun fromSaveData(
+ saveData: SaveDataDto.MobsControllerSaveData,
+ gameItemsHolder: GameItemsHolder,
+ tooltipManager: TooltipManager
+ ): MobsController {
+ saveData.verifyVersion(SAVE_DATA_VERSION)
+
+ return MobsController(gameItemsHolder, tooltipManager)
+ .apply {
+ _mobs.addAll(saveData.mobs.map { mob -> Mob.fromSaveData(mob) })
+ player = Player.fromSaveData(saveData.player, gameItemsHolder, tooltipManager)
+ }
+ }
}
}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/Pig.kt b/core/src/ru/deadsoftware/cavedroid/game/mobs/Pig.kt
index b3d647d328aae2fd0a65f89f66b92ff3c5b0e832..b080e4078730713b897e8a45f1e1ba3655b81239 100644 (file)
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.math.Vector2
import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
import ru.deadsoftware.cavedroid.misc.utils.drawSprite
import ru.deadsoftware.cavedroid.misc.utils.mobs.MobSprites.Pig.getBackgroundLeg
@@ -54,13 +55,52 @@ class Pig(x: Float, y: Float) : PeacefulMob(x, y, WIDTH, HEIGHT, randomDir(), MA
spriteBatch.drawSprite(getForegroundLeg(), leftLegX, legY, anim, tint = tintColor)
spriteBatch.drawSprite(getForegroundLeg(), rightLegX, legY, anim, tint = tintColor)
}
+
+ override fun getSaveData(): SaveDataDto.PigSaveData {
+ return SaveDataDto.PigSaveData(
+ version = SAVE_DATA_VERSION,
+ x = x,
+ y = y,
+ width = width,
+ height = height,
+ velocityX = velocity.x,
+ velocityY = velocity.y,
+ type = mType,
+ animDelta = mAnimDelta,
+ anim = mAnim,
+ direction = mDirection,
+ dead = mDead,
+ canJump = mCanJump,
+ flyMode = mFlyMode,
+ maxHealth = mMaxHealth,
+ health = mHealth
+ )
+ }
-
- private companion object {
+ companion object {
+ private const val SAVE_DATA_VERSION = 1
+
private const val WIDTH = 25f
private const val HEIGHT = 18f
private const val SPEED = 48f
private const val JUMP_VELOCITY = -133.332f
private const val MAX_HEALTH = 10
+
+ fun fromSaveData(saveData: SaveDataDto.PigSaveData): Pig {
+ saveData.verifyVersion(SAVE_DATA_VERSION)
+
+ return Pig(saveData.x, saveData.y).apply {
+ velocity.x = saveData.velocityX
+ velocity.y = saveData.velocityY
+ mAnimDelta = saveData.animDelta
+ mAnim = saveData.anim
+ mDirection = saveData.direction
+ mDead = saveData.dead
+ mCanJump = saveData.canJump
+ mFlyMode = saveData.flyMode
+ mMaxHealth = saveData.maxHealth
+ mHealth = saveData.health
+ }
+ }
}
}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/player/Inventory.kt b/core/src/ru/deadsoftware/cavedroid/game/mobs/player/Inventory.kt
index 43f298c876e9702fb5e675e5b429602b22fbf13d..8f0b90d721a709f3b5c2015d16d82c943ae1e779 100644 (file)
package ru.deadsoftware.cavedroid.game.mobs.player
import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
import ru.deadsoftware.cavedroid.game.model.item.Item
import ru.deadsoftware.cavedroid.game.objects.drop.Drop
import ru.deadsoftware.cavedroid.game.ui.TooltipManager
+import ru.deadsoftware.cavedroid.misc.Saveable
import java.io.Serializable
-class Inventory(
+class Inventory @JvmOverloads constructor(
val size: Int,
val hotbarSize: Int,
gameItemsHolder: GameItemsHolder,
tooltipManager: TooltipManager,
-) : Serializable {
+ initialItems: List<InventoryItem>? = null
+) : Serializable, Saveable {
@Suppress("UNNECESSARY_LATEINIT")
@Transient
@Transient
private lateinit var fallbackItem: InventoryItem
+ private val _items: Array<InventoryItem>
+
init {
fallbackItem = gameItemsHolder.fallbackItem.toInventoryItem()
this.tooltipManager = tooltipManager
if (size < 0 || hotbarSize < 0 || hotbarSize > size) {
throw IllegalArgumentException("Invalid inventory sizes: hotbarSize=$hotbarSize; size=$size")
}
- }
- private val _items = Array(size) { InventoryItem(gameItemsHolder.fallbackItem) }
+ _items = Array(size) { index -> initialItems?.getOrNull(index) ?: InventoryItem(gameItemsHolder.fallbackItem) }
+ }
val items get() = _items.asList() as MutableList<InventoryItem>
val hotbarItems get() = items.subList(0, hotbarSize)
- var activeSlot = 0
+ private var _activeSlot = 0
+
+ var activeSlot
+ get() = _activeSlot
set(value) {
if (value in 0 ..< hotbarSize) {
- field = value
+ _activeSlot = value
showCurrentItemTooltip()
}
}
_items[i] = fallbackItem
}
}
+
+ override fun getSaveData(): SaveDataDto.InventorySaveData {
+ return SaveDataDto.InventorySaveData(
+ version = SAVE_DATA_VERSION,
+ size = size,
+ hotbarSize = hotbarSize,
+ activeSlot = _activeSlot,
+ items = items.map(InventoryItem::getSaveData)
+ )
+ }
+
+ companion object {
+ private const val SAVE_DATA_VERSION = 1
+
+ fun fromSaveData(
+ saveData: SaveDataDto.InventorySaveData,
+ gameItemsHolder: GameItemsHolder,
+ tooltipManager: TooltipManager,
+ ): Inventory {
+ saveData.verifyVersion(SAVE_DATA_VERSION)
+
+ return Inventory(
+ size = saveData.size,
+ hotbarSize = saveData.hotbarSize,
+ gameItemsHolder = gameItemsHolder,
+ tooltipManager = tooltipManager,
+ initialItems = saveData.items.map { item -> InventoryItem.fromSaveData(item, gameItemsHolder) }
+ ).apply {
+ _activeSlot = saveData.activeSlot
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/player/Player.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/player/Player.java
index 8ea87c11d8b42868a4dc393c9f100689496b4788..f454b0c4d05a978e3a88d4387710d7535905e718 100644 (file)
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
+import org.jetbrains.annotations.NotNull;
import ru.deadsoftware.cavedroid.game.GameItemsHolder;
import ru.deadsoftware.cavedroid.game.mobs.Mob;
import ru.deadsoftware.cavedroid.game.mobs.MobsController;
import ru.deadsoftware.cavedroid.game.model.block.Block;
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto;
import ru.deadsoftware.cavedroid.game.model.item.InventoryItem;
import ru.deadsoftware.cavedroid.game.model.item.Item;
import ru.deadsoftware.cavedroid.game.objects.drop.Drop;
public class Player extends Mob {
+ private static final int SAVE_DATA_VERSION = 1;
+
private static final float SPEED = 69.072f;
private static final float JUMP_VELOCITY = -133.332f;
private static final int SURVIVAL_CURSOR_RANGE = 4;
private float hitAnim = 0f;
private float hitAnimDelta = ANIMATION_SPEED;
- public final Inventory inventory;
+ public Inventory inventory;
public int gameMode;
public boolean swim;
SpriteUtilsKt.drawSprite(spriteBatch, frontHand, x + 2, y + 8, frontHandAnim);
}
+ @NotNull
+ @Override
+ public SaveDataDto.PlayerSaveData getSaveData() {
+ return new SaveDataDto.PlayerSaveData(
+ SAVE_DATA_VERSION,
+ mType,
+ mAnimDelta,
+ mAnim,
+ mDirection,
+ mDead,
+ mCanJump,
+ mFlyMode,
+ mMaxHealth,
+ mHealth,
+ x,
+ y,
+ width,
+ height,
+ getVelocity().x,
+ getVelocity().y,
+ hitting,
+ hittingWithDamage,
+ hitAnim,
+ hitAnimDelta,
+ inventory.getSaveData(),
+ gameMode,
+ swim,
+ headRotation,
+ blockDamage,
+ cursorX,
+ cursorY,
+ spawnPoint != null ? spawnPoint.x : 0f,
+ spawnPoint != null ? spawnPoint.y : 0f,
+ controlMode
+ );
+ }
+
+ public static Player fromSaveData(
+ SaveDataDto.PlayerSaveData saveData,
+ GameItemsHolder gameItemsHolder,
+ TooltipManager tooltipManager
+ ) {
+ saveData.verifyVersion(SAVE_DATA_VERSION);
+
+ Player player = new Player(gameItemsHolder, tooltipManager);
+
+ player.mType = saveData.getType();
+ player.mAnimDelta = saveData.getAnimDelta();
+ player.mAnim = saveData.getAnim();
+ player.mDirection = saveData.getDirection();
+ player.mDead = saveData.getDead();
+ player.mCanJump = saveData.getCanJump();
+ player.mFlyMode = saveData.getFlyMode();
+ player.mMaxHealth = saveData.getMaxHealth();
+ player.mHealth = saveData.getHealth();
+ player.x = saveData.getX();
+ player.y = saveData.getY();
+ player.width = saveData.getWidth();
+ player.height = saveData.getHeight();
+ player.hitting = saveData.getHitting();
+ player.hittingWithDamage = saveData.getHittingWithDamage();
+ player.hitAnim = saveData.getHitAnim();
+ player.hitAnimDelta = saveData.getHitAnimDelta();
+ player.inventory = Inventory.Companion.fromSaveData(saveData.getInventory(), gameItemsHolder, tooltipManager);
+ player.gameMode = saveData.getGameMode();
+ player.swim = saveData.getSwim();
+ player.headRotation = saveData.getHeadRotation();
+ player.blockDamage = saveData.getBlockDamage();
+ player.cursorX = saveData.getCursorX();
+ player.cursorY = saveData.getCursorY();
+ player.spawnPoint = new Vector2(saveData.getSpawnPointX(), saveData.getSpawnPointY());
+ player.controlMode = saveData.getControlMode();
+
+ return player;
+ }
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/dto/SaveDataDto.kt b/core/src/ru/deadsoftware/cavedroid/game/model/dto/SaveDataDto.kt
--- /dev/null
@@ -0,0 +1,191 @@
+package ru.deadsoftware.cavedroid.game.model.dto
+
+import kotlinx.serialization.Contextual
+import kotlinx.serialization.Serializable
+import ru.deadsoftware.cavedroid.game.mobs.Mob
+import ru.deadsoftware.cavedroid.game.mobs.player.Player.ControlMode
+
+@Serializable
+sealed class SaveDataDto {
+
+ abstract val version: Int
+
+ fun verifyVersion(expectedVersion: Int) {
+ require(version == expectedVersion) {
+ "${this::class.simpleName} version mismatch ($version != $expectedVersion)"
+ }
+ }
+
+ @Serializable
+ sealed class ContainerSaveDataDto : SaveDataDto() {
+ abstract val size: Int
+ abstract val items: List<InventoryItemSaveData>
+ }
+
+ @Serializable
+ sealed class RectangleObjectSaveDataDto : SaveDataDto() {
+ abstract val x: Float
+ abstract val y: Float
+ abstract val width: Float
+ abstract val height: Float
+ abstract val velocityX: Float
+ abstract val velocityY: Float
+ }
+
+ @Serializable
+ sealed class MobSaveDataDto : RectangleObjectSaveDataDto() {
+ abstract val type: Mob.Type
+ abstract val animDelta: Int
+ abstract val anim: Float
+ abstract val direction: Mob.Direction
+ abstract val dead: Boolean
+ abstract val canJump: Boolean
+ abstract val flyMode: Boolean
+ abstract val maxHealth: Int
+ abstract val health: Int
+ }
+
+ @Serializable
+ data class InventoryItemSaveData(
+ override val version: Int,
+ val itemKey: String,
+ val amount: Int,
+ ) : SaveDataDto()
+
+ @Serializable
+ data class InventorySaveData(
+ override val version: Int,
+ override val size: Int,
+ val hotbarSize: Int,
+ val activeSlot: Int,
+ override val items: List<InventoryItemSaveData>,
+ ) : ContainerSaveDataDto()
+
+ @Serializable
+ data class FurnaceSaveData(
+ override val version: Int,
+ override val size: Int,
+ val currentFuelItemKey: String?,
+ override val items: List<InventoryItemSaveData>,
+ val startBurnTimeMs: Long,
+ val startSmeltTimeMs: Long,
+ val burnProgress: Float,
+ val smeltProgress: Float,
+ ) : ContainerSaveDataDto()
+
+ @Serializable
+ data class ChestSaveData(
+ override val version: Int,
+ override val size: Int,
+ override val items: List<InventoryItemSaveData>
+ ) : ContainerSaveDataDto()
+
+ @Serializable
+ data class ContainerControllerSaveData(
+ override val version: Int,
+ val containerMap: Map<String, @Contextual ContainerSaveDataDto>,
+ ): SaveDataDto()
+
+ @Serializable
+ data class DropSaveData(
+ override val version: Int,
+ override val x: Float,
+ override val y: Float,
+ override val width: Float,
+ override val height: Float,
+ override val velocityX: Float,
+ override val velocityY: Float,
+ val itemKey: String,
+ val amount: Int,
+ val pickedUp: Boolean
+ ) : RectangleObjectSaveDataDto()
+
+ @Serializable
+ data class DropControllerSaveData(
+ override val version: Int,
+ val drops: List<DropSaveData>
+ ) : SaveDataDto()
+
+ @Serializable
+ data class PigSaveData(
+ override val version: Int,
+ override val x: Float,
+ override val y: Float,
+ override val width: Float,
+ override val height: Float,
+ override val velocityX: Float,
+ override val velocityY: Float,
+ override val type: Mob.Type,
+ override val animDelta: Int,
+ override val anim: Float,
+ override val direction: Mob.Direction,
+ override val dead: Boolean,
+ override val canJump: Boolean,
+ override val flyMode: Boolean,
+ override val maxHealth: Int,
+ override val health: Int,
+ ) : MobSaveDataDto()
+
+ @Serializable
+ data class FallingBlockSaveData(
+ override val version: Int,
+ override val x: Float,
+ override val y: Float,
+ override val width: Float,
+ override val height: Float,
+ override val velocityX: Float,
+ override val velocityY: Float,
+ override val type: Mob.Type,
+ override val animDelta: Int,
+ override val anim: Float,
+ override val direction: Mob.Direction,
+ override val dead: Boolean,
+ override val canJump: Boolean,
+ override val flyMode: Boolean,
+ override val maxHealth: Int,
+ override val health: Int,
+ val blockKey: String,
+ ) : MobSaveDataDto()
+
+ @Serializable
+ data class PlayerSaveData(
+ override val version: Int,
+ override val type: Mob.Type,
+ override val animDelta: Int,
+ override val anim: Float,
+ override val direction: Mob.Direction,
+ override val dead: Boolean,
+ override val canJump: Boolean,
+ override val flyMode: Boolean,
+ override val maxHealth: Int,
+ override val health: Int,
+ override val x: Float,
+ override val y: Float,
+ override val width: Float,
+ override val height: Float,
+ override val velocityX: Float,
+ override val velocityY: Float,
+ val hitting: Boolean,
+ val hittingWithDamage: Boolean,
+ val hitAnim: Float,
+ val hitAnimDelta: Float,
+ val inventory: InventorySaveData,
+ val gameMode: Int,
+ val swim: Boolean,
+ val headRotation: Float,
+ val blockDamage: Float,
+ val cursorX: Int,
+ val cursorY: Int,
+ val spawnPointX: Float,
+ val spawnPointY: Float,
+ val controlMode: ControlMode,
+ ) : MobSaveDataDto()
+
+ @Serializable
+ data class MobsControllerSaveData(
+ override val version: Int,
+ val mobs: List<@Contextual MobSaveDataDto>,
+ val player: PlayerSaveData,
+ ) : SaveDataDto()
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/item/InventoryItem.kt b/core/src/ru/deadsoftware/cavedroid/game/model/item/InventoryItem.kt
index 3cd406b731f5e2e0dc8a128926a4baef24d5a246..71909880fde87171235d4e665c39ca1f4837fad2 100644 (file)
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
import ru.deadsoftware.cavedroid.misc.Assets
+import ru.deadsoftware.cavedroid.misc.Saveable
import ru.deadsoftware.cavedroid.misc.utils.drawSprite
import ru.deadsoftware.cavedroid.misc.utils.drawString
import ru.deadsoftware.cavedroid.misc.utils.px
class InventoryItem @JvmOverloads constructor(
val itemKey: String,
_amount: Int = 1,
-) : Serializable {
+) : Serializable, Saveable {
var amount = _amount
set(value) {
}
}
+ override fun getSaveData(): SaveDataDto.InventoryItemSaveData {
+ return SaveDataDto.InventoryItemSaveData(
+ version = SAVE_DATA_VERSION,
+ itemKey = itemKey,
+ amount = amount,
+ )
+ }
+
companion object {
+ private const val SAVE_DATA_VERSION = 1
@OptIn(ExperimentalContracts::class)
fun InventoryItem?.isNoneOrNull(): Boolean {
contract { returns(false) implies(this@isNoneOrNull != null) }
return this?.item == null || this.item.isNone()
}
+
+
+ fun fromSaveData(
+ saveData: SaveDataDto.InventoryItemSaveData,
+ gameItemsHolder: GameItemsHolder? = null
+ ): InventoryItem {
+ saveData.verifyVersion(SAVE_DATA_VERSION)
+
+ val inventoryItem = InventoryItem(saveData.itemKey, saveData.amount)
+ gameItemsHolder?.let(inventoryItem::init)
+
+ return inventoryItem
+ }
}
+
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/container/Chest.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/container/Chest.kt
index be820bc3e2a19a73cf70b35ded134bd6683b82ae..a9e893be3cbad21c09b3eb81c9c40167c95b92e2 100644 (file)
package ru.deadsoftware.cavedroid.game.objects.container
import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.misc.Saveable
-class Chest(gameItemsHolder: GameItemsHolder) : Container(SIZE, gameItemsHolder) {
+class Chest @JvmOverloads constructor(
+ gameItemsHolder: GameItemsHolder,
+ initialItems: List<InventoryItem>? = null
+) : Container(SIZE, gameItemsHolder, initialItems), Saveable {
override fun update(gameItemsHolder: GameItemsHolder) {
// no-op
}
+ override fun getSaveData(): SaveDataDto.ChestSaveData {
+ return SaveDataDto.ChestSaveData(
+ version = SAVE_DATA_VERSION,
+ size = size,
+ items = items.map(InventoryItem::getSaveData)
+ )
+ }
+
companion object {
+ private const val SAVE_DATA_VERSION = 1
private const val SIZE = 27
+
+ fun fromSaveData(saveData: SaveDataDto.ChestSaveData, gameItemsHolder: GameItemsHolder): Chest {
+ saveData.verifyVersion(SAVE_DATA_VERSION)
+
+ return Chest(
+ gameItemsHolder = gameItemsHolder,
+ initialItems = saveData.items.map { item -> InventoryItem.fromSaveData(item, gameItemsHolder) }
+ )
+ }
}
}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/container/Container.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/container/Container.kt
index 1e13c640cda5f1cdd8e64dc816e81881b85a6504..05ec7444fd79243e9ebbd35d129de5d668dd2f85 100644 (file)
package ru.deadsoftware.cavedroid.game.objects.container
import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.misc.Saveable
import java.io.Serializable
import javax.annotation.OverridingMethodsMustInvokeSuper
-abstract class Container(
+abstract class Container @JvmOverloads constructor(
val size: Int,
- gameItemsHolder: GameItemsHolder
-) : Serializable {
+ gameItemsHolder: GameItemsHolder,
+ initialItems: List<InventoryItem>? = null,
+) : Serializable, Saveable {
- private val _items = Array(size) { gameItemsHolder.fallbackItem.toInventoryItem() }
+ private val _items = Array(size) { index ->
+ initialItems?.getOrNull(index) ?: gameItemsHolder.fallbackItem.toInventoryItem()
+ }
val items get() = _items.asList() as MutableList<InventoryItem>
abstract fun update(gameItemsHolder: GameItemsHolder)
+ abstract override fun getSaveData(): SaveDataDto.ContainerSaveDataDto
+
+ companion object {
+ fun fromSaveData(saveData: SaveDataDto.ContainerSaveDataDto, gameItemsHolder: GameItemsHolder): Container {
+ return when (saveData) {
+ is SaveDataDto.FurnaceSaveData -> Furnace.fromSaveData(saveData, gameItemsHolder)
+ is SaveDataDto.ChestSaveData -> Chest.fromSaveData(saveData, gameItemsHolder)
+
+ is SaveDataDto.InventorySaveData -> {
+ throw IllegalArgumentException("Cannot load Container from InventorySaveData")
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/container/ContainerController.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/container/ContainerController.kt
index 3facb6f542e980806b3cc03e4e58e7280de7b98f..bffb846d6116ef42439a8032e156ea19fb393e23 100644 (file)
import ru.deadsoftware.cavedroid.game.GameItemsHolder
import ru.deadsoftware.cavedroid.game.GameScope
import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
import ru.deadsoftware.cavedroid.game.model.item.InventoryItem.Companion.isNoneOrNull
import ru.deadsoftware.cavedroid.game.objects.drop.DropController
+import ru.deadsoftware.cavedroid.misc.Saveable
import ru.deadsoftware.cavedroid.misc.utils.px
import java.io.Serializable
import javax.inject.Inject
class ContainerController @Inject constructor(
_dropController: DropController,
_gameItemsHolder: GameItemsHolder
-) : Serializable {
+) : Serializable, Saveable {
@Suppress("UNNECESSARY_LATEINIT")
@Transient
}
}
+ override fun getSaveData(): SaveDataDto.ContainerControllerSaveData {
+ return SaveDataDto.ContainerControllerSaveData(
+ version = SAVE_DATA_VERSION,
+ containerMap = containerMap.mapValues { (_, container) -> container.getSaveData() },
+ )
+ }
+
companion object {
+ private const val SAVE_DATA_VERSION = 1
private const val TAG = "ContainerController"
+
+ fun fromSaveData(
+ saveData: SaveDataDto.ContainerControllerSaveData,
+ dropController: DropController,
+ gameItemsHolder: GameItemsHolder
+ ): ContainerController {
+ saveData.verifyVersion(SAVE_DATA_VERSION)
+
+ return ContainerController(
+ dropController,
+ gameItemsHolder
+ ).apply {
+ containerMap.putAll(
+ saveData.containerMap.mapValues { (_, containerSaveData) ->
+ Container.fromSaveData(containerSaveData, gameItemsHolder)
+ }
+ )
+ }
+ }
}
}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/container/Furnace.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/container/Furnace.kt
index 1a75e89f83657b8222c2a7ba5e2e54f17bcf0508..92eeab9eae5dcb239746a19c66e7139fd0e54ca9 100644 (file)
import com.badlogic.gdx.math.MathUtils
import com.badlogic.gdx.utils.TimeUtils
import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
import ru.deadsoftware.cavedroid.game.model.item.InventoryItem.Companion.isNoneOrNull
import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.misc.Saveable
+
+class Furnace @JvmOverloads constructor(
+ gameItemsHolder: GameItemsHolder,
+ initialItems: List<InventoryItem>? = null
+) : Container(SIZE, gameItemsHolder, initialItems), Saveable {
-class Furnace(gameItemsHolder: GameItemsHolder) : Container(SIZE, gameItemsHolder) {
-
var fuel: InventoryItem
get() = items[FUEL_INDEX]
set(value) {
}
fun canSmelt(): Boolean {
- return (result.isNoneOrNull() || (result.item.params.key == input.item.params.smeltProductKey) )&&
+ return (result.isNoneOrNull() || (result.item.params.key == input.item.params.smeltProductKey)) &&
!input.isNoneOrNull() && input.item.params.smeltProductKey != null &&
(!fuel.isNoneOrNull() || burnProgress > 0f)
}
@@ -118,7 +123,7 @@ class Furnace(gameItemsHolder: GameItemsHolder) : Container(SIZE, gameItemsHolde
if (isActive && smeltProgress >= 1f) {
val productKey = requireNotNull(input.item.params.smeltProductKey)
val res = gameItemsHolder.getItem(productKey)
- if (result.isNoneOrNull()) {
+ if (result.isNoneOrNull()) {
result = res.toInventoryItem()
} else {
result.add()
@@ -132,7 +137,21 @@ class Furnace(gameItemsHolder: GameItemsHolder) : Container(SIZE, gameItemsHolde
}
}
+ override fun getSaveData(): SaveDataDto.FurnaceSaveData {
+ return SaveDataDto.FurnaceSaveData(
+ version = SAVE_DATA_VERSION,
+ size = size,
+ currentFuelItemKey = currentFuelKey,
+ items = items.map(InventoryItem::getSaveData),
+ startBurnTimeMs = startBurnTimeMs,
+ startSmeltTimeMs = smeltStarTimeMs,
+ burnProgress = burnProgress,
+ smeltProgress = smeltProgress,
+ )
+ }
+
companion object {
+ private const val SAVE_DATA_VERSION = 1
private const val SIZE = 3
private const val TAG = "Furnace"
@@ -141,6 +160,23 @@ class Furnace(gameItemsHolder: GameItemsHolder) : Container(SIZE, gameItemsHolde
const val RESULT_INDEX = 2
const val SMELTING_TIME_MS = 10000L
+
+ fun fromSaveData(saveData: SaveDataDto.FurnaceSaveData, gameItemsHolder: GameItemsHolder): Furnace {
+ saveData.verifyVersion(SAVE_DATA_VERSION)
+
+ return Furnace(
+ gameItemsHolder = gameItemsHolder,
+ initialItems = saveData.items.map { item ->
+ InventoryItem.fromSaveData(item, gameItemsHolder)
+ }
+ ).apply {
+ currentFuelKey = saveData.currentFuelItemKey
+ startBurnTimeMs = saveData.startSmeltTimeMs
+ smeltStarTimeMs = saveData.startSmeltTimeMs
+ burnProgress = saveData.burnProgress
+ smeltProgress = saveData.smeltProgress
+ }
+ }
}
}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/drop/Drop.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/drop/Drop.kt
index edb3c81ff657a63fd41b865e4dbaea9a7dc01230..548c44a5bbfb7a12fa0237dfa56d94cc2402d8a4 100644 (file)
import com.badlogic.gdx.math.Rectangle
import com.badlogic.gdx.math.Vector2
import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.misc.Saveable
class Drop @JvmOverloads constructor(
x: Float,
y: Float,
_item: Item,
_amount: Int = 1,
-) : Rectangle(x, y, DROP_SIZE, DROP_SIZE) {
+) : Rectangle(x, y, DROP_SIZE, DROP_SIZE), Saveable {
var amount: Int = _amount
private set
)
}
+ override fun getSaveData(): SaveDataDto.DropSaveData {
+ return SaveDataDto.DropSaveData(
+ version = SAVE_DATA_VERSION,
+ x = x,
+ y = y,
+ width = width,
+ height = height,
+ velocityX = velocity.x,
+ velocityY = velocity.y,
+ itemKey = itemKey,
+ amount = amount,
+ pickedUp = pickedUp
+ )
+ }
+
companion object {
+ private const val SAVE_DATA_VERSION = 1
+
private const val MAGNET_DISTANCE = 8f
const val MAGNET_VELOCITY = 256f
const val DROP_SIZE = 8f
private fun getInitialVelocity(): Vector2 = Vector2(0f, -1f)
+
+ fun fromSaveData(saveData: SaveDataDto.DropSaveData, gameItemsHolder: GameItemsHolder): Drop {
+ saveData.verifyVersion(SAVE_DATA_VERSION)
+
+ return Drop(
+ x = saveData.x,
+ y = saveData.y,
+ _item = gameItemsHolder.getItem(saveData.itemKey),
+ _amount = saveData.amount,
+ ).apply {
+ velocity.x = saveData.velocityX
+ velocity.y = saveData.velocityY
+ pickedUp = saveData.pickedUp
+ }
+ }
}
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/drop/DropController.java b/core/src/ru/deadsoftware/cavedroid/game/objects/drop/DropController.java
index 0213c971cbed124b6787a9fec6c570636ff8e47b..f8299139a2723cf9565b56670b74f4fef0ad7e82 100644 (file)
import org.jetbrains.annotations.NotNull;
import ru.deadsoftware.cavedroid.game.GameItemsHolder;
import ru.deadsoftware.cavedroid.game.GameScope;
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto;
import ru.deadsoftware.cavedroid.game.model.item.InventoryItem;
import ru.deadsoftware.cavedroid.game.model.item.Item;
+import ru.deadsoftware.cavedroid.misc.Saveable;
import javax.inject.Inject;
import java.io.Serializable;
import java.util.LinkedList;
@GameScope
-public class DropController implements Serializable {
+public class DropController implements Serializable, Saveable {
+
+ private static final int SAVE_DATA_VERSION = 1;
public interface Callback {
void run(Drop drop);
return mDrops.iterator();
}
+ @Override
+ @NotNull
+ public SaveDataDto.DropControllerSaveData getSaveData() {
+ final LinkedList<SaveDataDto.DropSaveData> dropSaveData = new LinkedList<>();
+ for (Drop drop : mDrops) {
+ dropSaveData.add(drop.getSaveData());
+ }
+ return new SaveDataDto.DropControllerSaveData(SAVE_DATA_VERSION, dropSaveData);
+ }
+
+ public static DropController fromSaveData(
+ SaveDataDto.DropControllerSaveData saveData, GameItemsHolder gameItemsHolder) {
+ saveData.verifyVersion(SAVE_DATA_VERSION);
+
+ DropController controller = new DropController();
+
+ for (SaveDataDto.DropSaveData dropSaveData : saveData.getDrops()) {
+ controller.mDrops.add(Drop.Companion.fromSaveData(dropSaveData, gameItemsHolder));
+ }
+
+ return controller;
+ }
+
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/save/GameSaveData.kt b/core/src/ru/deadsoftware/cavedroid/game/save/GameSaveData.kt
--- /dev/null
@@ -0,0 +1,55 @@
+package ru.deadsoftware.cavedroid.game.save
+
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.objects.container.ContainerController
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController
+
+class GameSaveData(
+ private var mobsController: MobsController?,
+ private var dropController: DropController?,
+ private var containerController: ContainerController?,
+ private var foreMap: Array<Array<Block>>?,
+ private var backMap: Array<Array<Block>>?
+) {
+
+ fun retrieveMobsController(): MobsController {
+ val value = requireNotNull(mobsController)
+ mobsController = null
+ return value
+ }
+
+ fun retrieveDropController(): DropController {
+ val value = requireNotNull(dropController)
+ dropController = null
+ return value
+ }
+
+ fun retrieveContainerController(): ContainerController {
+ val value = requireNotNull(containerController)
+ containerController = null
+ return value
+ }
+
+ fun retrieveForeMap(): Array<Array<Block>> {
+ val value = requireNotNull(foreMap)
+ foreMap = null
+ return value
+ }
+
+ fun retrieveBackMap(): Array<Array<Block>> {
+ val value = requireNotNull(backMap)
+ backMap = null
+ return value
+ }
+
+ fun isEmpty(): Boolean {
+ return mobsController == null &&
+ dropController == null &&
+ containerController == null &&
+ foreMap == null &&
+ backMap == null
+ }
+
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/save/GameSaveLoader.kt b/core/src/ru/deadsoftware/cavedroid/game/save/GameSaveLoader.kt
--- /dev/null
@@ -0,0 +1,255 @@
+package ru.deadsoftware.cavedroid.game.save
+
+import com.badlogic.gdx.Gdx
+import com.badlogic.gdx.files.FileHandle
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.decodeFromByteArray
+import kotlinx.serialization.encodeToByteArray
+import kotlinx.serialization.protobuf.ProtoBuf
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+import ru.deadsoftware.cavedroid.game.objects.container.ContainerController
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import java.nio.ByteBuffer
+import java.util.zip.GZIPInputStream
+import java.util.zip.GZIPOutputStream
+
+@OptIn(ExperimentalSerializationApi::class)
+object GameSaveLoader {
+
+ private const val MAP_SAVE_VERSION: UByte = 2u
+
+ private const val SAVES_DIR = "/saves"
+ private const val DROP_FILE = "/drop.dat"
+ private const val MOBS_FILE = "/mobs.dat"
+ private const val CONTAINERS_FILE = "/containers.dat"
+ private const val DICT_FILE = "/dict"
+ private const val FOREMAP_FILE = "/foremap.dat.gz"
+ private const val BACKMAP_FILE = "/backmap.dat.gz"
+
+ private fun Int.toByteArray(): ByteArray {
+ return ByteBuffer.allocate(Int.SIZE_BYTES)
+ .putInt(this)
+ .array()
+ }
+
+ private fun Short.toByteArray(): ByteArray {
+ return ByteBuffer.allocate(Short.SIZE_BYTES)
+ .putShort(this)
+ .array()
+ }
+
+ private fun buildBlocksDictionary(
+ foreMap: Array<Array<Block>>,
+ backMap: Array<Array<Block>>
+ ): Map<String, Int> {
+ val maps = sequenceOf(foreMap.asSequence(), backMap.asSequence())
+
+ return maps.flatten()
+ .flatMap(Array<Block>::asSequence)
+ .toSet()
+ .mapIndexed { index, block -> block.params.key to index }
+ .toMap()
+ }
+
+ private fun saveDict(file: FileHandle, dict: Map<String, Int>) {
+ val result = dict.asSequence()
+ .sortedBy { it.value }
+ .joinToString(separator = "\n") { it.key }
+ .encodeToByteArray()
+
+ file.writeBytes(result, false)
+ }
+
+ private fun compressMap(map: Array<Array<Block>>, dict: Map<String, Int>): ByteArray {
+ if (dict.size > 0xff) {
+ throw IllegalArgumentException("Cannot save this map as bytes")
+ }
+
+ val width = map.size
+ val height = map[0].size
+
+ val blocks = sequence {
+ for (y in 0 ..< height) {
+ for (x in 0 ..< width) {
+ yield(map[x][y])
+ }
+ }
+ }
+
+ val result = sequence {
+ var run = 0
+ var runValue: UByte? = null
+
+ yield(MAP_SAVE_VERSION.toByte())
+ width.toByteArray().forEach { yield(it) }
+ height.toByteArray().forEach { yield(it) }
+
+ blocks.forEach { block ->
+ val key = block.params.key
+
+ val blockId = dict[key]?.toUByte()
+ ?: throw IllegalArgumentException("Dictionary does not contain key $key")
+
+ if (blockId != runValue || run == Int.MAX_VALUE) {
+ if (run > 0 && runValue != null) {
+ run.toByteArray().forEach { yield(it) }
+ yield(runValue!!.toByte())
+ }
+ run = 1
+ runValue = blockId
+ } else {
+ run++
+ }
+ }
+
+ run.toByteArray().forEach { yield(it) }
+ yield(runValue!!.toByte())
+ }
+
+ return result.toList().toByteArray()
+ }
+
+ private fun decompressMap(
+ bytes: ByteArray,
+ dict: List<String>,
+ gameItemsHolder: GameItemsHolder
+ ): Array<Array<Block>> {
+ val version = bytes.first().toUByte()
+ require(version == MAP_SAVE_VERSION)
+
+ val width = ByteBuffer.wrap(bytes, 1, Int.SIZE_BYTES).getInt()
+ val height = ByteBuffer.wrap(bytes, 1 + Int.SIZE_BYTES, Int.SIZE_BYTES).getInt()
+
+ val blocks = buildList {
+ for (i in 1 + (Int.SIZE_BYTES shl 1) .. bytes.lastIndex step Int.SIZE_BYTES + 1) {
+ val run = ByteBuffer.wrap(bytes, i, Int.SIZE_BYTES).getInt()
+ val blockId = bytes[i + Int.SIZE_BYTES].toUByte().toInt()
+
+ for (j in 0 ..< run) {
+ add(gameItemsHolder.getBlock(dict[blockId]))
+ }
+ }
+ }
+
+ return Array(width) { x ->
+ Array(height) { y ->
+ blocks[x + y * width]
+ }
+ }
+ }
+
+ private fun loadMap(
+ gameItemsHolder: GameItemsHolder,
+ savesPath: String
+ ): Pair<Array<Array<Block>>, Array<Array<Block>>> {
+ val dict = Gdx.files.absolute("$savesPath$DICT_FILE").readString().split("\n")
+
+ val foreMap: Array<Array<Block>>
+ with(GZIPInputStream(Gdx.files.absolute("$savesPath$FOREMAP_FILE").read())) {
+ foreMap = decompressMap(readBytes(), dict, gameItemsHolder)
+ close()
+ }
+
+ val backMap: Array<Array<Block>>
+ with(GZIPInputStream(Gdx.files.absolute("$savesPath$BACKMAP_FILE").read())) {
+ backMap = decompressMap(readBytes(), dict, gameItemsHolder)
+ close()
+ }
+
+ return foreMap to backMap
+ }
+
+ private fun saveMap(gameWorld: GameWorld, savesPath: String) {
+ val fullForeMap = gameWorld.fullForeMap
+ val fullBackMap = gameWorld.fullBackMap
+
+ val dict = buildBlocksDictionary(fullForeMap, fullBackMap)
+
+ saveDict(Gdx.files.absolute("$savesPath$DICT_FILE"), dict)
+
+ with(GZIPOutputStream(Gdx.files.absolute("$savesPath$FOREMAP_FILE").write(false))) {
+ write(compressMap(fullForeMap, dict))
+ close()
+ }
+
+ with(GZIPOutputStream(Gdx.files.absolute("$savesPath$BACKMAP_FILE").write(false))) {
+ write(compressMap(fullBackMap, dict))
+ close()
+ }
+ }
+
+ fun load(
+ mainConfig: MainConfig,
+ gameItemsHolder: GameItemsHolder,
+ tooltipManager: TooltipManager
+ ): GameSaveData {
+ val gameFolder = mainConfig.gameFolder
+ val savesPath = "$gameFolder$SAVES_DIR"
+
+ val dropFile = Gdx.files.absolute("$savesPath$DROP_FILE")
+ val mobsFile = Gdx.files.absolute("$savesPath$MOBS_FILE")
+ val containersFile = Gdx.files.absolute("$savesPath$CONTAINERS_FILE")
+
+ val dropBytes = dropFile.readBytes()
+ val mobsBytes = mobsFile.readBytes()
+ val containersBytes = containersFile.readBytes()
+
+ val dropController = ProtoBuf.decodeFromByteArray<SaveDataDto.DropControllerSaveData>(dropBytes)
+ .let { saveData -> DropController.fromSaveData(saveData, gameItemsHolder) }
+ val mobsController = ProtoBuf.decodeFromByteArray<SaveDataDto.MobsControllerSaveData>(mobsBytes)
+ .let { saveData -> MobsController.fromSaveData(saveData, gameItemsHolder, tooltipManager) }
+ val containerController = ProtoBuf.decodeFromByteArray<SaveDataDto.ContainerControllerSaveData>(containersBytes)
+ .let { saveData -> ContainerController.fromSaveData(saveData, dropController, gameItemsHolder) }
+
+ val (foreMap, backMap) = loadMap(gameItemsHolder, savesPath)
+
+ return GameSaveData(mobsController, dropController, containerController, foreMap, backMap)
+ }
+
+ fun save(
+ mainConfig: MainConfig,
+ dropController: DropController,
+ mobsController: MobsController,
+ containerController: ContainerController,
+ gameWorld: GameWorld
+ ) {
+ val gameFolder = mainConfig.gameFolder
+ val savesPath = "$gameFolder$SAVES_DIR"
+
+ Gdx.files.absolute(savesPath).mkdirs()
+
+ val dropFile = Gdx.files.absolute("$savesPath$DROP_FILE")
+ val mobsFile = Gdx.files.absolute("$savesPath$MOBS_FILE")
+ val containersFile = Gdx.files.absolute("$savesPath$CONTAINERS_FILE")
+
+ val dropBytes = ProtoBuf.encodeToByteArray(dropController.getSaveData())
+ val mobsBytes = ProtoBuf.encodeToByteArray(mobsController.getSaveData())
+ val containersBytes = ProtoBuf.encodeToByteArray(containerController.getSaveData())
+
+ dropFile.writeBytes(dropBytes, false)
+ mobsFile.writeBytes(mobsBytes, false)
+ containersFile.writeBytes(containersBytes, false)
+
+ saveMap(gameWorld, savesPath)
+ }
+
+ fun exists(mainConfig: MainConfig): Boolean {
+ val gameFolder = mainConfig.gameFolder
+ val savesPath = "$gameFolder$SAVES_DIR"
+
+ return Gdx.files.absolute("$savesPath$DROP_FILE").exists() &&
+ Gdx.files.absolute("$savesPath$MOBS_FILE").exists() &&
+ Gdx.files.absolute("$savesPath$CONTAINERS_FILE").exists() &&
+ Gdx.files.absolute("$savesPath$DICT_FILE").exists() &&
+ Gdx.files.absolute("$savesPath$FOREMAP_FILE").exists() &&
+ Gdx.files.absolute("$savesPath$BACKMAP_FILE").exists()
+ }
+
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuMain.java b/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuMain.java
index 6a40fc534b1a5d04e5fb5d6d80c4ae94b8487777..29aeb8fb272b53f58101634c15a23e38573b48d6 100644 (file)
package ru.deadsoftware.cavedroid.menu.submenus;
-import com.badlogic.gdx.Gdx;
import ru.deadsoftware.cavedroid.MainConfig;
-import ru.deadsoftware.cavedroid.game.GameSaver;
+import ru.deadsoftware.cavedroid.game.save.GameSaveLoader;
import ru.deadsoftware.cavedroid.menu.MenuProc;
import ru.deadsoftware.cavedroid.menu.objects.Button;
import ru.deadsoftware.cavedroid.menu.objects.ButtonEventListener;
@Override
protected void initButtons() {
loadButtonsFromJson(mAssetLoader.getAssetHandle("json/menu_main_buttons.json"));
- if (GameSaver.exists(mMainConfig)) {
+ if (GameSaveLoader.INSTANCE.exists(mMainConfig)) {
getButtons().get("load_game").setType(Button.NORMAL);
}
}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/Saveable.kt b/core/src/ru/deadsoftware/cavedroid/misc/Saveable.kt
--- /dev/null
@@ -0,0 +1,7 @@
+package ru.deadsoftware.cavedroid.misc
+
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+
+interface Saveable {
+ fun getSaveData(): SaveDataDto
+}