From 11065630c0f7913d6023c92035641ffde1cb6a5f Mon Sep 17 00:00:00 2001 From: sluicebox Date: Wed, 20 Nov 2019 13:57:41 -0800 Subject: SCI: Fix CAMELOT menu items when restoring --- engines/sci/engine/savegame.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ engines/sci/engine/selector.cpp | 1 + engines/sci/engine/selector.h | 1 + 3 files changed, 43 insertions(+) diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 6d0abf75af..3423a0126a 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -1194,6 +1194,47 @@ extern void showScummVMDialog(const Common::String &message); void gamestate_afterRestoreFixUp(EngineState *s, int savegameId) { switch (g_sci->getGameId()) { + case GID_CAMELOT: { + // WORKAROUND: CAMELOT depends on its dynamic menu state persisting. The menu items' + // enabled states determines when the player can draw or sheathe their sword and + // open a purse. If these aren't updated then the player may be unable to perform + // necessary actions, or may be able to perform unexpected ones that break the game. + // Since we don't persist menu state (yet) we need to recreate it from game state. + // + // - Action \ Open Purse: Enabled while one of the purses is in inventory. + // - Action \ Draw Sword: Enabled while flag 3 is set, unless disabled by room scripts. + // * The text "Draw Sword" toggles to "Sheathe Sword" depending on global 124, + // but this is only cosmetic. Exported proc #1 in script 997 refreshes this + // when the sword status or room changes. + // + // After evaluating all the scripts that disable the sword, we enforce the few + // that prevent breaking the game: room 50 under the aqueduct and sitting with + // the scholar while in room 82 (ego view 84). + // + // FIXME: Save and restore full menu state as SSCI did and don't apply these + // workarounds when restoring saves that contain menu state. + + // Action \ Open Purse + reg_t enablePurse = NULL_REG; + Common::Array purses = s->_segMan->findObjectsByName("purse"); + reg_t ego = s->variables[VAR_GLOBAL][0]; + for (uint i = 0; i < purses.size(); ++i) { + reg_t purseOwner = readSelector(s->_segMan, purses[i], SELECTOR(owner)); + if (purseOwner == ego) { + enablePurse = TRUE_REG; + break; + } + } + g_sci->_gfxMenu->kernelSetAttribute(1281 >> 8, 1281 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, enablePurse); + + // Action \ Draw Sword + bool hasSword = (s->variables[VAR_GLOBAL][250].getOffset() & 0x1000); // flag 3 + bool underAqueduct = (s->variables[VAR_GLOBAL][11].getOffset() == 50); + bool sittingWithScholar = (readSelectorValue(s->_segMan, ego, SELECTOR(view)) == 84); + reg_t enableSword = (hasSword && !underAqueduct && !sittingWithScholar) ? TRUE_REG : NULL_REG; + g_sci->_gfxMenu->kernelSetAttribute(1283 >> 8, 1283 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, enableSword); + break; + } case GID_MOTHERGOOSE: // WORKAROUND: Mother Goose SCI0 // Script 200 / rm200::newRoom will set global C5h directly right after creating a child to the diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index 6fbccb81a3..6196495954 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -169,6 +169,7 @@ void Kernel::mapSelectors() { FIND_SELECTOR(setStep); FIND_SELECTOR(setMotion); FIND_SELECTOR(cycleSpeed); + FIND_SELECTOR(owner); #ifdef ENABLE_SCI32 FIND_SELECTOR(data); diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h index fa2df8ced6..68b36b3bc2 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -135,6 +135,7 @@ struct SelectorCache { Selector setStep; Selector setMotion; Selector cycleSpeed; + Selector owner; #ifdef ENABLE_SCI32 Selector data; // Used by Array()/String() -- cgit v1.2.3