diff options
Diffstat (limited to 'engines/zvision/scripting/actions.cpp')
-rw-r--r-- | engines/zvision/scripting/actions.cpp | 1011 |
1 files changed, 853 insertions, 158 deletions
diff --git a/engines/zvision/scripting/actions.cpp b/engines/zvision/scripting/actions.cpp index 517278e155..9a8b734e0c 100644 --- a/engines/zvision/scripting/actions.cpp +++ b/engines/zvision/scripting/actions.cpp @@ -8,12 +8,12 @@ * 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. @@ -21,380 +21,1075 @@ */ #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/controls/timer_node.h" -#include "zvision/scripting/controls/animation_control.h" - -#include "common/file.h" - -#include "audio/decoders/wave.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/graphics_effect.h" +#include "zvision/graphics/effects/fog.h" +#include "zvision/graphics/effects/light.h" +#include "zvision/graphics/effects/wave.h" +#include "zvision/graphics/cursors/cursor_manager.h" namespace ZVision { +ResultAction::ResultAction(ZVision *engine, int32 slotkey) : _engine(engine), _slotKey(slotkey), _scriptManager(engine->getScriptManager()) { +} + ////////////////////////////////////////////////////////////////////////////// // ActionAdd ////////////////////////////////////////////////////////////////////////////// -ActionAdd::ActionAdd(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u,%u)", &_key, &_value); +ActionAdd::ActionAdd(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _key = 0; + _value = 0; + + sscanf(line.c_str(), "%u,%d", &_key, &_value); } -bool ActionAdd::execute(ZVision *engine) { - engine->getScriptManager()->addToStateValue(_key, _value); +bool ActionAdd::execute() { + _scriptManager->setStateValue(_key, _scriptManager->getStateValue(_key) + _value); return true; } - ////////////////////////////////////////////////////////////////////////////// // ActionAssign ////////////////////////////////////////////////////////////////////////////// -ActionAssign::ActionAssign(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u, %u)", &_key, &_value); +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(_scriptManager, buf); } -bool ActionAssign::execute(ZVision *engine) { - engine->getScriptManager()->setStateValue(_key, _value); - return true; +ActionAssign::~ActionAssign() { + delete _value; } +bool ActionAssign::execute() { + _scriptManager->setStateValue(_key, _value->getValue()); + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionAttenuate ////////////////////////////////////////////////////////////////////////////// -ActionAttenuate::ActionAttenuate(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u, %d)", &_key, &_attenuation); +ActionAttenuate::ActionAttenuate(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _key = 0; + _attenuation = 0; + + sscanf(line.c_str(), "%u, %d", &_key, &_attenuation); } -bool ActionAttenuate::execute(ZVision *engine) { - // TODO: Implement +bool ActionAttenuate::execute() { + 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; } - ////////////////////////////////////////////////////////////////////////////// // ActionChangeLocation ////////////////////////////////////////////////////////////////////////////// -ActionChangeLocation::ActionChangeLocation(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%c,%c,%c%c,%u)", &_world, &_room, &_node, &_view, &_offset); +ActionChangeLocation::ActionChangeLocation(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _world = 'g'; + _room = 'a'; + _node = 'r'; + _view = 'y'; + _offset = 0; + + sscanf(line.c_str(), "%c, %c, %c%c, %u", &_world, &_room, &_node, &_view, &_offset); } -bool ActionChangeLocation::execute(ZVision *engine) { +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; } - ////////////////////////////////////////////////////////////////////////////// // ActionCrossfade ////////////////////////////////////////////////////////////////////////////// -ActionCrossfade::ActionCrossfade(const Common::String &line) { +ActionCrossfade::ActionCrossfade(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _keyOne = 0; + _keyTwo = 0; + _oneStartVolume = 0; + _twoStartVolume = 0; + _oneEndVolume = 0; + _twoEndVolume = 0; + _timeInMillis = 0; + sscanf(line.c_str(), - "%*[^(](%u %u %u %u %u %u %u)", - &_keyOne, &_keyTwo, &_oneStartVolume, &_twoStartVolume, &_oneEndVolume, &_twoEndVolume, &_timeInMillis); + "%u %u %d %d %d %d %d", + &_keyOne, &_keyTwo, &_oneStartVolume, &_twoStartVolume, &_oneEndVolume, &_twoEndVolume, &_timeInMillis); +} + +bool ActionCrossfade::execute() { + if (_keyOne) { + 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); + + mus->setFade(_timeInMillis, (_oneEndVolume * 255) / 100); + } + } + + if (_keyTwo) { + 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); + + mus->setFade(_timeInMillis, (_twoEndVolume * 255) / 100); + } + } + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionCursor +////////////////////////////////////////////////////////////////////////////// + +ActionCursor::ActionCursor(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + Common::String up = line; + up.toUppercase(); + _action = 0; + + if (up[0] == 'B') + _action = 2; + else if (up[0] == 'I') + _action = 3; + else if (up[0] == 'U') + _action = 0; + else if (up[0] == 'H') + _action = 1; } -bool ActionCrossfade::execute(ZVision *engine) { - // TODO: Implement +bool ActionCursor::execute() { + switch (_action) { + case 1: + _engine->getCursorManager()->showMouse(false); + break; + default: + _engine->getCursorManager()->showMouse(true); + break; + } return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionDelayRender +////////////////////////////////////////////////////////////////////////////// + +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() { + _engine->setRenderDelay(_framesToDelay); + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionDisableControl ////////////////////////////////////////////////////////////////////////////// -ActionDisableControl::ActionDisableControl(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u)", &_key); +ActionDisableControl::ActionDisableControl(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _key = 0; + + sscanf(line.c_str(), "%u", &_key); +} + +bool ActionDisableControl::execute() { + _scriptManager->setStateFlag(_key, Puzzle::DISABLED); + return true; } -bool ActionDisableControl::execute(ZVision *engine) { - debug("Disabling control %u", _key); - - ScriptManager *scriptManager = engine->getScriptManager(); - scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) | ScriptManager::DISABLED); +////////////////////////////////////////////////////////////////////////////// +// ActionDisplayMessage +////////////////////////////////////////////////////////////////////////////// +ActionDisplayMessage::ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _control = 0; + _msgid = 0; + + sscanf(line.c_str(), "%hd %hd", &_control, &_msgid); +} + +bool ActionDisplayMessage::execute() { + Control *ctrl = _scriptManager->getControl(_control); + if (ctrl && ctrl->getType() == Control::CONTROL_TITLER) { + TitlerControl *titler = (TitlerControl *)ctrl; + titler->setString(_msgid); + } return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionDissolve +////////////////////////////////////////////////////////////////////////////// + +ActionDissolve::ActionDissolve(ZVision *engine) : + ResultAction(engine, 0) { +} + +bool ActionDissolve::execute() { + // Cause black screen flick + // _engine->getRenderManager()->bkgFill(0, 0, 0); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// 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) { + _distSlot = 0; + _speed = 0; + _startAngle = 60.0; + _endAngle = 60.0; + _startLineScale = 1.0; + _endLineScale = 1.0; + + sscanf(line.c_str(), "%hd %hd %f %f %f %f", &_distSlot, &_speed, &_startAngle, &_endAngle, &_startLineScale, &_endLineScale); +} + +ActionDistort::~ActionDistort() { + _scriptManager->killSideFx(_distSlot); +} + +bool ActionDistort::execute() { + if (_scriptManager->getSideFX(_distSlot)) + return true; + + _scriptManager->addSideFX(new DistortNode(_engine, _distSlot, _speed, _startAngle, _endAngle, _startLineScale, _endLineScale)); + + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionEnableControl ////////////////////////////////////////////////////////////////////////////// -ActionEnableControl::ActionEnableControl(const Common::String &line) { - sscanf(line.c_str(), "%*[^(](%u)", &_key); +ActionEnableControl::ActionEnableControl(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _key = 0; + + sscanf(line.c_str(), "%u", &_key); +} + +bool ActionEnableControl::execute() { + _scriptManager->unsetStateFlag(_key, Puzzle::DISABLED); + return true; } -bool ActionEnableControl::execute(ZVision *engine) { - debug("Enabling control %u", _key); +////////////////////////////////////////////////////////////////////////////// +// ActionFlushMouseEvents +////////////////////////////////////////////////////////////////////////////// - ScriptManager *scriptManager = engine->getScriptManager(); - scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) & ~ScriptManager::DISABLED); +ActionFlushMouseEvents::ActionFlushMouseEvents(ZVision *engine, int32 slotkey) : + ResultAction(engine, slotkey) { +} +bool ActionFlushMouseEvents::execute() { + _scriptManager->flushEvent(Common::EVENT_LBUTTONUP); + _scriptManager->flushEvent(Common::EVENT_LBUTTONDOWN); return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionInventory +////////////////////////////////////////////////////////////////////////////// + +ActionInventory::ActionInventory(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _type = -1; + _key = 0; + + char buf[25]; + sscanf(line.c_str(), "%24s %d", buf, &_key); + + if (strcmp(buf, "add") == 0) { + _type = 0; + } else if (strcmp(buf, "addi") == 0) { + _type = 1; + } else if (strcmp(buf, "drop") == 0) { + _type = 2; + } else if (strcmp(buf, "dropi") == 0) { + _type = 3; + } else if (strcmp(buf, "cycle") == 0) { + _type = 4; + } + +} + +bool ActionInventory::execute() { + switch (_type) { + case 0: // add + _scriptManager->inventoryAdd(_key); + break; + case 1: // addi + _scriptManager->inventoryAdd(_scriptManager->getStateValue(_key)); + break; + case 2: // drop + if (_key >= 0) + _scriptManager->inventoryDrop(_key); + else + _scriptManager->inventoryDrop(_scriptManager->getStateValue(StateKey_InventoryItem)); + break; + case 3: // dropi + _scriptManager->inventoryDrop(_scriptManager->getStateValue(_key)); + break; + case 4: // cycle + _scriptManager->inventoryCycle(); + break; + default: + break; + } + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionKill - only used by ZGI +////////////////////////////////////////////////////////////////////////////// + +ActionKill::ActionKill(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _key = 0; + _type = 0; + char keytype[25]; + sscanf(line.c_str(), "%24s", keytype); + if (keytype[0] == '"') { + if (!scumm_stricmp(keytype, "\"ANIM\"")) + _type = ScriptingEffect::SCRIPTING_EFFECT_ANIM; + else if (!scumm_stricmp(keytype, "\"AUDIO\"")) + _type = ScriptingEffect::SCRIPTING_EFFECT_AUDIO; + else if (!scumm_stricmp(keytype, "\"DISTORT\"")) + _type = ScriptingEffect::SCRIPTING_EFFECT_DISTORT; + else if (!scumm_stricmp(keytype, "\"PANTRACK\"")) + _type = ScriptingEffect::SCRIPTING_EFFECT_PANTRACK; + else if (!scumm_stricmp(keytype, "\"REGION\"")) + _type = ScriptingEffect::SCRIPTING_EFFECT_REGION; + else if (!scumm_stricmp(keytype, "\"TIMER\"")) + _type = ScriptingEffect::SCRIPTING_EFFECT_TIMER; + else if (!scumm_stricmp(keytype, "\"TTYTEXT\"")) + _type = ScriptingEffect::SCRIPTING_EFFECT_TTYTXT; + else if (!scumm_stricmp(keytype, "\"ALL\"")) + _type = ScriptingEffect::SCRIPTING_EFFECT_ALL; + } else + _key = atoi(keytype); +} + +bool ActionKill::execute() { + if (_type) + _scriptManager->killSideFxType((ScriptingEffect::ScriptingEffectType)_type); + else + _scriptManager->killSideFx(_key); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionMenuBarEnable +////////////////////////////////////////////////////////////////////////////// + +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->getMenuHandler()->setEnable(_menus); + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionMusic ////////////////////////////////////////////////////////////////////////////// -ActionMusic::ActionMusic(const Common::String &line) : _volume(255) { - uint type; - char fileNameBuffer[26]; - uint loop; - uint 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; + char volumeBuffer[15]; + + // Volume is optional. If it doesn't appear, assume full volume + strcpy(volumeBuffer, "100"); - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%u %25s %u %u)", &_key, &type, fileNameBuffer, &loop, &volume); + sscanf(line.c_str(), "%u %24s %u %14s", &type, fileNameBuffer, &loop, volumeBuffer); - // type 4 are midi sound effect files + // 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) { - _soundType = Audio::Mixer::kSFXSoundType; - _fileName = Common::String::format("midi/%s/%u.wav", fileNameBuffer, loop); - _loop = false; + _midi = true; + int note; + int prog; + sscanf(line.c_str(), "%u %d %d %14s", &type, &prog, ¬e, volumeBuffer); + _volume = new ValueSlot(_scriptManager, volumeBuffer); + _note = note; + _prog = prog; } else { - // TODO: See what the other types are so we can specify the correct Mixer::SoundType. In the meantime use kPlainSoundType - _soundType = Audio::Mixer::kPlainSoundType; + _midi = false; _fileName = Common::String(fileNameBuffer); _loop = loop == 1 ? true : false; + 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); } - // 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; - } + // 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); + } -bool ActionMusic::execute(ZVision *engine) { - Audio::RewindableAudioStream *audioStream; +ActionMusic::~ActionMusic() { + if (!_universe) + _scriptManager->killSideFx(_slotKey); + delete _volume; +} - if (_fileName.contains(".wav")) { - Common::File *file = new Common::File(); - if (file->open(_fileName)) { - audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES); - } else { - warning("Unable to open %s", _fileName.c_str()); - return false; - } - } else { - audioStream = makeRawZorkStream(_fileName, engine); +bool ActionMusic::execute() { + if (_scriptManager->getSideFX(_slotKey)) { + _scriptManager->killSideFx(_slotKey); + _scriptManager->setStateValue(_slotKey, 2); } - - if (_loop) { - Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES); - engine->_mixer->playStream(_soundType, 0, loopingAudioStream, -1, _volume); + + uint volume = _volume->getValue(); + + if (_midi) { + _scriptManager->addSideFX(new MusicMidiNode(_engine, _slotKey, _prog, _note, volume)); } else { - engine->_mixer->playStream(_soundType, 0, audioStream, -1, _volume); + if (!_engine->getSearchManager()->hasFile(_fileName)) + return true; + + // 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; } +////////////////////////////////////////////////////////////////////////////// +// ActionPanTrack +////////////////////////////////////////////////////////////////////////////// + +ActionPanTrack::ActionPanTrack(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey), + _pos(0), + _musicSlot(0) { + + sscanf(line.c_str(), "%u %d", &_musicSlot, &_pos); +} + +ActionPanTrack::~ActionPanTrack() { + _scriptManager->killSideFx(_slotKey); +} + +bool ActionPanTrack::execute() { + if (_scriptManager->getSideFX(_slotKey)) + return true; + + _scriptManager->addSideFX(new PanTrackNode(_engine, _slotKey, _musicSlot, _pos)); + + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionPreferences +////////////////////////////////////////////////////////////////////////////// + +ActionPreferences::ActionPreferences(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + if (line.compareToIgnoreCase("save") == 0) + _save = true; + else + _save = false; +} + +bool ActionPreferences::execute() { + if (_save) + _engine->saveSettings(); + else + _engine->loadSettings(); + + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionPreloadAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPreloadAnimation::ActionPreloadAnimation(const Common::String &line) { - char fileName[26]; +ActionPreloadAnimation::ActionPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _mask = 0; + _framerate = 0; - // The two %*u are always 0 and dont seem to have a use - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%25s %*u %*u %u %u)", &_key, fileName, &_mask, &_framerate); + char fileName[25]; + + // 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); + + // 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); } -bool ActionPreloadAnimation::execute(ZVision *engine) { - // TODO: We ignore the mask and framerate atm. Mask refers to a key color used for binary alpha. We assume the framerate is the default framerate embedded in the videos - - // TODO: Check if the Control already exists +ActionPreloadAnimation::~ActionPreloadAnimation() { + _scriptManager->deleteSideFx(_slotKey); +} - // Create the control, but disable it until PlayPreload is called - ScriptManager *scriptManager = engine->getScriptManager(); - scriptManager->addControl(new AnimationControl(engine, _key, _fileName)); - scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) | ScriptManager::DISABLED); +bool ActionPreloadAnimation::execute() { + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_slotKey); + if (!nod) { + nod = new AnimationEffect(_engine, _slotKey, _fileName, _mask, _framerate, false); + _scriptManager->addSideFX(nod); + } else + nod->stop(); + _scriptManager->setStateValue(_slotKey, 2); return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionUnloadAnimation +////////////////////////////////////////////////////////////////////////////// + +ActionUnloadAnimation::ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _key = 0; + + sscanf(line.c_str(), "%u", &_key); +} + +bool ActionUnloadAnimation::execute() { + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_key); + + if (nod && nod->getType() == ScriptingEffect::SCRIPTING_EFFECT_ANIM) + _scriptManager->deleteSideFx(_key); + + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionPlayAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPlayAnimation::ActionPlayAnimation(const Common::String &line) { - char fileName[26]; +ActionPlayAnimation::ActionPlayAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _x = 0; + _y = 0; + _x2 = 0; + _y2 = 0; + _start = 0; + _end = 0; + _loopCount = 0; + _mask = 0; + _framerate = 0; + + char fileName[25]; // The two %*u are always 0 and dont seem to have a use sscanf(line.c_str(), - "%*[^:]:%*[^:]:%u(%25s %u %u %u %u %u %u %u %*u %*u %u %u)", - &_key, fileName, &_x, &_y, &_width, &_height, &_start, &_end, &_loopCount, &_mask, &_framerate); + "%24s %u %u %u %u %u %u %d %*u %*u %d %d", + fileName, &_x, &_y, &_x2, &_y2, &_start, &_end, &_loopCount, &_mask, &_framerate); + + // 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; } -bool ActionPlayAnimation::execute(ZVision *engine) { - // TODO: Implement - return true; +ActionPlayAnimation::~ActionPlayAnimation() { + _scriptManager->deleteSideFx(_slotKey); } +bool ActionPlayAnimation::execute() { + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_slotKey); + + if (!nod) { + nod = new AnimationEffect(_engine, _slotKey, _fileName, _mask, _framerate); + _scriptManager->addSideFX(nod); + } else + nod->stop(); + + if (nod) + nod->addPlayNode(_slotKey, _x, _y, _x2, _y2, _start, _end, _loopCount); + + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionPlayPreloadAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(const Common::String &line) { +ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _controlKey = 0; + _x1 = 0; + _y1 = 0; + _x2 = 0; + _y2 = 0; + _startFrame = 0; + _endFrame = 0; + _loopCount = 0; + sscanf(line.c_str(), - "%*[^:]:%*[^:]:%u(%u %u %u %u %u %u %u %u)", - &_animationKey, &_controlKey, &_x1, &_y1, &_x2, &_y2, &_startFrame, &_endFrame, &_loopCount); + "%u %u %u %u %u %u %u %u", + &_controlKey, &_x1, &_y1, &_x2, &_y2, &_startFrame, &_endFrame, &_loopCount); } -bool ActionPlayPreloadAnimation::execute(ZVision *engine) { - // Find the control - AnimationControl *control = (AnimationControl *)engine->getScriptManager()->getControl(_controlKey); +bool ActionPlayPreloadAnimation::execute() { + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_controlKey); - // Set the needed values within the control - control->setAnimationKey(_animationKey); - control->setLoopCount(_loopCount); - control->setXPos(_x1); - control->setYPost(_y1); - - // Enable the control. ScriptManager will take care of the rest - control->enable(); + if (nod) + nod->addPlayNode(_slotKey, _x1, _y1, _x2, _y2, _startFrame, _endFrame, _loopCount); return true; } - ////////////////////////////////////////////////////////////////////////////// // ActionQuit ////////////////////////////////////////////////////////////////////////////// -bool ActionQuit::execute(ZVision *engine) { - engine->quitGame(); +bool ActionQuit::execute() { + _engine->quitGame(); return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionRegion - only used by Zork: Nemesis +////////////////////////////////////////////////////////////////////////////// + +ActionRegion::ActionRegion(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _delay = 0; + _type = 0; + _unk1 = 0; + _unk2 = 0; + + char art[64]; + char custom[64]; + + int32 x1 = 0, x2 = 0, y1 = 0, y2 = 0; + + sscanf(line.c_str(), "%s %d %d %d %d %hu %hu %hu %hu %s", art, &x1, &y1, &x2, &y2, &_delay, &_type, &_unk1, &_unk2, custom); + _art = Common::String(art); + _custom = Common::String(custom); + _rect = Common::Rect(x1, y1, x2 + 1, y2 + 1); +} + +ActionRegion::~ActionRegion() { + _scriptManager->killSideFx(_slotKey); +} + +bool ActionRegion::execute() { + if (_scriptManager->getSideFX(_slotKey)) + return true; + + 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); + effect = new WaveFx(_engine, _slotKey, _rect, _unk1, frames, centerX, centerY, amplitude, waveln, speed); + } + break; + case 1: { + uint16 aX, aY, aD; + if (_engine->getRenderManager()->getRenderTable()->getRenderState() == RenderTable::PANORAMA) + sscanf(_art.c_str(), "useart[%hu,%hu,%hu]", &aY, &aX, &aD); + else + sscanf(_art.c_str(), "useart[%hu,%hu,%hu]", &aX, &aY, &aD); + int8 minD; + int8 maxD; + EffectMap *_map = _engine->getRenderManager()->makeEffectMap(Common::Point(aX, aY), aD, _rect, &minD, &maxD); + effect = new LightFx(_engine, _slotKey, _rect, _unk1, _map, atoi(_custom.c_str()), minD, maxD); + } + break; + case 9: { + int16 dum1; + int32 dum2; + char buf[64]; + sscanf(_custom.c_str(), "%hd,%d,%s", &dum1, &dum2, buf); + Graphics::Surface tempMask; + _engine->getRenderManager()->readImageToSurface(_art, tempMask); + if (_rect.width() != tempMask.w) + _rect.setWidth(tempMask.w); + if (_rect.height() != tempMask.h) + _rect.setHeight(tempMask.h); + + EffectMap *_map = _engine->getRenderManager()->makeEffectMap(tempMask, 0); + effect = new FogFx(_engine, _slotKey, _rect, _unk1, _map, Common::String(buf)); + } + break; + default: + break; + } + + if (effect) { + _scriptManager->addSideFX(new RegionNode(_engine, _slotKey, effect, _delay)); + _engine->getRenderManager()->addEffect(effect); + } + + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionRandom ////////////////////////////////////////////////////////////////////////////// -ActionRandom::ActionRandom(const Common::String &line) { - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u, %u)", &_key, &_max); +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(_scriptManager, maxBuffer); +} + +ActionRandom::~ActionRandom() { + delete _max; } -bool ActionRandom::execute(ZVision *engine) { - uint randNumber = engine->getRandomSource()->getRandomNumber(_max); - engine->getScriptManager()->setStateValue(_key, randNumber); +bool ActionRandom::execute() { + uint randNumber = _engine->getRandomSource()->getRandomNumber(_max->getValue()); + _scriptManager->setStateValue(_slotKey, randNumber); return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionRestoreGame +////////////////////////////////////////////////////////////////////////////// + +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(-1); + return false; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionRotateTo +////////////////////////////////////////////////////////////////////////////// + +ActionRotateTo::ActionRotateTo(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _time = 0; + _toPos = 0; + + sscanf(line.c_str(), "%d, %d", &_toPos, &_time); +} + +bool ActionRotateTo::execute() { + _engine->getRenderManager()->rotateTo(_toPos, _time); + + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionSetPartialScreen ////////////////////////////////////////////////////////////////////////////// -ActionSetPartialScreen::ActionSetPartialScreen(const Common::String &line) { - char fileName[26]; - uint color; +ActionSetPartialScreen::ActionSetPartialScreen(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _x = 0; + _y = 0; + + char fileName[25]; - sscanf(line.c_str(), "%*[^(](%u %u %25s %*u %u)", &_x, &_y, fileName, &color); + sscanf(line.c_str(), "%u %u %24s %*u %d", &_x, &_y, fileName, &_backgroundColor); _fileName = Common::String(fileName); - if (color > 0xFFFF) { + if (_backgroundColor > 65535) { warning("Background color for ActionSetPartialScreen is bigger than a uint16"); } - _backgroundColor = color; } -bool ActionSetPartialScreen::execute(ZVision *engine) { - RenderManager *renderManager = engine->getRenderManager(); - - if (_backgroundColor > 0) { - renderManager->clearWorkingWindowTo555Color(_backgroundColor); +bool ActionSetPartialScreen::execute() { + RenderManager *renderManager = _engine->getRenderManager(); + + if (_engine->getGameId() == GID_NEMESIS) { + if (_backgroundColor) + renderManager->renderImageToBackground(_fileName, _x, _y, 0, 0); + else + renderManager->renderImageToBackground(_fileName, _x, _y); + } else { + if (_backgroundColor >= 0) + renderManager->renderImageToBackground(_fileName, _x, _y, _backgroundColor); + else if (_backgroundColor == -2) + renderManager->renderImageToBackground(_fileName, _x, _y, 0, 0); + else + renderManager->renderImageToBackground(_fileName, _x, _y); } - renderManager->renderImageToScreen(_fileName, _x, _y); return true; } - ////////////////////////////////////////////////////////////////////////////// // ActionSetScreen ////////////////////////////////////////////////////////////////////////////// -ActionSetScreen::ActionSetScreen(const Common::String &line) { - char fileName[26]; - sscanf(line.c_str(), "%*[^(](%25[^)])", fileName); +ActionSetScreen::ActionSetScreen(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + char fileName[25]; + sscanf(line.c_str(), "%24s", fileName); _fileName = Common::String(fileName); } -bool ActionSetScreen::execute(ZVision *engine) { - engine->getRenderManager()->setBackgroundImage(_fileName); +bool ActionSetScreen::execute() { + _engine->getRenderManager()->setBackgroundImage(_fileName); return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionStop +////////////////////////////////////////////////////////////////////////////// + +ActionStop::ActionStop(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _key = 0; + sscanf(line.c_str(), "%u", &_key); +} + +bool ActionStop::execute() { + _scriptManager->stopSideFx(_key); + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionStreamVideo ////////////////////////////////////////////////////////////////////////////// -ActionStreamVideo::ActionStreamVideo(const Common::String &line) { - char fileName[26]; - uint skippable; +ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotkey, const Common::String &line) : + ResultAction(engine, slotkey) { + _x1 = 0; + _x2 = 0; + _y1 = 0; + _y2 = 0; + _flags = 0; - sscanf(line.c_str(), "%*[^(](%25s %u %u %u %u %u %u)", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skippable); + char fileName[25]; + uint skipline = 0; //skipline - render video with skip every second line, not skippable. + + sscanf(line.c_str(), "%24s %u %u %u %u %u %u", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skipline); _fileName = Common::String(fileName); - _skippable = (skippable == 0) ? false : true; + _skippable = true; } -bool ActionStreamVideo::execute(ZVision *engine) { - ZorkAVIDecoder decoder; - if (!decoder.loadFile(_fileName)) { +bool ActionStreamVideo::execute() { + 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; + 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 + + decoder = _engine->loadAnimation(_fileName); + Subtitle *sub = (subtitleExists) ? new Subtitle(_engine, subname, switchToHires) : NULL; + + _engine->getCursorManager()->showMouse(false); + + 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); } - Common::Rect destRect; - if ((_flags & DIFFERENT_DIMENSIONS) == DIFFERENT_DIMENSIONS) { - destRect = Common::Rect(_x1, _y1, _x2, _y2); + _engine->playVideo(*decoder, destRect, _skippable, sub); + + if (switchToHires) { + _engine->initScreen(); + _engine->getRenderManager()->initSubArea(WINDOW_WIDTH, WINDOW_HEIGHT, _engine->_workingWindow); } - engine->playVideo(decoder, destRect, _skippable); + _engine->getCursorManager()->showMouse(true); + + delete decoder; + delete sub; + return true; } +////////////////////////////////////////////////////////////////////////////// +// ActionSyncSound +////////////////////////////////////////////////////////////////////////////// + +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 %24s", &_syncto, ¬Used, fileName); + + _fileName = Common::String(fileName); +} + +bool ActionSyncSound::execute() { + ScriptingEffect *fx = _scriptManager->getSideFX(_syncto); + if (!fx) + return true; + + if (!(fx->getType() & ScriptingEffect::SCRIPTING_EFFECT_ANIM)) + return true; + + _scriptManager->addSideFX(new SyncSoundNode(_engine, _slotKey, _fileName, _syncto)); + return true; +} ////////////////////////////////////////////////////////////////////////////// // ActionTimer ////////////////////////////////////////////////////////////////////////////// -ActionTimer::ActionTimer(const Common::String &line) { - sscanf(line.c_str(), "%*[^:]:%*[^:]:%u(%u)", &_key, &_time); +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(_scriptManager, timeBuffer); +} + +ActionTimer::~ActionTimer() { + delete _time; + _scriptManager->killSideFx(_slotKey); +} + +bool ActionTimer::execute() { + if (_scriptManager->getSideFX(_slotKey)) + return true; + _scriptManager->addSideFX(new TimerNode(_engine, _slotKey, _time->getValue())); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// ActionTtyText +////////////////////////////////////////////////////////////////////////////// + +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 %63s %u", &x1, &y1, &x2, &y2, filename, &_delay); + _r = Common::Rect(x1, y1, x2, y2); + _filename = Common::String(filename); +} + +ActionTtyText::~ActionTtyText() { + _scriptManager->killSideFx(_slotKey); } -bool ActionTimer::execute(ZVision *engine) { - engine->getScriptManager()->addControl(new TimerNode(engine, _key, _time)); +bool ActionTtyText::execute() { + if (_scriptManager->getSideFX(_slotKey)) + return true; + _scriptManager->addSideFX(new ttyTextNode(_engine, _slotKey, _filename, _r, _delay)); return true; } |