scummvm/saga/script.cpp
Andrew Kurushin 8543306e56 all opcodes revisited
all ITE opcodes supported (now we should go through scriptFunctions)

svn-id: r16617
2005-01-22 16:32:30 +00:00

787 lines
21 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2004-2005 The ScummVM project
*
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
// Scripting module: Script resource handling functions
#include "saga/saga.h"
#include "saga/gfx.h"
#include "saga/rscfile_mod.h"
#include "saga/console.h"
#include "saga/script.h"
#include "saga/stream.h"
#include "saga/interface.h"
#include "saga/actordata.h"
#include "saga/scene.h"
#include "saga/events.h"
#include "saga/actor.h"
#include "saga/objectdata.h"
#include "saga/objectmap.h"
namespace Saga {
// Initializes the scripting module.
// Loads script resource look-up table, initializes script data system
Script::Script() {
RSCFILE_CONTEXT *resourceContext;
byte *resourcePointer;
size_t resourceLength;
int prevTell;
int result;
int i, j;
byte *stringsPointer;
size_t stringsLength;
//initialize member variables
_dbg_thread = 0;
_dbg_singlestep = 0;
_scriptContext = 0;
_voiceLUTPresent = false;
_abortEnabled = true;
_skipSpeeches = false;
_conversingThread = NULL;
_firstObjectSet = false;
_secondObjectNeeded = false;
_pendingVerb = kVerbNone;
_currentVerb = kVerbNone;
_stickyVerb = kVerbWalkTo;
_leftButtonVerb = kVerbNone;
_rightButtonVerb = kVerbNone;
_pointerObject = 0;
_staticSize = 0;
_commonBufferSize = COMMON_BUFFER_SIZE;
_commonBuffer = (byte*)malloc(_commonBufferSize);
memset(_commonBuffer, 0, _commonBufferSize);
debug(0, "Initializing scripting subsystem");
// Load script resource file context
_scriptContext = _vm->getFileContext(GAME_SCRIPTFILE, 0);
if (_scriptContext == NULL) {
error("Script::Script() Couldn't get script file context");
}
resourceContext = _vm->getFileContext(GAME_RESOURCEFILE, 0);
if (resourceContext == NULL) {
error("Script::Script() Couldn't get resource file context");
}
debug(0, "Loading module LUT from resource %u.", _vm->getResourceDescription()->moduleLUTResourceId);
result = RSC_LoadResource(resourceContext, _vm->getResourceDescription()->moduleLUTResourceId, &resourcePointer, &resourceLength);
if (result != SUCCESS) {
error("Script::Script() Couldn't load module LUT resource");
}
// Create logical script LUT from resource
if (resourceLength % S_LUT_ENTRYLEN_ITECD == 0) {
_modulesLUTEntryLen = S_LUT_ENTRYLEN_ITECD;
} else if (resourceLength % S_LUT_ENTRYLEN_ITEDISK == 0) {
_modulesLUTEntryLen = S_LUT_ENTRYLEN_ITEDISK;
} else {
error("Script::Script() Invalid script lookup table length (%d)", resourceLength);
}
// Calculate number of entries
_modulesCount = resourceLength / _modulesLUTEntryLen;
debug(0, "LUT has %d entries.", _modulesCount);
// Allocate space for logical LUT
_modules = (ModuleData *)malloc(_modulesCount * sizeof(*_modules));
if (_modules == NULL) {
memoryError("Script::Script()");
}
// Convert LUT resource to logical LUT
MemoryReadStreamEndian scriptS(resourcePointer, resourceLength, IS_BIG_ENDIAN);
for (i = 0; i < _modulesCount; i++) {
memset(&_modules[i], 0, sizeof(ModuleData));
prevTell = scriptS.pos();
_modules[i].scriptResourceId = scriptS.readUint16();
_modules[i].stringsResourceId = scriptS.readUint16();
_modules[i].voicesResourceId = scriptS.readUint16();
if (_modules[i].voicesResourceId > 0) {
_voiceLUTPresent = true;
}
// Skip the unused portion of the structure
for (j = scriptS.pos(); j < prevTell + _modulesLUTEntryLen; j++) {
if (scriptS.readByte() != 0)
warning("Unused scriptLUT part isn't really unused for LUT %d (pos: %d)", i, j);
}
}
RSC_FreeResource(resourcePointer);
result = RSC_LoadResource(resourceContext, RID_ITE_MAIN_STRINGS, &stringsPointer, &stringsLength); // fixme: IHNM
if ((result != SUCCESS) || (stringsLength == 0)) {
error("Error loading strings list resource");
}
_vm->loadStrings(_mainStrings, stringsPointer, stringsLength);
RSC_FreeResource(stringsPointer);
setupScriptFuncList();
_initialized = true;
}
// Shut down script module gracefully; free all allocated module resources
Script::~Script() {
if (!_initialized) {
error("Script not initialized");
}
debug(0, "Shutting down scripting subsystem.");
_mainStrings.freeMem();
freeModules();
free(_modules);
free(_commonBuffer);
_initialized = false;
}
void Script::loadModule(int scriptModuleNumber) {
byte *resourcePointer;
size_t resourceLength;
int result;
// Validate script number
if ((scriptModuleNumber < 0) || (scriptModuleNumber >= _modulesCount)) {
error("Script::loadScript() Invalid script module number");
}
if (_modules[scriptModuleNumber].loaded) {
return;
}
// Initialize script data structure
debug(0, "Loading script module #%d", scriptModuleNumber);
result = RSC_LoadResource(_scriptContext, _modules[scriptModuleNumber].scriptResourceId, &resourcePointer, &resourceLength);
if (result != SUCCESS) {
error("Script::loadModule() unable to load module base resource");
}
loadModuleBase(_modules[scriptModuleNumber], resourcePointer, resourceLength);
RSC_FreeResource(resourcePointer);
result = RSC_LoadResource(_scriptContext, _modules[scriptModuleNumber].stringsResourceId, &resourcePointer, &resourceLength);
if ((result != SUCCESS) || (resourceLength == 0)) {
error("Script::loadModule() Error loading strings list resource");
}
_vm->loadStrings(_modules[scriptModuleNumber].strings, resourcePointer, resourceLength);
RSC_FreeResource(resourcePointer);
if (_modules[scriptModuleNumber].voicesResourceId > 0) {
result = RSC_LoadResource(_scriptContext, _modules[scriptModuleNumber].voicesResourceId, &resourcePointer, &resourceLength);
if (result != SUCCESS) {
error("Script::loadModule() Error loading voice LUT resource");
}
loadModuleVoiceLUT(_modules[scriptModuleNumber], resourcePointer, resourceLength);
RSC_FreeResource(resourcePointer);
}
_modules[scriptModuleNumber].staticOffset = _staticSize;
_staticSize += _modules[scriptModuleNumber].staticSize;
if (_staticSize > _commonBufferSize) {
error("Script::loadModule() _staticSize > _commonBufferSize");
}
_modules[scriptModuleNumber].loaded = true;
}
void Script::freeModules() {
int i;
for (i = 0; i < _modulesCount; i++) {
if (_modules[i].loaded) {
_modules[i].freeMem();
}
}
_staticSize = 0;
}
void Script::loadModuleBase(ModuleData &module, const byte *resourcePointer, size_t resourceLength) {
int i;
debug(0, "Loading module base...");
module.moduleBase = (byte*)malloc(resourceLength);
module.moduleBaseSize = resourceLength;
memcpy(module.moduleBase, resourcePointer, resourceLength);
MemoryReadStreamEndian scriptS(module.moduleBase, module.moduleBaseSize, IS_BIG_ENDIAN);
module.entryPointsCount = scriptS.readUint16();
scriptS.readUint16(); //skip
module.entryPointsTableOffset = scriptS.readUint16();
scriptS.readUint16(); //skip
if ((module.moduleBaseSize - module.entryPointsTableOffset) < (module.entryPointsCount * SCRIPT_TBLENTRY_LEN)) {
error("Script::loadModuleBase() Invalid table offset");
}
if (module.entryPointsCount > SCRIPT_MAX) {
error("Script::loadModuleBase()Script limit exceeded");
}
module.entryPoints = (EntryPoint *)malloc(module.entryPointsCount * sizeof(*module.entryPoints));
if (module.entryPoints == NULL) {
memoryError("Script::loadModuleBase");
}
// Read in the entrypoint table
module.staticSize = scriptS.readUint16();
while (scriptS.pos() < module.entryPointsTableOffset)
scriptS.readByte();
for (i = 0; i < module.entryPointsCount; i++) {
// First uint16 is the offset of the entrypoint name from the start
// of the bytecode resource, second uint16 is the offset of the
// bytecode itself for said entrypoint
module.entryPoints[i].nameOffset = scriptS.readUint16();
module.entryPoints[i].offset = scriptS.readUint16();
// Perform a simple range check on offset values
if ((module.entryPoints[i].nameOffset >= module.moduleBaseSize) || (module.entryPoints[i].offset >= module.moduleBaseSize)) {
error("Script::loadModuleBase() Invalid offset encountered in script entrypoint table");
}
}
}
void Script::loadModuleVoiceLUT(ModuleData &module, const byte *resourcePointer, size_t resourceLength) {
uint16 i;
module.voiceLUT.voicesCount = resourceLength / 2;
if (module.voiceLUT.voicesCount != module.strings.stringsCount) {
error("Script::loadModuleVoiceLUT() Voice LUT entries do not match strings entries");
}
module.voiceLUT.voices = (uint16 *)malloc(module.voiceLUT.voicesCount * sizeof(*module.voiceLUT.voices));
if (module.voiceLUT.voices == NULL) {
error("Script::loadModuleVoiceLUT() not enough memory");
}
MemoryReadStreamEndian scriptS(resourcePointer, resourceLength, IS_BIG_ENDIAN);
for (i = 0; i < module.voiceLUT.voicesCount; i++) {
module.voiceLUT.voices[i] = scriptS.readUint16();
}
}
void Script::scriptInfo() {
/* uint32 n_entrypoints;
uint32 i;
char *name_ptr;
if (currentScript() == NULL) {
return;
}
if (!currentScript()->loaded) {
return;
}
n_entrypoints = currentScript()->bytecode->n_entrypoints;
_vm->_console->DebugPrintf("Current script contains %d entrypoints:\n", n_entrypoints);
for (i = 0; i < n_entrypoints; i++) {
name_ptr = (char *)currentScript()->bytecode->bytecode_p +
currentScript()->bytecode->entrypoints[i].name_offset;
_vm->_console->DebugPrintf("%lu: %s\n", i, name_ptr);
}*/
}
void Script::scriptExec(int argc, const char **argv) {
/* uint16 ep_num;
ep_num = atoi(argv[1]);
if (_dbg_thread == NULL) {
_vm->_console->DebugPrintf("Creating debug thread...\n");
_dbg_thread = createThread();
if (_dbg_thread == NULL) {
_vm->_console->DebugPrintf("Thread creation failed.\n");
return;
}
}
if (ep_num >= currentScript()->bytecode->n_entrypoints) {
_vm->_console->DebugPrintf("Invalid entrypoint.\n");
return;
}
executeThread(_dbg_thread, ep_num);*/
}
// verb
void Script::showVerb(int statuscolor) {
const char *verbName;
const char *object1Name;
const char *object2Name;
char statusString[STATUS_TEXT_LEN];
if (_leftButtonVerb == kVerbNone) {
_vm->_interface->setStatusText("");
return;
}
verbName = _mainStrings.getString(_leftButtonVerb - 1);
if (objectTypeId(_currentObject[0]) == kGameObjectNone) {
_vm->_interface->setStatusText(verbName);
return;
}
object1Name = _vm->getObjectName(_currentObject[0]);
if (!_secondObjectNeeded) {
snprintf(statusString, STATUS_TEXT_LEN, "%s %s", verbName, object1Name);
_vm->_interface->setStatusText(statusString);
return;
}
if (objectTypeId(_currentObject[1]) != kGameObjectNone) {
object2Name = _vm->getObjectName(_currentObject[1]);
} else {
object2Name = "";
}
if (_leftButtonVerb == kVerbGive) {
snprintf(statusString, STATUS_TEXT_LEN, "Give %s to %s", object1Name, object2Name);
_vm->_interface->setStatusText(statusString);
} else {
if (_leftButtonVerb == kVerbUse) {
snprintf(statusString, STATUS_TEXT_LEN, "Use %s with %s", object1Name, object2Name);
_vm->_interface->setStatusText(statusString);
} else {
snprintf(statusString, STATUS_TEXT_LEN, "%s %s", verbName, object1Name);
_vm->_interface->setStatusText(statusString);
}
}
if (statuscolor != -1)
_vm->_interface->setStatusOnceColor(statuscolor);
}
void Script::setVerb(int verb) {
_pendingObject[0] = ID_NOTHING;
_currentObject[0] = ID_NOTHING;
_pendingObject[1] = ID_NOTHING;
_currentObject[1] = ID_NOTHING;
_firstObjectSet = false;
_secondObjectNeeded = false;
setLeftButtonVerb( verb );
showVerb();
}
void Script::setLeftButtonVerb(int verb) {
int oldVerb = _currentVerb;
_currentVerb = _leftButtonVerb = verb;
if ((_currentVerb != oldVerb) && (_vm->_interface->getMode() == kPanelMain)){
if (oldVerb > kVerbNone)
_vm->_interface->drawVerb(oldVerb, 2);
if (_currentVerb > kVerbNone)
_vm->_interface->drawVerb(_currentVerb, 2);
}
}
void Script::setRightButtonVerb(int verb) {
int oldVerb = _rightButtonVerb;
_rightButtonVerb = verb;
if ((_rightButtonVerb != oldVerb) && (_vm->_interface->getMode() == kPanelMain)){
if (oldVerb > kVerbNone)
_vm->_interface->drawVerb(oldVerb, 2);
if (_rightButtonVerb > kVerbNone)
_vm->_interface->drawVerb(_rightButtonVerb, 2);
}
}
void Script::doVerb() {
int scriptEntrypointNumber = 0;
int scriptModuleNumber = 0;
int objectType;
EVENT event;
const char *excuseText;
int excuseSampleResourceId;
const HitZone *hitZone;
objectType = objectTypeId(_pendingObject[0]);
if (_pendingVerb == kVerbGive) {
scriptEntrypointNumber = _vm->_actor->getObjectScriptEntrypointNumber(_pendingObject[1]);
if (_vm->_actor->getObjectFlags(_pendingObject[1]) & (kFollower|kProtagonist|kExtended)) {
scriptModuleNumber = 0;
} else {
scriptModuleNumber = _vm->_scene->getScriptModuleNumber();
}
} else {
if (_pendingVerb == kVerbUse) {
if ((objectTypeId(_pendingObject[1]) > kGameObjectNone) && (objectType < objectTypeId(_pendingObject[1]))) {
SWAP(_pendingObject[0], _pendingObject[1]);
objectType = objectTypeId(_pendingObject[0]);
}
}
if (objectType == kGameObjectHitZone) {
scriptModuleNumber = _vm->_scene->getScriptModuleNumber();
hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(_pendingObject[0]));
if ((hitZone->getFlags() & kHitZoneExit) == 0) {
scriptEntrypointNumber = hitZone->getScriptNumber();
}
} else {
if (objectType & (kGameObjectActor | kGameObjectObject)) {
scriptEntrypointNumber = _vm->_actor->getObjectScriptEntrypointNumber(_pendingObject[0]);
if ((objectType == kGameObjectActor) && !(_vm->_actor->getObjectFlags(_pendingObject[0]) & (kFollower|kProtagonist|kExtended))) {
scriptModuleNumber = _vm->_scene->getScriptModuleNumber();
} else {
scriptModuleNumber = 0;
}
}
}
}
if (scriptEntrypointNumber > 0) {
event.type = ONESHOT_EVENT;
event.code = SCRIPT_EVENT;
event.op = EVENT_EXEC_NONBLOCKING;
event.time = 0;
event.param = scriptModuleNumber;
event.param2 = scriptEntrypointNumber;
event.param3 = _pendingVerb; // Action
event.param4 = _pendingObject[0]; // Object
event.param5 = _pendingObject[1]; // With Object
event.param6 = (objectType == kGameObjectActor) ? _pendingObject[0] : ID_PROTAG; // Actor
_vm->_events->queue(&event);
} else {
_vm->getExcuseInfo(_pendingVerb, excuseText, excuseSampleResourceId);
if (excuseText)
_vm->_actor->actorSpeech(ID_PROTAG, &excuseText, 1, excuseSampleResourceId, 0);
}
if ((_currentVerb == kVerbWalkTo) || (_currentVerb == kVerbLookAt)) {
_stickyVerb = _currentVerb;
}
_pendingVerb = kVerbNone;
_currentObject[0] = _currentObject[1] = ID_NOTHING;
setLeftButtonVerb(_stickyVerb);
setPointerVerb();
}
void Script::setPointerVerb() {
Point mousePoint;
mousePoint = _vm->getMousePos();
if (_vm->_interface->isActive()) {
_pointerObject = ID_PROTAG;
whichObject(mousePoint);
}
}
void Script::hitObject(bool leftButton) {
int verb;
verb = leftButton ? _leftButtonVerb : _rightButtonVerb;
if (verb > kVerbNone) {
if (_firstObjectSet) {
if (_secondObjectNeeded) {
_pendingObject[0] = _currentObject[0];
_pendingObject[1] = _currentObject[1];
_pendingVerb = verb;
_leftButtonVerb = verb;
if (_pendingVerb > kVerbNone)
showVerb(kITEColorBrightWhite);
else
showVerb();
_secondObjectNeeded = false;
_firstObjectSet = false;
return;
}
} else {
if (verb == kVerbGive) {
_secondObjectNeeded = true;
} else {
if (verb == kVerbUse) {
if (_currentObjectFlags[0] & kObjUseWith) {
_secondObjectNeeded = true;
}
}
}
if (!_secondObjectNeeded) {
_pendingObject[0] = _currentObject[0];
_pendingObject[1] = ID_NOTHING;
_pendingVerb = verb;
_secondObjectNeeded = false;
_firstObjectSet = false;
} else {
_firstObjectSet = true;
}
}
_leftButtonVerb = verb;
if (_pendingVerb > kVerbNone)
showVerb(kITEColorBrightWhite);
else
showVerb();
}
}
void Script::playfieldClick(const Point& mousePoint, bool leftButton) {
Location pickLocation;
const HitZone *hitZone;
Point specialPoint;
_vm->_actor->abortSpeech();
if ((_vm->_actor->_protagonist->currentAction != kActionWait) &&
(_vm->_actor->_protagonist->currentAction != kActionFreeze) &&
(_vm->_actor->_protagonist->currentAction != kActionWalkToLink) &&
(_vm->_actor->_protagonist->currentAction != kActionWalkToPoint)) {
return;
}
if (_pendingVerb > kVerbNone) {
setLeftButtonVerb(kVerbWalkTo);
}
if (_pointerObject != ID_NOTHING) {
hitObject( leftButton );
} else {
_pendingObject[0] = ID_NOTHING;
_pendingObject[1] = ID_NOTHING;
_pendingVerb = kVerbWalkTo;
}
// tiled stuff
if (_vm->_scene->getFlags() & kSceneFlagISO) {
//todo: it
} else {
pickLocation.fromScreenPoint(mousePoint);
}
hitZone = NULL;
if (objectTypeId(_pendingObject[0]) == kGameObjectHitZone) {
hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(_pendingObject[0]));
} else {
if ((_pendingVerb == kVerbUse) && (objectTypeId(_pendingObject[1]) == kGameObjectHitZone)) {
hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(_pendingObject[1]));
}
}
if (hitZone != NULL) {
if (hitZone->getFlags() & kHitZoneNoWalk) {
_vm->_actor->actorFaceTowardsPoint(ID_PROTAG, pickLocation);
doVerb();
return;
}
if (hitZone->getFlags() & kHitZoneProject) {
if (!hitZone->getSpecialPoint(specialPoint)) {
error("Script::playfieldClick SpecialPoint not found");
}
// tiled stuff
if (_vm->_scene->getFlags() & kSceneFlagISO) {
//todo: it
} else {
pickLocation.fromScreenPoint(specialPoint);
}
}
}
switch (_pendingVerb) {
case kVerbWalkTo:
case kVerbPickUp:
case kVerbOpen:
case kVerbClose:
case kVerbUse:
_vm->_actor->actorWalkTo(ID_PROTAG, pickLocation);
break;
case kVerbLookAt:
if (objectTypeId(_pendingObject[0]) != kGameObjectActor ) {
_vm->_actor->actorWalkTo(ID_PROTAG, pickLocation);
} else {
doVerb();
}
break;
case kVerbTalkTo:
case kVerbGive:
doVerb();
break;
}
}
void Script::whichObject(const Point& mousePoint) {
uint16 objectId;
int16 objectFlags;
int newRightButtonVerb;
uint16 newObjectId;
ActorData *actor;
Location pickLocation;
int hitZoneIndex;
const HitZone * hitZone;
objectId = ID_NOTHING;
objectFlags = 0;
_leftButtonVerb = _currentVerb;
newRightButtonVerb = kVerbNone;
if (_vm->_actor->_protagonist->currentAction == kActionWalkDir) {
} else {
newObjectId = _vm->_actor->hitTest(mousePoint);
if (newObjectId != ID_NOTHING) {
if (objectTypeId(newObjectId) == kGameObjectObject) {
objectId = newObjectId;
objectFlags = 0;
newRightButtonVerb = kVerbLookAt;
if ((_currentVerb == kVerbTalkTo) || ((_currentVerb == kVerbGive) && _firstObjectSet)) {
objectId = ID_NOTHING;
newObjectId = ID_NOTHING;
}
} else {
actor = _vm->_actor->getActor(newObjectId);
objectId = newObjectId;
objectFlags = kObjUseWith;
newRightButtonVerb = kVerbTalkTo;
if ((_currentVerb == kVerbPickUp) ||
(_currentVerb == kVerbOpen) ||
(_currentVerb == kVerbClose) ||
((_currentVerb == kVerbGive) && !_firstObjectSet) ||
((_currentVerb == kVerbUse) && !(actor->flags & kFollower))) {
objectId = ID_NOTHING;
newObjectId = ID_NOTHING;
}
}
}
if (newObjectId == ID_NOTHING) {
if (_vm->_scene->getFlags() & kSceneFlagISO) {
//todo: it
} else {
pickLocation.x = mousePoint.x;
pickLocation.y = mousePoint.y;
pickLocation.z = 0;
}
hitZoneIndex = _vm->_scene->_objectMap->hitTest(mousePoint);
if ((hitZoneIndex != -1)) {
hitZone = _vm->_scene->_objectMap->getHitZone(hitZoneIndex);
objectId = hitZone->getHitZoneId();
objectFlags = 0;
newRightButtonVerb = hitZone->getRightButtonVerb() & 0x7f;
if (newRightButtonVerb == kVerbWalkOnly) {
if (_firstObjectSet) {
objectId = ID_NOTHING;
} else {
newRightButtonVerb = _leftButtonVerb = kVerbWalkTo;
}
} else {
if (_firstObjectSet) {
objectId = ID_NOTHING;
} else {
newRightButtonVerb = _leftButtonVerb = kVerbLookAt;
}
}
if (newRightButtonVerb >= kVerbOptions) {
newRightButtonVerb = kVerbNone;
}
if ((_currentVerb == kVerbTalkTo) || ((_currentVerb == kVerbGive) && !_firstObjectSet)) {
objectId = ID_NOTHING;
newObjectId = ID_NOTHING;
}
if ((_leftButtonVerb == kVerbUse) && (hitZone->getRightButtonVerb() & 0x80)) {
objectFlags = kObjUseWith;
}
}
}
}
if (objectId != _pointerObject) {
_pointerObject = objectId;
_currentObject[_firstObjectSet ? 1 : 0] = objectId;
_currentObjectFlags[_firstObjectSet ? 1 : 0] = objectFlags;
if (_pendingVerb == kVerbNone) {
showVerb();
}
}
if (newRightButtonVerb != _rightButtonVerb) {
setRightButtonVerb(newRightButtonVerb);
}
}
// console wrappers
void Script::CF_script_togglestep() {
_dbg_singlestep = !_dbg_singlestep;
}
} // End of namespace Saga