scummvm/engines/toltecs/script.cpp
2014-02-18 02:39:39 +01:00

1052 lines
27 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
// TODO: Clean up game variable handling and move it to ToltecsEngine
#include "common/error.h"
#include "graphics/cursorman.h"
#include "toltecs/toltecs.h"
#include "toltecs/animation.h"
#include "toltecs/menu.h"
#include "toltecs/movie.h"
#include "toltecs/music.h"
#include "toltecs/palette.h"
#include "toltecs/resource.h"
#include "toltecs/script.h"
#include "toltecs/segmap.h"
#include "toltecs/sound.h"
namespace Toltecs {
static const VarType varTypes[] = {
vtByte, vtWord, vtWord, vtByte, vtWord, // 0 - 4
vtWord, vtWord, vtWord, vtWord, vtWord, // 5 - 9
vtWord, vtWord, vtByte, vtWord, vtWord, // 10 - 14
vtWord, vtWord, vtWord, vtWord, vtWord, // 15 - 19
vtWord, vtWord // 20 - 21
};
static const char *varNames[] = {
"mouseDisabled", "mouseY", "mouseX", "mouseButton", "verbLineY", // 0 - 4
"verbLineX", "verbLineWidth", "verbLineCount", "verbLineNum", "talkTextItemNum", // 5 - 9
"talkTextY", "talkTextX", "talkTextFontColor", "cameraY", "cameraX", // 10 - 14
"walkSpeedY", "walkSpeedX", "flag01", "sceneResIndex", "guiHeight", // 15 - 19
"sceneHeight", "sceneWidth" // 20 - 21
};
ScriptInterpreter::ScriptInterpreter(ToltecsEngine *vm) : _vm(vm) {
_stack = new byte[kScriptStackSize];
memset(_slots, 0, sizeof(_slots));
_savedSp = 0;
_slots[kMaxScriptSlots - 1].size = 1024;
_slots[kMaxScriptSlots - 1].data = new byte[_slots[kMaxScriptSlots - 1].size];
setupScriptFunctions();
}
ScriptInterpreter::~ScriptInterpreter() {
delete[] _stack;
for (int i = 0; i < kMaxScriptSlots; i++)
delete[] _slots[i].data;
for (uint i = 0; i < _scriptFuncs.size(); ++i)
delete _scriptFuncs[i];
}
typedef Common::Functor0Mem<void, ScriptInterpreter> ScriptFunctionF;
#define RegisterScriptFunction(x) \
_scriptFuncs.push_back(new ScriptFunctionF(this, &ScriptInterpreter::x)); \
_scriptFuncNames.push_back(#x);
void ScriptInterpreter::setupScriptFunctions() {
// 0
RegisterScriptFunction(sfNop);
RegisterScriptFunction(sfNop);
RegisterScriptFunction(sfGetGameVar);
RegisterScriptFunction(sfSetGameVar);
RegisterScriptFunction(sfUpdateScreen);
// 5
RegisterScriptFunction(sfGetRandomNumber);
RegisterScriptFunction(sfDrawGuiTextMulti);
RegisterScriptFunction(sfUpdateVerbLine);
RegisterScriptFunction(sfSetFontColor);
RegisterScriptFunction(sfGetTalkTextDuration);
// 10
RegisterScriptFunction(sfTalk);
RegisterScriptFunction(sfFindPaletteFragment);
RegisterScriptFunction(sfClearPaletteFragments);
RegisterScriptFunction(sfAddPaletteFragment);
RegisterScriptFunction(sfSetDeltaAnimPalette);
// 15
RegisterScriptFunction(sfSetUnkPaletteEffect);
RegisterScriptFunction(sfBuildColorTransTable);
RegisterScriptFunction(sfSetDeltaMainPalette);
RegisterScriptFunction(sfLoadScript);
RegisterScriptFunction(sfRegisterFont);
// 20
RegisterScriptFunction(sfLoadAddPalette);
RegisterScriptFunction(sfLoadScene);
RegisterScriptFunction(sfSetGuiHeight);
RegisterScriptFunction(sfFindMouseInRectIndex1);
RegisterScriptFunction(sfFindMouseInRectIndex2);
// 25
RegisterScriptFunction(sfDrawGuiImage);
RegisterScriptFunction(sfAddAnimatedSpriteNoLoop);
RegisterScriptFunction(sfAddAnimatedSprite);
RegisterScriptFunction(sfAddStaticSprite);
RegisterScriptFunction(sfAddAnimatedSpriteScaled);
// 30
RegisterScriptFunction(sfFindPath);
RegisterScriptFunction(sfWalk);
RegisterScriptFunction(sfScrollCameraUp);
RegisterScriptFunction(sfScrollCameraDown);
RegisterScriptFunction(sfScrollCameraLeft);
// 35
RegisterScriptFunction(sfScrollCameraRight);
RegisterScriptFunction(sfScrollCameraUpEx);
RegisterScriptFunction(sfScrollCameraDownEx);
RegisterScriptFunction(sfScrollCameraLeftEx);
RegisterScriptFunction(sfScrollCameraRightEx);
// 40
RegisterScriptFunction(sfSetCamera);
RegisterScriptFunction(sfGetCameraChanged);
RegisterScriptFunction(sfGetRgbModifiertAtPoint);
RegisterScriptFunction(sfStartAnim);
RegisterScriptFunction(sfAnimNextFrame);
// 45
RegisterScriptFunction(sfNop);
RegisterScriptFunction(sfGetAnimFrameNumber);
RegisterScriptFunction(sfGetAnimStatus);
RegisterScriptFunction(sfStartShakeScreen);
RegisterScriptFunction(sfStopShakeScreen);
// 50
RegisterScriptFunction(sfStartSequence);
RegisterScriptFunction(sfEndSequence);
RegisterScriptFunction(sfSetSequenceVolume);
RegisterScriptFunction(sfPlayPositionalSound);
RegisterScriptFunction(sfPlaySound2);
// 55
RegisterScriptFunction(sfClearScreen);
RegisterScriptFunction(sfNop);
RegisterScriptFunction(sfHandleInput);
RegisterScriptFunction(sfRunOptionsScreen);
RegisterScriptFunction(sfPrecacheSprites);
// 60
RegisterScriptFunction(sfPrecacheSounds1);
RegisterScriptFunction(sfDeletePrecachedFiles);
RegisterScriptFunction(sfPrecacheSounds2);
RegisterScriptFunction(sfRestoreStackPtr);
RegisterScriptFunction(sfSaveStackPtr);
// 65
RegisterScriptFunction(sfPlayMovie);
RegisterScriptFunction(sfNop);
}
void ScriptInterpreter::loadScript(uint resIndex, uint slotIndex) {
if (_slots[slotIndex].resIndex && _slots[slotIndex].resIndex != resIndex && _vm->_screen->isTalkTextActive(slotIndex)) {
// WORKAROUND: This happens when examining the assembled
// pickaxe. It could lead to random characters being printed,
// or possibly even crashes, when subtitles are enabled.
//
// According to johndoe and he said there may be some bug or
// missing feature that causes this situation to happen at all,
// but he was ok with this workaround for now.
warning("Possible script bug: Loading script %d into slot %d that has an active talk text, probably for script %d", resIndex, slotIndex, _slots[slotIndex].resIndex);
_vm->_screen->finishTalkTextItem(slotIndex);
}
delete[] _slots[slotIndex].data;
_slots[slotIndex].resIndex = resIndex;
Resource *scriptResource = _vm->_res->load(resIndex);
_slots[slotIndex].size = scriptResource->size;
_slots[slotIndex].data = new byte[_slots[slotIndex].size];
memcpy(_slots[slotIndex].data, scriptResource->data, _slots[slotIndex].size);
}
void ScriptInterpreter::setMainScript(uint slotIndex) {
_switchLocalDataNear = true;
_switchLocalDataFar = false;
_switchLocalDataToStack = false;
_cmpBitTest = false;
_regs.reg0 = 0;
_regs.reg1 = 0;
_regs.reg2 = 0;
_regs.reg3 = 0;
_regs.reg4 = slotIndex;
_regs.reg5 = 0;
_regs.reg6 = 0;
_regs.sp = 4096;
_regs.reg8 = 0;
_code = getSlotData(_regs.reg4);
}
void ScriptInterpreter::runScript() {
while (!_vm->shouldQuit()) {
if (_vm->_movieSceneFlag)
_vm->_mouseButton = 0;
if (_vm->_saveLoadRequested != 0) {
if (_vm->_saveLoadRequested == 1)
_vm->loadGameState(_vm->_saveLoadSlot);
else if (_vm->_saveLoadRequested == 2)
_vm->saveGameState(_vm->_saveLoadSlot, _vm->_saveLoadDescription);
_vm->_saveLoadRequested = 0;
}
if (_switchLocalDataNear) {
_switchLocalDataNear = false;
_localData = getSlotData(_regs.reg4);
}
if (_switchLocalDataFar) {
_switchLocalDataFar = false;
_localData = getSlotData(_regs.reg5);
_switchLocalDataNear = true;
}
if (_switchLocalDataToStack) {
_switchLocalDataToStack = false;
_localData = _stack + 2;
_switchLocalDataNear = true;
}
byte opcode = readByte();
execOpcode(opcode);
}
}
byte ScriptInterpreter::readByte() {
return *_code++;
}
int16 ScriptInterpreter::readInt16() {
int16 value = READ_LE_UINT16(_code);
_code += 2;
return value;
}
void ScriptInterpreter::execOpcode(byte opcode) {
int16 ofs;
debug(2, "opcode = %d", opcode);
switch (opcode) {
case 0:
{
// ok
_subCode = _code;
byte length = readByte();
if (length == 0) {
warning("Opcode length is 0 when calling script function");
return;
}
debug(2, "length = %d", length);
uint16 index = readInt16();
execScriptFunction(index);
_code += length - 2;
break;
}
case 1:
_regs.reg0 = readInt16();
break;
case 2:
_regs.reg1 = readInt16();
break;
case 3:
_regs.reg3 = readInt16();
break;
case 4:
_regs.reg5 = _regs.reg0;
break;
case 5:
_regs.reg3 = _regs.reg0;
break;
case 6:
_regs.reg1 = _regs.reg0;
break;
case 7:
_regs.reg1 = localRead16(_regs.reg3);
break;
case 8:
localWrite16(_regs.reg3, _regs.reg0);
break;
case 9:
localWrite16(readInt16(), _regs.reg0);
break;
case 10:
localWrite8(readInt16(), _regs.reg0);
break;
case 11:
localWrite16(readInt16(), _regs.reg5);
break;
case 12:
localWrite16(readInt16(), _regs.reg4);
break;
case 13:
localWrite16(readInt16(), _regs.reg3);
break;
case 14:
_regs.reg3 = localRead16(readInt16());
break;
case 15:
_regs.reg2 = localRead16(_regs.reg1);
break;
case 16:
_regs.reg2 = localRead16(_regs.reg1 + readInt16());
break;
case 17:
_regs.reg2 = _regs.reg0;
break;
case 18:
_regs.reg0 += readInt16();
break;
case 19:
localWrite16(_regs.reg3, localRead16(_regs.reg3) + _regs.reg0);
break;
case 20:
_regs.reg0 += _regs.reg2;
break;
case 21:
_regs.reg3 += _regs.sp;
break;
case 22:
_regs.reg1 += _regs.sp;
break;
case 23:
localWrite16(_regs.reg3, localRead16(_regs.reg3) - _regs.reg0);
break;
case 24:
_regs.reg0 /= readInt16();
break;
case 25:
localWrite16(_regs.reg3, localRead16(_regs.reg3) / _regs.reg0);
break;
case 26:
// NOP
break;
case 27:
_regs.reg0 *= readInt16();
break;
case 28:
localWrite16(_regs.reg3, localRead16(_regs.reg3) * _regs.reg0);
break;
case 29:
_regs.reg0 *= _regs.reg2;
break;
case 30:
localWrite16(_regs.reg3, localRead16(_regs.reg3) + 1);
break;
case 31:
localWrite16(_regs.reg3, localRead16(_regs.reg3) - 1);
break;
case 32:
_switchLocalDataFar = true;
break;
case 33:
_switchLocalDataToStack = true;
break;
case 34:
pushInt16(_regs.reg0);
break;
case 35:
pushInt16(_regs.reg1);
break;
case 36:
_regs.reg1 = popInt16();
break;
case 37:
_regs.reg0 = popInt16();
break;
case 38:
_regs.reg2 = -_regs.reg2;
break;
case 39:
_regs.reg8 = readInt16();
_cmpBitTest = false;
break;
case 40:
_regs.reg8 = _regs.reg0;
_cmpBitTest = false;
break;
case 41:
_regs.reg8 = readInt16();
_cmpBitTest = true;
break;
case 42:
_regs.reg8 = _regs.reg0;
_cmpBitTest = true;
break;
case 43:
_code = getSlotData(_regs.reg4) + _regs.reg0;
break;
case 44:
_code = getSlotData(_regs.reg5) + _regs.reg0;
_regs.reg4 = _regs.reg5;
_switchLocalDataNear = true;
break;
case 45:
pushInt16(_code - getSlotData(_regs.reg4));
pushInt16(_regs.reg4);
_code = getSlotData(_regs.reg4) + _regs.reg0;
break;
case 46:
pushInt16(_code - getSlotData(_regs.reg4));
pushInt16(_regs.reg4);
_code = getSlotData(_regs.reg5) + _regs.reg0;
_regs.reg4 = _regs.reg5;
_switchLocalDataNear = true;
break;
case 47:
_regs.reg4 = popInt16();
ofs = popInt16();
_code = getSlotData(_regs.reg4) + ofs;
_switchLocalDataNear = true;
break;
case 48:
_regs.reg4 = popInt16();
ofs = popInt16();
_code = getSlotData(_regs.reg4) + ofs;
_regs.sp += _regs.reg0;
_switchLocalDataNear = true;
break;
case 49:
ofs = readByte();
_code += ofs;
break;
case 50:
if (_cmpBitTest) {
_regs.reg1 &= _regs.reg8;
if (_regs.reg1 == 0)
_code += 4;
} else {
if (_regs.reg1 == _regs.reg8)
_code += 4;
}
_code++;
break;
case 51:
if (_cmpBitTest) {
_regs.reg1 &= _regs.reg8;
if (_regs.reg1 != 0)
_code += 4;
} else {
if (_regs.reg1 != _regs.reg8)
_code += 4;
}
_code++;
break;
case 52:
if ((uint16)_regs.reg1 >= (uint16)_regs.reg8)
_code += 4;
_code++;
break;
case 53:
if ((uint16)_regs.reg1 <= (uint16)_regs.reg8)
_code += 4;
_code++;
break;
case 54:
if ((uint16)_regs.reg1 < (uint16)_regs.reg8)
_code += 4;
_code++;
break;
case 55:
if ((uint16)_regs.reg1 > (uint16)_regs.reg8)
_code += 4;
_code++;
break;
default:
// Most likely a script bug. Throw a warning and ignore it.
// The original ignores invalid opcodes as well - bug #3604025.
warning("Invalid opcode %d", opcode);
}
}
void ScriptInterpreter::execScriptFunction(uint16 index) {
if (index >= _scriptFuncs.size())
error("ScriptInterpreter::execScriptFunction() Invalid script function index %d", index);
debug(1, "execScriptFunction %s (%d)", _scriptFuncNames[index], index);
(*_scriptFuncs[index])();
}
int16 ScriptInterpreter::getGameVar(uint variable) {
debug(2, "ScriptInterpreter::getGameVar(%d{%s})", variable, varNames[variable]);
switch (variable) {
case 0: return _vm->_mouseDisabled;
case 1: return _vm->_mouseY;
case 2: return _vm->_mouseX;
case 3: return _vm->_mouseButton;
case 4: return _vm->_screen->_verbLineY;
case 5: return _vm->_screen->_verbLineX;
case 6: return _vm->_screen->_verbLineWidth;
case 7: return _vm->_screen->_verbLineCount;
case 8: return _vm->_screen->_verbLineNum;
case 9: return _vm->_screen->_talkTextItemNum;
case 10: return _vm->_screen->_talkTextY;
case 11: return _vm->_screen->_talkTextX;
case 12: return _vm->_screen->_talkTextFontColor;
case 13: return _vm->_cameraY;
case 14: return _vm->_cameraX;
case 15: return _vm->_walkSpeedY;
case 16: return _vm->_walkSpeedX;
case 17: return _vm->_flag01;
case 18: return _vm->_sceneResIndex;
case 19: return _vm->_guiHeight;
case 20: return _vm->_sceneHeight;
case 21: return _vm->_sceneWidth;
default:
warning("Getting unimplemented game variable %s (%d)", varNames[variable], variable);
return 0;
}
}
void ScriptInterpreter::setGameVar(uint variable, int16 value) {
debug(2, "ScriptInterpreter::setGameVar(%d{%s}, %d)", variable, varNames[variable], value);
switch (variable) {
case 0:
_vm->_mouseDisabled = value;
CursorMan.showMouse(value == 0);
break;
case 3:
_vm->_mouseButton = value;
break;
case 4:
_vm->_screen->_verbLineY = value;
break;
case 5:
_vm->_screen->_verbLineX = value;
break;
case 6:
_vm->_screen->_verbLineWidth = value;
break;
case 7:
_vm->_screen->_verbLineCount = value;
break;
case 8:
_vm->_screen->_verbLineNum = value;
break;
case 9:
_vm->_screen->_talkTextItemNum = value;
break;
case 10:
_vm->_screen->_talkTextY = value;
break;
case 11:
_vm->_screen->_talkTextX = value;
break;
case 12:
_vm->_screen->_talkTextFontColor = value;
break;
case 13:
_vm->_cameraY = value;
break;
case 14:
_vm->_cameraX = value;
break;
case 15:
_vm->_walkSpeedY = value;
break;
case 16:
_vm->_walkSpeedX = value;
break;
case 17:
_vm->_flag01 = value != 0;
break;
case 18:
_vm->_sceneResIndex = value;
break;
case 19:
_vm->_guiHeight = value;
break;
case 20:
_vm->_sceneHeight = value;
break;
case 21:
_vm->_sceneWidth = value;
break;
case 1:
case 2:
default:
warning("Setting unimplemented game variable %s (%d) to %d", varNames[variable], variable, value);
break;
}
}
byte ScriptInterpreter::arg8(int16 offset) {
return _subCode[offset];
}
int16 ScriptInterpreter::arg16(int16 offset) {
return READ_LE_UINT16(&_subCode[offset]);
}
void ScriptInterpreter::pushInt16(int16 value) {
WRITE_LE_UINT16(_stack + _regs.sp, value);
_regs.sp -= 2;
}
int16 ScriptInterpreter::popInt16() {
_regs.sp += 2;
return READ_LE_UINT16(_stack + _regs.sp);
}
void ScriptInterpreter::localWrite8(int16 offset, byte value) {
//debug(2, "localWrite8(%d, %d)", offset, value);
_localData[offset] = value;
}
byte ScriptInterpreter::localRead8(int16 offset) {
//debug(2, "localRead8(%d) -> %d", offset, _localData[offset]);
return _localData[offset];
}
void ScriptInterpreter::localWrite16(int16 offset, int16 value) {
//debug(2, "localWrite16(%d, %d)", offset, value);
WRITE_LE_UINT16(&_localData[offset], value);
}
int16 ScriptInterpreter::localRead16(int16 offset) {
//debug(2, "localRead16(%d) -> %d", offset, (int16)READ_LE_UINT16(&_localData[offset]));
return (int16)READ_LE_UINT16(&_localData[offset]);
}
byte *ScriptInterpreter::localPtr(int16 offset) {
//debug(2, "localPtr(%d)", offset);
return &_localData[offset];
}
void ScriptInterpreter::saveState(Common::WriteStream *out) {
// Save registers
out->writeUint16LE(_regs.reg0);
out->writeUint16LE(_regs.reg1);
out->writeUint16LE(_regs.reg2);
out->writeUint16LE(_regs.reg3);
out->writeUint16LE(_regs.reg4);
out->writeUint16LE(_regs.reg5);
out->writeUint16LE(_regs.reg6);
out->writeUint16LE(_regs.sp);
out->writeUint16LE(_regs.reg8);
// Save slots
for (int slot = 0; slot < kMaxScriptSlots; slot++) {
out->writeUint32LE(_slots[slot].size);
out->writeUint16LE(_slots[slot].resIndex);
if (_slots[slot].size > 0)
out->write(_slots[slot].data, _slots[slot].size);
}
// Save stack
out->write(_stack, kScriptStackSize);
out->writeUint16LE(_savedSp);
// Save IP
out->writeUint16LE((int16)(_code - getSlotData(_regs.reg4)));
}
void ScriptInterpreter::loadState(Common::ReadStream *in) {
// Load registers
_regs.reg0 = in->readUint16LE();
_regs.reg1 = in->readUint16LE();
_regs.reg2 = in->readUint16LE();
_regs.reg3 = in->readUint16LE();
_regs.reg4 = in->readUint16LE();
_regs.reg5 = in->readUint16LE();
_regs.reg6 = in->readUint16LE();
_regs.sp = in->readUint16LE();
_regs.reg8 = in->readUint16LE();
// Load slots
for (int slot = 0; slot < kMaxScriptSlots; slot++) {
_slots[slot].size = in->readUint32LE();
_slots[slot].resIndex = in->readUint16LE();
_slots[slot].data = NULL;
if (_slots[slot].size > 0) {
_slots[slot].data = new byte[_slots[slot].size];
in->read(_slots[slot].data, _slots[slot].size);
}
}
// Load stack
in->read(_stack, kScriptStackSize);
_savedSp = in->readUint16LE();
// Load IP
_code = getSlotData(_regs.reg4) + in->readUint16LE();
}
void ScriptInterpreter::sfNop() {
// NOP
}
void ScriptInterpreter::sfGetGameVar() {
int16 value = getGameVar(arg16(3));
localWrite16(arg16(5), value);
}
void ScriptInterpreter::sfSetGameVar() {
int16 varIndex = arg16(3);
assert(varIndex <= 21);
VarType varType = varTypes[varIndex];
int16 value = 0;
if (varType == vtByte)
value = arg8(5);
else if (varType == vtWord)
value = arg16(5);
setGameVar(varIndex, value);
}
void ScriptInterpreter::sfUpdateScreen() {
_vm->updateScreen();
}
void ScriptInterpreter::sfGetRandomNumber() {
localWrite16(arg16(5), _vm->_rnd->getRandomNumber(arg16(3) - 1));
}
void ScriptInterpreter::sfDrawGuiTextMulti() {
_vm->_screen->drawGuiTextMulti((byte *)localPtr(arg16(3)));
}
void ScriptInterpreter::sfUpdateVerbLine() {
_vm->_screen->updateVerbLine(arg16(5), arg16(3));
}
void ScriptInterpreter::sfSetFontColor() {
_vm->_screen->_fontColor1 = 0;
_vm->_screen->_fontColor2 = arg8(3);
}
void ScriptInterpreter::sfGetTalkTextDuration() {
localWrite16(arg16(3), _vm->_screen->getTalkTextDuration());
}
void ScriptInterpreter::sfTalk() {
_vm->talk(arg16(5), arg16(3));
}
void ScriptInterpreter::sfFindPaletteFragment() {
localWrite16(arg16(5), _vm->_palette->findFragment(arg16(3)));
}
void ScriptInterpreter::sfClearPaletteFragments() {
_vm->_palette->clearFragments();
}
void ScriptInterpreter::sfAddPaletteFragment() {
_vm->_palette->addFragment(arg16(3), arg16(5));
}
void ScriptInterpreter::sfSetDeltaAnimPalette() {
_vm->_palette->setDeltaPalette(_vm->_palette->getAnimPalette(), arg8(6), (char)arg8(5), arg8(4), arg8(3));
}
void ScriptInterpreter::sfSetUnkPaletteEffect() {
error("ScriptInterpreter::sfSetUnkPaletteEffect called"); // unused
}
void ScriptInterpreter::sfBuildColorTransTable() {
_vm->_palette->buildColorTransTable(arg8(4), (char)arg8(3), arg8(5));
}
void ScriptInterpreter::sfSetDeltaMainPalette() {
_vm->_palette->setDeltaPalette(_vm->_palette->getMainPalette(), arg8(6), (char)arg8(5), arg8(4), arg8(3));
}
void ScriptInterpreter::sfLoadScript() {
int16 codeOfs = _code - getSlotData(_regs.reg4);
loadScript(arg16(4), arg8(3));
_code = getSlotData(_regs.reg4) + codeOfs;
_switchLocalDataNear = true;
}
void ScriptInterpreter::sfRegisterFont() {
_vm->_screen->registerFont(arg8(3), arg16(4));
}
void ScriptInterpreter::sfLoadAddPalette() {
_vm->_palette->loadAddPalette(arg16(4), arg8(3));
}
void ScriptInterpreter::sfLoadScene() {
if (arg8(3) == 0) {
// FIXME: Originally, this was stopSpeech(). However, we need to stop
// ALL sounds here (including sound effects and background sounds)
// before purgeCache() is called, otherwise the sound buffers will be
// invalidated. This is apparent when moving from a scene that has
// background sounds (such as the canyon at the beginning), to another
// one that doesn't (such as the map), and does not stop the sounds
// already playing. In this case, the engine will either crash or
// garbage will be heard through the speakers.
// TODO: We should either move purgeCache() elsewhere, or monitor
// which resources are still used before purging the cache.
_vm->_sound->stopAll();
_vm->_res->purgeCache();
_vm->loadScene(arg16(4));
} else {
_vm->_screen->loadMouseCursor(arg16(4));
}
}
void ScriptInterpreter::sfSetGuiHeight() {
_vm->setGuiHeight(arg8(3));
}
void ScriptInterpreter::sfFindMouseInRectIndex1() {
int16 index = -1;
if (_vm->_mouseY < _vm->_cameraHeight) {
int16 slotIndex = arg16(5);
index = _vm->findRectAtPoint(getSlotData(slotIndex) + arg16(3),
_vm->_mouseX + _vm->_cameraX,
_vm->_mouseY + _vm->_cameraY,
arg16(11) + 1, arg16(7),
getSlotData(slotIndex) + _slots[slotIndex].size);
}
localWrite16(arg16(9), index);
}
void ScriptInterpreter::sfFindMouseInRectIndex2() {
int16 index = -1;
if (_vm->_sceneResIndex != 0) {
if (_vm->_mouseY < _vm->_cameraHeight) {
int16 slotIndex = arg16(5);
index = _vm->findRectAtPoint(getSlotData(slotIndex) + arg16(3),
_vm->_mouseX + _vm->_cameraX,
_vm->_mouseY + _vm->_cameraY,
0, arg16(7),
getSlotData(slotIndex) + _slots[slotIndex].size);
}
}
localWrite16(arg16(9), index);
}
void ScriptInterpreter::sfDrawGuiImage() {
_vm->_screen->drawGuiImage(arg16(5), arg16(3), arg16(7));
}
void ScriptInterpreter::sfAddAnimatedSpriteNoLoop() {
_vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte *)localPtr(0), (int16 *)localPtr(arg16(9)), false, 2);
}
void ScriptInterpreter::sfAddAnimatedSprite() {
_vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte *)localPtr(0), (int16 *)localPtr(arg16(9)), true, 2);
}
void ScriptInterpreter::sfAddStaticSprite() {
_vm->_screen->addStaticSprite(_subCode + 3);
}
void ScriptInterpreter::sfAddAnimatedSpriteScaled() {
_vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte *)localPtr(0), (int16 *)localPtr(arg16(9)), true, 1);
}
void ScriptInterpreter::sfFindPath() {
_vm->_segmap->findPath((int16 *)(getSlotData(arg16(13)) + arg16(11)), arg16(9), arg16(7), arg16(5), arg16(3));
}
void ScriptInterpreter::sfWalk() {
_vm->walk(getSlotData(arg16(5)) + arg16(3));
}
void ScriptInterpreter::sfScrollCameraUp() {
_vm->scrollCameraUp(4);
}
void ScriptInterpreter::sfScrollCameraDown() {
_vm->scrollCameraDown(4);
}
void ScriptInterpreter::sfScrollCameraLeft() {
_vm->scrollCameraLeft(4);
}
void ScriptInterpreter::sfScrollCameraRight() {
_vm->scrollCameraRight(4);
}
void ScriptInterpreter::sfScrollCameraUpEx() {
_vm->scrollCameraUp(arg16(3));
}
void ScriptInterpreter::sfScrollCameraDownEx() {
_vm->scrollCameraDown(arg16(3));
}
void ScriptInterpreter::sfScrollCameraLeftEx() {
_vm->scrollCameraLeft(arg16(3));
}
void ScriptInterpreter::sfScrollCameraRightEx() {
_vm->scrollCameraRight(arg16(3));
}
void ScriptInterpreter::sfSetCamera() {
_vm->setCamera(arg16(5), arg16(3));
}
void ScriptInterpreter::sfGetCameraChanged() {
localWrite16(arg16(3), _vm->getCameraChanged() ? 1 : 0);
}
void ScriptInterpreter::sfGetRgbModifiertAtPoint() {
byte *rgb = getSlotData(arg16(11)) + arg16(9);
_vm->_segmap->getRgbModifiertAtPoint(arg16(5), arg16(3), arg16(7), rgb[0], rgb[1], rgb[2]);
}
void ScriptInterpreter::sfStartAnim() {
_vm->_anim->start(arg16(3));
}
void ScriptInterpreter::sfAnimNextFrame() {
_vm->_anim->nextFrame();
}
void ScriptInterpreter::sfGetAnimFrameNumber() {
localWrite16(arg16(3), _vm->_anim->getFrameNumber());
}
void ScriptInterpreter::sfGetAnimStatus() {
int16 status = _vm->_anim->getStatus();
if (status == 0 || status == 1) {
// TODO mov screenFlag01, 0
}
localWrite16(arg16(3), status);
}
void ScriptInterpreter::sfStartShakeScreen() {
_vm->_screen->startShakeScreen(arg16(3));
}
void ScriptInterpreter::sfStopShakeScreen() {
_vm->_screen->stopShakeScreen();
}
void ScriptInterpreter::sfStartSequence() {
int16 sequenceResIndex = arg16(3);
debug(1, "ScriptInterpreter::sfStartSequence(%d)", sequenceResIndex);
if (sequenceResIndex >= 0) {
//_vm->_arc->dump(sequenceResIndex, "music"); // DEBUG: Dump music so we know what's in there
_vm->_music->playSequence(sequenceResIndex);
}
}
void ScriptInterpreter::sfEndSequence() {
_vm->_music->stopSequence();
}
void ScriptInterpreter::sfSetSequenceVolume() {
// TODO
//debug("ScriptInterpreter::sfSetSequenceVolume");
}
void ScriptInterpreter::sfPlayPositionalSound() {
_vm->_sound->playSoundAtPos(arg16(3), arg16(9), arg16(7));
}
void ScriptInterpreter::sfPlaySound2() {
_vm->_sound->playSound(arg16(3), arg16(5), arg16(7));
}
void ScriptInterpreter::sfClearScreen() {
// TODO: Occurs on every scene change, but seems unneeded
//debug("ScriptInterpreter::sfClearScreen");
}
void ScriptInterpreter::sfHandleInput() {
int16 varOfs = arg16(3);
int16 keyCode = 0;
if (_vm->_rightButtonDown) {
keyCode = 1;
} else {
// Convert keyboard scancode to IBM PC scancode.
// Only scancodes known to be used (so far) are converted.
switch (_vm->_keyState.keycode) {
case Common::KEYCODE_ESCAPE:
keyCode = 1;
break;
case Common::KEYCODE_F10:
keyCode = 68;
break;
default:
break;
}
}
localWrite16(varOfs, keyCode);
}
void ScriptInterpreter::sfRunOptionsScreen() {
_vm->showMenu(kMenuIdMain);
}
/**
* NOTE: The opcodes sfPrecacheSprites, sfPrecacheSounds1, sfPrecacheSounds2 and
* sfDeletePrecachedFiles were used by the original engine to handle precaching
* of data so the game doesn't stall while playing (due to the slow speed of
* CD-Drives back then). This is not needed in ScummVM since all supported
* systems are fast enough to load data in-game.
*/
void ScriptInterpreter::sfPrecacheSprites() {
// See note above
}
void ScriptInterpreter::sfPrecacheSounds1() {
// See note above
}
void ScriptInterpreter::sfDeletePrecachedFiles() {
// See note above
}
void ScriptInterpreter::sfPrecacheSounds2() {
// See note above
}
void ScriptInterpreter::sfRestoreStackPtr() {
_regs.sp = _savedSp;
}
void ScriptInterpreter::sfSaveStackPtr() {
_savedSp = _regs.sp;
}
void ScriptInterpreter::sfPlayMovie() {
CursorMan.showMouse(false);
_vm->_moviePlayer->playMovie(arg16(3));
CursorMan.showMouse(true);
}
} // End of namespace Toltecs