mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-16 10:04:28 +02:00
fix save game
This commit is contained in:
@@ -330,7 +330,7 @@ struct Camera : ICamera {
|
||||
|
||||
if (indexA == level->cameraFramesCount - 1) {
|
||||
if (level->isCutsceneLevel())
|
||||
game->loadNextLevel(true);
|
||||
game->loadNextLevel();
|
||||
else {
|
||||
Character *lara = (Character*)owner;
|
||||
if (lara->health > 0.0f)
|
||||
|
@@ -49,9 +49,9 @@ struct ICamera {
|
||||
|
||||
struct IGame {
|
||||
virtual ~IGame() {}
|
||||
virtual void loadLevel(TR::LevelID id, bool showSaveGame) {}
|
||||
virtual void loadNextLevel(bool showSaveGame) {}
|
||||
virtual void saveGame(bool checkpoint) {}
|
||||
virtual void loadLevel(TR::LevelID id) {}
|
||||
virtual void loadNextLevel() {}
|
||||
virtual void saveGame(bool checkpoint, bool updateStats) {}
|
||||
virtual void loadGame(int slot) {}
|
||||
virtual void applySettings(const Core::Settings &settings) {}
|
||||
|
||||
@@ -1079,7 +1079,7 @@ struct Controller {
|
||||
switch (fx) {
|
||||
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::FINISH_LEVEL : game->loadNextLevel(true); break;
|
||||
case TR::Effect::FINISH_LEVEL : game->loadNextLevel(); break;
|
||||
case TR::Effect::FLIP_MAP : game->flipMap(); break;
|
||||
default : cmdEffect(fx); break;
|
||||
}
|
||||
|
@@ -188,7 +188,7 @@ namespace Game {
|
||||
|
||||
if (Input::down[ik5] && !level->inventory->isActive()) {
|
||||
if (level->players[0]->canSaveGame())
|
||||
level->saveGame(true);
|
||||
level->saveGame(true, false);
|
||||
Input::down[ik5] = false;
|
||||
}
|
||||
|
||||
|
@@ -754,6 +754,20 @@ namespace TR {
|
||||
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) {
|
||||
if (useEasyStart) {
|
||||
switch (version) {
|
||||
|
@@ -1096,7 +1096,7 @@ struct Inventory {
|
||||
Controller *controller = (Controller*)e.controller;
|
||||
controller->deactivate(true);
|
||||
}
|
||||
game->saveGame(index > -1);
|
||||
game->saveGame(index > -1, false);
|
||||
}
|
||||
toggle(playerIndex, targetPage);
|
||||
}
|
||||
@@ -1220,7 +1220,7 @@ struct Inventory {
|
||||
}
|
||||
|
||||
if (!isActive() && nextLevel != TR::LVL_MAX)
|
||||
game->loadLevel(nextLevel, false);
|
||||
game->loadLevel(nextLevel);
|
||||
}
|
||||
|
||||
void chooseItem() {
|
||||
|
@@ -1788,7 +1788,7 @@ struct Lara : Character {
|
||||
if (level->state.tracks[track].once) {
|
||||
timer += Core::deltaTime;
|
||||
if (timer > 3.0f)
|
||||
game->loadNextLevel(false);
|
||||
game->loadNextLevel();
|
||||
} else {
|
||||
if (state != STATE_WATER_OUT)
|
||||
return 0;
|
||||
@@ -2063,7 +2063,7 @@ struct Lara : Character {
|
||||
cameraTarget = (Controller*)level->entities[cmd.args].controller;
|
||||
break;
|
||||
case TR::Action::END :
|
||||
game->loadNextLevel(true);
|
||||
game->loadNextLevel();
|
||||
break;
|
||||
case TR::Action::SOUNDTRACK : {
|
||||
int track = doTutorial(cmd.args);
|
||||
|
99
src/level.h
99
src/level.h
@@ -69,26 +69,35 @@ struct Level : IGame {
|
||||
float animTexTimer;
|
||||
|
||||
// IGame implementation ========
|
||||
virtual void loadLevel(TR::LevelID id, bool showSaveGame) {
|
||||
virtual void loadLevel(TR::LevelID id) {
|
||||
if (nextLevel != TR::LVL_MAX) return;
|
||||
|
||||
sndWater = sndTrack = NULL;
|
||||
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))
|
||||
invShow(camera->cameraIndex, Inventory::PAGE_SAVEGAME, -1);
|
||||
nextLevel = id;
|
||||
}
|
||||
|
||||
virtual void loadNextLevel(bool showSaveGame) {
|
||||
virtual void loadNextLevel() {
|
||||
#ifdef _OS_WEB
|
||||
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;
|
||||
}
|
||||
#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) {
|
||||
@@ -103,14 +112,17 @@ struct Level : IGame {
|
||||
|
||||
// allocate oversized data for save slot
|
||||
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;
|
||||
|
||||
// game progress stats
|
||||
SaveProgress *gameStats = (SaveProgress*)ptr;
|
||||
ptr += sizeof(*gameStats);
|
||||
*gameStats = level.gameStats;
|
||||
// level progress stats
|
||||
SaveProgress *levelStats = (SaveProgress*)ptr;
|
||||
ptr += sizeof(*levelStats);
|
||||
if (dummy)
|
||||
memset(levelStats, 0, sizeof(*levelStats));
|
||||
else
|
||||
*levelStats = level.levelStats;
|
||||
|
||||
// inventory items
|
||||
int32 *itemsCount = (int32*)ptr;
|
||||
@@ -129,7 +141,7 @@ struct Level : IGame {
|
||||
for (int i = 0; i < inventory->itemsCount; 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;
|
||||
ptr += sizeof(*item);
|
||||
@@ -142,11 +154,6 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
if (checkpoint) {
|
||||
// level progress stats
|
||||
SaveProgress *levelStats = (SaveProgress*)ptr;
|
||||
ptr += sizeof(*levelStats);
|
||||
*levelStats = level.levelStats;
|
||||
|
||||
// level state
|
||||
SaveState *state = (SaveState*)ptr;
|
||||
ptr += sizeof(*state);
|
||||
@@ -182,9 +189,9 @@ struct Level : IGame {
|
||||
uint8 *data = slot.data;
|
||||
uint8 *ptr = data;
|
||||
|
||||
// game progress stats
|
||||
level.gameStats = *(SaveProgress*)ptr;
|
||||
ptr += sizeof(level.gameStats);
|
||||
// level progress stats
|
||||
level.levelStats = *(SaveProgress*)ptr;
|
||||
ptr += sizeof(level.levelStats);
|
||||
|
||||
// inventory items
|
||||
int32 itemsCount = *(int32*)ptr;
|
||||
@@ -199,10 +206,6 @@ struct Level : IGame {
|
||||
if (slot.level & LVL_FLAG_CHECKPOINT) {
|
||||
clearEntities();
|
||||
|
||||
// level progress stats
|
||||
level.levelStats = *(SaveProgress*)ptr;
|
||||
ptr += sizeof(level.levelStats);
|
||||
|
||||
// level state
|
||||
level.state = *(SaveState*)ptr;
|
||||
ptr += sizeof(level.state);
|
||||
@@ -235,10 +238,15 @@ struct Level : IGame {
|
||||
ptr += (sizeof(SaveEntity) - sizeof(SaveEntity::Extra)) + entity->extraSize;
|
||||
}
|
||||
|
||||
if (level.state.flags.flipped) {
|
||||
flipMap();
|
||||
}
|
||||
|
||||
uint8 track = level.state.flags.track;
|
||||
level.state.flags.track = 0;
|
||||
playTrack(track);
|
||||
}
|
||||
} else
|
||||
memset(&level.levelStats, 0, sizeof(level.levelStats));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (saveResult == SAVE_RESULT_WAIT)
|
||||
@@ -265,18 +273,22 @@ struct Level : IGame {
|
||||
LOG("Save Game...\n");
|
||||
|
||||
TR::LevelID id = level.id;
|
||||
if (!checkpoint) {
|
||||
ASSERT(nextLevel != TR::LVL_MAX);
|
||||
id = nextLevel;
|
||||
|
||||
SaveSlot slot;
|
||||
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();
|
||||
|
||||
int size;
|
||||
@@ -843,7 +855,8 @@ struct Level : IGame {
|
||||
}
|
||||
*/
|
||||
saveResult = SAVE_RESULT_SUCCESS;
|
||||
parseLoadSlot();
|
||||
if (loadSlot != -1 && (saveSlots[loadSlot].level & ~LVL_FLAG_CHECKPOINT) == level.id)
|
||||
parseLoadSlot();
|
||||
|
||||
Core::resetTime();
|
||||
}
|
||||
@@ -1685,7 +1698,7 @@ struct Level : IGame {
|
||||
if (level.isCutsceneLevel() && waitTrack) {
|
||||
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
|
||||
loadNextLevel(true);
|
||||
loadNextLevel();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1706,7 +1719,7 @@ struct Level : IGame {
|
||||
int playerIndex = (Input::lastState[0] == cInventory) ? 0 : 1;
|
||||
|
||||
if (level.isCutsceneLevel()) { // skip cutscene level
|
||||
loadNextLevel(true);
|
||||
loadNextLevel();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1723,12 +1736,12 @@ struct Level : IGame {
|
||||
if (inventory->titleTimer > 1.0f)
|
||||
return;
|
||||
|
||||
if (loadSlot > -1 && nextLevel == TR::LVL_MAX) {
|
||||
if (loadSlot > -1 && nextLevel == TR::LVL_MAX && !level.isCutsceneLevel()) {
|
||||
if (inventory->isActive())
|
||||
return;
|
||||
|
||||
TR::LevelID id = TR::LevelID(saveSlots[loadSlot].level & ~LVL_FLAG_CHECKPOINT);
|
||||
loadLevel(id, false);
|
||||
loadLevel(id);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -163,7 +163,7 @@ void removeSaveSlot(TR::LevelID levelID, bool checkpoint) {
|
||||
if (TR::getGameVersionByLevel(id) != version)
|
||||
continue;
|
||||
|
||||
if ((checkpoint && isCheckpointSlot) || (!checkpoint && levelID == id)) {
|
||||
if (isCheckpointSlot || (!checkpoint && levelID == id)) {
|
||||
delete[] slot.data;
|
||||
saveSlots.remove(i);
|
||||
i--;
|
||||
|
Reference in New Issue
Block a user