diff options
Diffstat (limited to 'engines/zvision/scripting')
39 files changed, 1875 insertions, 829 deletions
diff --git a/engines/zvision/scripting/actions.cpp b/engines/zvision/scripting/actions.cpp index 0422a2c028..e1380b0eb2 100644 --- a/engines/zvision/scripting/actions.cpp +++ b/engines/zvision/scripting/actions.cpp @@ -21,42 +21,44 @@ */ #include "common/scummsys.h" +#include "video/video_decoder.h" #include "zvision/scripting/actions.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" #include "zvision/graphics/render_manager.h" -#include "zvision/sound/zork_raw.h" -#include "zvision/video/zork_avi_decoder.h" -#include "zvision/scripting/sidefx/timer_node.h" -#include "zvision/scripting/sidefx/music_node.h" -#include "zvision/scripting/sidefx/syncsound_node.h" -#include "zvision/scripting/sidefx/animation_node.h" -#include "zvision/scripting/sidefx/distort_node.h" -#include "zvision/scripting/sidefx/ttytext_node.h" -#include "zvision/scripting/sidefx/region_node.h" +#include "zvision/file/save_manager.h" +#include "zvision/scripting/menu.h" +#include "zvision/scripting/effects/timer_effect.h" +#include "zvision/scripting/effects/music_effect.h" +#include "zvision/scripting/effects/syncsound_effect.h" +#include "zvision/scripting/effects/animation_effect.h" +#include "zvision/scripting/effects/distort_effect.h" +#include "zvision/scripting/effects/ttytext_effect.h" +#include "zvision/scripting/effects/region_effect.h" #include "zvision/scripting/controls/titler_control.h" #include "zvision/graphics/render_table.h" -#include "zvision/graphics/effect.h" +#include "zvision/graphics/graphics_effect.h" #include "zvision/graphics/effects/fog.h" #include "zvision/graphics/effects/light.h" #include "zvision/graphics/effects/wave.h" -#include "zvision/core/save_manager.h" #include "zvision/graphics/cursors/cursor_manager.h" -#include "common/file.h" - -#include "audio/decoders/wave.h" - namespace ZVision { +ResultAction::ResultAction(ZVision *engine, int32 slotKey) : + _engine(engine), + _slotKey(slotKey), + _scriptManager(engine->getScriptManager()) { +} + ////////////////////////////////////////////////////////////////////////////// // ActionAdd ////////////////////////////////////////////////////////////////////////////// -ActionAdd::ActionAdd(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionAdd::ActionAdd(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; _value = 0; @@ -64,7 +66,7 @@ ActionAdd::ActionAdd(ZVision *engine, int32 slotkey, const Common::String &line) } bool ActionAdd::execute() { - _engine->getScriptManager()->setStateValue(_key, _engine->getScriptManager()->getStateValue(_key) + _value); + _scriptManager->setStateValue(_key, _scriptManager->getStateValue(_key) + _value); return true; } @@ -72,23 +74,22 @@ bool ActionAdd::execute() { // ActionAssign ////////////////////////////////////////////////////////////////////////////// -ActionAssign::ActionAssign(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionAssign::ActionAssign(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; char buf[64]; memset(buf, 0, 64); sscanf(line.c_str(), "%u, %s", &_key, buf); - _value = new ValueSlot(_engine->getScriptManager(), buf); + _value = new ValueSlot(_scriptManager, buf); } ActionAssign::~ActionAssign() { - if (_value) - delete _value; + delete _value; } bool ActionAssign::execute() { - _engine->getScriptManager()->setStateValue(_key, _value->getValue()); + _scriptManager->setStateValue(_key, _value->getValue()); return true; } @@ -96,8 +97,8 @@ bool ActionAssign::execute() { // ActionAttenuate ////////////////////////////////////////////////////////////////////////////// -ActionAttenuate::ActionAttenuate(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionAttenuate::ActionAttenuate(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; _attenuation = 0; @@ -105,10 +106,10 @@ ActionAttenuate::ActionAttenuate(ZVision *engine, int32 slotkey, const Common::S } bool ActionAttenuate::execute() { - SideFX *fx = _engine->getScriptManager()->getSideFX(_key); - if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { - MusicNode *mus = (MusicNode *)fx; - mus->setVolume(255 - (abs(_attenuation) >> 7)); + ScriptingEffect *fx = _scriptManager->getSideFX(_key); + if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) { + MusicNodeBASE *mus = (MusicNodeBASE *)fx; + mus->setVolume(255 * (10000 - abs(_attenuation)) / 10000 ); } return true; } @@ -117,8 +118,8 @@ bool ActionAttenuate::execute() { // ActionChangeLocation ////////////////////////////////////////////////////////////////////////////// -ActionChangeLocation::ActionChangeLocation(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionChangeLocation::ActionChangeLocation(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _world = 'g'; _room = 'a'; _node = 'r'; @@ -130,7 +131,7 @@ ActionChangeLocation::ActionChangeLocation(ZVision *engine, int32 slotkey, const bool ActionChangeLocation::execute() { // We can't directly call ScriptManager::ChangeLocationIntern() because doing so clears all the Puzzles, and thus would corrupt the current puzzle checking - _engine->getScriptManager()->changeLocation(_world, _room, _node, _view, _offset); + _scriptManager->changeLocation(_world, _room, _node, _view, _offset); // Tell the puzzle system to stop checking any more puzzles return false; } @@ -139,8 +140,8 @@ bool ActionChangeLocation::execute() { // ActionCrossfade ////////////////////////////////////////////////////////////////////////////// -ActionCrossfade::ActionCrossfade(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionCrossfade::ActionCrossfade(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _keyOne = 0; _keyTwo = 0; _oneStartVolume = 0; @@ -156,9 +157,9 @@ ActionCrossfade::ActionCrossfade(ZVision *engine, int32 slotkey, const Common::S bool ActionCrossfade::execute() { if (_keyOne) { - SideFX *fx = _engine->getScriptManager()->getSideFX(_keyOne); - if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { - MusicNode *mus = (MusicNode *)fx; + ScriptingEffect *fx = _scriptManager->getSideFX(_keyOne); + if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) { + MusicNodeBASE *mus = (MusicNodeBASE *)fx; if (_oneStartVolume >= 0) mus->setVolume((_oneStartVolume * 255) / 100); @@ -167,9 +168,9 @@ bool ActionCrossfade::execute() { } if (_keyTwo) { - SideFX *fx = _engine->getScriptManager()->getSideFX(_keyTwo); - if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { - MusicNode *mus = (MusicNode *)fx; + ScriptingEffect *fx = _scriptManager->getSideFX(_keyTwo); + if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) { + MusicNodeBASE *mus = (MusicNodeBASE *)fx; if (_twoStartVolume >= 0) mus->setVolume((_twoStartVolume * 255) / 100); @@ -183,8 +184,8 @@ bool ActionCrossfade::execute() { // ActionCursor ////////////////////////////////////////////////////////////////////////////// -ActionCursor::ActionCursor(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionCursor::ActionCursor(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { Common::String up = line; up.toUppercase(); _action = 0; @@ -215,10 +216,13 @@ bool ActionCursor::execute() { // ActionDelayRender ////////////////////////////////////////////////////////////////////////////// -ActionDelayRender::ActionDelayRender(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionDelayRender::ActionDelayRender(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _framesToDelay = 0; sscanf(line.c_str(), "%u", &_framesToDelay); + // Limit to 10 frames maximum. This fixes the script bug in ZGI scene px10 + // (outside Frobozz Electric building), where this is set to 100 (bug #6791). + _framesToDelay = MIN<uint32>(_framesToDelay, 10); } bool ActionDelayRender::execute() { @@ -230,32 +234,15 @@ bool ActionDelayRender::execute() { // ActionDisableControl ////////////////////////////////////////////////////////////////////////////// -ActionDisableControl::ActionDisableControl(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionDisableControl::ActionDisableControl(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; sscanf(line.c_str(), "%u", &_key); } bool ActionDisableControl::execute() { - _engine->getScriptManager()->setStateFlag(_key, Puzzle::DISABLED); - return true; -} - -////////////////////////////////////////////////////////////////////////////// -// ActionDisableVenus -////////////////////////////////////////////////////////////////////////////// - -ActionDisableVenus::ActionDisableVenus(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { - _key = 0; - - sscanf(line.c_str(), "%d", &_key); -} - -bool ActionDisableVenus::execute() { - _engine->getScriptManager()->setStateValue(_key, 0); - + _scriptManager->setStateFlag(_key, Puzzle::DISABLED); return true; } @@ -263,8 +250,8 @@ bool ActionDisableVenus::execute() { // ActionDisplayMessage ////////////////////////////////////////////////////////////////////////////// -ActionDisplayMessage::ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionDisplayMessage::ActionDisplayMessage(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _control = 0; _msgid = 0; @@ -272,7 +259,7 @@ ActionDisplayMessage::ActionDisplayMessage(ZVision *engine, int32 slotkey, const } bool ActionDisplayMessage::execute() { - Control *ctrl = _engine->getScriptManager()->getControl(_control); + Control *ctrl = _scriptManager->getControl(_control); if (ctrl && ctrl->getType() == Control::CONTROL_TITLER) { TitlerControl *titler = (TitlerControl *)ctrl; titler->setString(_msgid); @@ -295,11 +282,11 @@ bool ActionDissolve::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionDistort +// ActionDistort - only used by Zork: Nemesis for the "treatment" puzzle in the Sanitarium (aj30) ////////////////////////////////////////////////////////////////////////////// -ActionDistort::ActionDistort(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionDistort::ActionDistort(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _distSlot = 0; _speed = 0; _startAngle = 60.0; @@ -311,14 +298,14 @@ ActionDistort::ActionDistort(ZVision *engine, int32 slotkey, const Common::Strin } ActionDistort::~ActionDistort() { - _engine->getScriptManager()->killSideFx(_distSlot); + _scriptManager->killSideFx(_distSlot); } bool ActionDistort::execute() { - if (_engine->getScriptManager()->getSideFX(_distSlot)) + if (_scriptManager->getSideFX(_distSlot)) return true; - _engine->getScriptManager()->addSideFX(new DistortNode(_engine, _distSlot, _speed, _startAngle, _endAngle, _startLineScale, _endLineScale)); + _scriptManager->addSideFX(new DistortNode(_engine, _distSlot, _speed, _startAngle, _endAngle, _startLineScale, _endLineScale)); return true; } @@ -327,15 +314,15 @@ bool ActionDistort::execute() { // ActionEnableControl ////////////////////////////////////////////////////////////////////////////// -ActionEnableControl::ActionEnableControl(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionEnableControl::ActionEnableControl(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; sscanf(line.c_str(), "%u", &_key); } bool ActionEnableControl::execute() { - _engine->getScriptManager()->unsetStateFlag(_key, Puzzle::DISABLED); + _scriptManager->unsetStateFlag(_key, Puzzle::DISABLED); return true; } @@ -343,13 +330,13 @@ bool ActionEnableControl::execute() { // ActionFlushMouseEvents ////////////////////////////////////////////////////////////////////////////// -ActionFlushMouseEvents::ActionFlushMouseEvents(ZVision *engine, int32 slotkey) : - ResultAction(engine, slotkey) { +ActionFlushMouseEvents::ActionFlushMouseEvents(ZVision *engine, int32 slotKey) : + ResultAction(engine, slotKey) { } bool ActionFlushMouseEvents::execute() { - _engine->getScriptManager()->flushEvent(Common::EVENT_LBUTTONUP); - _engine->getScriptManager()->flushEvent(Common::EVENT_LBUTTONDOWN); + _scriptManager->flushEvent(Common::EVENT_LBUTTONUP); + _scriptManager->flushEvent(Common::EVENT_LBUTTONDOWN); return true; } @@ -357,13 +344,13 @@ bool ActionFlushMouseEvents::execute() { // ActionInventory ////////////////////////////////////////////////////////////////////////////// -ActionInventory::ActionInventory(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionInventory::ActionInventory(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _type = -1; _key = 0; char buf[25]; - sscanf(line.c_str(), "%25s %d", buf, &_key); + sscanf(line.c_str(), "%24s %d", buf, &_key); if (strcmp(buf, "add") == 0) { _type = 0; @@ -382,22 +369,22 @@ ActionInventory::ActionInventory(ZVision *engine, int32 slotkey, const Common::S bool ActionInventory::execute() { switch (_type) { case 0: // add - _engine->getScriptManager()->inventoryAdd(_key); + _scriptManager->inventoryAdd(_key); break; case 1: // addi - _engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(_key)); + _scriptManager->inventoryAdd(_scriptManager->getStateValue(_key)); break; case 2: // drop if (_key >= 0) - _engine->getScriptManager()->inventoryDrop(_key); + _scriptManager->inventoryDrop(_key); else - _engine->getScriptManager()->inventoryDrop(_engine->getScriptManager()->getStateValue(StateKey_InventoryItem)); + _scriptManager->inventoryDrop(_scriptManager->getStateValue(StateKey_InventoryItem)); break; case 3: // dropi - _engine->getScriptManager()->inventoryDrop(_engine->getScriptManager()->getStateValue(_key)); + _scriptManager->inventoryDrop(_scriptManager->getStateValue(_key)); break; case 4: // cycle - _engine->getScriptManager()->inventoryCycle(); + _scriptManager->inventoryCycle(); break; default: break; @@ -406,41 +393,41 @@ bool ActionInventory::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionKill +// ActionKill - only used by ZGI ////////////////////////////////////////////////////////////////////////////// -ActionKill::ActionKill(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionKill::ActionKill(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; _type = 0; char keytype[25]; - sscanf(line.c_str(), "%25s", keytype); + sscanf(line.c_str(), "%24s", keytype); if (keytype[0] == '"') { if (!scumm_stricmp(keytype, "\"ANIM\"")) - _type = SideFX::SIDEFX_ANIM; + _type = ScriptingEffect::SCRIPTING_EFFECT_ANIM; else if (!scumm_stricmp(keytype, "\"AUDIO\"")) - _type = SideFX::SIDEFX_AUDIO; + _type = ScriptingEffect::SCRIPTING_EFFECT_AUDIO; else if (!scumm_stricmp(keytype, "\"DISTORT\"")) - _type = SideFX::SIDEFX_DISTORT; + _type = ScriptingEffect::SCRIPTING_EFFECT_DISTORT; else if (!scumm_stricmp(keytype, "\"PANTRACK\"")) - _type = SideFX::SIDEFX_PANTRACK; + _type = ScriptingEffect::SCRIPTING_EFFECT_PANTRACK; else if (!scumm_stricmp(keytype, "\"REGION\"")) - _type = SideFX::SIDEFX_REGION; + _type = ScriptingEffect::SCRIPTING_EFFECT_REGION; else if (!scumm_stricmp(keytype, "\"TIMER\"")) - _type = SideFX::SIDEFX_TIMER; + _type = ScriptingEffect::SCRIPTING_EFFECT_TIMER; else if (!scumm_stricmp(keytype, "\"TTYTEXT\"")) - _type = SideFX::SIDEFX_TTYTXT; + _type = ScriptingEffect::SCRIPTING_EFFECT_TTYTXT; else if (!scumm_stricmp(keytype, "\"ALL\"")) - _type = SideFX::SIDEFX_ALL; + _type = ScriptingEffect::SCRIPTING_EFFECT_ALL; } else _key = atoi(keytype); } bool ActionKill::execute() { if (_type) - _engine->getScriptManager()->killSideFxType((SideFX::SideFXType)_type); + _scriptManager->killSideFxType((ScriptingEffect::ScriptingEffectType)_type); else - _engine->getScriptManager()->killSideFx(_key); + _scriptManager->killSideFx(_key); return true; } @@ -448,15 +435,15 @@ bool ActionKill::execute() { // ActionMenuBarEnable ////////////////////////////////////////////////////////////////////////////// -ActionMenuBarEnable::ActionMenuBarEnable(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionMenuBarEnable::ActionMenuBarEnable(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _menus = 0xFFFF; sscanf(line.c_str(), "%hu", &_menus); } bool ActionMenuBarEnable::execute() { - _engine->menuBarEnable(_menus); + _engine->getMenuHandler()->setEnable(_menus); return true; } @@ -464,57 +451,76 @@ bool ActionMenuBarEnable::execute() { // ActionMusic ////////////////////////////////////////////////////////////////////////////// -ActionMusic::ActionMusic(ZVision *engine, int32 slotkey, const Common::String &line, bool global) : - ResultAction(engine, slotkey), - _volume(255), +ActionMusic::ActionMusic(ZVision *engine, int32 slotKey, const Common::String &line, bool global) : + ResultAction(engine, slotKey), _note(0), _prog(0), _universe(global) { uint type = 0; char fileNameBuffer[25]; uint loop = 0; - uint volume = 255; + char volumeBuffer[15]; - sscanf(line.c_str(), "%u %25s %u %u", &type, fileNameBuffer, &loop, &volume); + // Volume is optional. If it doesn't appear, assume full volume + strcpy(volumeBuffer, "100"); - // type 4 are midi sound effect files + sscanf(line.c_str(), "%u %24s %u %14s", &type, fileNameBuffer, &loop, volumeBuffer); + + // Type 4 actions are MIDI commands, not files. These are only used by + // Zork: Nemesis, for the flute and piano puzzles (tj4e and ve6f, as well + // as vr) if (type == 4) { _midi = true; int note; int prog; - sscanf(line.c_str(), "%u %d %d %u", &type, &prog, ¬e, &volume); - _volume = volume; + sscanf(line.c_str(), "%u %d %d %14s", &type, &prog, ¬e, volumeBuffer); + _volume = new ValueSlot(_scriptManager, volumeBuffer); _note = note; _prog = prog; } else { _midi = false; _fileName = Common::String(fileNameBuffer); _loop = loop == 1 ? true : false; - - // Volume is optional. If it doesn't appear, assume full volume - if (volume != 255) { - // Volume in the script files is mapped to [0, 100], but the ScummVM mixer uses [0, 255] - _volume = volume * 255 / 100; + if (volumeBuffer[0] != '[' && atoi(volumeBuffer) > 100) { + // I thought I saw a case like this in Zork Nemesis, so + // let's guard against it. + warning("ActionMusic: Adjusting volume for %s from %s to 100", _fileName.c_str(), volumeBuffer); + strcpy(volumeBuffer, "100"); } + _volume = new ValueSlot(_scriptManager, volumeBuffer); } + + // WORKAROUND for a script bug in Zork Nemesis, rooms mq70/mq80. + // Fixes an edge case where the player goes to the dark room with the grue + // without holding a torch, and then quickly runs away before the grue's + // sound effect finishes. Fixes script bug #6794. + if (engine->getGameId() == GID_NEMESIS && _slotKey == 14822 && _scriptManager->getStateValue(_slotKey) == 2) + _scriptManager->setStateValue(_slotKey, 0); + } ActionMusic::~ActionMusic() { if (!_universe) - _engine->getScriptManager()->killSideFx(_slotKey); + _scriptManager->killSideFx(_slotKey); + delete _volume; } bool ActionMusic::execute() { - if (_engine->getScriptManager()->getSideFX(_slotKey)) - return true; + if (_scriptManager->getSideFX(_slotKey)) { + _scriptManager->killSideFx(_slotKey); + _scriptManager->setStateValue(_slotKey, 2); + } + + uint volume = _volume->getValue(); if (_midi) { - _engine->getScriptManager()->addSideFX(new MusicMidiNode(_engine, _slotKey, _prog, _note, _volume)); + _scriptManager->addSideFX(new MusicMidiNode(_engine, _slotKey, _prog, _note, volume)); } else { if (!_engine->getSearchManager()->hasFile(_fileName)) return true; - _engine->getScriptManager()->addSideFX(new MusicNode(_engine, _slotKey, _fileName, _loop, _volume)); + // Volume in the script files is mapped to [0, 100], but the ScummVM mixer uses [0, 255] + _scriptManager->addSideFX(new MusicNode(_engine, _slotKey, _fileName, _loop, volume * 255 / 100)); } return true; @@ -524,8 +530,8 @@ bool ActionMusic::execute() { // ActionPanTrack ////////////////////////////////////////////////////////////////////////////// -ActionPanTrack::ActionPanTrack(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey), +ActionPanTrack::ActionPanTrack(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey), _pos(0), _musicSlot(0) { @@ -533,14 +539,14 @@ ActionPanTrack::ActionPanTrack(ZVision *engine, int32 slotkey, const Common::Str } ActionPanTrack::~ActionPanTrack() { - _engine->getScriptManager()->killSideFx(_slotKey); + _scriptManager->killSideFx(_slotKey); } bool ActionPanTrack::execute() { - if (_engine->getScriptManager()->getSideFX(_slotKey)) + if (_scriptManager->getSideFX(_slotKey)) return true; - _engine->getScriptManager()->addSideFX(new PanTrackNode(_engine, _slotKey, _musicSlot, _pos)); + _scriptManager->addSideFX(new PanTrackNode(_engine, _slotKey, _musicSlot, _pos)); return true; } @@ -549,8 +555,8 @@ bool ActionPanTrack::execute() { // ActionPreferences ////////////////////////////////////////////////////////////////////////////// -ActionPreferences::ActionPreferences(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionPreferences::ActionPreferences(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { if (line.compareToIgnoreCase("save") == 0) _save = true; else @@ -570,38 +576,38 @@ bool ActionPreferences::execute() { // ActionPreloadAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPreloadAnimation::ActionPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionPreloadAnimation::ActionPreloadAnimation(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _mask = 0; _framerate = 0; char fileName[25]; - // The two %*u are always 0 and dont seem to have a use - sscanf(line.c_str(), "%25s %*u %*u %d %d", fileName, &_mask, &_framerate); + // The two %*u are usually 0 and dont seem to have a use + sscanf(line.c_str(), "%24s %*u %*u %d %d", fileName, &_mask, &_framerate); - if (_mask > 0) { - byte r, g, b; - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(_mask, r, g, b); - _mask = _engine->_pixelFormat.RGBToColor(r, g, b); - } + // Mask 0 means "no transparency" in this case. Since we use a common blitting + // code for images and animations, we set it to -1 to avoid confusion with + // color 0, which is used as a mask in some images + if (_mask == 0) + _mask = -1; _fileName = Common::String(fileName); } ActionPreloadAnimation::~ActionPreloadAnimation() { - _engine->getScriptManager()->deleteSideFx(_slotKey); + _scriptManager->deleteSideFx(_slotKey); } bool ActionPreloadAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_slotKey); + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_slotKey); if (!nod) { - nod = new AnimationNode(_engine, _slotKey, _fileName, _mask, _framerate, false); - _engine->getScriptManager()->addSideFX(nod); + nod = new AnimationEffect(_engine, _slotKey, _fileName, _mask, _framerate, false); + _scriptManager->addSideFX(nod); } else nod->stop(); - _engine->getScriptManager()->setStateValue(_slotKey, 2); + _scriptManager->setStateValue(_slotKey, 2); return true; } @@ -609,18 +615,18 @@ bool ActionPreloadAnimation::execute() { // ActionUnloadAnimation ////////////////////////////////////////////////////////////////////////////// -ActionUnloadAnimation::ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionUnloadAnimation::ActionUnloadAnimation(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; sscanf(line.c_str(), "%u", &_key); } bool ActionUnloadAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_key); + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_key); - if (nod && nod->getType() == SideFX::SIDEFX_ANIM) - _engine->getScriptManager()->deleteSideFx(_key); + if (nod && nod->getType() == ScriptingEffect::SCRIPTING_EFFECT_ANIM) + _scriptManager->deleteSideFx(_key); return true; } @@ -629,8 +635,8 @@ bool ActionUnloadAnimation::execute() { // ActionPlayAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPlayAnimation::ActionPlayAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionPlayAnimation::ActionPlayAnimation(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _x = 0; _y = 0; _x2 = 0; @@ -645,28 +651,35 @@ ActionPlayAnimation::ActionPlayAnimation(ZVision *engine, int32 slotkey, const C // The two %*u are always 0 and dont seem to have a use sscanf(line.c_str(), - "%25s %u %u %u %u %u %u %d %*u %*u %d %d", + "%24s %u %u %u %u %u %u %d %*u %*u %d %d", fileName, &_x, &_y, &_x2, &_y2, &_start, &_end, &_loopCount, &_mask, &_framerate); - if (_mask > 0) { - byte r, g, b; - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(_mask, r, g, b); - _mask = _engine->_pixelFormat.RGBToColor(r, g, b); - } + // Mask 0 means "no transparency" in this case. Since we use a common blitting + // code for images and animations, we set it to -1 to avoid confusion with + // color 0, which is used as a mask in some images + if (_mask == 0) + _mask = -1; _fileName = Common::String(fileName); + + // WORKAROUND for bug #6769, location me1g.scr (the "Alchemical debacle" + // video in ZGI). We only scale up by 2x, in AnimationEffect::process(), + // but the dimensions of the target frame are off by 2 pixels. We fix that + // here, so that the video can be scaled. + if (_fileName == "me1ga011.avi" && _y2 == 213) + _y2 = 215; } ActionPlayAnimation::~ActionPlayAnimation() { - _engine->getScriptManager()->deleteSideFx(_slotKey); + _scriptManager->deleteSideFx(_slotKey); } bool ActionPlayAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_slotKey); + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_slotKey); if (!nod) { - nod = new AnimationNode(_engine, _slotKey, _fileName, _mask, _framerate); - _engine->getScriptManager()->addSideFX(nod); + nod = new AnimationEffect(_engine, _slotKey, _fileName, _mask, _framerate); + _scriptManager->addSideFX(nod); } else nod->stop(); @@ -680,8 +693,8 @@ bool ActionPlayAnimation::execute() { // ActionPlayPreloadAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _controlKey = 0; _x1 = 0; _y1 = 0; @@ -697,7 +710,7 @@ ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(ZVision *engine, int32 sl } bool ActionPlayPreloadAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_controlKey); + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_controlKey); if (nod) nod->addPlayNode(_slotKey, _x1, _y1, _x2, _y2, _startFrame, _endFrame, _loopCount); @@ -716,11 +729,11 @@ bool ActionQuit::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionRegion +// ActionRegion - only used by Zork: Nemesis ////////////////////////////////////////////////////////////////////////////// -ActionRegion::ActionRegion(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionRegion::ActionRegion(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _delay = 0; _type = 0; _unk1 = 0; @@ -738,20 +751,20 @@ ActionRegion::ActionRegion(ZVision *engine, int32 slotkey, const Common::String } ActionRegion::~ActionRegion() { - _engine->getScriptManager()->killSideFx(_slotKey); + _scriptManager->killSideFx(_slotKey); } bool ActionRegion::execute() { - if (_engine->getScriptManager()->getSideFX(_slotKey)) + if (_scriptManager->getSideFX(_slotKey)) return true; - Effect *effct = NULL; + GraphicsEffect *effect = NULL; switch (_type) { case 0: { uint16 centerX, centerY, frames; double amplitude, waveln, speed; sscanf(_custom.c_str(), "%hu,%hu,%hu,%lf,%lf,%lf,", ¢erX, ¢erY, &frames, &litude, &waveln, &speed); - effct = new WaveFx(_engine, _slotKey, _rect, _unk1, frames, centerX, centerY, amplitude, waveln, speed); + effect = new WaveFx(_engine, _slotKey, _rect, _unk1, frames, centerX, centerY, amplitude, waveln, speed); } break; case 1: { @@ -763,7 +776,7 @@ bool ActionRegion::execute() { int8 minD; int8 maxD; EffectMap *_map = _engine->getRenderManager()->makeEffectMap(Common::Point(aX, aY), aD, _rect, &minD, &maxD); - effct = new LightFx(_engine, _slotKey, _rect, _unk1, _map, atoi(_custom.c_str()), minD, maxD); + effect = new LightFx(_engine, _slotKey, _rect, _unk1, _map, atoi(_custom.c_str()), minD, maxD); } break; case 9: { @@ -779,16 +792,16 @@ bool ActionRegion::execute() { _rect.setHeight(tempMask.h); EffectMap *_map = _engine->getRenderManager()->makeEffectMap(tempMask, 0); - effct = new FogFx(_engine, _slotKey, _rect, _unk1, _map, Common::String(buf)); + effect = new FogFx(_engine, _slotKey, _rect, _unk1, _map, Common::String(buf)); } break; default: break; } - if (effct) { - _engine->getScriptManager()->addSideFX(new RegionNode(_engine, _slotKey, effct, _delay)); - _engine->getRenderManager()->addEffect(effct); + if (effect) { + _scriptManager->addSideFX(new RegionNode(_engine, _slotKey, effect, _delay)); + _engine->getRenderManager()->addEffect(effect); } return true; @@ -798,22 +811,21 @@ bool ActionRegion::execute() { // ActionRandom ////////////////////////////////////////////////////////////////////////////// -ActionRandom::ActionRandom(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionRandom::ActionRandom(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { char maxBuffer[64]; memset(maxBuffer, 0, 64); sscanf(line.c_str(), "%s", maxBuffer); - _max = new ValueSlot(_engine->getScriptManager(), maxBuffer); + _max = new ValueSlot(_scriptManager, maxBuffer); } ActionRandom::~ActionRandom() { - if (_max) - delete _max; + delete _max; } bool ActionRandom::execute() { uint randNumber = _engine->getRandomSource()->getRandomNumber(_max->getValue()); - _engine->getScriptManager()->setStateValue(_slotKey, randNumber); + _scriptManager->setStateValue(_slotKey, randNumber); return true; } @@ -821,15 +833,15 @@ bool ActionRandom::execute() { // ActionRestoreGame ////////////////////////////////////////////////////////////////////////////// -ActionRestoreGame::ActionRestoreGame(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionRestoreGame::ActionRestoreGame(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { char buf[128]; sscanf(line.c_str(), "%s", buf); _fileName = Common::String(buf); } bool ActionRestoreGame::execute() { - _engine->getSaveManager()->loadGame(_fileName); + _engine->getSaveManager()->loadGame(-1); return false; } @@ -837,8 +849,8 @@ bool ActionRestoreGame::execute() { // ActionRotateTo ////////////////////////////////////////////////////////////////////////////// -ActionRotateTo::ActionRotateTo(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionRotateTo::ActionRotateTo(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _time = 0; _toPos = 0; @@ -846,7 +858,7 @@ ActionRotateTo::ActionRotateTo(ZVision *engine, int32 slotkey, const Common::Str } bool ActionRotateTo::execute() { - _engine->rotateTo(_toPos, _time); + _engine->getRenderManager()->rotateTo(_toPos, _time); return true; } @@ -855,27 +867,18 @@ bool ActionRotateTo::execute() { // ActionSetPartialScreen ////////////////////////////////////////////////////////////////////////////// -ActionSetPartialScreen::ActionSetPartialScreen(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionSetPartialScreen::ActionSetPartialScreen(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _x = 0; _y = 0; char fileName[25]; - int color; - sscanf(line.c_str(), "%u %u %25s %*u %d", &_x, &_y, fileName, &color); + sscanf(line.c_str(), "%u %u %24s %*u %d", &_x, &_y, fileName, &_backgroundColor); _fileName = Common::String(fileName); - if (color >= 0) { - byte r, g, b; - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(color, r, g, b); - _backgroundColor = _engine->_pixelFormat.RGBToColor(r, g, b); - } else { - _backgroundColor = color; - } - - if (color > 65535) { + if (_backgroundColor > 65535) { warning("Background color for ActionSetPartialScreen is bigger than a uint16"); } } @@ -904,10 +907,10 @@ bool ActionSetPartialScreen::execute() { // ActionSetScreen ////////////////////////////////////////////////////////////////////////////// -ActionSetScreen::ActionSetScreen(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionSetScreen::ActionSetScreen(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { char fileName[25]; - sscanf(line.c_str(), "%25s", fileName); + sscanf(line.c_str(), "%24s", fileName); _fileName = Common::String(fileName); } @@ -919,35 +922,17 @@ bool ActionSetScreen::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionSetVenus -////////////////////////////////////////////////////////////////////////////// - -ActionSetVenus::ActionSetVenus(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { - _key = 0; - - sscanf(line.c_str(), "%d", &_key); -} - -bool ActionSetVenus::execute() { - if (_engine->getScriptManager()->getStateValue(_key)) - _engine->getScriptManager()->setStateValue(StateKey_Venus, _key); - - return true; -} - -////////////////////////////////////////////////////////////////////////////// // ActionStop ////////////////////////////////////////////////////////////////////////////// -ActionStop::ActionStop(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionStop::ActionStop(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; sscanf(line.c_str(), "%u", &_key); } bool ActionStop::execute() { - _engine->getScriptManager()->stopSideFx(_key); + _scriptManager->stopSideFx(_key); return true; } @@ -955,8 +940,8 @@ bool ActionStop::execute() { // ActionStreamVideo ////////////////////////////////////////////////////////////////////////////// -ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _x1 = 0; _x2 = 0; _y1 = 0; @@ -966,42 +951,69 @@ ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotkey, const Commo char fileName[25]; uint skipline = 0; //skipline - render video with skip every second line, not skippable. - sscanf(line.c_str(), "%25s %u %u %u %u %u %u", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skipline); + sscanf(line.c_str(), "%24s %u %u %u %u %u %u", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skipline); _fileName = Common::String(fileName); _skippable = true; } bool ActionStreamVideo::execute() { - ZorkAVIDecoder decoder; - Common::File *_file = _engine->getSearchManager()->openFile(_fileName); - - if (_file) { - if (!decoder.loadStream(_file)) { + Video::VideoDecoder *decoder; + Common::Rect destRect = Common::Rect(_x1, _y1, _x2 + 1, _y2 + 1); + Common::String subname = _fileName; + subname.setChar('s', subname.size() - 3); + subname.setChar('u', subname.size() - 2); + subname.setChar('b', subname.size() - 1); + bool subtitleExists = _engine->getSearchManager()->hasFile(subname); + bool switchToHires = false; + +// NOTE: We only show the hires MPEG2 videos when libmpeg2 is compiled in, +// otherwise we fall back to the lowres ones +#ifdef USE_MPEG2 + Common::String hiresFileName = _fileName; + hiresFileName.setChar('d', hiresFileName.size() - 8); + hiresFileName.setChar('v', hiresFileName.size() - 3); + hiresFileName.setChar('o', hiresFileName.size() - 2); + hiresFileName.setChar('b', hiresFileName.size() - 1); + + if (_scriptManager->getStateValue(StateKey_MPEGMovies) == 1 &&_engine->getSearchManager()->hasFile(hiresFileName)) { + // TODO: Enable once AC3 support is implemented + if (!_engine->getSearchManager()->hasFile(_fileName)) // Check for the regular video return true; - } - - _engine->getCursorManager()->showMouse(false); + warning("The hires videos of the DVD version of ZGI aren't supported yet, using lowres"); + //_fileName = hiresFileName; + //switchToHires = true; + } else if (!_engine->getSearchManager()->hasFile(_fileName)) + return true; +#else + if (!_engine->getSearchManager()->hasFile(_fileName)) + return true; +#endif - Common::Rect destRect = Common::Rect(_x1, _y1, _x2 + 1, _y2 + 1); + decoder = _engine->loadAnimation(_fileName); + Subtitle *sub = (subtitleExists) ? new Subtitle(_engine, subname, switchToHires) : NULL; - Common::String subname = _fileName; - subname.setChar('s', subname.size() - 3); - subname.setChar('u', subname.size() - 2); - subname.setChar('b', subname.size() - 1); + _engine->getCursorManager()->showMouse(false); - Subtitle *sub = NULL; + if (switchToHires) { + _engine->initHiresScreen(); + destRect = Common::Rect(40, -40, 760, 440); + Common::Rect workingWindow = _engine->_workingWindow; + workingWindow.translate(0, -40); + _engine->getRenderManager()->initSubArea(HIRES_WINDOW_WIDTH, HIRES_WINDOW_HEIGHT, workingWindow); + } - if (_engine->getSearchManager()->hasFile(subname)) - sub = new Subtitle(_engine, subname); + _engine->playVideo(*decoder, destRect, _skippable, sub); - _engine->playVideo(decoder, destRect, _skippable, sub); + if (switchToHires) { + _engine->initScreen(); + _engine->getRenderManager()->initSubArea(WINDOW_WIDTH, WINDOW_HEIGHT, _engine->_workingWindow); + } - _engine->getCursorManager()->showMouse(true); + _engine->getCursorManager()->showMouse(true); - if (sub) - delete sub; - } + delete decoder; + delete sub; return true; } @@ -1010,31 +1022,27 @@ bool ActionStreamVideo::execute() { // ActionSyncSound ////////////////////////////////////////////////////////////////////////////// -ActionSyncSound::ActionSyncSound(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionSyncSound::ActionSyncSound(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _syncto = 0; char fileName[25]; int notUsed = 0; - sscanf(line.c_str(), "%d %d %25s", &_syncto, ¬Used, fileName); + sscanf(line.c_str(), "%d %d %24s", &_syncto, ¬Used, fileName); _fileName = Common::String(fileName); } bool ActionSyncSound::execute() { - SideFX *fx = _engine->getScriptManager()->getSideFX(_syncto); + ScriptingEffect *fx = _scriptManager->getSideFX(_syncto); if (!fx) return true; - if (!(fx->getType() & SideFX::SIDEFX_ANIM)) + if (!(fx->getType() & ScriptingEffect::SCRIPTING_EFFECT_ANIM)) return true; - AnimationNode *animnode = (AnimationNode *)fx; - if (animnode->getFrameDelay() > 200) // Hack for fix incorrect framedelay in some animpreload - animnode->setNewFrameDelay(66); // ~15fps - - _engine->getScriptManager()->addSideFX(new SyncSoundNode(_engine, _slotKey, _fileName, _syncto)); + _scriptManager->addSideFX(new SyncSoundNode(_engine, _slotKey, _fileName, _syncto)); return true; } @@ -1042,24 +1050,23 @@ bool ActionSyncSound::execute() { // ActionTimer ////////////////////////////////////////////////////////////////////////////// -ActionTimer::ActionTimer(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionTimer::ActionTimer(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { char timeBuffer[64]; memset(timeBuffer, 0, 64); sscanf(line.c_str(), "%s", timeBuffer); - _time = new ValueSlot(_engine->getScriptManager(), timeBuffer); + _time = new ValueSlot(_scriptManager, timeBuffer); } ActionTimer::~ActionTimer() { - if (_time) - delete _time; - _engine->getScriptManager()->killSideFx(_slotKey); + delete _time; + _scriptManager->killSideFx(_slotKey); } bool ActionTimer::execute() { - if (_engine->getScriptManager()->getSideFX(_slotKey)) + if (_scriptManager->getSideFX(_slotKey)) return true; - _engine->getScriptManager()->addSideFX(new TimerNode(_engine, _slotKey, _time->getValue())); + _scriptManager->addSideFX(new TimerNode(_engine, _slotKey, _time->getValue())); return true; } @@ -1067,25 +1074,25 @@ bool ActionTimer::execute() { // ActionTtyText ////////////////////////////////////////////////////////////////////////////// -ActionTtyText::ActionTtyText(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionTtyText::ActionTtyText(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _delay = 0; char filename[64]; int32 x1 = 0, y1 = 0, x2 = 0, y2 = 0; - sscanf(line.c_str(), "%d %d %d %d %64s %u", &x1, &y1, &x2, &y2, filename, &_delay); + sscanf(line.c_str(), "%d %d %d %d %63s %u", &x1, &y1, &x2, &y2, filename, &_delay); _r = Common::Rect(x1, y1, x2, y2); _filename = Common::String(filename); } ActionTtyText::~ActionTtyText() { - _engine->getScriptManager()->killSideFx(_slotKey); + _scriptManager->killSideFx(_slotKey); } bool ActionTtyText::execute() { - if (_engine->getScriptManager()->getSideFX(_slotKey)) + if (_scriptManager->getSideFX(_slotKey)) return true; - _engine->getScriptManager()->addSideFX(new ttyTextNode(_engine, _slotKey, _filename, _r, _delay)); + _scriptManager->addSideFX(new ttyTextNode(_engine, _slotKey, _filename, _r, _delay)); return true; } diff --git a/engines/zvision/scripting/actions.h b/engines/zvision/scripting/actions.h index 292e25e19c..bde1baa291 100644 --- a/engines/zvision/scripting/actions.h +++ b/engines/zvision/scripting/actions.h @@ -32,6 +32,7 @@ namespace ZVision { // Forward declaration of ZVision. This file is included before ZVision is declared class ZVision; +class ScriptManager; class ValueSlot; /** @@ -40,7 +41,7 @@ class ValueSlot; */ class ResultAction { public: - ResultAction(ZVision *engine, int32 slotkey) : _engine(engine), _slotKey(slotkey) {} + ResultAction(ZVision *engine, int32 slotkey); virtual ~ResultAction() {} /** * This is called by the script system whenever a Puzzle's criteria are found to be true. @@ -53,6 +54,7 @@ public: virtual bool execute() = 0; protected: ZVision *_engine; + ScriptManager *_scriptManager; int32 _slotKey; }; @@ -124,14 +126,6 @@ private: uint8 _action; }; -class ActionDebug : public ResultAction { -public: - ActionDebug(ZVision *engine, int32 slotkey, const Common::String &line); - bool execute(); - -private: -}; - class ActionDelayRender : public ResultAction { public: ActionDelayRender(ZVision *engine, int32 slotkey, const Common::String &line); @@ -150,15 +144,6 @@ private: uint32 _key; }; -class ActionDisableVenus : public ResultAction { -public: - ActionDisableVenus(ZVision *engine, int32 slotkey, const Common::String &line); - bool execute(); - -private: - int32 _key; -}; - class ActionDisplayMessage : public ResultAction { public: ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line); @@ -241,7 +226,7 @@ public: private: Common::String _fileName; bool _loop; - byte _volume; + ValueSlot *_volume; bool _universe; bool _midi; int8 _note; @@ -284,7 +269,6 @@ public: bool execute(); private: - uint32 _animationKey; uint32 _controlKey; uint32 _x1; uint32 _y1; @@ -338,7 +322,7 @@ private: uint16 _unk2; }; -// TODO: See if this exists in ZGI. It doesn't in ZNem +// Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j) class ActionUnloadAnimation : public ResultAction { public: ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line); @@ -397,15 +381,6 @@ private: Common::String _fileName; }; -class ActionSetVenus : public ResultAction { -public: - ActionSetVenus(ZVision *engine, int32 slotkey, const Common::String &line); - bool execute(); - -private: - int32 _key; -}; - class ActionStop : public ResultAction { public: ActionStop(ZVision *engine, int32 slotkey, const Common::String &line); diff --git a/engines/zvision/scripting/control.cpp b/engines/zvision/scripting/control.cpp index 127f35ef12..81123eb99b 100644 --- a/engines/zvision/scripting/control.cpp +++ b/engines/zvision/scripting/control.cpp @@ -72,6 +72,7 @@ void Control::parsePanoramaControl(ZVision *engine, Common::SeekableReadStream & renderTable->generateRenderTable(); } +// Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view) void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream) { RenderTable *renderTable = engine->getRenderManager()->getRenderTable(); renderTable->setRenderState(RenderTable::TILT); diff --git a/engines/zvision/scripting/control.h b/engines/zvision/scripting/control.h index 803d0cf1ce..108b83fd00 100644 --- a/engines/zvision/scripting/control.h +++ b/engines/zvision/scripting/control.h @@ -36,6 +36,11 @@ namespace ZVision { class ZVision; +/** + * The base class for all Controls. + * + * Controls are the things that the user interacts with. Ex: A lever on the door + */ class Control { public: diff --git a/engines/zvision/scripting/controls/fist_control.cpp b/engines/zvision/scripting/controls/fist_control.cpp index 34a64b4298..f79c82dc79 100644 --- a/engines/zvision/scripting/controls/fist_control.cpp +++ b/engines/zvision/scripting/controls/fist_control.cpp @@ -46,10 +46,6 @@ FistControl::FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream _order = 0; _fistnum = 0; - _frameCur = -1; - _frameEnd = -1; - _frameTime = 0; - _lastRenderedFrame = -1; _animationId = 0; clearFistArray(_fistsUp); @@ -95,41 +91,28 @@ FistControl::~FistControl() { _entries.clear(); } -void FistControl::renderFrame(uint frameNumber) { - if ((int32)frameNumber == _lastRenderedFrame) - return; - - _lastRenderedFrame = frameNumber; - - const Graphics::Surface *frameData; - - if (_animation) { - _animation->seekToFrame(frameNumber); - frameData = _animation->decodeNextFrame(); - if (frameData) - _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _anmRect); - } -} - bool FistControl::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; - if (_frameCur >= 0 && _frameEnd >= 0) - if (_frameCur <= _frameEnd) { - _frameTime -= deltaTimeInMillis; - - if (_frameTime <= 0) { - _frameTime = 1000.0 / _animation->getDuration().framerate(); - - renderFrame(_frameCur); - - _frameCur++; + if (_animation && _animation->isPlaying()) { + if (_animation->endOfVideo()) { + _animation->stop(); + _engine->getScriptManager()->setStateValue(_animationId, 2); + return false; + } - if (_frameCur > _frameEnd) - _engine->getScriptManager()->setStateValue(_animationId, 2); - } + if (_animation->needsUpdate()) { + const Graphics::Surface *frameData = _animation->decodeNextFrame(); + if (frameData) + // WORKAROUND: Ignore the target frame dimensions for the finger animations. + // The target dimensions specify an area smaller than expected, thus if we + // scale the finger videos to fit these dimensions, they are not aligned + // correctly. Not scaling these videos yields a result identical to the + // original. Fixes bug #6784. + _engine->getRenderManager()->blitSurfaceToBkg(*frameData, _anmRect.left, _anmRect.top); } + } return false; } @@ -160,9 +143,12 @@ bool FistControl::onMouseUp(const Common::Point &screenSpacePos, const Common::P for (int i = 0; i < _numEntries; i++) if (_entries[i]._bitsStrt == oldStatus && _entries[i]._bitsEnd == _fiststatus) { - _frameCur = _entries[i]._anmStrt; - _frameEnd = _entries[i]._anmEnd; - _frameTime = 0; + if (_animation) { + _animation->stop(); + _animation->seekToFrame(_entries[i]._anmStrt); + _animation->setEndFrame(_entries[i]._anmEnd); + _animation->start(); + } _engine->getScriptManager()->setStateValue(_animationId, 1); _engine->getScriptManager()->setStateValue(_soundKey, _entries[i]._sound); diff --git a/engines/zvision/scripting/controls/fist_control.h b/engines/zvision/scripting/controls/fist_control.h index 0a6b977ead..d7cbcb1f71 100644 --- a/engines/zvision/scripting/controls/fist_control.h +++ b/engines/zvision/scripting/controls/fist_control.h @@ -34,6 +34,7 @@ namespace Video { namespace ZVision { +// Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e) class FistControl : public Control { public: FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); @@ -63,10 +64,6 @@ private: Video::VideoDecoder *_animation; Common::Rect _anmRect; int32 _soundKey; - int32 _frameCur; - int32 _frameEnd; - int32 _frameTime; - int32 _lastRenderedFrame; int32 _animationId; public: @@ -75,7 +72,6 @@ public: bool process(uint32 deltaTimeInMillis); private: - void renderFrame(uint frameNumber); void readDescFile(const Common::String &fileName); void clearFistArray(Common::Array< Common::Array<Common::Rect> > &arr); uint32 readBits(const char *str); diff --git a/engines/zvision/scripting/controls/hotmov_control.cpp b/engines/zvision/scripting/controls/hotmov_control.cpp index e77272ec73..182447a990 100644 --- a/engines/zvision/scripting/controls/hotmov_control.cpp +++ b/engines/zvision/scripting/controls/hotmov_control.cpp @@ -41,10 +41,7 @@ HotMovControl::HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadSt : Control(engine, key, CONTROL_HOTMOV) { _animation = NULL; _cycle = 0; - _curFrame = -1; - _lastRenderedFrame = -1; _frames.clear(); - _frameTime = 0; _cyclesCount = 0; _framesCount = 0; @@ -78,6 +75,7 @@ HotMovControl::HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadSt sscanf(values.c_str(), "%s", filename); values = Common::String(filename); _animation = _engine->loadAnimation(values); + _animation->start(); } else if (param.matchString("venus_id", true)) { _venusId = atoi(values.c_str()); } @@ -95,41 +93,26 @@ HotMovControl::~HotMovControl() { _frames.clear(); } -void HotMovControl::renderFrame(uint frameNumber) { - if ((int)frameNumber == _lastRenderedFrame) - return; - - _lastRenderedFrame = frameNumber; - - const Graphics::Surface *frameData; - - if (_animation) { - _animation->seekToFrame(frameNumber); - frameData = _animation->decodeNextFrame(); - if (frameData) - _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _rectangle); - } -} - bool HotMovControl::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; if (_cycle < _cyclesCount) { - _frameTime -= deltaTimeInMillis; + if (_animation && _animation->endOfVideo()) { + _cycle++; - if (_frameTime <= 0) { - _curFrame++; - if (_curFrame >= _framesCount) { - _curFrame = 0; - _cycle++; - } - if (_cycle != _cyclesCount) - renderFrame(_curFrame); - else + if (_cycle == _cyclesCount) { _engine->getScriptManager()->setStateValue(_key, 2); + return false; + } + + _animation->rewind(); + } - _frameTime = 1000.0 / _animation->getDuration().framerate(); + if (_animation && _animation->needsUpdate()) { + const Graphics::Surface *frameData = _animation->decodeNextFrame(); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _rectangle); } } @@ -140,8 +123,11 @@ bool HotMovControl::onMouseMove(const Common::Point &screenSpacePos, const Commo if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; + if (!_animation) + return false; + if (_cycle < _cyclesCount) { - if (_frames[_curFrame].contains(backgroundImageSpacePos)) { + if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) { _engine->getCursorManager()->changeCursor(CursorIndex_Active); return true; } @@ -154,8 +140,11 @@ bool HotMovControl::onMouseUp(const Common::Point &screenSpacePos, const Common: if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; + if (!_animation) + return false; + if (_cycle < _cyclesCount) { - if (_frames[_curFrame].contains(backgroundImageSpacePos)) { + if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) { setVenus(); _engine->getScriptManager()->setStateValue(_key, 1); return true; diff --git a/engines/zvision/scripting/controls/hotmov_control.h b/engines/zvision/scripting/controls/hotmov_control.h index b18d44c7a6..99d1fd0979 100644 --- a/engines/zvision/scripting/controls/hotmov_control.h +++ b/engines/zvision/scripting/controls/hotmov_control.h @@ -34,6 +34,7 @@ namespace Video { namespace ZVision { +// Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g) class HotMovControl : public Control { public: HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); @@ -41,9 +42,6 @@ public: private: int32 _framesCount; - int32 _frameTime; - int32 _curFrame; - int32 _lastRenderedFrame; int32 _cycle; int32 _cyclesCount; Video::VideoDecoder *_animation; @@ -55,7 +53,6 @@ public: bool process(uint32 deltaTimeInMillis); private: - void renderFrame(uint frameNumber); void readHsFile(const Common::String &fileName); }; diff --git a/engines/zvision/scripting/controls/input_control.cpp b/engines/zvision/scripting/controls/input_control.cpp index e75cc15743..9525333ef0 100644 --- a/engines/zvision/scripting/controls/input_control.cpp +++ b/engines/zvision/scripting/controls/input_control.cpp @@ -39,16 +39,14 @@ namespace ZVision { InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) : Control(engine, key, CONTROL_INPUT), + _background(0), _nextTabstop(0), _focused(false), _textChanged(false), - _cursorOffset(0), _enterPressed(false), _readOnly(false), _txtWidth(0), - _animation(NULL), - _frameDelay(0), - _frame(-1) { + _animation(NULL) { // Loop until we find the closing brace Common::String line = stream.readLine(); _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line); @@ -80,13 +78,13 @@ InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStre sscanf(values.c_str(), "%u", &fontFormatNumber); - _stringInit.readAllStyle(_engine->getStringManager()->getTextLine(fontFormatNumber)); + _stringInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber)); } else if (param.matchString("chooser_init_string", true)) { uint fontFormatNumber; sscanf(values.c_str(), "%u", &fontFormatNumber); - _stringChooserInit.readAllStyle(_engine->getStringManager()->getTextLine(fontFormatNumber)); + _stringChooserInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber)); } else if (param.matchString("next_tabstop", true)) { sscanf(values.c_str(), "%u", &_nextTabstop); } else if (param.matchString("cursor_dimensions", true)) { @@ -96,11 +94,10 @@ InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStre } else if (param.matchString("cursor_animation", true)) { char fileName[25]; - sscanf(values.c_str(), "%25s %*u", fileName); + sscanf(values.c_str(), "%24s %*u", fileName); _animation = _engine->loadAnimation(fileName); - _frame = -1; - _frameDelay = 0; + _animation->start(); } else if (param.matchString("focus", true)) { _focused = true; _engine->getScriptManager()->setFocusControlKey(_key); @@ -112,6 +109,15 @@ InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStre _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line); getParams(line, param, values); } + + _maxTxtWidth = _textRectangle.width(); + if (_animation) + _maxTxtWidth -= _animation->getWidth(); +} + +InputControl::~InputControl() { + _background->free(); + delete _background; } bool InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { @@ -194,36 +200,42 @@ bool InputControl::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; + if (!_background) { + _background = _engine->getRenderManager()->getBkgRect(_textRectangle); + } + // First see if we need to render the text if (_textChanged) { // Blit the text using the RenderManager Graphics::Surface txt; - txt.create(_textRectangle.width(), _textRectangle.height(), _engine->_pixelFormat); + txt.copyFrom(*_background); + + int32 oldTxtWidth = _txtWidth; if (!_readOnly || !_focused) - _txtWidth = _engine->getTextRenderer()->drawTxt(_currentInputText, _stringInit, txt); + _txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringInit, txt); else - _txtWidth = _engine->getTextRenderer()->drawTxt(_currentInputText, _stringChooserInit, txt); + _txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringChooserInit, txt); - _engine->getRenderManager()->blitSurfaceToBkg(txt, _textRectangle.left, _textRectangle.top); + if (_readOnly || _txtWidth <= _maxTxtWidth) + _engine->getRenderManager()->blitSurfaceToBkg(txt, _textRectangle.left, _textRectangle.top); + else { + // Assume the last character caused the overflow. + _currentInputText.deleteLastChar(); + _txtWidth = oldTxtWidth; + } txt.free(); } if (_animation && !_readOnly && _focused) { - bool needDraw = true;// = _textChanged; - _frameDelay -= deltaTimeInMillis; - if (_frameDelay <= 0) { - _frame = (_frame + 1) % _animation->getFrameCount(); - _frameDelay = 1000.0 / _animation->getDuration().framerate(); - needDraw = true; - } + if (_animation->endOfVideo()) + _animation->rewind(); - if (needDraw) { - _animation->seekToFrame(_frame); + if (_animation->needsUpdate()) { const Graphics::Surface *srf = _animation->decodeNextFrame(); - uint32 xx = _textRectangle.left + _txtWidth; + int16 xx = _textRectangle.left + _txtWidth; if (xx >= _textRectangle.left + (_textRectangle.width() - (int16)_animation->getWidth())) xx = _textRectangle.left + _textRectangle.width() - (int16)_animation->getWidth(); _engine->getRenderManager()->blitSurfaceToBkg(*srf, xx, _textRectangle.top); diff --git a/engines/zvision/scripting/controls/input_control.h b/engines/zvision/scripting/controls/input_control.h index 99f7f5287d..6abdb3c692 100644 --- a/engines/zvision/scripting/controls/input_control.h +++ b/engines/zvision/scripting/controls/input_control.h @@ -38,25 +38,25 @@ namespace ZVision { class InputControl : public Control { public: InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + ~InputControl(); private: + Graphics::Surface *_background; Common::Rect _textRectangle; Common::Rect _headerRectangle; - cTxtStyle _stringInit; - cTxtStyle _stringChooserInit; + TextStyleState _stringInit; + TextStyleState _stringChooserInit; uint32 _nextTabstop; bool _focused; Common::String _currentInputText; bool _textChanged; - uint _cursorOffset; bool _enterPressed; bool _readOnly; int16 _txtWidth; + int16 _maxTxtWidth; Video::VideoDecoder *_animation; - int32 _frameDelay; - int16 _frame; public: void focus() { diff --git a/engines/zvision/scripting/controls/lever_control.cpp b/engines/zvision/scripting/controls/lever_control.cpp index 8faa18357c..0f105b424c 100644 --- a/engines/zvision/scripting/controls/lever_control.cpp +++ b/engines/zvision/scripting/controls/lever_control.cpp @@ -64,12 +64,12 @@ LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStre while (!stream.eos() && !line.contains('}')) { if (param.matchString("descfile", true)) { char levFileName[25]; - sscanf(values.c_str(), "%25s", levFileName); + sscanf(values.c_str(), "%24s", levFileName); parseLevFile(levFileName); } else if (param.matchString("cursor", true)) { char cursorName[25]; - sscanf(values.c_str(), "%25s", cursorName); + sscanf(values.c_str(), "%24s", cursorName); _cursor = _engine->getCursorManager()->getCursorId(Common::String(cursorName)); } @@ -232,10 +232,13 @@ bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common if (angle >= (int)iter->angle - ANGLE_DELTA && angle <= (int)iter->angle + ANGLE_DELTA) { _currentFrame = iter->toFrame; renderFrame(_currentFrame); + _engine->getScriptManager()->setStateValue(_key, _currentFrame); break; } } } + _engine->getCursorManager()->changeCursor(_cursor); + cursorWasChanged = true; } else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { _engine->getCursorManager()->changeCursor(_cursor); cursorWasChanged = true; diff --git a/engines/zvision/scripting/controls/lever_control.h b/engines/zvision/scripting/controls/lever_control.h index fdf4a649dc..8787234c51 100644 --- a/engines/zvision/scripting/controls/lever_control.h +++ b/engines/zvision/scripting/controls/lever_control.h @@ -34,6 +34,7 @@ namespace Video { namespace ZVision { +// Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e) class LeverControl : public Control { public: LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); diff --git a/engines/zvision/scripting/controls/paint_control.cpp b/engines/zvision/scripting/controls/paint_control.cpp index df06bb814e..62dde3d170 100644 --- a/engines/zvision/scripting/controls/paint_control.cpp +++ b/engines/zvision/scripting/controls/paint_control.cpp @@ -114,12 +114,18 @@ PaintControl::PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStre PaintControl::~PaintControl() { // Clear the state value back to 0 //_engine->getScriptManager()->setStateValue(_key, 0); - if (_paint) + if (_paint) { + _paint->free(); delete _paint; - if (_brush) + } + if (_brush) { + _brush->free(); delete _brush; - if (_bkg) + } + if (_bkg) { + _bkg->free(); delete _bkg; + } } bool PaintControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { diff --git a/engines/zvision/scripting/controls/paint_control.h b/engines/zvision/scripting/controls/paint_control.h index 8097290ac2..8c01f0e68a 100644 --- a/engines/zvision/scripting/controls/paint_control.h +++ b/engines/zvision/scripting/controls/paint_control.h @@ -32,6 +32,7 @@ namespace ZVision { +// Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g) class PaintControl : public Control { public: PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); diff --git a/engines/zvision/scripting/controls/safe_control.cpp b/engines/zvision/scripting/controls/safe_control.cpp index 71be692431..4d2a91a1ad 100644 --- a/engines/zvision/scripting/controls/safe_control.cpp +++ b/engines/zvision/scripting/controls/safe_control.cpp @@ -49,10 +49,7 @@ SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream _outerRadiusSqr = 0; _zeroPointer = 0; _startPointer = 0; - _curFrame = -1; _targetFrame = 0; - _frameTime = 0; - _lastRenderedFrame = -1; // Loop until we find the closing brace Common::String line = stream.readLine(); @@ -64,6 +61,7 @@ SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream while (!stream.eos() && !line.contains('}')) { if (param.matchString("animation", true)) { _animation = _engine->loadAnimation(values); + _animation->start(); } else if (param.matchString("rectangle", true)) { int x; int y; @@ -104,7 +102,9 @@ SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line); getParams(line, param, values); } - renderFrame(_curState); + + if (_animation) + _animation->seekToFrame(_curState); } SafeControl::~SafeControl() { @@ -113,44 +113,22 @@ SafeControl::~SafeControl() { } -void SafeControl::renderFrame(uint frameNumber) { - if (frameNumber == 0) { - _lastRenderedFrame = frameNumber; - } else if ((int16)frameNumber < _lastRenderedFrame) { - _lastRenderedFrame = frameNumber; - frameNumber = (_statesCount * 2) - frameNumber; - } else { - _lastRenderedFrame = frameNumber; - } - - const Graphics::Surface *frameData; - int x = _rectangle.left; - int y = _rectangle.top; - - _animation->seekToFrame(frameNumber); - frameData = _animation->decodeNextFrame(); - if (frameData) - _engine->getRenderManager()->blitSurfaceToBkg(*frameData, x, y); -} - bool SafeControl::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; - if (_curFrame != _targetFrame) { - _frameTime -= deltaTimeInMillis; - - if (_frameTime <= 0) { - if (_curFrame < _targetFrame) { - _curFrame++; - renderFrame(_curFrame); - } else if (_curFrame > _targetFrame) { - _curFrame--; - renderFrame(_curFrame); - } - _frameTime = 1000.0 / _animation->getDuration().framerate(); - } + if (_animation && _animation->getCurFrame() != _targetFrame && _animation->needsUpdate()) { + // If we're past the target frame, move back one + if (_animation->getCurFrame() > _targetFrame) + _animation->seekToFrame(_animation->getCurFrame() - 1); + + const Graphics::Surface *frameData = _animation->decodeNextFrame(); + if (_animation->getCurFrame() == _targetFrame) + _engine->getScriptManager()->setStateValue(_key, _curState); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkg(*frameData, _rectangle.left, _rectangle.top); } + return false; } @@ -187,13 +165,12 @@ bool SafeControl::onMouseUp(const Common::Point &screenSpacePos, const Common::P int16 tmp2 = (m_state + _curState - _zeroPointer + _statesCount - 1) % _statesCount; - _curFrame = (_curState + _statesCount - _startPointer) % _statesCount; + if (_animation) + _animation->seekToFrame((_curState + _statesCount - _startPointer) % _statesCount); _curState = (_statesCount * 2 + tmp2) % _statesCount; _targetFrame = (_curState + _statesCount - _startPointer) % _statesCount; - - _engine->getScriptManager()->setStateValue(_key, _curState); return true; } } diff --git a/engines/zvision/scripting/controls/safe_control.h b/engines/zvision/scripting/controls/safe_control.h index 6e1095e304..3e8c17635c 100644 --- a/engines/zvision/scripting/controls/safe_control.h +++ b/engines/zvision/scripting/controls/safe_control.h @@ -34,6 +34,7 @@ namespace Video { namespace ZVision { +// Only used in Zork Nemesis, handles the safe in the Asylum (ac4g) class SafeControl : public Control { public: SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); @@ -51,19 +52,12 @@ private: int32 _outerRadiusSqr; int16 _zeroPointer; int16 _startPointer; - int16 _curFrame; int16 _targetFrame; - int32 _frameTime; - - int16 _lastRenderedFrame; public: bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); bool process(uint32 deltaTimeInMillis); - -private: - void renderFrame(uint frameNumber); }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/save_control.cpp b/engines/zvision/scripting/controls/save_control.cpp index b35611feca..2ac77c4776 100644 --- a/engines/zvision/scripting/controls/save_control.cpp +++ b/engines/zvision/scripting/controls/save_control.cpp @@ -29,7 +29,8 @@ #include "zvision/scripting/script_manager.h" #include "zvision/text/string_manager.h" -#include "zvision/core/save_manager.h" +#include "zvision/file/save_manager.h" +#include "zvision/graphics/render_manager.h" #include "common/str.h" #include "common/stream.h" @@ -97,18 +98,16 @@ bool SaveControl::process(uint32 deltaTimeInMillis) { if (inp->getText().size() > 0) { bool toSave = true; if (iter->exist) - if (!_engine->askQuestion(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEXIST))) + if (!_engine->getRenderManager()->askQuestion(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEXIST))) toSave = false; if (toSave) { - // FIXME: At this point, the screen shows the save control, so the save game thumbnails will always - // show the save control - _engine->getSaveManager()->saveGameBuffered(iter->saveId, inp->getText()); - _engine->delayedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVED), 2000); + _engine->getSaveManager()->saveGame(iter->saveId, inp->getText(), true); + _engine->getRenderManager()->delayedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVED), 2000); _engine->getScriptManager()->changeLocation(_engine->getScriptManager()->getLastMenuLocation()); } } else { - _engine->timedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEMPTY), 2000); + _engine->getRenderManager()->timedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEMPTY), 2000); } } else { _engine->getSaveManager()->loadGame(iter->saveId); diff --git a/engines/zvision/scripting/controls/titler_control.cpp b/engines/zvision/scripting/controls/titler_control.cpp index 10ba0af655..683d6660af 100644 --- a/engines/zvision/scripting/controls/titler_control.cpp +++ b/engines/zvision/scripting/controls/titler_control.cpp @@ -67,20 +67,22 @@ TitlerControl::TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadSt if (!_rectangle.isEmpty()) { _surface = new Graphics::Surface; - _surface->create(_rectangle.width(), _rectangle.height(), _engine->_pixelFormat); + _surface->create(_rectangle.width(), _rectangle.height(), _engine->_resourcePixelFormat); _surface->fillRect(Common::Rect(_surface->w, _surface->h), 0); } } TitlerControl::~TitlerControl() { - if (_surface) + if (_surface) { + _surface->free(); delete _surface; + } } void TitlerControl::setString(int strLine) { if (strLine != _curString && strLine >= 0 && strLine < (int)_strings.size()) { _surface->fillRect(Common::Rect(_surface->w, _surface->h), 0); - _engine->getTextRenderer()->drawTxtInOneLine(_strings[strLine], *_surface); + _engine->getTextRenderer()->drawTextWithWordWrapping(_strings[strLine], *_surface); _engine->getRenderManager()->blitSurfaceToBkg(*_surface, _rectangle.left, _rectangle.top); _curString = strLine; } diff --git a/engines/zvision/scripting/controls/titler_control.h b/engines/zvision/scripting/controls/titler_control.h index 075e47c9e9..dd96e4a846 100644 --- a/engines/zvision/scripting/controls/titler_control.h +++ b/engines/zvision/scripting/controls/titler_control.h @@ -32,6 +32,7 @@ namespace ZVision { +// Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons class TitlerControl : public Control { public: TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); diff --git a/engines/zvision/scripting/sidefx/animation_node.cpp b/engines/zvision/scripting/effects/animation_effect.cpp index 3a21227d1a..511a0db353 100644 --- a/engines/zvision/scripting/sidefx/animation_node.cpp +++ b/engines/zvision/scripting/effects/animation_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/animation_node.h" +#include "zvision/scripting/effects/animation_effect.h" #include "zvision/zvision.h" #include "zvision/graphics/render_manager.h" @@ -33,20 +33,27 @@ namespace ZVision { -AnimationNode::AnimationNode(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool DisposeAfterUse) - : SideFX(engine, controlKey, SIDEFX_ANIM), - _DisposeAfterUse(DisposeAfterUse), +AnimationEffect::AnimationEffect(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool disposeAfterUse) + : ScriptingEffect(engine, controlKey, SCRIPTING_EFFECT_ANIM), + _disposeAfterUse(disposeAfterUse), _mask(mask), _animation(NULL) { _animation = engine->loadAnimation(fileName); - _frmDelay = 1000.0 / _animation->getDuration().framerate(); - if (frate > 0) - _frmDelay = 1000.0 / frate; + if (frate > 0) { + _frmDelayOverride = (int32)(1000.0 / frate); + + // WORKAROUND: We do not allow the engine to delay more than 66 msec + // per frame (15fps max) + if (_frmDelayOverride > 66) + _frmDelayOverride = 66; + } else { + _frmDelayOverride = 0; + } } -AnimationNode::~AnimationNode() { +AnimationEffect::~AnimationEffect() { if (_animation) delete _animation; @@ -56,60 +63,83 @@ AnimationNode::~AnimationNode() { if (it != _playList.end()) { _engine->getScriptManager()->setStateValue((*it).slot, 2); - if ((*it)._scaled) + if ((*it)._scaled) { + (*it)._scaled->free(); delete(*it)._scaled; + } } _playList.clear(); } -bool AnimationNode::process(uint32 deltaTimeInMillis) { +bool AnimationEffect::process(uint32 deltaTimeInMillis) { + ScriptManager *scriptManager = _engine->getScriptManager(); + RenderManager *renderManager = _engine->getRenderManager(); + RenderTable::RenderState renderState = renderManager->getRenderTable()->getRenderState(); + bool isPanorama = (renderState == RenderTable::PANORAMA); + int16 velocity = _engine->getMouseVelocity() + _engine->getKeyboardVelocity(); + + // Do not update animation nodes in panoramic mode while turning, if the user + // has set this option + if (scriptManager->getStateValue(StateKey_NoTurnAnim) == 1 && isPanorama && velocity) + return false; + PlayNodes::iterator it = _playList.begin(); if (it != _playList.end()) { playnode *nod = &(*it); - nod->_delay -= deltaTimeInMillis; - if (nod->_delay <= 0) { - nod->_delay += _frmDelay; - - const Graphics::Surface *frame = NULL; - - if (nod->_curFrame == -1) { // Start of new playlist node - nod->_curFrame = nod->start; - - _animation->seekToFrame(nod->_curFrame); - frame = _animation->decodeNextFrame(); - - nod->_delay = _frmDelay; - if (nod->slot) - _engine->getScriptManager()->setStateValue(nod->slot, 1); - } else { - nod->_curFrame++; - - if (nod->_curFrame > nod->stop) { - nod->loop--; - - if (nod->loop == 0) { - if (nod->slot >= 0) - _engine->getScriptManager()->setStateValue(nod->slot, 2); - if (nod->_scaled) - delete nod->_scaled; - _playList.erase(it); - return _DisposeAfterUse; - } - - nod->_curFrame = nod->start; - _animation->seekToFrame(nod->_curFrame); + if (nod->_curFrame == -1) { + // The node is just beginning playback + nod->_curFrame = nod->start; + + _animation->start(); + _animation->seekToFrame(nod->start); + _animation->setEndFrame(nod->stop); + + nod->_delay = deltaTimeInMillis; // Force the frame to draw + if (nod->slot) + scriptManager->setStateValue(nod->slot, 1); + } else if (_animation->endOfVideo()) { + // The node has reached the end; check if we need to loop + nod->loop--; + + if (nod->loop == 0) { + if (nod->slot >= 0) + scriptManager->setStateValue(nod->slot, 2); + if (nod->_scaled) { + nod->_scaled->free(); + delete nod->_scaled; } + _playList.erase(it); + return _disposeAfterUse; + } - frame = _animation->decodeNextFrame(); + nod->_curFrame = nod->start; + _animation->seekToFrame(nod->start); + } + + // Check if we need to draw a frame + bool needsUpdate = false; + if (_frmDelayOverride == 0) { + // If not overridden, use the VideoDecoder's check + needsUpdate = _animation->needsUpdate(); + } else { + // Otherwise, implement our own timing + nod->_delay -= deltaTimeInMillis; + + if (nod->_delay <= 0) { + nod->_delay += _frmDelayOverride; + needsUpdate = true; } + } - if (frame) { + if (needsUpdate) { + const Graphics::Surface *frame = _animation->decodeNextFrame(); + if (frame) { uint32 dstw; uint32 dsth; - if (_engine->getRenderManager()->getRenderTable()->getRenderState() == RenderTable::PANORAMA) { + if (isPanorama) { dstw = nod->pos.height(); dsth = nod->pos.width(); } else { @@ -126,6 +156,7 @@ bool AnimationNode::process(uint32 deltaTimeInMillis) { if (frame->w > dstw || frame->h > dsth || (frame->w == dstw / 2 && frame->h == dsth / 2)) { if (nod->_scaled) if (nod->_scaled->w != dstw || nod->_scaled->h != dsth) { + nod->_scaled->free(); delete nod->_scaled; nod->_scaled = NULL; } @@ -135,22 +166,17 @@ bool AnimationNode::process(uint32 deltaTimeInMillis) { nod->_scaled->create(dstw, dsth, frame->format); } - _engine->getRenderManager()->scaleBuffer(frame->getPixels(), nod->_scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, dstw, dsth); + renderManager->scaleBuffer(frame->getPixels(), nod->_scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, dstw, dsth); frame = nod->_scaled; } - if (_engine->getRenderManager()->getRenderTable()->getRenderState() == RenderTable::PANORAMA) { + if (isPanorama) { Graphics::Surface *transposed = RenderManager::tranposeSurface(frame); - if (_mask > 0) - _engine->getRenderManager()->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top, _mask); - else - _engine->getRenderManager()->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top); + renderManager->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top, _mask); + transposed->free(); delete transposed; } else { - if (_mask > 0) - _engine->getRenderManager()->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top, _mask); - else - _engine->getRenderManager()->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top); + renderManager->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top, _mask); } } } @@ -159,16 +185,12 @@ bool AnimationNode::process(uint32 deltaTimeInMillis) { return false; } -void AnimationNode::addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops) { +void AnimationEffect::addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops) { playnode nod; nod.loop = loops; nod.pos = Common::Rect(x, y, x2 + 1, y2 + 1); nod.start = startFrame; - nod.stop = endFrame; - - if (nod.stop >= (int)_animation->getFrameCount()) - nod.stop = _animation->getFrameCount() - 1; - + nod.stop = CLIP<int>(endFrame, 0, _animation->getFrameCount() - 1); nod.slot = slot; nod._curFrame = -1; nod._delay = 0; @@ -176,12 +198,14 @@ void AnimationNode::addPlayNode(int32 slot, int x, int y, int x2, int y2, int st _playList.push_back(nod); } -bool AnimationNode::stop() { +bool AnimationEffect::stop() { PlayNodes::iterator it = _playList.begin(); if (it != _playList.end()) { _engine->getScriptManager()->setStateValue((*it).slot, 2); - if ((*it)._scaled) + if ((*it)._scaled) { + (*it)._scaled->free(); delete(*it)._scaled; + } } _playList.clear(); @@ -190,21 +214,4 @@ bool AnimationNode::stop() { return false; } -void AnimationNode::setNewFrameDelay(int32 newDelay) { - if (newDelay > 0) { - PlayNodes::iterator it = _playList.begin(); - if (it != _playList.end()) { - playnode *nod = &(*it); - float percent = (float)nod->_delay / (float)_frmDelay; - nod->_delay = percent * newDelay; // Scale to new max - } - - _frmDelay = newDelay; - } -} - -int32 AnimationNode::getFrameDelay() { - return _frmDelay; -} - } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/animation_node.h b/engines/zvision/scripting/effects/animation_effect.h index 3adfd91f32..fd6e24ab8b 100644 --- a/engines/zvision/scripting/sidefx/animation_node.h +++ b/engines/zvision/scripting/effects/animation_effect.h @@ -23,7 +23,7 @@ #ifndef ZVISION_ANIMATION_NODE_H #define ZVISION_ANIMATION_NODE_H -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" #include "common/rect.h" #include "common/list.h" @@ -39,10 +39,10 @@ namespace ZVision { class ZVision; -class AnimationNode : public SideFX { +class AnimationEffect : public ScriptingEffect { public: - AnimationNode(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool DisposeAfterUse = true); - ~AnimationNode(); + AnimationEffect(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool disposeAfterUse = true); + ~AnimationEffect(); struct playnode { Common::Rect pos; @@ -61,10 +61,10 @@ private: PlayNodes _playList; int32 _mask; - bool _DisposeAfterUse; + bool _disposeAfterUse; Video::VideoDecoder *_animation; - int32 _frmDelay; + int32 _frmDelayOverride; public: bool process(uint32 deltaTimeInMillis); @@ -72,9 +72,6 @@ public: void addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops = 1); bool stop(); - - void setNewFrameDelay(int32 newDelay); - int32 getFrameDelay(); }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/distort_node.cpp b/engines/zvision/scripting/effects/distort_effect.cpp index 0d5c8b1ed5..113b5d048d 100644 --- a/engines/zvision/scripting/sidefx/distort_node.cpp +++ b/engines/zvision/scripting/effects/distort_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/distort_node.h" +#include "zvision/scripting/effects/distort_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -34,7 +34,7 @@ namespace ZVision { DistortNode::DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale) - : SideFX(engine, key, SIDEFX_DISTORT) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_DISTORT) { _angle = _engine->getRenderManager()->getRenderTable()->getAngle(); _linScale = _engine->getRenderManager()->getRenderTable()->getLinscale(); @@ -52,7 +52,7 @@ DistortNode::DistortNode(ZVision *engine, uint32 key, int16 speed, float startAn _diffLinScale = endLineScale - startLineScale; _frmSpeed = (float)speed / 15.0; - _frames = ceil((5.0 - _frmSpeed * 2.0) / _frmSpeed); + _frames = (int)ceil((5.0 - _frmSpeed * 2.0) / _frmSpeed); if (_frames <= 0) _frames = 1; @@ -65,7 +65,6 @@ DistortNode::~DistortNode() { } bool DistortNode::process(uint32 deltaTimeInMillis) { - float updTime = deltaTimeInMillis / (1000.0 / 60.0); if (_incr) diff --git a/engines/zvision/scripting/sidefx/distort_node.h b/engines/zvision/scripting/effects/distort_effect.h index 787a69bdde..c64f10e6ff 100644 --- a/engines/zvision/scripting/sidefx/distort_node.h +++ b/engines/zvision/scripting/effects/distort_effect.h @@ -23,13 +23,13 @@ #ifndef ZVISION_DISTORT_NODE_H #define ZVISION_DISTORT_NODE_H -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" namespace ZVision { class ZVision; -class DistortNode : public SideFX { +class DistortNode : public ScriptingEffect { public: DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale); ~DistortNode(); diff --git a/engines/zvision/scripting/sidefx/music_node.cpp b/engines/zvision/scripting/effects/music_effect.cpp index 56598189f6..e3fdc96dba 100644 --- a/engines/zvision/scripting/sidefx/music_node.cpp +++ b/engines/zvision/scripting/effects/music_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/music_node.h" +#include "zvision/scripting/effects/music_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -36,16 +36,33 @@ namespace ZVision { -MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool loop, int8 volume) - : MusicNodeBASE(engine, key, SIDEFX_AUDIO) { +static const uint8 dbMapLinear[256] = +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, +2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, +4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, +8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, +14, 15, 15, 16, 16, 17, 18, 18, 19, 20, 21, 21, 22, 23, 24, 25, +26, 27, 28, 29, 30, 31, 32, 33, 34, 36, 37, 38, 40, 41, 43, 45, +46, 48, 50, 52, 53, 55, 57, 60, 62, 64, 67, 69, 72, 74, 77, 80, +83, 86, 89, 92, 96, 99, 103, 107, 111, 115, 119, 123, 128, 133, 137, 143, +148, 153, 159, 165, 171, 177, 184, 191, 198, 205, 212, 220, 228, 237, 245, 255}; + +MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool loop, uint8 volume) + : MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) { _loop = loop; _volume = volume; + _deltaVolume = 0; + _balance = 0; _crossfade = false; _crossfadeTarget = 0; _crossfadeTime = 0; - _attenuate = 0; - _pantrack = false; - _pantrackPosition = 0; _sub = NULL; _stereo = false; _loaded = false; @@ -66,9 +83,9 @@ MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool if (_loop) { Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES); - _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopingAudioStream, -1, _volume); + _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopingAudioStream, -1, dbMapLinear[_volume]); } else { - _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audioStream, -1, _volume); + _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audioStream, -1, dbMapLinear[_volume]); } if (_key != StateKey_NotSet) @@ -88,26 +105,23 @@ MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool } MusicNode::~MusicNode() { - if (!_loaded) + if (_loaded) _engine->_mixer->stopHandle(_handle); if (_key != StateKey_NotSet) _engine->getScriptManager()->setStateValue(_key, 2); if (_sub) delete _sub; - debug(1, "MusicNode: %d destroyed\n", _key); + debug(1, "MusicNode: %d destroyed", _key); } -void MusicNode::setPanTrack(int16 pos) { - if (!_stereo) { - _pantrack = true; - _pantrackPosition = pos; - setVolume(_volume); - } +void MusicNode::setDeltaVolume(uint8 volume) { + _deltaVolume = volume; + setVolume(_volume); } -void MusicNode::unsetPanTrack() { - _pantrack = false; - setVolume(_volume); +void MusicNode::setBalance(int8 balance) { + _balance = balance; + _engine->_mixer->setChannelBalance(_handle, _balance); } void MusicNode::setFade(int32 time, uint8 target) { @@ -126,7 +140,7 @@ bool MusicNode::process(uint32 deltaTimeInMillis) { if (_crossfadeTime > 0) { if ((int32)deltaTimeInMillis > _crossfadeTime) deltaTimeInMillis = _crossfadeTime; - _newvol += floor(((float)(_crossfadeTarget - _newvol) / (float)_crossfadeTime)) * (float)deltaTimeInMillis; + _newvol += (int)(floor(((float)(_crossfadeTarget - _newvol) / (float)_crossfadeTime)) * (float)deltaTimeInMillis); _crossfadeTime -= deltaTimeInMillis; } else { _crossfade = false; @@ -134,10 +148,10 @@ bool MusicNode::process(uint32 deltaTimeInMillis) { } } - if (_pantrack || _volume != _newvol) + if (_volume != _newvol) setVolume(_newvol); - if (_sub) + if (_sub && _engine->getScriptManager()->getStateValue(StateKey_Subtitles) == 1) _sub->process(_engine->_mixer->getSoundElapsedTime(_handle) / 100); } return false; @@ -146,59 +160,89 @@ bool MusicNode::process(uint32 deltaTimeInMillis) { void MusicNode::setVolume(uint8 newVolume) { if (!_loaded) return; - if (_pantrack) { - int curX = _engine->getScriptManager()->getStateValue(StateKey_ViewPos); - curX -= _pantrackPosition; - int32 _width = _engine->getRenderManager()->getBkgSize().x; - if (curX < (-_width) / 2) - curX += _width; - else if (curX >= _width / 2) - curX -= _width; - - float norm = (float)curX / ((float)_width / 2.0); - float lvl = fabs(norm); - if (lvl > 0.5) - lvl = (lvl - 0.5) * 1.7; - else - lvl = 1.0; - float bal = sin(-norm * 3.1415926) * 127.0; + _volume = newVolume; - if (_engine->_mixer->isSoundHandleActive(_handle)) { - _engine->_mixer->setChannelBalance(_handle, bal); - _engine->_mixer->setChannelVolume(_handle, newVolume * lvl); - } - } else { - if (_engine->_mixer->isSoundHandleActive(_handle)) { - _engine->_mixer->setChannelBalance(_handle, 0); - _engine->_mixer->setChannelVolume(_handle, newVolume); - } - } + if (_deltaVolume >= _volume) + _engine->_mixer->setChannelVolume(_handle, 0); + else + _engine->_mixer->setChannelVolume(_handle, dbMapLinear[_volume - _deltaVolume]); +} - _volume = newVolume; +uint8 MusicNode::getVolume() { + return _volume; } PanTrackNode::PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos) - : SideFX(engine, key, SIDEFX_PANTRACK) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_PANTRACK) { _slot = slot; + _position = pos; - SideFX *fx = _engine->getScriptManager()->getSideFX(slot); - if (fx && fx->getType() == SIDEFX_AUDIO) { - MusicNodeBASE *mus = (MusicNodeBASE *)fx; - mus->setPanTrack(pos); - } + // Try to set pan value for music node immediately + process(0); } PanTrackNode::~PanTrackNode() { - SideFX *fx = _engine->getScriptManager()->getSideFX(_slot); - if (fx && fx->getType() == SIDEFX_AUDIO) { +} + +bool PanTrackNode::process(uint32 deltaTimeInMillis) { + ScriptManager * scriptManager = _engine->getScriptManager(); + ScriptingEffect *fx = scriptManager->getSideFX(_slot); + if (fx && fx->getType() == SCRIPTING_EFFECT_AUDIO) { MusicNodeBASE *mus = (MusicNodeBASE *)fx; - mus->unsetPanTrack(); + + int curPos = scriptManager->getStateValue(StateKey_ViewPos); + int16 _width = _engine->getRenderManager()->getBkgSize().x; + int16 _halfWidth = _width / 2; + int16 _quarterWidth = _width / 4; + + int tmp = 0; + if (curPos <= _position) + tmp = _position - curPos; + else + tmp = _position - curPos + _width; + + int balance = 0; + + if (tmp > _halfWidth) + tmp -= _width; + + if (tmp > _quarterWidth) { + balance = 1; + tmp = _halfWidth - tmp; + } else if (tmp < -_quarterWidth) { + balance = -1; + tmp = -_halfWidth - tmp; + } + + // Originally it's value -90...90 but we use -127...127 and therefore 360 replaced by 508 + mus->setBalance( (508 * tmp) / _width ); + + tmp = (360 * tmp) / _width; + + int deltaVol = balance; + + // This value sets how fast volume goes off than sound source back of you + // By this value we can hack some "bugs" have place in originall game engine like beat sound in ZGI-dc10 + int volumeCorrection = 2; + + if (_engine->getGameId() == GID_GRANDINQUISITOR) { + if (scriptManager->getCurrentLocation() == "dc10") + volumeCorrection = 5; + } + + if (deltaVol != 0) + deltaVol = (mus->getVolume() * volumeCorrection) * (90 - tmp * balance) / 90; + if (deltaVol > 255) + deltaVol = 255; + + mus->setDeltaVolume(deltaVol); } + return false; } MusicMidiNode::MusicMidiNode(ZVision *engine, uint32 key, int8 program, int8 note, int8 volume) - : MusicNodeBASE(engine, key, SIDEFX_AUDIO) { + : MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) { _volume = volume; _prog = program; _noteNumber = note; @@ -225,10 +269,10 @@ MusicMidiNode::~MusicMidiNode() { _engine->getScriptManager()->setStateValue(_key, 2); } -void MusicMidiNode::setPanTrack(int16 pos) { +void MusicMidiNode::setDeltaVolume(uint8 volume) { } -void MusicMidiNode::unsetPanTrack() { +void MusicMidiNode::setBalance(int8 balance) { } void MusicMidiNode::setFade(int32 time, uint8 target) { @@ -245,4 +289,8 @@ void MusicMidiNode::setVolume(uint8 newVolume) { _volume = newVolume; } +uint8 MusicMidiNode::getVolume() { + return _volume; +} + } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/music_node.h b/engines/zvision/scripting/effects/music_effect.h index 09bdc3707e..7657be8e09 100644 --- a/engines/zvision/scripting/sidefx/music_node.h +++ b/engines/zvision/scripting/effects/music_effect.h @@ -24,8 +24,8 @@ #define ZVISION_MUSIC_NODE_H #include "audio/mixer.h" -#include "zvision/scripting/sidefx.h" -#include "zvision/graphics/subtitles.h" +#include "zvision/scripting/scripting_effect.h" +#include "zvision/text/subtitles.h" namespace Common { class String; @@ -33,9 +33,9 @@ class String; namespace ZVision { -class MusicNodeBASE : public SideFX { +class MusicNodeBASE : public ScriptingEffect { public: - MusicNodeBASE(ZVision *engine, uint32 key, SideFXType type) : SideFX(engine, key, type) {} + MusicNodeBASE(ZVision *engine, uint32 key, ScriptingEffectType type) : ScriptingEffect(engine, key, type) {} ~MusicNodeBASE() {} /** @@ -48,16 +48,16 @@ public: virtual bool process(uint32 deltaTimeInMillis) = 0; virtual void setVolume(uint8 volume) = 0; - - virtual void setPanTrack(int16 pos) = 0; - virtual void unsetPanTrack() = 0; + virtual uint8 getVolume() = 0; + virtual void setDeltaVolume(uint8 volume) = 0; + virtual void setBalance(int8 balance) = 0; virtual void setFade(int32 time, uint8 target) = 0; }; class MusicNode : public MusicNodeBASE { public: - MusicNode(ZVision *engine, uint32 key, Common::String &file, bool loop, int8 volume); + MusicNode(ZVision *engine, uint32 key, Common::String &file, bool loop, uint8 volume); ~MusicNode(); /** @@ -70,17 +70,16 @@ public: bool process(uint32 deltaTimeInMillis); void setVolume(uint8 volume); - - void setPanTrack(int16 pos); - void unsetPanTrack(); + uint8 getVolume(); + void setDeltaVolume(uint8 volume); + void setBalance(int8 balance); void setFade(int32 time, uint8 target); private: - bool _pantrack; - int32 _pantrackPosition; - int32 _attenuate; uint8 _volume; + uint8 _deltaVolume; + int8 _balance; bool _loop; bool _crossfade; uint8 _crossfadeTarget; @@ -91,6 +90,7 @@ private: bool _loaded; }; +// Only used by Zork: Nemesis, for the flute and piano puzzles (tj4e and ve6f, as well as vr) class MusicMidiNode : public MusicNodeBASE { public: MusicMidiNode(ZVision *engine, uint32 key, int8 program, int8 note, int8 volume); @@ -106,9 +106,9 @@ public: bool process(uint32 deltaTimeInMillis); void setVolume(uint8 volume); - - void setPanTrack(int16 pos); - void unsetPanTrack(); + uint8 getVolume(); + void setDeltaVolume(uint8 volume); + void setBalance(int8 balance); void setFade(int32 time, uint8 target); @@ -120,13 +120,16 @@ private: int8 _prog; }; -class PanTrackNode : public SideFX { +class PanTrackNode : public ScriptingEffect { public: PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos); ~PanTrackNode(); + bool process(uint32 deltaTimeInMillis); + private: uint32 _slot; + int16 _position; }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/region_node.cpp b/engines/zvision/scripting/effects/region_effect.cpp index de613d8af2..78061cf4de 100644 --- a/engines/zvision/scripting/sidefx/region_node.cpp +++ b/engines/zvision/scripting/effects/region_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/region_node.h" +#include "zvision/scripting/effects/region_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -30,8 +30,8 @@ namespace ZVision { -RegionNode::RegionNode(ZVision *engine, uint32 key, Effect *effect, uint32 delay) - : SideFX(engine, key, SIDEFX_REGION) { +RegionNode::RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay) + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_REGION) { _effect = effect; _delay = delay; _timeLeft = 0; diff --git a/engines/zvision/scripting/sidefx/region_node.h b/engines/zvision/scripting/effects/region_effect.h index ec716b6e3e..4fc16224ff 100644 --- a/engines/zvision/scripting/sidefx/region_node.h +++ b/engines/zvision/scripting/effects/region_effect.h @@ -25,16 +25,16 @@ #include "graphics/surface.h" -#include "zvision/scripting/sidefx.h" -#include "zvision/graphics/effect.h" +#include "zvision/scripting/scripting_effect.h" +#include "zvision/graphics/graphics_effect.h" namespace ZVision { class ZVision; -class RegionNode : public SideFX { +class RegionNode : public ScriptingEffect { public: - RegionNode(ZVision *engine, uint32 key, Effect *effect, uint32 delay); + RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay); ~RegionNode(); /** @@ -49,7 +49,7 @@ public: private: int32 _timeLeft; uint32 _delay; - Effect *_effect; + GraphicsEffect *_effect; }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/syncsound_node.cpp b/engines/zvision/scripting/effects/syncsound_effect.cpp index c1f139694b..70ba97deb8 100644 --- a/engines/zvision/scripting/sidefx/syncsound_node.cpp +++ b/engines/zvision/scripting/effects/syncsound_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/syncsound_node.h" +#include "zvision/scripting/effects/syncsound_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -36,7 +36,7 @@ namespace ZVision { SyncSoundNode::SyncSoundNode(ZVision *engine, uint32 key, Common::String &filename, int32 syncto) - : SideFX(engine, key, SIDEFX_AUDIO) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_AUDIO) { _syncto = syncto; _sub = NULL; @@ -76,7 +76,7 @@ bool SyncSoundNode::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getSideFX(_syncto) == NULL) return stop(); - if (_sub) + if (_sub && _engine->getScriptManager()->getStateValue(StateKey_Subtitles) == 1) _sub->process(_engine->_mixer->getSoundElapsedTime(_handle) / 100); } return false; diff --git a/engines/zvision/scripting/sidefx/syncsound_node.h b/engines/zvision/scripting/effects/syncsound_effect.h index 7cd02a8aef..0eabff77a3 100644 --- a/engines/zvision/scripting/sidefx/syncsound_node.h +++ b/engines/zvision/scripting/effects/syncsound_effect.h @@ -24,15 +24,15 @@ #define ZVISION_SYNCSOUND_NODE_H #include "audio/mixer.h" -#include "zvision/scripting/sidefx.h" -#include "zvision/graphics/subtitles.h" +#include "zvision/scripting/scripting_effect.h" +#include "zvision/text/subtitles.h" namespace Common { class String; } namespace ZVision { -class SyncSoundNode : public SideFX { +class SyncSoundNode : public ScriptingEffect { public: SyncSoundNode(ZVision *engine, uint32 key, Common::String &file, int32 syncto); ~SyncSoundNode(); diff --git a/engines/zvision/scripting/sidefx/timer_node.cpp b/engines/zvision/scripting/effects/timer_effect.cpp index 170f6e7472..778f9dec6c 100644 --- a/engines/zvision/scripting/sidefx/timer_node.cpp +++ b/engines/zvision/scripting/effects/timer_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/timer_node.h" +#include "zvision/scripting/effects/timer_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -32,7 +32,7 @@ namespace ZVision { TimerNode::TimerNode(ZVision *engine, uint32 key, uint timeInSeconds) - : SideFX(engine, key, SIDEFX_TIMER) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_TIMER) { _timeLeft = 0; if (_engine->getGameId() == GID_NEMESIS) diff --git a/engines/zvision/scripting/sidefx/timer_node.h b/engines/zvision/scripting/effects/timer_effect.h index 7a26aff251..5e45d54d7d 100644 --- a/engines/zvision/scripting/sidefx/timer_node.h +++ b/engines/zvision/scripting/effects/timer_effect.h @@ -23,13 +23,13 @@ #ifndef ZVISION_TIMER_NODE_H #define ZVISION_TIMER_NODE_H -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" namespace ZVision { class ZVision; -class TimerNode : public SideFX { +class TimerNode : public ScriptingEffect { public: TimerNode(ZVision *engine, uint32 key, uint timeInSeconds); ~TimerNode(); diff --git a/engines/zvision/scripting/sidefx/ttytext_node.cpp b/engines/zvision/scripting/effects/ttytext_effect.cpp index 9a7fa01649..8d340dae39 100644 --- a/engines/zvision/scripting/sidefx/ttytext_node.cpp +++ b/engines/zvision/scripting/effects/ttytext_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/ttytext_node.h" +#include "zvision/scripting/effects/ttytext_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -35,7 +35,7 @@ namespace ZVision { ttyTextNode::ttyTextNode(ZVision *engine, uint32 key, const Common::String &file, const Common::Rect &r, int32 delay) : - SideFX(engine, key, SIDEFX_TTYTXT), + ScriptingEffect(engine, key, SCRIPTING_EFFECT_TTYTXT), _fnt(engine) { _delay = delay; _r = r; @@ -56,10 +56,10 @@ ttyTextNode::ttyTextNode(ZVision *engine, uint32 key, const Common::String &file delete infile; } - _img.create(_r.width(), _r.height(), _engine->_pixelFormat); - _style.sharp = true; - _style.readAllStyle(_txtbuf); - _style.setFont(_fnt); + _img.create(_r.width(), _r.height(), _engine->_resourcePixelFormat); + _state._sharp = true; + _state.readAllStyles(_txtbuf); + _state.updateFontWithTextState(_fnt); _engine->getScriptManager()->setStateValue(_key, 1); } @@ -74,29 +74,27 @@ bool ttyTextNode::process(uint32 deltaTimeInMillis) { if (_nexttime < 0) { if (_txtpos < _txtbuf.size()) { if (_txtbuf[_txtpos] == '<') { - int32 strt = _txtpos; - int32 endt = 0; + int32 start = _txtpos; + int32 end = 0; int16 ret = 0; while (_txtbuf[_txtpos] != '>' && _txtpos < _txtbuf.size()) _txtpos++; - endt = _txtpos; - if (strt != -1) - if ((endt - strt - 1) > 0) - ret = _style.parseStyle(_txtbuf.c_str() + strt + 1, endt - strt - 1); - - if (ret & (TXT_RET_FNTCHG | TXT_RET_FNTSTL | TXT_RET_NEWLN)) { - if (ret & TXT_RET_FNTCHG) - _style.setFont(_fnt); - if (ret & TXT_RET_FNTSTL) - _style.setFontStyle(_fnt); - - if (ret & TXT_RET_NEWLN) - newline(); + end = _txtpos; + if (start != -1) { + if ((end - start - 1) > 0) { + ret = _state.parseStyle(_txtbuf.c_str() + start + 1, end - start - 1); + } + } + + if (ret & (TEXT_CHANGE_FONT_TYPE | TEXT_CHANGE_FONT_STYLE)) { + _state.updateFontWithTextState(_fnt); + } else if (ret & TEXT_CHANGE_NEWLINE) { + newline(); } - if (ret & TXT_RET_HASSTBOX) { + if (ret & TEXT_CHANGE_HAS_STATE_BOX) { Common::String buf; - buf = Common::String::format("%d", _engine->getScriptManager()->getStateValue(_style.statebox)); + buf = Common::String::format("%d", _engine->getScriptManager()->getStateValue(_state._statebox)); for (uint8 j = 0; j < buf.size(); j++) outchar(buf[j]); @@ -158,7 +156,7 @@ void ttyTextNode::newline() { } void ttyTextNode::outchar(uint16 chr) { - uint32 clr = _engine->_pixelFormat.RGBToColor(_style.red, _style.green, _style.blue); + uint32 clr = _engine->_resourcePixelFormat.RGBToColor(_state._red, _state._green, _state._blue); if (_dx + _fnt.getCharWidth(chr) > _r.width()) newline(); diff --git a/engines/zvision/scripting/sidefx/ttytext_node.h b/engines/zvision/scripting/effects/ttytext_effect.h index b6cbed3e34..18cbbbaee3 100644 --- a/engines/zvision/scripting/sidefx/ttytext_node.h +++ b/engines/zvision/scripting/effects/ttytext_effect.h @@ -26,16 +26,16 @@ #include "common/rect.h" #include "graphics/surface.h" -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" #include "zvision/text/text.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" namespace Common { class String; } namespace ZVision { -class ttyTextNode : public SideFX { +class ttyTextNode : public ScriptingEffect { public: ttyTextNode(ZVision *engine, uint32 key, const Common::String &file, const Common::Rect &r, int32 delay); ~ttyTextNode(); @@ -51,7 +51,7 @@ public: private: Common::Rect _r; - cTxtStyle _style; + TextStyleState _state; StyledTTFont _fnt; Common::String _txtbuf; uint32 _txtpos; diff --git a/engines/zvision/scripting/menu.cpp b/engines/zvision/scripting/menu.cpp new file mode 100644 index 0000000000..064bd1b57d --- /dev/null +++ b/engines/zvision/scripting/menu.cpp @@ -0,0 +1,761 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "zvision/graphics/render_manager.h" +#include "zvision/scripting/menu.h" + +namespace ZVision { + +enum { + kMainMenuSave = 0, + kMainMenuLoad = 1, + kMainMenuPrefs = 2, + kMainMenuExit = 3 +}; + +enum { + kMenuItem = 0, + kMenuMagic = 1, + kMenuMain = 2 +}; + +MenuHandler::MenuHandler(ZVision *engine) { + _engine = engine; + menuBarFlag = 0xFFFF; +} + +MenuZGI::MenuZGI(ZVision *engine) : + MenuHandler(engine) { + menuMouseFocus = -1; + inMenu = false; + scrolled[0] = false; + scrolled[1] = false; + scrolled[2] = false; + scrollPos[0] = 0; + scrollPos[1] = 0; + scrollPos[2] = 0; + mouseOnItem = -1; + redraw = false; + clean = false; + + char buf[24]; + for (int i = 1; i < 4; i++) { + sprintf(buf, "gmzau%2.2x1.tga", i); + _engine->getRenderManager()->readImageToSurface(buf, menuBack[i - 1][0], false); + sprintf(buf, "gmzau%2.2x1.tga", i + 0x10); + _engine->getRenderManager()->readImageToSurface(buf, menuBack[i - 1][1], false); + } + for (int i = 0; i < 4; i++) { + sprintf(buf, "gmzmu%2.2x1.tga", i); + _engine->getRenderManager()->readImageToSurface(buf, menuBar[i][0], false); + sprintf(buf, "gmznu%2.2x1.tga", i); + _engine->getRenderManager()->readImageToSurface(buf, menuBar[i][1], false); + } + + for (int i = 0; i < 50; i++) { + items[i][0] = NULL; + items[i][1] = NULL; + itemId[i] = 0; + } + + for (int i = 0; i < 12; i++) { + magic[i][0] = NULL; + magic[i][1] = NULL; + magicId[i] = 0; + } +} + +MenuZGI::~MenuZGI() { + for (int i = 0; i < 3; i++) { + menuBack[i][0].free(); + menuBack[i][1].free(); + } + for (int i = 0; i < 4; i++) { + menuBar[i][0].free(); + menuBar[i][1].free(); + } + for (int i = 0; i < 50; i++) { + if (items[i][0]) { + items[i][0]->free(); + delete items[i][0]; + } + if (items[i][1]) { + items[i][1]->free(); + delete items[i][1]; + } + } + for (int i = 0; i < 12; i++) { + if (magic[i][0]) { + magic[i][0]->free(); + delete magic[i][0]; + } + if (magic[i][1]) { + magic[i][1]->free(); + delete magic[i][1]; + } + } +} + +void MenuZGI::onMouseUp(const Common::Point &Pos) { + if (Pos.y < 40) { + switch (menuMouseFocus) { + case kMenuItem: + if (menuBarFlag & kMenubarItems) { + int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); + if (itemCount == 0) + itemCount = 20; + + for (int i = 0; i < itemCount; i++) { + int itemspace = (600 - 28) / itemCount; + + if (Common::Rect(scrollPos[kMenuItem] + itemspace * i, 0, + scrollPos[kMenuItem] + itemspace * i + 28, 32).contains(Pos)) { + int32 mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem); + if (mouseItem >= 0 && mouseItem < 0xE0) { + _engine->getScriptManager()->inventoryDrop(mouseItem); + _engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i)); + _engine->getScriptManager()->setStateValue(StateKey_Inv_StartSlot + i, mouseItem); + + redraw = true; + } + } + } + } + break; + + case kMenuMagic: + if (menuBarFlag & kMenubarMagic) { + for (int i = 0; i < 12; i++) { + + uint itemnum = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i); + if (itemnum != 0) { + if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1) + itemnum = 0xEE + i; + else + itemnum = 0xE0 + i; + } + if (itemnum) + if (_engine->getScriptManager()->getStateValue(StateKey_InventoryItem) == 0 || _engine->getScriptManager()->getStateValue(StateKey_InventoryItem) >= 0xE0) + if (Common::Rect(668 + 47 * i - scrollPos[kMenuMagic], 0, + 668 + 47 * i - scrollPos[kMenuMagic] + 28, 32).contains(Pos)) + _engine->getScriptManager()->setStateValue(StateKey_Active_Spell, itemnum); + } + + } + break; + + case kMenuMain: + + // Exit + if (menuBarFlag & kMenubarExit) + if (Common::Rect(320 + 135, + scrollPos[kMenuMain], + 320 + 135 + 135, + scrollPos[kMenuMain] + 32).contains(Pos)) { + _engine->ifQuit(); + } + + // Settings + if (menuBarFlag & kMenubarSettings) + if (Common::Rect(320 , + scrollPos[kMenuMain], + 320 + 135, + scrollPos[kMenuMain] + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 'p', 'e', 0); + } + + // Load + if (menuBarFlag & kMenubarRestore) + if (Common::Rect(320 - 135, + scrollPos[kMenuMain], + 320, + scrollPos[kMenuMain] + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 'r', 'e', 0); + } + + // Save + if (menuBarFlag & kMenubarSave) + if (Common::Rect(320 - 135 * 2, + scrollPos[kMenuMain], + 320 - 135, + scrollPos[kMenuMain] + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 's', 'e', 0); + } + break; + } + } +} + +void MenuZGI::onMouseMove(const Common::Point &Pos) { + if (Pos.y < 40) { + + if (!inMenu) + redraw = true; + inMenu = true; + switch (menuMouseFocus) { + case kMenuItem: + if (menuBarFlag & kMenubarItems) { + int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); + if (itemCount == 0) + itemCount = 20; + else if (itemCount > 50) + itemCount = 50; + + int lastItem = mouseOnItem; + + mouseOnItem = -1; + + for (int i = 0; i < itemCount; i++) { + int itemspace = (600 - 28) / itemCount; + + if (Common::Rect(scrollPos[kMenuItem] + itemspace * i, 0, + scrollPos[kMenuItem] + itemspace * i + 28, 32).contains(Pos)) { + mouseOnItem = i; + break; + } + } + + if (lastItem != mouseOnItem) + if (_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + mouseOnItem) || + _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + lastItem)) + redraw = true; + } + break; + + case kMenuMagic: + if (menuBarFlag & kMenubarMagic) { + int lastItem = mouseOnItem; + mouseOnItem = -1; + for (int i = 0; i < 12; i++) { + if (Common::Rect(668 + 47 * i - scrollPos[kMenuMagic], 0, + 668 + 47 * i - scrollPos[kMenuMagic] + 28, 32).contains(Pos)) { + mouseOnItem = i; + break; + } + } + + if (lastItem != mouseOnItem) + if (_engine->getScriptManager()->getStateValue(StateKey_Spell_1 + mouseOnItem) || + _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + lastItem)) + redraw = true; + + } + break; + + case kMenuMain: { + int lastItem = mouseOnItem; + mouseOnItem = -1; + + // Exit + if (menuBarFlag & kMenubarExit) + if (Common::Rect(320 + 135, + scrollPos[kMenuMain], + 320 + 135 + 135, + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuExit; + } + + // Settings + if (menuBarFlag & kMenubarSettings) + if (Common::Rect(320 , + scrollPos[kMenuMain], + 320 + 135, + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuPrefs; + } + + // Load + if (menuBarFlag & kMenubarRestore) + if (Common::Rect(320 - 135, + scrollPos[kMenuMain], + 320, + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuLoad; + } + + // Save + if (menuBarFlag & kMenubarSave) + if (Common::Rect(320 - 135 * 2, + scrollPos[kMenuMain], + 320 - 135, + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuSave; + } + + if (lastItem != mouseOnItem) + redraw = true; + } + break; + + default: + int cur_menu = menuMouseFocus; + if (Common::Rect(64, 0, 64 + 512, 8).contains(Pos)) { // Main + menuMouseFocus = kMenuMain; + scrolled[kMenuMain] = false; + scrollPos[kMenuMain] = menuBack[kMenuMain][1].h - menuBack[kMenuMain][0].h; + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 2); + } + + if (menuBarFlag & kMenubarMagic) + if (Common::Rect(640 - 28, 0, 640, 32).contains(Pos)) { // Magic + menuMouseFocus = kMenuMagic; + scrolled[kMenuMagic] = false; + scrollPos[kMenuMagic] = 28; + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 3); + } + + if (menuBarFlag & kMenubarItems) + if (Common::Rect(0, 0, 28, 32).contains(Pos)) { // Items + menuMouseFocus = kMenuItem; + scrolled[kMenuItem] = false; + scrollPos[kMenuItem] = 28 - 600; + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 1); + } + + if (cur_menu != menuMouseFocus) + clean = true; + + break; + } + } else { + if (inMenu) + clean = true; + inMenu = false; + if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0) + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 0); + menuMouseFocus = -1; + } +} + +void MenuZGI::process(uint32 deltatime) { + if (clean) { + _engine->getRenderManager()->clearMenuSurface(); + clean = false; + } + switch (menuMouseFocus) { + case kMenuItem: + if (menuBarFlag & kMenubarItems) + if (!scrolled[kMenuItem]) { + redraw = true; + float scrl = 600.0 * (deltatime / 1000.0); + + if (scrl == 0) + scrl = 1.0; + + scrollPos[kMenuItem] += (int)scrl; + + if (scrollPos[kMenuItem] >= 0) { + scrolled[kMenuItem] = true; + scrollPos[kMenuItem] = 0; + } + } + if (redraw) { + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuItem][0], scrollPos[kMenuItem], 0); + + int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); + if (itemCount == 0) + itemCount = 20; + else if (itemCount > 50) + itemCount = 50; + + for (int i = 0; i < itemCount; i++) { + int itemspace = (600 - 28) / itemCount; + + bool inrect = false; + + if (mouseOnItem == i) + inrect = true; + + uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i); + + if (curItemId != 0) { + if (itemId[i] != curItemId) { + char buf[16]; + sprintf(buf, "gmzwu%2.2x1.tga", curItemId); + items[i][0] = _engine->getRenderManager()->loadImage(buf, false); + sprintf(buf, "gmzxu%2.2x1.tga", curItemId); + items[i][1] = _engine->getRenderManager()->loadImage(buf, false); + itemId[i] = curItemId; + } + + if (inrect) + _engine->getRenderManager()->blitSurfaceToMenu(*items[i][1], scrollPos[kMenuItem] + itemspace * i, 0, 0); + else + _engine->getRenderManager()->blitSurfaceToMenu(*items[i][0], scrollPos[kMenuItem] + itemspace * i, 0, 0); + + } else { + if (items[i][0]) { + items[i][0]->free(); + delete items[i][0]; + items[i][0] = NULL; + } + if (items[i][1]) { + items[i][1]->free(); + delete items[i][1]; + items[i][1] = NULL; + } + itemId[i] = 0; + } + } + + redraw = false; + } + break; + + case kMenuMagic: + if (menuBarFlag & kMenubarMagic) + if (!scrolled[kMenuMagic]) { + redraw = true; + float scrl = 600.0 * (deltatime / 1000.0); + + if (scrl == 0) + scrl = 1.0; + + scrollPos[kMenuMagic] += (int)scrl; + + if (scrollPos[kMenuMagic] >= 600) { + scrolled[kMenuMagic] = true; + scrollPos[kMenuMagic] = 600; + } + } + if (redraw) { + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuMagic][0], 640 - scrollPos[kMenuMagic], 0); + + for (int i = 0; i < 12; i++) { + bool inrect = false; + + if (mouseOnItem == i) + inrect = true; + + uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i); + if (curItemId) { + if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1) + curItemId = 0xEE + i; + else + curItemId = 0xE0 + i; + } + + if (curItemId != 0) { + if (itemId[i] != curItemId) { + char buf[16]; + sprintf(buf, "gmzwu%2.2x1.tga", curItemId); + magic[i][0] = _engine->getRenderManager()->loadImage(buf, false); + sprintf(buf, "gmzxu%2.2x1.tga", curItemId); + magic[i][1] = _engine->getRenderManager()->loadImage(buf, false); + magicId[i] = curItemId; + } + + if (inrect) + _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][1], 668 + 47 * i - scrollPos[kMenuMagic], 0, 0); + else + _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][0], 668 + 47 * i - scrollPos[kMenuMagic], 0, 0); + + } else { + if (magic[i][0]) { + magic[i][0]->free(); + delete magic[i][0]; + magic[i][0] = NULL; + } + if (magic[i][1]) { + magic[i][1]->free(); + delete magic[i][1]; + magic[i][1] = NULL; + } + magicId[i] = 0; + } + } + redraw = false; + } + break; + + case kMenuMain: + if (!scrolled[kMenuMain]) { + redraw = true; + float scrl = 32.0 * 2.0 * (deltatime / 1000.0); + + if (scrl == 0) + scrl = 1.0; + + scrollPos[kMenuMain] += (int)scrl; + + if (scrollPos[kMenuMain] >= 0) { + scrolled[kMenuMain] = true; + scrollPos[kMenuMain] = 0; + } + } + if (redraw) { + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuMain][0], 30, scrollPos[kMenuMain]); + + if (menuBarFlag & kMenubarExit) { + if (mouseOnItem == kMainMenuExit) + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuExit][1], 320 + 135, scrollPos[kMenuMain]); + else + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuExit][0], 320 + 135, scrollPos[kMenuMain]); + } + if (menuBarFlag & kMenubarSettings) { + if (mouseOnItem == kMainMenuPrefs) + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuPrefs][1], 320, scrollPos[kMenuMain]); + else + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuPrefs][0], 320, scrollPos[kMenuMain]); + } + if (menuBarFlag & kMenubarRestore) { + if (mouseOnItem == kMainMenuLoad) + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuLoad][1], 320 - 135, scrollPos[kMenuMain]); + else + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuLoad][0], 320 - 135, scrollPos[kMenuMain]); + } + if (menuBarFlag & kMenubarSave) { + if (mouseOnItem == kMainMenuSave) + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuSave][1], 320 - 135 * 2, scrollPos[kMenuMain]); + else + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuSave][0], 320 - 135 * 2, scrollPos[kMenuMain]); + } + redraw = false; + } + break; + default: + if (redraw) { + if (inMenu) { + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuMain][1], 30, 0); + + if (menuBarFlag & kMenubarItems) + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuItem][1], 0, 0); + + if (menuBarFlag & kMenubarMagic) + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuMagic][1], 640 - 28, 0); + } + redraw = false; + } + break; + } +} + +MenuNemesis::MenuNemesis(ZVision *engine) : + MenuHandler(engine) { + inMenu = false; + scrolled = false; + scrollPos = 0; + mouseOnItem = -1; + redraw = false; + delay = 0; + + char buf[24]; + for (int i = 0; i < 4; i++) + for (int j = 0; j < 6; j++) { + sprintf(buf, "butfrm%d%d.tga", i + 1, j); + _engine->getRenderManager()->readImageToSurface(buf, but[i][j], false); + } + + _engine->getRenderManager()->readImageToSurface("bar.tga", menuBar, false); + + frm = 0; +} + +MenuNemesis::~MenuNemesis() { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 6; j++) + but[i][j].free(); + + menuBar.free(); +} + +static const int16 buts[4][2] = { {120 , 64}, {144, 184}, {128, 328}, {120, 456} }; + +void MenuNemesis::onMouseUp(const Common::Point &Pos) { + if (Pos.y < 40) { + // Exit + if (menuBarFlag & kMenubarExit) + if (Common::Rect(buts[3][1], + scrollPos, + buts[3][0] + buts[3][1], + scrollPos + 32).contains(Pos)) { + _engine->ifQuit(); + frm = 5; + redraw = true; + } + + // Settings + if (menuBarFlag & kMenubarSettings) + if (Common::Rect(buts[2][1], + scrollPos, + buts[2][0] + buts[2][1], + scrollPos + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 'p', 'e', 0); + frm = 5; + redraw = true; + } + + // Load + if (menuBarFlag & kMenubarRestore) + if (Common::Rect(buts[1][1], + scrollPos, + buts[1][0] + buts[1][1], + scrollPos + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 'r', 'e', 0); + frm = 5; + redraw = true; + } + + // Save + if (menuBarFlag & kMenubarSave) + if (Common::Rect(buts[0][1], + scrollPos, + buts[0][0] + buts[0][1], + scrollPos + 32).contains(Pos)) { + _engine->getScriptManager()->changeLocation('g', 'j', 's', 'e', 0); + frm = 5; + redraw = true; + } + } +} + +void MenuNemesis::onMouseMove(const Common::Point &Pos) { + if (Pos.y < 40) { + + inMenu = true; + + if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 2) + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 2); + + int lastItem = mouseOnItem; + mouseOnItem = -1; + + // Exit + if (menuBarFlag & kMenubarExit) + if (Common::Rect(buts[3][1], + scrollPos, + buts[3][0] + buts[3][1], + scrollPos + 32).contains(Pos)) { + mouseOnItem = kMainMenuExit; + } + + // Settings + if (menuBarFlag & kMenubarSettings) + if (Common::Rect(buts[2][1], + scrollPos, + buts[2][0] + buts[2][1], + scrollPos + 32).contains(Pos)) { + mouseOnItem = kMainMenuPrefs; + } + + // Load + if (menuBarFlag & kMenubarRestore) + if (Common::Rect(buts[1][1], + scrollPos, + buts[1][0] + buts[1][1], + scrollPos + 32).contains(Pos)) { + mouseOnItem = kMainMenuLoad; + } + + // Save + if (menuBarFlag & kMenubarSave) + if (Common::Rect(buts[0][1], + scrollPos, + buts[0][0] + buts[0][1], + scrollPos + 32).contains(Pos)) { + mouseOnItem = kMainMenuSave; + } + + if (lastItem != mouseOnItem) { + redraw = true; + frm = 0; + delay = 200; + } + } else { + inMenu = false; + if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0) + _engine->getScriptManager()->setStateValue(StateKey_MenuState, 0); + mouseOnItem = -1; + } +} + +void MenuNemesis::process(uint32 deltatime) { + if (inMenu) { + if (!scrolled) { + float scrl = 32.0 * 2.0 * (deltatime / 1000.0); + + if (scrl == 0) + scrl = 1.0; + + scrollPos += (int)scrl; + redraw = true; + } + + if (scrollPos >= 0) { + scrolled = true; + scrollPos = 0; + } + + if (mouseOnItem != -1) { + delay -= deltatime; + if (delay <= 0 && frm < 4) { + delay = 200; + frm++; + redraw = true; + } + } + + if (redraw) { + _engine->getRenderManager()->blitSurfaceToMenu(menuBar, 64, scrollPos); + + if (menuBarFlag & kMenubarExit) + if (mouseOnItem == kMainMenuExit) + _engine->getRenderManager()->blitSurfaceToMenu(but[3][frm], buts[3][1], scrollPos); + + if (menuBarFlag & kMenubarSettings) + if (mouseOnItem == kMainMenuPrefs) + _engine->getRenderManager()->blitSurfaceToMenu(but[2][frm], buts[2][1], scrollPos); + + if (menuBarFlag & kMenubarRestore) + if (mouseOnItem == kMainMenuLoad) + _engine->getRenderManager()->blitSurfaceToMenu(but[1][frm], buts[1][1], scrollPos); + + if (menuBarFlag & kMenubarSave) + if (mouseOnItem == kMainMenuSave) + _engine->getRenderManager()->blitSurfaceToMenu(but[0][frm], buts[0][1], scrollPos); + + redraw = false; + } + } else { + scrolled = false; + if (scrollPos > -32) { + float scrl = 32.0 * 2.0 * (deltatime / 1000.0); + + if (scrl == 0) + scrl = 1.0; + + Common::Rect cl(64, (int16)(32 + scrollPos - scrl), 64 + 512, 32 + scrollPos + 1); + _engine->getRenderManager()->clearMenuSurface(cl); + + scrollPos -= (int)scrl; + redraw = true; + } else + scrollPos = -32; + + if (redraw) { + _engine->getRenderManager()->blitSurfaceToMenu(menuBar, 64, scrollPos); + redraw = false; + } + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/scripting/menu.h b/engines/zvision/scripting/menu.h new file mode 100644 index 0000000000..f6b21b9c97 --- /dev/null +++ b/engines/zvision/scripting/menu.h @@ -0,0 +1,119 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ZVISION_MENU_H +#define ZVISION_MENU_H + +#include "graphics/surface.h" +#include "common/rect.h" + +#include "zvision/zvision.h" +#include "zvision/scripting/script_manager.h" + +namespace ZVision { + +enum menuBar { + kMenubarExit = 0x1, + kMenubarSettings = 0x2, + kMenubarRestore = 0x4, + kMenubarSave = 0x8, + kMenubarItems = 0x100, + kMenubarMagic = 0x200 +}; + +class MenuHandler { +public: + MenuHandler(ZVision *engine); + virtual ~MenuHandler() {}; + virtual void onMouseMove(const Common::Point &Pos) {}; + virtual void onMouseDown(const Common::Point &Pos) {}; + virtual void onMouseUp(const Common::Point &Pos) {}; + virtual void process(uint32 deltaTimeInMillis) {}; + + void setEnable(uint16 flags) { + menuBarFlag = flags; + } + uint16 getEnable() { + return menuBarFlag; + } +protected: + uint16 menuBarFlag; + ZVision *_engine; +}; + +class MenuZGI: public MenuHandler { +public: + MenuZGI(ZVision *engine); + ~MenuZGI(); + void onMouseMove(const Common::Point &Pos); + void onMouseUp(const Common::Point &Pos); + void process(uint32 deltaTimeInMillis); +private: + Graphics::Surface menuBack[3][2]; + Graphics::Surface menuBar[4][2]; + Graphics::Surface *items[50][2]; + uint itemId[50]; + + Graphics::Surface *magic[12][2]; + uint magicId[12]; + + int menuMouseFocus; + bool inMenu; + + int mouseOnItem; + + bool scrolled[3]; + int16 scrollPos[3]; + + bool clean; + bool redraw; + +}; + +class MenuNemesis: public MenuHandler { +public: + MenuNemesis(ZVision *engine); + ~MenuNemesis(); + void onMouseMove(const Common::Point &Pos); + void onMouseUp(const Common::Point &Pos); + void process(uint32 deltaTimeInMillis); +private: + Graphics::Surface but[4][6]; + Graphics::Surface menuBar; + + bool inMenu; + + int mouseOnItem; + + bool scrolled; + int16 scrollPos; + + bool redraw; + + int frm; + int16 delay; + +}; + +} // End of namespace ZVision + +#endif 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; diff --git a/engines/zvision/scripting/script_manager.cpp b/engines/zvision/scripting/script_manager.cpp index 4c1e69072d..70eaab2a0a 100644 --- a/engines/zvision/scripting/script_manager.cpp +++ b/engines/zvision/scripting/script_manager.cpp @@ -27,9 +27,10 @@ #include "zvision/zvision.h" #include "zvision/graphics/render_manager.h" #include "zvision/graphics/cursors/cursor_manager.h" -#include "zvision/core/save_manager.h" +#include "zvision/file/save_manager.h" #include "zvision/scripting/actions.h" -#include "zvision/scripting/sidefx/timer_node.h" +#include "zvision/scripting/menu.h" +#include "zvision/scripting/effects/timer_effect.h" #include "common/algorithm.h" #include "common/hashmap.h" @@ -71,21 +72,23 @@ void ScriptManager::initialize() { } void ScriptManager::update(uint deltaTimeMillis) { - if (_currentLocation.node != _nextLocation.node || - _currentLocation.room != _nextLocation.room || - _currentLocation.view != _nextLocation.view || - _currentLocation.world != _nextLocation.world) - ChangeLocationReal(); + if (_currentLocation != _nextLocation) { + ChangeLocationReal(false); + } updateNodes(deltaTimeMillis); - if (! execScope(nodeview)) + if (!execScope(nodeview)) { return; - if (! execScope(room)) + } + if (!execScope(room)) { return; - if (! execScope(world)) + } + if (!execScope(world)) { return; - if (! execScope(universe)) + } + if (!execScope(universe)) { return; + } updateControls(deltaTimeMillis); } @@ -96,17 +99,22 @@ bool ScriptManager::execScope(ScriptScope &scope) { scope.scopeQueue = tmp; scope.scopeQueue->clear(); - for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) + for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) { (*PuzzleIter)->addedBySetState = false; + } if (scope.procCount < 2 || getStateValue(StateKey_ExecScopeStyle)) { - for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) - if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) + for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) { + if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) { return false; + } + } } else { - for (PuzzleList::iterator PuzzleIter = scope.execQueue->begin(); PuzzleIter != scope.execQueue->end(); ++PuzzleIter) - if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) + for (PuzzleList::iterator PuzzleIter = scope.execQueue->begin(); PuzzleIter != scope.execQueue->end(); ++PuzzleIter) { + if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) { return false; + } + } } if (scope.procCount < 2) { @@ -118,9 +126,11 @@ bool ScriptManager::execScope(ScriptScope &scope) { void ScriptManager::referenceTableAddPuzzle(uint32 key, PuzzleRef ref) { if (_referenceTable.contains(key)) { Common::Array<PuzzleRef> *arr = &_referenceTable[key]; - for (uint32 i = 0; i < arr->size(); i++) - if ((*arr)[i].puz == ref.puz) + for (uint32 i = 0; i < arr->size(); i++) { + if ((*arr)[i].puz == ref.puz) { return; + } + } } _referenceTable[key].push_back(ref); @@ -138,9 +148,11 @@ void ScriptManager::addPuzzlesToReferenceTable(ScriptScope &scope) { referenceTableAddPuzzle(puzzlePtr->key, ref); // Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle - for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = (*PuzzleIter)->criteriaList.begin(); criteriaIter != (*PuzzleIter)->criteriaList.end(); ++criteriaIter) - for (Common::List<Puzzle::CriteriaEntry>::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) + for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = (*PuzzleIter)->criteriaList.begin(); criteriaIter != (*PuzzleIter)->criteriaList.end(); ++criteriaIter) { + for (Common::List<Puzzle::CriteriaEntry>::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { referenceTableAddPuzzle(entryIter->key, ref); + } + } } } @@ -158,8 +170,9 @@ void ScriptManager::updateNodes(uint deltaTimeMillis) { } void ScriptManager::updateControls(uint deltaTimeMillis) { - if (!_activeControls) + if (!_activeControls) { return; + } // Process only one event if (!_controlEvents.empty()) { @@ -186,21 +199,24 @@ void ScriptManager::updateControls(uint deltaTimeMillis) { _controlEvents.pop_front(); } - for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); iter++) - if ((*iter)->process(deltaTimeMillis)) + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); iter++) { + if ((*iter)->process(deltaTimeMillis)) { break; + } + } } bool ScriptManager::checkPuzzleCriteria(Puzzle *puzzle, uint counter) { // Check if the puzzle is already finished // Also check that the puzzle isn't disabled - if (getStateValue(puzzle->key) == 1 || (getStateFlag(puzzle->key) & Puzzle::DISABLED) == Puzzle::DISABLED) { + if (getStateValue(puzzle->key) == 1 || (getStateFlag(puzzle->key) & Puzzle::DISABLED)) { return true; } // Check each Criteria - if (counter == 0 && (getStateFlag(puzzle->key) & Puzzle::DO_ME_NOW) == 0) + if (counter == 0 && (getStateFlag(puzzle->key) & Puzzle::DO_ME_NOW) == 0) { return true; + } bool criteriaMet = false; for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = puzzle->criteriaList.begin(); criteriaIter != puzzle->criteriaList.end(); ++criteriaIter) { @@ -209,10 +225,11 @@ bool ScriptManager::checkPuzzleCriteria(Puzzle *puzzle, uint counter) { for (Common::List<Puzzle::CriteriaEntry>::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { // Get the value to compare against int argumentValue; - if (entryIter->argumentIsAKey) + if (entryIter->argumentIsAKey) { argumentValue = getStateValue(entryIter->argument); - else + } else { argumentValue = entryIter->argument; + } // Do the comparison switch (entryIter->criteriaOperator) { @@ -250,8 +267,9 @@ bool ScriptManager::checkPuzzleCriteria(Puzzle *puzzle, uint counter) { setStateValue(puzzle->key, 1); for (Common::List<ResultAction *>::iterator resultIter = puzzle->resultActions.begin(); resultIter != puzzle->resultActions.end(); ++resultIter) { - if (!(*resultIter)->execute()) + if (!(*resultIter)->execute()) { return false; + } } } @@ -274,13 +292,15 @@ void ScriptManager::cleanScriptScope(ScriptScope &scope) { scope.privQueueTwo.clear(); scope.scopeQueue = &scope.privQueueOne; scope.execQueue = &scope.privQueueTwo; - for (PuzzleList::iterator iter = scope.puzzles.begin(); iter != scope.puzzles.end(); ++iter) + for (PuzzleList::iterator iter = scope.puzzles.begin(); iter != scope.puzzles.end(); ++iter) { delete(*iter); + } scope.puzzles.clear(); - for (ControlList::iterator iter = scope.controls.begin(); iter != scope.controls.end(); ++iter) + for (ControlList::iterator iter = scope.controls.begin(); iter != scope.controls.end(); ++iter) { delete(*iter); + } scope.controls.clear(); @@ -288,44 +308,49 @@ void ScriptManager::cleanScriptScope(ScriptScope &scope) { } int ScriptManager::getStateValue(uint32 key) { - if (_globalState.contains(key)) + if (_globalState.contains(key)) { return _globalState[key]; - else + } else { return 0; + } } void ScriptManager::queuePuzzles(uint32 key) { if (_referenceTable.contains(key)) { Common::Array<PuzzleRef> *arr = &_referenceTable[key]; - for (int32 i = arr->size() - 1; i >= 0; i--) + for (int32 i = arr->size() - 1; i >= 0; i--) { if (!(*arr)[i].puz->addedBySetState) { (*arr)[i].scope->scopeQueue->push_back((*arr)[i].puz); (*arr)[i].puz->addedBySetState = true; } + } } } void ScriptManager::setStateValue(uint32 key, int value) { - if (value == 0) + if (value == 0) { _globalState.erase(key); - else + } else { _globalState[key] = value; + } queuePuzzles(key); } void ScriptManager::setStateValueSilent(uint32 key, int value) { - if (value == 0) + if (value == 0) { _globalState.erase(key); - else + } else { _globalState[key] = value; + } } uint ScriptManager::getStateFlag(uint32 key) { - if (_globalStateFlags.contains(key)) + if (_globalStateFlags.contains(key)) { return _globalStateFlags[key]; - else + } else { return 0; + } } void ScriptManager::setStateFlag(uint32 key, uint value) { @@ -335,10 +360,11 @@ void ScriptManager::setStateFlag(uint32 key, uint value) { } void ScriptManager::setStateFlagSilent(uint32 key, uint value) { - if (value == 0) + if (value == 0) { _globalStateFlags.erase(key); - else + } else { _globalStateFlags[key] = value; + } } void ScriptManager::unsetStateFlag(uint32 key, uint value) { @@ -347,23 +373,29 @@ void ScriptManager::unsetStateFlag(uint32 key, uint value) { if (_globalStateFlags.contains(key)) { _globalStateFlags[key] &= ~value; - if (_globalStateFlags[key] == 0) + if (_globalStateFlags[key] == 0) { _globalStateFlags.erase(key); + } } } Control *ScriptManager::getControl(uint32 key) { - for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) - if ((*iter)->getKey() == key) + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { + if ((*iter)->getKey() == key) { return *iter; + } + } + return nullptr; } void ScriptManager::focusControl(uint32 key) { - if (!_activeControls) + if (!_activeControls) { return; - if (_currentlyFocusedControl == key) + } + if (_currentlyFocusedControl == key) { return; + } for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { uint32 controlKey = (*iter)->getKey(); @@ -381,11 +413,11 @@ void ScriptManager::setFocusControlKey(uint32 key) { _currentlyFocusedControl = key; } -void ScriptManager::addSideFX(SideFX *fx) { +void ScriptManager::addSideFX(ScriptingEffect *fx) { _activeSideFx.push_back(fx); } -SideFX *ScriptManager::getSideFX(uint32 key) { +ScriptingEffect *ScriptManager::getSideFX(uint32 key) { for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) { if ((*iter)->getKey() == key) { return (*iter); @@ -429,7 +461,7 @@ void ScriptManager::killSideFx(uint32 key) { } } -void ScriptManager::killSideFxType(SideFX::SideFXType type) { +void ScriptManager::killSideFxType(ScriptingEffect::ScriptingEffectType type) { for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end();) { if ((*iter)->getType() & type) { (*iter)->kill(); @@ -442,50 +474,60 @@ void ScriptManager::killSideFxType(SideFX::SideFXType type) { } void ScriptManager::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_activeControls) + if (!_activeControls) { return; + } for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) { - if ((*iter)->onMouseDown(screenSpacePos, backgroundImageSpacePos)) + if ((*iter)->onMouseDown(screenSpacePos, backgroundImageSpacePos)) { return; + } } } void ScriptManager::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_activeControls) + if (!_activeControls) { return; + } for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) { - if ((*iter)->onMouseUp(screenSpacePos, backgroundImageSpacePos)) + if ((*iter)->onMouseUp(screenSpacePos, backgroundImageSpacePos)) { return; + } } } bool ScriptManager::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_activeControls) + if (!_activeControls) { return false; + } for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) { - if ((*iter)->onMouseMove(screenSpacePos, backgroundImageSpacePos)) + if ((*iter)->onMouseMove(screenSpacePos, backgroundImageSpacePos)) { return true; + } } return false; } void ScriptManager::onKeyDown(Common::KeyState keyState) { - if (!_activeControls) + if (!_activeControls) { return; + } for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { - if ((*iter)->onKeyDown(keyState)) + if ((*iter)->onKeyDown(keyState)) { return; + } } } void ScriptManager::onKeyUp(Common::KeyState keyState) { - if (!_activeControls) + if (!_activeControls) { return; + } for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { - if ((*iter)->onKeyUp(keyState)) + if ((*iter)->onKeyUp(keyState)) { return; + } } } @@ -499,8 +541,8 @@ void ScriptManager::changeLocation(char _world, char _room, char _node, char _vi _nextLocation.node = _node; _nextLocation.view = _view; _nextLocation.offset = offset; - // If next location 0000 - it's indicate to go to previous location. - if (_nextLocation.world == '0' && _nextLocation.room == '0' && _nextLocation.node == '0' && _nextLocation.view == '0') { + // If next location is 0000, return to the previous location. + if (_nextLocation == "0000") { if (getStateValue(StateKey_World) != 'g' || getStateValue(StateKey_Room) != 'j') { _nextLocation.world = getStateValue(StateKey_LastWorld); _nextLocation.room = getStateValue(StateKey_LastRoom); @@ -517,36 +559,42 @@ void ScriptManager::changeLocation(char _world, char _room, char _node, char _vi } } -void ScriptManager::ChangeLocationReal() { +void ScriptManager::ChangeLocationReal(bool isLoading) { assert(_nextLocation.world != 0); debug(1, "Changing location to: %c %c %c %c %u", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view, _nextLocation.offset); - if (_nextLocation.world == 'g' && _nextLocation.room == 'j' && !ConfMan.getBool("originalsaveload")) { - if ((_nextLocation.node == 's' || _nextLocation.node == 'r') && _nextLocation.view == 'e') { + const bool enteringMenu = (_nextLocation.world == 'g' && _nextLocation.room == 'j'); + const bool leavingMenu = (_currentLocation.world == 'g' && _currentLocation.room == 'j'); + const bool isSaveScreen = (enteringMenu && _nextLocation.node == 's' && _nextLocation.view == 'e'); + const bool isRestoreScreen = (enteringMenu && _nextLocation.node == 'r' && _nextLocation.view == 'e'); + + if (enteringMenu && !ConfMan.getBool("originalsaveload")) { + if (isSaveScreen || isRestoreScreen) { // Hook up the ScummVM save/restore dialog - bool isSave = (_nextLocation.node == 's'); - bool gameSavedOrLoaded = _engine->getSaveManager()->scummVMSaveLoadDialog(isSave); - if (!gameSavedOrLoaded || isSave) { + bool gameSavedOrLoaded = _engine->getSaveManager()->scummVMSaveLoadDialog(isSaveScreen); + if (!gameSavedOrLoaded || isSaveScreen) { // Reload the current room _nextLocation.world = _currentLocation.world; _nextLocation.room = _currentLocation.room; _nextLocation.node = _currentLocation.node; _nextLocation.view = _currentLocation.view; _nextLocation.offset = _currentLocation.offset; - _currentLocation.world = '0'; + + return; + } else { + _currentLocation.world = 'g'; _currentLocation.room = '0'; _currentLocation.node = '0'; _currentLocation.view = '0'; _currentLocation.offset = 0; - } else - return; + } } } _engine->setRenderDelay(2); - if (getStateValue(StateKey_World) != 'g' || getStateValue(StateKey_Room) != 'j') { - if (_nextLocation.world != 'g' || _nextLocation.room != 'j') { + if (!leavingMenu) { + if (!isLoading && !enteringMenu) { setStateValue(StateKey_LastWorld, getStateValue(StateKey_World)); setStateValue(StateKey_LastRoom, getStateValue(StateKey_Room)); setStateValue(StateKey_LastNode, getStateValue(StateKey_Node)); @@ -561,16 +609,13 @@ void ScriptManager::ChangeLocationReal() { } } - if (_nextLocation.world == 'g' && _nextLocation.room == 'j') { - if (_nextLocation.node == 's' && _nextLocation.view == 'e' && - _currentLocation.world != 'g' && _currentLocation.room != 'j') + if (enteringMenu) { + if (isSaveScreen && !leavingMenu) { _engine->getSaveManager()->prepareSaveBuffer(); + } } else { - if (_currentLocation.world == 'g' && _currentLocation.room == 'j') + if (leavingMenu) { _engine->getSaveManager()->flushSaveBuffer(); - else { - // Auto save - //_engine->getSaveManager()->autoSave(); } } @@ -583,7 +628,7 @@ void ScriptManager::ChangeLocationReal() { _referenceTable.clear(); addPuzzlesToReferenceTable(universe); - _engine->menuBarEnable(0xFFFF); + _engine->getMenuHandler()->setEnable(0xFFFF); if (_nextLocation.world != _currentLocation.world) { cleanScriptScope(nodeview); @@ -634,7 +679,7 @@ void ScriptManager::ChangeLocationReal() { // Change the background position _engine->getRenderManager()->setBackgroundPosition(_nextLocation.offset); - if (_currentLocation.world == 0 && _currentLocation.room == 0 && _currentLocation.node == 0 && _currentLocation.view == 0) { + if (_currentLocation == "0000") { _currentLocation = _nextLocation; execScope(world); execScope(room); @@ -652,7 +697,7 @@ void ScriptManager::ChangeLocationReal() { execScope(nodeview); } - _engine->checkBorders(); + _engine->getRenderManager()->checkBorders(); } void ScriptManager::serialize(Common::WriteStream *stream) { @@ -667,26 +712,30 @@ void ScriptManager::serialize(Common::WriteStream *stream) { stream->writeByte(getStateValue(StateKey_View)); stream->writeUint32LE(getStateValue(StateKey_ViewPos)); - for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) { (*iter)->serialize(stream); + } stream->writeUint32BE(MKTAG('F', 'L', 'A', 'G')); int32 slots = 20000; - if (_engine->getGameId() == GID_NEMESIS) + if (_engine->getGameId() == GID_NEMESIS) { slots = 30000; + } stream->writeUint32LE(slots * 2); - for (int32 i = 0; i < slots; i++) + for (int32 i = 0; i < slots; i++) { stream->writeUint16LE(getStateFlag(i)); + } stream->writeUint32BE(MKTAG('P', 'U', 'Z', 'Z')); stream->writeUint32LE(slots * 2); - for (int32 i = 0; i < slots; i++) + for (int32 i = 0; i < slots; i++) { stream->writeSint16LE(getStateValue(i)); + } } void ScriptManager::deserialize(Common::SeekableReadStream *stream) { @@ -703,8 +752,9 @@ void ScriptManager::deserialize(Common::SeekableReadStream *stream) { _currentLocation.room = 0; _currentLocation.view = 0; - for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); iter++) + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); iter++) { delete(*iter); + } _activeSideFx.clear(); @@ -737,20 +787,23 @@ void ScriptManager::deserialize(Common::SeekableReadStream *stream) { case MKTAG('T', 'I', 'M', 'R'): { uint32 key = stream->readUint32LE(); uint32 time = stream->readUint32LE(); - if (_engine->getGameId() == GID_GRANDINQUISITOR) + if (_engine->getGameId() == GID_GRANDINQUISITOR) { time /= 100; - else if (_engine->getGameId() == GID_NEMESIS) + } else if (_engine->getGameId() == GID_NEMESIS) { time /= 1000; + } addSideFX(new TimerNode(_engine, key, time)); } break; case MKTAG('F', 'L', 'A', 'G'): - for (uint32 i = 0; i < tagSize / 2; i++) + for (uint32 i = 0; i < tagSize / 2; i++) { setStateFlagSilent(i, stream->readUint16LE()); + } break; case MKTAG('P', 'U', 'Z', 'Z'): - for (uint32 i = 0; i < tagSize / 2; i++) + for (uint32 i = 0; i < tagSize / 2; i++) { setStateValueSilent(i, stream->readUint16LE()); + } break; default: stream->seek(tagSize, SEEK_CUR); @@ -759,7 +812,7 @@ void ScriptManager::deserialize(Common::SeekableReadStream *stream) { _nextLocation = nextLocation; - ChangeLocationReal(); + ChangeLocationReal(true); _engine->setRenderDelay(10); setStateValue(StateKey_RestoreFlag, 1); @@ -804,10 +857,11 @@ void ScriptManager::flushEvent(Common::EventType type) { EventList::iterator it = _controlEvents.begin(); while (it != _controlEvents.end()) { - if ((*it).type == type) + if ((*it).type == type) { it = _controlEvents.erase(it); - else + } else { it++; + } } } @@ -836,12 +890,15 @@ ValueSlot::ValueSlot(ScriptManager *scriptManager, const char *slotValue): } int16 ValueSlot::getValue() { if (slot) { - if (value >= 0) + if (value >= 0) { return _scriptManager->getStateValue(value); - else + } + else { return 0; - } else + } + } else { return value; + } } } // End of namespace ZVision diff --git a/engines/zvision/scripting/script_manager.h b/engines/zvision/scripting/script_manager.h index 1e308faf0d..7c276bf917 100644 --- a/engines/zvision/scripting/script_manager.h +++ b/engines/zvision/scripting/script_manager.h @@ -25,7 +25,7 @@ #include "zvision/scripting/puzzle.h" #include "zvision/scripting/control.h" -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" #include "common/hashmap.h" #include "common/queue.h" @@ -87,6 +87,7 @@ enum StateKey { StateKey_JapanFonts = 75, StateKey_ExecScopeStyle = 76, StateKey_Brightness = 77, + StateKey_MPEGMovies = 78, StateKey_EF9_R = 91, StateKey_EF9_G = 92, StateKey_EF9_B = 93, @@ -94,7 +95,12 @@ enum StateKey { StateKey_Inv_Cnt_Slot = 100, StateKey_Inv_1_Slot = 101, StateKey_Inv_49_Slot = 149, - StateKey_Inv_TotalSlots = 150 + // ZGI only + StateKey_Inv_TotalSlots = 150, + StateKey_Inv_StartSlot = 151, + StateKey_Spell_1 = 191, + StateKey_Active_Spell = 205, + StateKey_Reversed_Spellbooc = 206 }; struct Location { @@ -107,11 +113,33 @@ struct Location { uint32 offset; }; +inline bool operator==(const Location& lhs, const Location& rhs) { + return ( + lhs.world == rhs.world && + lhs.room == rhs.room && + lhs.node == rhs.node && + lhs.view == rhs.view + ); +} + +inline bool operator==(const Location& lhs, const char* rhs) { + Common::String lhsStr = Common::String::format("%c%c%c%c", lhs.world, lhs.room, lhs.node, lhs.view); + return lhsStr == rhs; +} + +inline bool operator!=(const Location& lhs, const Location& rhs) { + return !(lhs == rhs); +} + +inline bool operator!=(const Location& lhs, const char* rhs) { + return !(lhs == rhs); +} + typedef Common::List<Puzzle *> PuzzleList; typedef Common::Queue<Puzzle *> PuzzleQueue; typedef Common::List<Control *> ControlList; typedef Common::HashMap<uint32, int32> StateMap; -typedef Common::List<SideFX *> SideFXList; +typedef Common::List<ScriptingEffect *> SideFXList; typedef Common::List<Common::Event> EventList; class ScriptManager { @@ -191,12 +219,12 @@ public: // Only change focus control without call focus/unfocus. void setFocusControlKey(uint32 key); - void addSideFX(SideFX *fx); - SideFX *getSideFX(uint32 key); + void addSideFX(ScriptingEffect *fx); + ScriptingEffect *getSideFX(uint32 key); void deleteSideFx(uint32 key); void stopSideFx(uint32 key); void killSideFx(uint32 key); - void killSideFxType(SideFX::SideFXType type); + void killSideFxType(ScriptingEffect::ScriptingEffectType type); void addEvent(Common::Event); void flushEvent(Common::EventType type); @@ -267,7 +295,7 @@ private: bool execScope(ScriptScope &scope); /** Perform change location */ - void ChangeLocationReal(); + void ChangeLocationReal(bool isLoading); int8 inventoryGetCount(); void inventorySetCount(int8 cnt); @@ -282,7 +310,7 @@ public: void inventoryDrop(int16 item); void inventoryCycle(); - // TODO: Make this private. It was only made public so Console::cmdParseAllScrFiles() could use it +private: /** * Parses a script file into triggers and events * @@ -291,7 +319,6 @@ public: */ void parseScrFile(const Common::String &fileName, ScriptScope &scope); -private: /** * Parses the stream into a Puzzle object * Helper method for parseScrFile. @@ -307,9 +334,10 @@ private: * * @param criteria Pointer to the Criteria object to fill * @param stream Scr file stream + * @param key Puzzle key (for workarounds) * @return Whether any criteria were read */ - bool parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList) const; + bool parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList, uint32 key) const; /** * Parses the stream into a ResultAction objects diff --git a/engines/zvision/scripting/sidefx.h b/engines/zvision/scripting/scripting_effect.h index 5bb14f0cdd..2a2153204f 100644 --- a/engines/zvision/scripting/sidefx.h +++ b/engines/zvision/scripting/scripting_effect.h @@ -20,8 +20,8 @@ * */ -#ifndef SIDEFX_H_INCLUDED -#define SIDEFX_H_INCLUDED +#ifndef SCRIPTING_EFFECT_H_INCLUDED +#define SCRIPTING_EFFECT_H_INCLUDED namespace Common { class SeekableReadStream; @@ -33,29 +33,39 @@ namespace ZVision { class ZVision; -class SideFX { +/** + * The base class that represents effects created from Actions. + * This class is virtual. + * + * Detailed Description: + * A scene has Controls. By interacting with the controls, the user + * causes Actions to execute. Certain Actions create 'effects', for + * example, a sound or an animation. This is the base class for + * those effects. + */ +class ScriptingEffect { public: - enum SideFXType { - SIDEFX_ANIM = 1, - SIDEFX_AUDIO = 2, - SIDEFX_DISTORT = 4, - SIDEFX_PANTRACK = 8, - SIDEFX_REGION = 16, - SIDEFX_TIMER = 32, - SIDEFX_TTYTXT = 64, - SIDEFX_UNK = 128, - SIDEFX_ALL = 255 + enum ScriptingEffectType { + SCRIPTING_EFFECT_ANIM = 1, + SCRIPTING_EFFECT_AUDIO = 2, + SCRIPTING_EFFECT_DISTORT = 4, + SCRIPTING_EFFECT_PANTRACK = 8, + SCRIPTING_EFFECT_REGION = 16, + SCRIPTING_EFFECT_TIMER = 32, + SCRIPTING_EFFECT_TTYTXT = 64, + SCRIPTING_EFFECT_UNKNOWN = 128, + SCRIPTING_EFFECT_ALL = 255 }; - SideFX() : _engine(0), _key(0), _type(SIDEFX_UNK) {} - SideFX(ZVision *engine, uint32 key, SideFXType type) : _engine(engine), _key(key), _type(type) {} - virtual ~SideFX() {} + ScriptingEffect() : _engine(0), _key(0), _type(SCRIPTING_EFFECT_UNKNOWN) {} + ScriptingEffect(ZVision *engine, uint32 key, ScriptingEffectType type) : _engine(engine), _key(key), _type(type) {} + virtual ~ScriptingEffect() {} uint32 getKey() { return _key; } - SideFXType getType() { + ScriptingEffectType getType() { return _type; } @@ -103,7 +113,7 @@ public: protected: ZVision *_engine; uint32 _key; - SideFXType _type; + ScriptingEffectType _type; // Static member functions public: @@ -111,4 +121,4 @@ public: }; } // End of namespace ZVision -#endif // SIDEFX_H_INCLUDED +#endif // SCRIPTING_EFFECT_H_INCLUDED |