aboutsummaryrefslogtreecommitdiff
path: root/engines/zvision/scripting/actions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/zvision/scripting/actions.cpp')
-rw-r--r--engines/zvision/scripting/actions.cpp1011
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, &note, 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,", &centerX, &centerY, &frames, &amplitude, &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, &notUsed, 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;
}