1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-14 00:54:05 +02:00

fix save game

This commit is contained in:
XProger
2018-10-15 09:35:08 +03:00
parent b67f5f0d64
commit 2b4cf9f83d
8 changed files with 81 additions and 54 deletions

View File

@@ -330,7 +330,7 @@ struct Camera : ICamera {
if (indexA == level->cameraFramesCount - 1) { if (indexA == level->cameraFramesCount - 1) {
if (level->isCutsceneLevel()) if (level->isCutsceneLevel())
game->loadNextLevel(true); game->loadNextLevel();
else { else {
Character *lara = (Character*)owner; Character *lara = (Character*)owner;
if (lara->health > 0.0f) if (lara->health > 0.0f)

View File

@@ -49,9 +49,9 @@ struct ICamera {
struct IGame { struct IGame {
virtual ~IGame() {} virtual ~IGame() {}
virtual void loadLevel(TR::LevelID id, bool showSaveGame) {} virtual void loadLevel(TR::LevelID id) {}
virtual void loadNextLevel(bool showSaveGame) {} virtual void loadNextLevel() {}
virtual void saveGame(bool checkpoint) {} virtual void saveGame(bool checkpoint, bool updateStats) {}
virtual void loadGame(int slot) {} virtual void loadGame(int slot) {}
virtual void applySettings(const Core::Settings &settings) {} virtual void applySettings(const Core::Settings &settings) {}
@@ -1079,7 +1079,7 @@ struct Controller {
switch (fx) { switch (fx) {
case TR::Effect::ROTATE_180 : angle.y = angle.y + PI; break; case TR::Effect::ROTATE_180 : angle.y = angle.y + PI; break;
case TR::Effect::FLOOR_SHAKE : game->setEffect(this, TR::Effect::Type(fx)); break; case TR::Effect::FLOOR_SHAKE : game->setEffect(this, TR::Effect::Type(fx)); break;
case TR::Effect::FINISH_LEVEL : game->loadNextLevel(true); break; case TR::Effect::FINISH_LEVEL : game->loadNextLevel(); break;
case TR::Effect::FLIP_MAP : game->flipMap(); break; case TR::Effect::FLIP_MAP : game->flipMap(); break;
default : cmdEffect(fx); break; default : cmdEffect(fx); break;
} }

View File

@@ -188,7 +188,7 @@ namespace Game {
if (Input::down[ik5] && !level->inventory->isActive()) { if (Input::down[ik5] && !level->inventory->isActive()) {
if (level->players[0]->canSaveGame()) if (level->players[0]->canSaveGame())
level->saveGame(true); level->saveGame(true, false);
Input::down[ik5] = false; Input::down[ik5] = false;
} }

View File

@@ -754,6 +754,20 @@ namespace TR {
return VER_UNKNOWN; return VER_UNKNOWN;
} }
LevelID getNextSaveLevel(LevelID id) {
Version version = getGameVersionByLevel(id);
LevelID end = getEndId(version);
while (id != end) {
id = LevelID(id + 1);
if (!isCutsceneLevel(id))
return id;
}
return LVL_MAX;
}
void getGameLevelFile(char *dst, Version version, LevelID id) { void getGameLevelFile(char *dst, Version version, LevelID id) {
if (useEasyStart) { if (useEasyStart) {
switch (version) { switch (version) {

View File

@@ -1096,7 +1096,7 @@ struct Inventory {
Controller *controller = (Controller*)e.controller; Controller *controller = (Controller*)e.controller;
controller->deactivate(true); controller->deactivate(true);
} }
game->saveGame(index > -1); game->saveGame(index > -1, false);
} }
toggle(playerIndex, targetPage); toggle(playerIndex, targetPage);
} }
@@ -1220,7 +1220,7 @@ struct Inventory {
} }
if (!isActive() && nextLevel != TR::LVL_MAX) if (!isActive() && nextLevel != TR::LVL_MAX)
game->loadLevel(nextLevel, false); game->loadLevel(nextLevel);
} }
void chooseItem() { void chooseItem() {

View File

@@ -1788,7 +1788,7 @@ struct Lara : Character {
if (level->state.tracks[track].once) { if (level->state.tracks[track].once) {
timer += Core::deltaTime; timer += Core::deltaTime;
if (timer > 3.0f) if (timer > 3.0f)
game->loadNextLevel(false); game->loadNextLevel();
} else { } else {
if (state != STATE_WATER_OUT) if (state != STATE_WATER_OUT)
return 0; return 0;
@@ -2063,7 +2063,7 @@ struct Lara : Character {
cameraTarget = (Controller*)level->entities[cmd.args].controller; cameraTarget = (Controller*)level->entities[cmd.args].controller;
break; break;
case TR::Action::END : case TR::Action::END :
game->loadNextLevel(true); game->loadNextLevel();
break; break;
case TR::Action::SOUNDTRACK : { case TR::Action::SOUNDTRACK : {
int track = doTutorial(cmd.args); int track = doTutorial(cmd.args);

View File

@@ -69,26 +69,35 @@ struct Level : IGame {
float animTexTimer; float animTexTimer;
// IGame implementation ======== // IGame implementation ========
virtual void loadLevel(TR::LevelID id, bool showSaveGame) { virtual void loadLevel(TR::LevelID id) {
if (nextLevel != TR::LVL_MAX) return; if (nextLevel != TR::LVL_MAX) return;
sndWater = sndTrack = NULL; sndWater = sndTrack = NULL;
Sound::stopAll(); Sound::stopAll();
nextLevel = id; if (loadSlot == -1 && !TR::isCutsceneLevel(level.id)) {
// update statistics info for current level
saveGame(false, true);
// save next level
level.id = TR::getNextSaveLevel(level.id); // get next not cutscene level
if (level.id != TR::LVL_MAX) {
memset(&level.levelStats, 0, sizeof(level.levelStats));
saveGame(false, false);
loadSlot = getSaveSlot(level.id, false);
}
}
if (showSaveGame && !TR::isCutsceneLevel(nextLevel)) nextLevel = id;
invShow(camera->cameraIndex, Inventory::PAGE_SAVEGAME, -1);
} }
virtual void loadNextLevel(bool showSaveGame) { virtual void loadNextLevel() {
#ifdef _OS_WEB #ifdef _OS_WEB
if (level.id == TR::LVL_TR1_2 && level.version != TR::VER_TR1_PC) { if (level.id == TR::LVL_TR1_2 && level.version != TR::VER_TR1_PC) {
loadLevel(TR::LVL_TR1_TITLE, showSaveGame); loadLevel(TR::LVL_TR1_TITLE);
return; return;
} }
#endif #endif
loadLevel((level.isEnd() || level.isHome()) ? level.getTitleId() : TR::LevelID(level.id + 1), showSaveGame); loadLevel((level.isEnd() || level.isHome()) ? level.getTitleId() : TR::LevelID(level.id + 1));
} }
virtual void invShow(int playerIndex, int page, int itemIndex = -1) { virtual void invShow(int playerIndex, int page, int itemIndex = -1) {
@@ -103,14 +112,17 @@ struct Level : IGame {
// allocate oversized data for save slot // allocate oversized data for save slot
slot.data = new uint8[sizeof(SaveProgress) + sizeof(SaveItem) * inventory->itemsCount + // for every save slot.data = new uint8[sizeof(SaveProgress) + sizeof(SaveItem) * inventory->itemsCount + // for every save
sizeof(SaveProgress) + sizeof(SaveState) + sizeof(SaveEntity) * level.entitiesCount]; // only for checkpoints sizeof(SaveState) + sizeof(SaveEntity) * level.entitiesCount]; // only for checkpoints
uint8 *ptr = slot.data; uint8 *ptr = slot.data;
// game progress stats // level progress stats
SaveProgress *gameStats = (SaveProgress*)ptr; SaveProgress *levelStats = (SaveProgress*)ptr;
ptr += sizeof(*gameStats); ptr += sizeof(*levelStats);
*gameStats = level.gameStats; if (dummy)
memset(levelStats, 0, sizeof(*levelStats));
else
*levelStats = level.levelStats;
// inventory items // inventory items
int32 *itemsCount = (int32*)ptr; int32 *itemsCount = (int32*)ptr;
@@ -129,7 +141,7 @@ struct Level : IGame {
for (int i = 0; i < inventory->itemsCount; i++) { for (int i = 0; i < inventory->itemsCount; i++) {
Inventory::Item *invItem = inventory->items[i]; Inventory::Item *invItem = inventory->items[i];
if (!TR::Entity::isPickup(TR::Entity::convFromInv(invItem->type))) continue; if (!checkpoint && !TR::Entity::isCrossLevelItem(TR::Entity::convFromInv(invItem->type))) continue;
SaveItem *item = (SaveItem*)ptr; SaveItem *item = (SaveItem*)ptr;
ptr += sizeof(*item); ptr += sizeof(*item);
@@ -142,11 +154,6 @@ struct Level : IGame {
} }
if (checkpoint) { if (checkpoint) {
// level progress stats
SaveProgress *levelStats = (SaveProgress*)ptr;
ptr += sizeof(*levelStats);
*levelStats = level.levelStats;
// level state // level state
SaveState *state = (SaveState*)ptr; SaveState *state = (SaveState*)ptr;
ptr += sizeof(*state); ptr += sizeof(*state);
@@ -182,9 +189,9 @@ struct Level : IGame {
uint8 *data = slot.data; uint8 *data = slot.data;
uint8 *ptr = data; uint8 *ptr = data;
// game progress stats // level progress stats
level.gameStats = *(SaveProgress*)ptr; level.levelStats = *(SaveProgress*)ptr;
ptr += sizeof(level.gameStats); ptr += sizeof(level.levelStats);
// inventory items // inventory items
int32 itemsCount = *(int32*)ptr; int32 itemsCount = *(int32*)ptr;
@@ -199,10 +206,6 @@ struct Level : IGame {
if (slot.level & LVL_FLAG_CHECKPOINT) { if (slot.level & LVL_FLAG_CHECKPOINT) {
clearEntities(); clearEntities();
// level progress stats
level.levelStats = *(SaveProgress*)ptr;
ptr += sizeof(level.levelStats);
// level state // level state
level.state = *(SaveState*)ptr; level.state = *(SaveState*)ptr;
ptr += sizeof(level.state); ptr += sizeof(level.state);
@@ -235,10 +238,15 @@ struct Level : IGame {
ptr += (sizeof(SaveEntity) - sizeof(SaveEntity::Extra)) + entity->extraSize; ptr += (sizeof(SaveEntity) - sizeof(SaveEntity::Extra)) + entity->extraSize;
} }
if (level.state.flags.flipped) {
flipMap();
}
uint8 track = level.state.flags.track; uint8 track = level.state.flags.track;
level.state.flags.track = 0; level.state.flags.track = 0;
playTrack(track); playTrack(track);
} } else
memset(&level.levelStats, 0, sizeof(level.levelStats));
} }
static void saveGameWriteAsync(Stream *stream, void *userData) { static void saveGameWriteAsync(Stream *stream, void *userData) {
@@ -253,7 +261,7 @@ struct Level : IGame {
} }
} }
virtual void saveGame(bool checkpoint) { virtual void saveGame(bool checkpoint, bool updateStats) {
ASSERT(saveResult != SAVE_RESULT_WAIT); ASSERT(saveResult != SAVE_RESULT_WAIT);
if (saveResult == SAVE_RESULT_WAIT) if (saveResult == SAVE_RESULT_WAIT)
@@ -265,18 +273,22 @@ struct Level : IGame {
LOG("Save Game...\n"); LOG("Save Game...\n");
TR::LevelID id = level.id; TR::LevelID id = level.id;
if (!checkpoint) {
ASSERT(nextLevel != TR::LVL_MAX); SaveSlot slot;
id = nextLevel; if (updateStats) {
int index = getSaveSlot(id, false);
if (index == -1) {
slot = createSaveSlot(id, false, true);
saveSlots.push(slot);
} else
slot = saveSlots[index];
SaveProgress *levelStats = (SaveProgress*)slot.data;
*levelStats = level.levelStats;
} else {
removeSaveSlot(id, checkpoint); // remove checkpoints and level saves
saveSlots.push(createSaveSlot(id, checkpoint));
} }
removeSaveSlot(id, checkpoint);
TR::LevelID startID = TR::getStartId(level.version);
if (!checkpoint && getSaveSlot(startID, false) == -1)
saveSlots.push(createSaveSlot(startID, false, true)); // add first level save game
saveSlots.push(createSaveSlot(id, checkpoint));
saveSlots.sort(); saveSlots.sort();
int size; int size;
@@ -843,7 +855,8 @@ struct Level : IGame {
} }
*/ */
saveResult = SAVE_RESULT_SUCCESS; saveResult = SAVE_RESULT_SUCCESS;
parseLoadSlot(); if (loadSlot != -1 && (saveSlots[loadSlot].level & ~LVL_FLAG_CHECKPOINT) == level.id)
parseLoadSlot();
Core::resetTime(); Core::resetTime();
} }
@@ -1685,7 +1698,7 @@ struct Level : IGame {
if (level.isCutsceneLevel() && waitTrack) { if (level.isCutsceneLevel() && waitTrack) {
if (!sndTrack && TR::LEVEL_INFO[level.id].track != TR::NO_TRACK) { if (!sndTrack && TR::LEVEL_INFO[level.id].track != TR::NO_TRACK) {
if (camera->timer > 0.0f) // for the case that audio stops before animation ends if (camera->timer > 0.0f) // for the case that audio stops before animation ends
loadNextLevel(true); loadNextLevel();
return; return;
} }
@@ -1706,7 +1719,7 @@ struct Level : IGame {
int playerIndex = (Input::lastState[0] == cInventory) ? 0 : 1; int playerIndex = (Input::lastState[0] == cInventory) ? 0 : 1;
if (level.isCutsceneLevel()) { // skip cutscene level if (level.isCutsceneLevel()) { // skip cutscene level
loadNextLevel(true); loadNextLevel();
return; return;
} }
@@ -1723,12 +1736,12 @@ struct Level : IGame {
if (inventory->titleTimer > 1.0f) if (inventory->titleTimer > 1.0f)
return; return;
if (loadSlot > -1 && nextLevel == TR::LVL_MAX) { if (loadSlot > -1 && nextLevel == TR::LVL_MAX && !level.isCutsceneLevel()) {
if (inventory->isActive()) if (inventory->isActive())
return; return;
TR::LevelID id = TR::LevelID(saveSlots[loadSlot].level & ~LVL_FLAG_CHECKPOINT); TR::LevelID id = TR::LevelID(saveSlots[loadSlot].level & ~LVL_FLAG_CHECKPOINT);
loadLevel(id, false); loadLevel(id);
return; return;
} }

View File

@@ -163,7 +163,7 @@ void removeSaveSlot(TR::LevelID levelID, bool checkpoint) {
if (TR::getGameVersionByLevel(id) != version) if (TR::getGameVersionByLevel(id) != version)
continue; continue;
if ((checkpoint && isCheckpointSlot) || (!checkpoint && levelID == id)) { if (isCheckpointSlot || (!checkpoint && levelID == id)) {
delete[] slot.data; delete[] slot.data;
saveSlots.remove(i); saveSlots.remove(i);
i--; i--;