diff options
Diffstat (limited to 'engines/zvision/scripting/scr_file_handling.cpp')
-rw-r--r-- | engines/zvision/scripting/scr_file_handling.cpp | 89 |
1 files changed, 78 insertions, 11 deletions
diff --git a/engines/zvision/scripting/scr_file_handling.cpp b/engines/zvision/scripting/scr_file_handling.cpp index c117da5ec2..edc1b8622c 100644 --- a/engines/zvision/scripting/scr_file_handling.cpp +++ b/engines/zvision/scripting/scr_file_handling.cpp @@ -47,15 +47,13 @@ namespace ZVision { void ScriptManager::parseScrFile(const Common::String &fileName, ScriptScope &scope) { Common::File file; if (!_engine->getSearchManager()->openFile(file, fileName)) { - warning("Script file not found: %s", fileName.c_str()); - return; + error("Script file not found: %s", fileName.c_str()); } while (!file.eos()) { Common::String line = file.readLine(); if (file.err()) { - warning("Error parsing scr file: %s", fileName.c_str()); - return; + error("Error parsing scr file: %s", fileName.c_str()); } trimCommentsAndWhiteSpace(&line); @@ -85,9 +83,18 @@ void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stre while (!stream.eos() && !line.contains('}')) { if (line.matchString("criteria {", true)) { - parseCriteria(stream, puzzle->criteriaList); + parseCriteria(stream, puzzle->criteriaList, puzzle->key); } else if (line.matchString("results {", true)) { parseResults(stream, puzzle->resultActions); + + // WORKAROUND for a script bug in Zork Nemesis, room ve5e (tuning + // fork box closeup). If the player leaves the screen while the + // box is open, puzzle 19398 shows the animation where the box + // closes, but the box state (state variable 19397) is not updated. + // We insert the missing assignment for the box state here. + // Fixes bug #6803. + if (_engine->getGameId() == GID_NEMESIS && puzzle->key == 19398) + puzzle->resultActions.push_back(new ActionAssign(_engine, 11, "19397, 0")); } else if (line.matchString("flags {", true)) { setStateFlag(puzzle->key, parseFlags(stream)); } @@ -99,11 +106,18 @@ void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stre puzzle->addedBySetState = false; } -bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList) const { +bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList, uint32 key) const { // Loop until we find the closing brace Common::String line = stream.readLine(); trimCommentsAndWhiteSpace(&line); + // Skip any commented out criteria. If all the criteria are commented out, + // we might end up with an invalid criteria list (bug #6776). + while (line.empty()) { + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } + // Criteria can be empty if (line.contains('}')) { return false; @@ -112,6 +126,21 @@ bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::Li // Create a new List to hold the CriteriaEntries criteriaList.push_back(Common::List<Puzzle::CriteriaEntry>()); + // WORKAROUND for a script bug in Zork: Nemesis, room td9e (fist puzzle) + // Since we patch the script that triggers when manipulating the left fist + // (below), we add an additional check for the left fist sound, so that it + // doesn't get killed immediately when the left fist animation starts. + // Together with the workaround below, it fixes bug #6783. + if (_engine->getGameId() == GID_NEMESIS && key == 3594) { + Puzzle::CriteriaEntry entry; + entry.key = 567; + entry.criteriaOperator = Puzzle::NOT_EQUAL_TO; + entry.argumentIsAKey = false; + entry.argument = 1; + + criteriaList.back().push_back(entry); + } + while (!stream.eos() && !line.contains('}')) { Puzzle::CriteriaEntry entry; @@ -123,6 +152,13 @@ bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::Li token = tokenizer.nextToken(); sscanf(token.c_str(), "[%u]", &(entry.key)); + // WORKAROUND for a script bug in Zork: Nemesis, room td9e (fist puzzle) + // Check for the state of animation 567 (left fist) when manipulating + // the fingers of the left fist (puzzle slots 3582, 3583). + // Together with the workaround above, it fixes bug #6783. + if (_engine->getGameId() == GID_NEMESIS && (key == 3582 || key == 3583) && entry.key == 568) + entry.key = 567; + // Parse the operator out of the second token token = tokenizer.nextToken(); if (token.c_str()[0] == '=') @@ -134,9 +170,17 @@ bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::Li else if (token.c_str()[0] == '<') entry.criteriaOperator = Puzzle::LESS_THAN; + // There are supposed to be three tokens, but there is no + // guarantee that there will be a space between the second and + // the third one (bug #6774) + if (token.size() == 1) { + token = tokenizer.nextToken(); + } else { + token.deleteChar(0); + } + // First determine if the last token is an id or a value // Then parse it into 'argument' - token = tokenizer.nextToken(); if (token.contains('[')) { sscanf(token.c_str(), "[%u]", &(entry.argument)); entry.argumentIsAKey = true; @@ -216,6 +260,7 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("animpreload", true)) { actionList.push_back(new ActionPreloadAnimation(_engine, slot, args)); } else if (act.matchString("animunload", true)) { + // Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j) actionList.push_back(new ActionUnloadAnimation(_engine, slot, args)); } else if (act.matchString("attenuate", true)) { actionList.push_back(new ActionAttenuate(_engine, slot, args)); @@ -234,12 +279,13 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("disable_control", true)) { actionList.push_back(new ActionDisableControl(_engine, slot, args)); } else if (act.matchString("disable_venus", true)) { - actionList.push_back(new ActionDisableVenus(_engine, slot, args)); + // Not used. Purposely left empty } else if (act.matchString("display_message", true)) { actionList.push_back(new ActionDisplayMessage(_engine, slot, args)); } else if (act.matchString("dissolve", true)) { actionList.push_back(new ActionDissolve(_engine)); } else if (act.matchString("distort", true)) { + // Only used by Zork: Nemesis for the "treatment" puzzle in the Sanitarium (aj30) actionList.push_back(new ActionDistort(_engine, slot, args)); } else if (act.matchString("enable_control", true)) { actionList.push_back(new ActionEnableControl(_engine, slot, args)); @@ -248,6 +294,7 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("inventory", true)) { actionList.push_back(new ActionInventory(_engine, slot, args)); } else if (act.matchString("kill", true)) { + // Only used by ZGI actionList.push_back(new ActionKill(_engine, slot, args)); } else if (act.matchString("menu_bar_enable", true)) { actionList.push_back(new ActionMenuBarEnable(_engine, slot, args)); @@ -264,8 +311,11 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("random", true)) { actionList.push_back(new ActionRandom(_engine, slot, args)); } else if (act.matchString("region", true)) { + // Only used by Zork: Nemesis actionList.push_back(new ActionRegion(_engine, slot, args)); } else if (act.matchString("restore_game", true)) { + // Only used by ZGI to load the restart game slot, r.svr. + // Used by the credits screen. actionList.push_back(new ActionRestoreGame(_engine, slot, args)); } else if (act.matchString("rotate_to", true)) { actionList.push_back(new ActionRotateTo(_engine, slot, args)); @@ -276,7 +326,7 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("set_screen", true)) { actionList.push_back(new ActionSetScreen(_engine, slot, args)); } else if (act.matchString("set_venus", true)) { - actionList.push_back(new ActionSetVenus(_engine, slot, args)); + // Not used. Purposely left empty } else if (act.matchString("stop", true)) { actionList.push_back(new ActionStop(_engine, slot, args)); } else if (act.matchString("streamvideo", true)) { @@ -337,6 +387,16 @@ Control *ScriptManager::parseControl(Common::String &line, Common::SeekableReadS Common::String controlType(controlTypeBuffer); if (controlType.equalsIgnoreCase("push_toggle")) { + // WORKAROUND for a script bug in ZGI: There is an invalid hotspot + // at scene em1h (bottom of tower), which points to a missing + // script em1n. This is a hotspot at the right of the screen. + // In the original, this hotspot doesn't lead anywhere anyway, + // so instead of moving to a missing scene, we just remove the + // hotspot altogether. The alternative would be to just process + // and ignore invalid scenes, but I don't think it's worth the + // effort. Fixes bug #6780. + if (_engine->getGameId() == GID_GRANDINQUISITOR && key == 5653) + return NULL; return new PushToggleControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("flat")) { Control::parseFlatControl(_engine); @@ -345,25 +405,32 @@ Control *ScriptManager::parseControl(Common::String &line, Common::SeekableReadS Control::parsePanoramaControl(_engine, stream); return NULL; } else if (controlType.equalsIgnoreCase("tilt")) { + // Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view) Control::parseTiltControl(_engine, stream); return NULL; - } else if (controlType.equalsIgnoreCase("lever")) { - return new LeverControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("slot")) { return new SlotControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("input")) { return new InputControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("save")) { return new SaveControl(_engine, key, stream); + } else if (controlType.equalsIgnoreCase("lever")) { + // Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e) + return new LeverControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("safe")) { + // Only used in Zork Nemesis, handles the safe in the Asylum (ac4g) return new SafeControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("hotmovie")) { + // Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g) return new HotMovControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("fist")) { + // Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e) return new FistControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("paint")) { + // Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g) return new PaintControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("titler")) { + // Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons (cjde) return new TitlerControl(_engine, key, stream); } return NULL; |