scummvm/scumm/script_v8.cpp

1744 lines
43 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2002-2003 The ScummVM project
*
* 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$
*
*/
#include "stdafx.h"
#include "scumm.h"
#include "actor.h"
#include "charset.h"
#include "intern.h"
#include "sound.h"
#include "verbs.h"
#include "smush/smush_player.h"
#include <time.h>
#define OPCODE(x) { &Scumm_v8::x, #x }
void Scumm_v8::setupOpcodes() {
static const OpcodeEntryV8 opcodes[256] = {
/* 00 */
OPCODE(o6_invalid),
OPCODE(o6_pushWord),
OPCODE(o6_pushWordVar),
OPCODE(o6_wordArrayRead),
/* 04 */
OPCODE(o6_wordArrayIndexedRead),
OPCODE(o6_dup),
OPCODE(o6_pop),
OPCODE(o6_not),
/* 08 */
OPCODE(o6_eq),
OPCODE(o6_neq),
OPCODE(o6_gt),
OPCODE(o6_lt),
/* 0C */
OPCODE(o6_le),
OPCODE(o6_ge),
OPCODE(o6_add),
OPCODE(o6_sub),
/* 10 */
OPCODE(o6_mul),
OPCODE(o6_div),
OPCODE(o6_land),
OPCODE(o6_lor),
/* 14 */
OPCODE(o6_band),
OPCODE(o6_bor),
OPCODE(o8_mod),
OPCODE(o6_invalid),
/* 18 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 1C */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 20 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 24 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 28 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 2C */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 30 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 34 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 38 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 3C */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 40 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 44 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 48 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 4C */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 50 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 54 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 58 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 5C */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 60 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 64 */
OPCODE(o6_jumpTrue),
OPCODE(o6_jumpFalse),
OPCODE(o6_jump),
OPCODE(o6_breakHere),
/* 68 */
OPCODE(o6_delayFrames),
OPCODE(o8_wait),
OPCODE(o6_delay),
OPCODE(o6_delaySeconds),
/* 6C */
OPCODE(o6_delayMinutes),
OPCODE(o6_writeWordVar),
OPCODE(o6_wordVarInc),
OPCODE(o6_wordVarDec),
/* 70 */
OPCODE(o8_dim),
OPCODE(o6_wordArrayWrite),
OPCODE(o6_wordArrayInc),
OPCODE(o6_wordArrayDec),
/* 74 */
OPCODE(o8_dim2),
OPCODE(o6_wordArrayIndexedWrite),
OPCODE(o8_arrayOps),
OPCODE(o6_invalid),
/* 78 */
OPCODE(o6_invalid),
OPCODE(o6_startScriptEx),
OPCODE(o6_startScript),
OPCODE(o6_stopObjectCode),
/* 7C */
OPCODE(o6_stopScript),
OPCODE(o6_jumpToScript),
OPCODE(o6_dummy), // O_RETURN boils down to a NOP
OPCODE(o6_startObjectEx),
/* 80 */
OPCODE(o6_stopObjectScript),
OPCODE(o6_cutscene),
OPCODE(o6_endCutscene),
OPCODE(o6_freezeUnfreeze),
/* 84 */
OPCODE(o6_beginOverride),
OPCODE(o6_endOverride),
OPCODE(o6_stopSentence),
OPCODE(o6_invalid),
/* 88 */
OPCODE(o6_invalid),
OPCODE(o6_setClass),
OPCODE(o6_setState),
OPCODE(o6_setOwner),
/* 8C */
OPCODE(o6_panCameraTo),
OPCODE(o6_actorFollowCamera),
OPCODE(o6_setCameraAt),
OPCODE(o6_printActor),
/* 90 */
OPCODE(o6_printEgo),
OPCODE(o6_talkActor),
OPCODE(o6_talkEgo),
OPCODE(o6_printLine),
/* 94 */
OPCODE(o6_printCursor),
OPCODE(o6_printDebug),
OPCODE(o6_printSystem),
OPCODE(o8_blastText),
/* 98 */
OPCODE(o8_drawObject),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* 9C */
OPCODE(o8_cursorCommand),
OPCODE(o6_loadRoom),
OPCODE(o6_loadRoomWithEgo),
OPCODE(o6_walkActorToObj),
/* A0 */
OPCODE(o6_walkActorTo),
OPCODE(o6_putActorInRoom),
OPCODE(o6_putActorAtObject),
OPCODE(o6_faceActor),
/* A4 */
OPCODE(o6_animateActor),
OPCODE(o6_doSentence),
OPCODE(o6_pickupObject),
OPCODE(o6_setBoxFlags),
/* A8 */
OPCODE(o8_createBoxMatrix),
OPCODE(o6_invalid),
OPCODE(o8_resourceRoutines),
OPCODE(o8_roomOps),
/* AC */
OPCODE(o8_actorOps),
OPCODE(o8_cameraOps),
OPCODE(o8_verbOps),
OPCODE(o6_startSound),
/* B0 */
OPCODE(o6_startMusic),
OPCODE(o6_stopSound),
OPCODE(o8_soundKludge),
OPCODE(o8_system),
/* B4 */
OPCODE(o6_saveRestoreVerbs),
OPCODE(o6_setObjectName),
OPCODE(o6_getDateTime),
OPCODE(o6_drawBox),
/* B8 */
OPCODE(o6_invalid),
OPCODE(o8_startVideo),
OPCODE(o8_kernelSetFunctions),
OPCODE(o6_invalid),
/* BC */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* C0 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* C4 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* C8 */
OPCODE(o6_startScriptQuick),
OPCODE(o6_startObjectQuick),
OPCODE(o6_pickOneOf),
OPCODE(o6_pickOneOfDefault),
/* CC */
OPCODE(o6_invalid),
OPCODE(o6_isAnyOf),
OPCODE(o6_getRandomNumber),
OPCODE(o6_getRandomNumberRange),
/* D0 */
OPCODE(o6_ifClassOfIs),
OPCODE(o6_getState),
OPCODE(o6_getOwner),
OPCODE(o6_isScriptRunning),
/* D4 */
OPCODE(o6_invalid),
OPCODE(o6_isSoundRunning),
OPCODE(o6_abs),
OPCODE(o6_invalid),
/* D8 */
OPCODE(o8_kernelGetFunctions),
OPCODE(o6_isActorInBox),
OPCODE(o6_getVerbEntrypoint),
OPCODE(o6_getActorFromXY),
/* DC */
OPCODE(o6_findObject),
OPCODE(o6_getVerbFromXY),
OPCODE(o6_invalid),
OPCODE(o6_findInventory),
/* E0 */
OPCODE(o6_getInventoryCount),
OPCODE(o6_getAnimateVariable),
OPCODE(o6_getActorRoom),
OPCODE(o6_getActorWalkBox),
/* E4 */
OPCODE(o6_getActorMoving),
OPCODE(o6_getActorCostume),
OPCODE(o6_getActorScaleX),
OPCODE(o6_getActorLayer),
/* E8 */
OPCODE(o6_getActorElevation),
OPCODE(o6_getActorWidth),
OPCODE(o6_getObjectNewDir),
OPCODE(o6_getObjectX),
/* EC */
OPCODE(o6_getObjectY),
OPCODE(o8_getActorChore),
OPCODE(o6_distObjectObject),
OPCODE(o6_distPtPt),
/* F0 */
OPCODE(o8_getObjectImageX),
OPCODE(o8_getObjectImageY),
OPCODE(o8_getObjectImageWidth),
OPCODE(o8_getObjectImageHeight),
/* F4 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o8_getStringWidth),
OPCODE(o8_getActorZPlane),
/* F8 */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
/* FC */
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
OPCODE(o6_invalid),
};
_opcodesV8 = opcodes;
}
void Scumm_v8::executeOpcode(byte i) {
OpcodeProcV8 op = _opcodesV8[i].proc;
(this->*op) ();
}
const char *Scumm_v8::getOpcodeDesc(byte i) {
return _opcodesV8[i].desc;
}
// In V8, the word size is 4 byte, not 2 bytes as in V6/V7 games
uint Scumm_v8::fetchScriptWord() {
int a;
if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
uint32 oldoffs = _scriptPointer - _scriptOrgPointer;
getScriptBaseAddress();
_scriptPointer = _scriptOrgPointer + oldoffs;
}
a = READ_LE_UINT32(_scriptPointer);
_scriptPointer += 4;
return a;
}
int Scumm_v8::fetchScriptWordSigned() {
return (int32)fetchScriptWord();
}
int Scumm_v8::readVar(uint var) {
debug(9, "readvar(%d)", var);
if (!(var & 0xF0000000)) {
checkRange(_numVariables - 1, 0, var, "Variable %d out of range(r)");
return _scummVars[var];
}
if (var & 0x80000000) {
var &= 0x7FFFFFFF;
checkRange(_numBitVariables - 1, 0, var, "Bit variable %d out of range(r)");
return (_bitVars[var >> 3] & (1 << (var & 7))) ? 1 : 0;
}
if (var & 0x40000000) {
var &= 0xFFFFFFF;
checkRange(25, 0, var, "Local variable %d out of range(r)");
return vm.localvar[_currentScript][var];
}
error("Illegal varbits (r)");
return -1;
}
void Scumm_v8::writeVar(uint var, int value) {
debug(9, "writeVar(%d, %d)", var, value);
if (!(var & 0xF0000000)) {
checkRange(_numVariables - 1, 0, var, "Variable %d out of range(w)");
_scummVars[var] = value;
if ((_varwatch == (int)var) || (_varwatch == 0)) {
if (vm.slot[_currentScript].number < 100)
debug(1, "vars[%d] = %d (via script-%d)", var, value, vm.slot[_currentScript].number);
else
debug(1, "vars[%d] = %d (via room-%d-%d)", var, value, _currentRoom, vm.slot[_currentScript].number);
}
return;
}
if (var & 0x80000000) {
var &= 0x7FFFFFFF;
checkRange(_numBitVariables - 1, 0, var, "Bit variable %d out of range(w)");
if (value)
_bitVars[var >> 3] |= (1 << (var & 7));
else
_bitVars[var >> 3] &= ~(1 << (var & 7));
return;
}
if (var & 0x40000000) {
var &= 0xFFFFFFF;
checkRange(25, 0, var, "Local variable %d out of range(w)");
vm.localvar[_currentScript][var] = value;
return;
}
error("Illegal varbits (w)");
}
void Scumm_v8::decodeParseString(int m, int n) {
byte b;
bool containsSpeech;
b = fetchScriptByte();
switch (b) {
case 0xC8:
setStringVars(m);
if (n)
_actorToPrintStrFor = pop();
break;
case 0xC9:
_string[m].t_xpos = _string[m].xpos;
_string[m].t_ypos = _string[m].ypos;
_string[m].t_center = _string[m].center;
_string[m].t_overhead = _string[m].overhead;
_string[m].t_no_talk_anim = _string[m].no_talk_anim;
_string[m].t_right = _string[m].right;
_string[m].t_color = _string[m].color;
_string[m].t_charset = _string[m].charset;
break;
case 0xCA:
_string[m].ypos = pop();
_string[m].xpos = pop();
_string[m].overhead = false;
break;
case 0xCB:
_string[m].color = pop();
break;
case 0xCC:
_string[m].center = true;
_string[m].overhead = false;
break;
case 0xCD: // SO_PRINT_CHARSET Set print character set
_string[m].charset = pop();
break;
case 0xCE:
_string[m].center = false;
_string[m].overhead = false;
break;
case 0xCF:
_string[m].overhead = true;
_string[m].no_talk_anim = false;
break;
case 0xD2:
case 0xD0: // SO_PRINT_MUMBLE
_string[m].no_talk_anim = true;
break;
case 0xD1:
_messagePtr = _scriptPointer;
containsSpeech = (_messagePtr[0] == '/');
if (containsSpeech) {
char pointer[20];
int i, j;
_scriptPointer += resStrLen(_scriptPointer) + 1;
translateText(_messagePtr, _transText);
for (i = 0, j = 0; (_messagePtr[i] != '/' || j == 0) && j < 19; i++) {
if (_messagePtr[i] != '/')
pointer[j++] = _messagePtr[i];
}
pointer[j] = 0;
int new_sound = _sound->playBundleSound(pointer);
if (new_sound != -1) {
// Stop any talking that's still going on
if (_sound->_talkChannel > -1)
_mixer->stop(_sound->_talkChannel);
_sound->_talkChannel = new_sound;
}
_messagePtr = _transText;
}
switch (m) {
case 0:
actorTalk();
break;
case 1:
drawString(1);
break;
case 2:
unkMessage1();
break;
case 3:
unkMessage2();
break;
case 5:{
byte buffer[256];
_msgPtrToAdd = buffer;
_messagePtr = addMessageToStack(_messagePtr);
enqueueText(buffer, _string[m].xpos, _string[m].ypos, _string[m].color, _string[m].charset, _string[m].center);
}
break;
}
if (!containsSpeech)
_scriptPointer = _messagePtr;
break;
// case 0xD2: // SO_PRINT_WRAP Set print wordwrap
// error("decodeParseString: SO_PRINT_MUMBLE");
// break;
default:
error("decodeParseString: default case 0x%x", b);
}
}
void Scumm::enqueueText(byte *text, int x, int y, byte color, byte charset, bool center) {
BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
assert(_blastTextQueuePos <= 32);
strcpy((char *)bt.text, (const char *)text);
bt.xpos = x;
bt.ypos = y;
bt.color = color;
bt.charset = charset;
bt.center = center;
}
void Scumm::drawBlastTexts() {
// FIXME
byte *buf;
byte c;
int i;
_charset->_ignoreCharsetMask = true;
for (i = 0; i < _blastTextQueuePos; i++) {
buf = _blastTextQueue[i].text;
_charset->_top = _blastTextQueue[i].ypos;
_charset->_startLeft = _charset->_left = _blastTextQueue[i].xpos;
_charset->_right = _screenWidth - 1;
_charset->_center = _blastTextQueue[i].center;
_charset->setColor(_blastTextQueue[i].color);
_charset->_disableOffsX = _charset->_firstChar = true;
_charset->setCurID(_blastTextQueue[i].charset);
_charset->_nextLeft = _blastTextQueue[i].xpos;
_charset->_nextTop = _blastTextQueue[i].ypos;
// Center text if necessary
if (_charset->_center) {
_charset->_nextLeft -= _charset->getStringWidth(0, buf) >> 1;
if (_charset->_nextLeft < 0)
_charset->_nextLeft = 0;
}
do {
c = *buf++;
if (c != 0 && c != 0xFF) {
_charset->_left = _charset->_nextLeft;
_charset->_top = _charset->_nextTop;
_charset->printChar(c);
_charset->_nextLeft = _charset->_left;
_charset->_nextTop = _charset->_top;
}
} while (c);
_blastTextQueue[i].rect = _charset->_str;
}
_charset->_ignoreCharsetMask = false;
}
void Scumm::removeBlastTexts() {
int i;
for (i = 0; i < _blastTextQueuePos; i++) {
restoreBG(_blastTextQueue[i].rect);
}
_blastTextQueuePos = 0;
}
void Scumm_v8::o8_mod() {
int a = pop();
push(pop() % a);
}
void Scumm_v8::o8_wait() {
int actnum, offs;
Actor *a;
byte subOp = fetchScriptByte();
switch (subOp) {
case 0x1E: // SO_WAIT_FOR_ACTOR Wait for actor (to finish current action?)
offs = fetchScriptWordSigned();
actnum = pop();
a = derefActorSafe(actnum, "o8_wait:SO_WAIT_FOR_ACTOR");
assert(a);
if (a->moving) {
_scriptPointer += offs;
o6_breakHere();
}
return;
case 0x1F: // SO_WAIT_FOR_MESSAGE Wait for message
if (VAR(VAR_HAVE_MSG))
break;
return;
case 0x20: // SO_WAIT_FOR_CAMERA Wait for camera (to finish current action?)
if (camera._dest != camera._cur)
break;
return;
case 0x21: // SO_WAIT_FOR_SENTENCE
if (_sentenceNum) {
if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
return;
break;
}
if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
return;
break;
case 0x22: // SO_WAIT_FOR_ANIMATION
offs = fetchScriptWordSigned();
actnum = pop();
a = derefActorSafe(actnum, "o8_wait:SO_WAIT_FOR_ANIMATION");
assert(a);
if (a->isInCurrentRoom() && a->needRedraw) {
_scriptPointer += offs;
o6_breakHere();
}
return;
case 0x23: // SO_WAIT_FOR_TURN
offs = fetchScriptWordSigned();
actnum = pop();
a = derefActorSafe(actnum, "o8_wait:SO_WAIT_FOR_TURN");
assert(a);
if (a->isInCurrentRoom() && a->moving & MF_TURN) {
_scriptPointer += offs;
o6_breakHere();
}
return;
default:
error("o8_wait: default case 0x%x", subOp);
}
_scriptPointer -= 2;
o6_breakHere();
}
void Scumm_v8::o8_dim() {
byte subOp = fetchScriptByte();
int array = fetchScriptWord();
switch (subOp) {
case 0x0A: // SO_ARRAY_SCUMMVAR
defineArray(array, 5, 0, pop());
break;
case 0x0B: // SO_ARRAY_STRING
defineArray(array, 4, 0, pop());
break;
case 0x0C: // SO_ARRAY_UNDIM
nukeArray(array);
break;
default:
error("o8_dim: default case 0x%x", subOp);
}
}
void Scumm_v8::o8_dim2() {
byte subOp = fetchScriptByte();
int array = fetchScriptWord(), a, b;
switch (subOp) {
case 0x0A: // SO_ARRAY_SCUMMVAR
b = pop();
a = pop();
defineArray(array, 5, a, b);
break;
case 0x0B: // SO_ARRAY_STRING
b = pop();
a = pop();
defineArray(array, 4, a, b);
break;
case 0x0C: // SO_ARRAY_UNDIM
nukeArray(array);
break;
default:
error("o8_dim2: default case 0x%x", subOp);
}
}
void Scumm_v8::o8_arrayOps() {
byte subOp = fetchScriptByte();
int array = fetchScriptWord();
int b, c, d, len;
ArrayHeader *ah;
int list[128];
switch (subOp) {
case 0x14: // SO_ASSIGN_STRING
b = pop();
len = resStrLen(_scriptPointer);
c = defineArray(array, 4, 0, len + 1);
ah = (ArrayHeader *)getResourceAddress(rtString, c);
copyScriptString(ah->data + b);
break;
case 0x15: // SO_ASSIGN_SCUMMVAR_LIST
b = pop();
len = getStackList(list, sizeof(list) / sizeof(list[0]));
d = readVar(array);
if (d == 0) {
defineArray(array, 5, 0, b + len);
}
while (--len >= 0) {
writeArray(array, 0, b + len, list[len]);
}
break;
case 0x16: // SO_ASSIGN_2DIM_LIST
b = pop();
len = getStackList(list, sizeof(list) / sizeof(list[0]));
d = readVar(array);
if (d == 0)
error("Must DIM a two dimensional array before assigning");
c = pop();
while (--len >= 0) {
writeArray(array, c, b + len, list[len]);
}
break;
default:
error("o8_arrayOps: default case 0x%x (array %d)", subOp, array);
}
}
void Scumm_v8::o8_blastText() {
// FIXME
decodeParseString(5, 0);
}
void Scumm_v8::o8_cursorCommand() {
byte subOp = fetchScriptByte();
int a, i;
int args[16];
switch (subOp) {
case 0xDC: // SO_CURSOR_ON Turn cursor on
_cursor.state = 1;
verbMouseOver(0);
break;
case 0xDD: // SO_CURSOR_OFF Turn cursor off
_cursor.state = 0;
verbMouseOver(0);
break;
case 0xDE: // SO_CURSOR_SOFT_ON Turn soft cursor on
_cursor.state++;
if (_cursor.state > 1)
error("Cursor state greater than 1 in script");
verbMouseOver(0);
break;
case 0xDF: // SO_CURSOR_SOFT_OFF Turn soft cursor off
_cursor.state--;
verbMouseOver(0);
break;
case 0xE0: // SO_USERPUT_ON
_userPut = 1;
break;
case 0xE1: // SO_USERPUT_OFF
_userPut = 0;
break;
case 0xE2: // SO_USERPUT_SOFT_ON
_userPut++;
break;
case 0xE3: // SO_USERPUT_SOFT_OFF
_userPut--;
break;
case 0xE4: // SO_CURSOR_IMAGE Set cursor image
{
int idx = pop();
int room, obj;
obj = popRoomAndObj(&room);
setCursorImg(obj, room, idx);
}
break;
case 0xE5: // SO_CURSOR_HOTSPOT Set cursor hotspot
a = pop();
setCursorHotspot(pop(), a);
break;
case 0xE6: // SO_CURSOR_TRANSPARENT Set cursor transparent color
makeCursorColorTransparent(pop());
break;
case 0xE7: { // SO_CHARSET_SET
int charset = pop();
warning("Set userface charset to %d", charset);
// loadCharset(charset);
break;
}
case 0xE8: // SO_CHARSET_COLOR
getStackList(args, sizeof(args) / sizeof(args[0]));
for (i = 0; i < 16; i++)
_charsetColorMap[i] = _charsetData[_string[1].t_charset][i] = (unsigned char)args[i];
break;
case 0xE9: // SO_CURSOR_PUT
{
int y = pop();
int x = pop();
_system->warp_mouse(x, y);
_system->update_screen();
}
break;
default:
error("o8_cursorCommand: default case 0x%x", subOp);
}
VAR(VAR_CURSORSTATE) = _cursor.state;
VAR(VAR_USERPUT) = _userPut;
}
void Scumm_v8::o8_createBoxMatrix() {
int i;
Actor *a;
createBoxMatrix();
for(i = 1; i < _numActors; i++) {
a = &_actors[i];
if (a && a->isInCurrentRoom())
a->adjustActorPos();
}
}
void Scumm_v8::o8_resourceRoutines() {
byte subOp = fetchScriptByte();
int resid = pop();
switch (subOp) {
case 0x3C: // SO_HEAP_LOAD_CHARSET Load character set to heap
ensureResourceLoaded(rtCharset, resid); // FIXME - is this correct?
break;
case 0x3D: // SO_HEAP_LOAD_COSTUME Load costume to heap
ensureResourceLoaded(rtCostume, resid);
break;
case 0x3E: // SO_HEAP_LOAD_OBJECT Load object to heap
{
int room = getObjectRoom(resid);
loadFlObject(resid, room);
}
break;
case 0x3F: // SO_HEAP_LOAD_ROOM Load room to heap
ensureResourceLoaded(rtRoom, resid);
break;
case 0x40: // SO_HEAP_LOAD_SCRIPT Load script to heap
ensureResourceLoaded(rtScript, resid);
break;
case 0x41: // SO_HEAP_LOAD_SOUND Load sound to heap
ensureResourceLoaded(rtSound, resid);
break;
case 0x42: // SO_HEAP_LOCK_COSTUME Lock costume in heap
lock(rtCostume, resid);
break;
case 0x43: // SO_HEAP_LOCK_ROOM Lock room in heap
lock(rtRoom, resid);
break;
case 0x44: // SO_HEAP_LOCK_SCRIPT Lock script in heap
lock(rtScript, resid);
break;
case 0x45: // SO_HEAP_LOCK_SOUND Lock sound in heap
lock(rtSound, resid);
break;
case 0x46: // SO_HEAP_UNLOCK_COSTUME Unlock costume
unlock(rtCostume, resid);
break;
case 0x47: // SO_HEAP_UNLOCK_ROOM Unlock room
unlock(rtRoom, resid);
break;
case 0x48: // SO_HEAP_UNLOCK_SCRIPT Unlock script
unlock(rtScript, resid);
break;
case 0x49: // SO_HEAP_UNLOCK_SOUND Unlock sound
unlock(rtSound, resid);
break;
case 0x4A: // SO_HEAP_NUKE_COSTUME Remove costume from heap
setResourceCounter(rtCostume, resid, 0x7F);
break;
case 0x4B: // SO_HEAP_NUKE_ROOM Remove room from heap
setResourceCounter(rtRoom, resid, 0x7F);
break;
case 0x4C: // SO_HEAP_NUKE_SCRIPT Remove script from heap
setResourceCounter(rtScript, resid, 0x7F);
break;
case 0x4D: // SO_HEAP_NUKE_SOUND Remove sound from heap
setResourceCounter(rtSound, resid, 0x7F);
break;
default:
error("o8_resourceRoutines: default case 0x%x", subOp);
}
}
void Scumm_v8::o8_roomOps() {
byte subOp = fetchScriptByte();
int a, b, c, d, e;
switch (subOp) {
case 0x52: // SO_ROOM_PALETTE Set room palette
d = pop();
c = pop();
b = pop();
a = pop();
setPalColor(d, a, b, c);
break;
case 0x55: // SO_ROOM_INTENSITY Set room intensity
// Not used in CMI???
c = pop();
b = pop();
a = pop();
darkenPalette(a, a, a, b, c);
break;
case 0x57: // SO_ROOM_FADE Fade room
a = pop();
if (a) {
_switchRoomEffect = (byte)(a);
_switchRoomEffect2 = (byte)(a >> 8);
} else {
fadeIn(_newEffect);
}
break;
case 0x58: // SO_ROOM_RGB_INTENSITY Set room color intensity
e = pop();
d = pop();
c = pop();
b = pop();
a = pop();
darkenPalette(a, b, c, d, e);
break;
case 0x59: // SO_ROOM_TRANSFORM Transform room
d = pop();
c = pop();
b = pop();
a = pop();
warning("o8_roomOps: unimplemented case SO_ROOM_TRANSFORM(%d,%d,%d,%d)", a, b, c, d);
break;
case 0x5A: // SO_ROOM_CYCLE_SPEED Set palette cycling speed
case 0x5B: // SO_ROOM_COPY_PALETTE Copy palette
error("o8_roomOps: unimplemented case %d", subOp);
break;
case 0x5C: // SO_ROOM_NEW_PALETTE New palette
a = pop();
setPalette(a);
break;
case 0x5D: // SO_ROOM_SAVE_GAME Save game
_saveLoadCompatible = true;
_saveLoadSlot = 1;
_saveLoadFlag = 1;
break;
case 0x5E: // SO_ROOM_LOAD_GAME Load game
_saveLoadCompatible = true;
_saveLoadSlot = 1;
_saveLoadFlag = 2;
break;
case 0x5F: // SO_ROOM_SATURATION Set saturation of room colors
e = pop();
d = pop();
c = pop();
b = pop();
a = pop();
desaturatePalette(a, b, c, d, e);
break;
default:
error("o8_roomOps: default case 0x%x", subOp);
}
}
void Scumm_v8::o8_actorOps() {
byte subOp = fetchScriptByte();
Actor *a;
int i, j;
if (subOp == 0x7A) {
_curActor = pop();
//printf("Setting current actor to %d\n", _curActor);
return;
}
a = derefActorSafe(_curActor, "o8_actorOps");
if (!a)
return;
switch (subOp) {
case 0x64: // SO_ACTOR_COSTUME Set actor costume
a->setActorCostume(pop());
break;
case 0x65: // SO_ACTOR_STEP_DIST Set actor width of steps
j = pop();
i = pop();
a->setActorWalkSpeed(i, j);
break;
case 0x67: // SO_ACTOR_ANIMATION_DEFAULT Set actor animation to default
a->initFrame = 1;
a->walkFrame = 2;
a->standFrame = 3;
a->talkFrame1 = 4;
a->talkFrame2 = 5;
break;
case 0x68: // SO_ACTOR_ANIMATION_INIT Initialize animation
a->initFrame = pop();
break;
case 0x69: // SO_ACTOR_ANIMATION_TALK Set actor animation to talk animation
a->talkFrame2 = pop();
a->talkFrame1 = pop();
break;
case 0x6A: // SO_ACTOR_ANIMATION_WALK Set actor animation to walk animation
a->walkFrame = pop();
break;
case 0x6B: // SO_ACTOR_ANIMATION_STAND Set actor animation to standing animation
a->standFrame = pop();
break;
case 0x6C: // SO_ACTOR_ANIMATION_SPEED Set speed of animation
a->animSpeed = pop();
a->animProgress = 0;
break;
case 0x6D: // SO_ACTOR_DEFAULT
// FIXME - is this right? Or maybe a->initActor(2) ?
//warning("o8_actorOps: SO_ACTOR_DEFAULT");
a->initActor(0);
break;
case 0x6E: // SO_ACTOR_ELEVATION
a->elevation = pop();
a->needRedraw = true;
a->needBgReset = true;
break;
case 0x6F: // SO_ACTOR_PALETTE Set actor palette
j = pop();
i = pop();
checkRange(31, 0, i, "Illegal palette slot %d");
a->palette[i] = j;
a->needRedraw = true;
break;
case 0x70: // SO_ACTOR_TALK_COLOR Set actor talk color
a->talkColor = pop();
break;
case 0x71: // SO_ACTOR_NAME Set name of actor
loadPtrToResource(rtActorName, a->number, NULL);
break;
case 0x72: // SO_ACTOR_WIDTH Set width of actor
a->width = pop();
break;
case 0x73: // SO_ACTOR_SCALE Set scaling of actor
a->scalex = a->scaley = pop();
a->needRedraw = true;
a->needBgReset = true;
break;
case 0x74: // SO_ACTOR_NEVER_ZCLIP ?
a->forceClip = 0;
break;
case 0x75: // SO_ACTOR_ALWAYS_ZCLIP ?
a->forceClip = pop();
// V8 uses 255 where we used to use 100
if (a->forceClip == 255)
a->forceClip = 100;
break;
case 0x76: // SO_ACTOR_IGNORE_BOXES Make actor ignore boxes
a->ignoreBoxes = true;
a->forceClip = 100;
if (a->isInCurrentRoom())
a->putActor(a->x, a->y, a->room);
break;
case 0x77: // SO_ACTOR_FOLLOW_BOXES Make actor follow boxes
a->ignoreBoxes = false;
a->forceClip = 100;
if (a->isInCurrentRoom())
a->putActor(a->x, a->y, a->room);
break;
case 0x78: // SO_ACTOR_SPECIAL_DRAW
a->shadow_mode = pop();
break;
case 0x79: // SO_ACTOR_TEXT_OFFSET Set text offset relative to actor
a->talkPosY = pop();
a->talkPosX = pop();
break;
// case 0x7A: // SO_ACTOR_INIT Set current actor (handled above)
case 0x7B: // SO_ACTOR_VARIABLE Set actor variable
// FIXME - is this right??
i = pop();
a->setAnimVar(pop(), i);
break;
case 0x7C: // SO_ACTOR_IGNORE_TURNS_ON Make actor ignore turns
warning("ignoreTurns not yet implemented");
a->ignoreTurns = true;
break;
case 0x7D: // SO_ACTOR_IGNORE_TURNS_OFF Make actor follow turns
a->ignoreTurns = false;
break;
case 0x7E: // SO_ACTOR_NEW New actor
// FIXME - is this right? Or maybe a->initActor(0) ?
warning("o8_actorOps: SO_ACTOR_NEW");
a->initActor(2);
break;
case 0x7F: // SO_ACTOR_DEPTH Set actor Z position
a->layer = pop();
break;
case 0x80: // SO_ACTOR_STOP
a->stopActorMoving();
a->startAnimActor(a->standFrame);
break;
case 0x81: // SO_ACTOR_FACE Make actor face angle
a->moving &= ~MF_TURN;
a->setDirection(pop());
break;
case 0x82: // SO_ACTOR_TURN Turn actor
a->turnToDirection(pop());
break;
case 0x83: // SO_ACTOR_WALK_SCRIPT Set walk script for actor?
a->walk_script = pop();
break;
case 0x84: // SO_ACTOR_TALK_SCRIPT Set talk script for actor?
a->talk_script = pop();
break;
case 0x85: // SO_ACTOR_WALK_PAUSE
a->moving |= MF_FROZEN;
break;
case 0x86: // SO_ACTOR_WALK_RESUME
a->moving &= ~MF_FROZEN;
break;
case 0x87: // SO_ACTOR_VOLUME Set volume of actor speech
// TODO - implement this!
i = pop();
warning("o8_actorOps: setActorVolume(%d) not implemented", i);
break;
case 0x88: // SO_ACTOR_FREQUENCY Set frequency of actor speech
a->talkFrequency = pop();
break;
case 0x89: // SO_ACTOR_PAN
// TODO - implement this!
i = pop();
warning("o8_actorOps: setActorPan(%d) not implemented", i);
break;
default:
error("o8_actorOps: default case 0x%x", subOp);
}
}
void Scumm_v8::o8_cameraOps() {
// TODO
byte subOp = fetchScriptByte();
switch (subOp) {
case 0x32: // SO_CAMERA_PAUSE
//warning("freezeCamera NYI");
break;
case 0x33: // SO_CAMERA_RESUME
//warning("unfreezeCamera NYI");
break;
default:
error("o8_cameraOps: default case 0x%x", subOp);
}
}
void Scumm_v8::o8_verbOps() {
byte subOp = fetchScriptByte();
VerbSlot *vs = NULL;
int slot, a, b;
_verbRedraw = true;
if (subOp == 0x96) {
_curVerb = pop();
_curVerbSlot = getVerbSlot(_curVerb, 0);
checkRange(_maxVerbs - 1, 0, _curVerbSlot, "Illegal new verb slot %d");
//printf("Setting current actor to %d\n", _curActor);
return;
}
assert(0 <= _curVerbSlot && _curVerbSlot < _maxVerbs);
vs = &_verbs[_curVerbSlot];
assert(vs);
switch (subOp) {
case 0x96: // SO_VERB_INIT Choose verb number for editing
// handled above!
break;
case 0x97: // SO_VERB_NEW New verb
if (_curVerbSlot == 0) {
for (slot = 1; slot < _maxVerbs; slot++) {
if (_verbs[slot].verbid == 0)
break;
}
if (slot >= _maxVerbs) {
error("Too many verbs");
}
_curVerbSlot = slot;
}
vs = &_verbs[_curVerbSlot];
vs->verbid = _curVerb;
vs->color = 2;
vs->hicolor = 0;
vs->dimcolor = 8;
vs->type = kTextVerbType;
vs->charset_nr = _string[0].t_charset;
vs->curmode = 0;
vs->saveid = 0;
vs->key = 0;
vs->center = 0;
vs->imgindex = 0;
break;
case 0x98: // SO_VERB_DELETE Delete verb
killVerb(_curVerbSlot);
break;
case 0x99: // SO_VERB_NAME Set verb name
loadPtrToResource(rtVerb, _curVerbSlot, NULL);
vs->type = kTextVerbType;
vs->imgindex = 0;
break;
case 0x9A: // SO_VERB_AT Set verb (X,Y) placement
vs->y = pop();
vs->x = pop();
break;
case 0x9B: // SO_VERB_ON Turn verb on
vs->curmode = 1;
break;
case 0x9C: // SO_VERB_OFF Turn verb off
vs->curmode = 0;
break;
case 0x9D: // SO_VERB_COLOR Set verb color
vs->color = pop();
break;
case 0x9E: // SO_VERB_HICOLOR Set verb highlighted color
vs->hicolor = pop();
break;
case 0xA0: // SO_VERB_DIMCOLOR Set verb dimmed (disabled) color
vs->dimcolor = pop();
break;
case 0xA1: // SO_VERB_DIM
vs->curmode = 2;
break;
case 0xA2: // SO_VERB_KEY Set keypress to associate with verb
vs->key = pop();
break;
case 0xA3: // SO_VERB_IMAGE Set verb image
b = pop();
a = pop();
if (_curVerbSlot && a != vs->imgindex) {
setVerbObject(b, a, _curVerbSlot);
vs->type = kImageVerbType;
vs->imgindex = a;
}
break;
case 0xA4: // SO_VERB_NAME_STR Set verb name
a = pop();
if (a == 0) {
loadPtrToResource(rtVerb, _curVerbSlot, (byte *)"");
} else {
loadPtrToResource(rtVerb, _curVerbSlot, getStringAddress(a));
}
vs->type = kTextVerbType;
vs->imgindex = 0;
break;
case 0xA5: // SO_VERB_CENTER Center verb
vs->center = 1;
break;
case 0xA6: // SO_VERB_CHARSET Choose charset for verb
// FIXME - TODO
vs->charset_nr = pop();
break;
case 0xA7: // SO_VERB_LINE_SPACING Choose linespacing for verb
// FIXME - TODO
// Note: it seems that var596 stores the "line spacing". It is used by various
// scripts that place verbs for that.
// Also, var595 contains the vertical position at which to start placing verbs (330)
pop();
break;
default:
error("o8_verbops: default case 0x%x", subOp);
}
}
void Scumm_v8::o8_soundKludge() {
int args[16];
int num = getStackList(args, sizeof(args) / sizeof(args[0]));
_sound->soundKludge(args, num);
}
void Scumm_v8::o8_system() {
// TODO
byte subOp = fetchScriptByte();
switch (subOp) {
case 0x28: // SO_SYSTEM_RESTART Restart game
// pauseGame(false);
// break;
case 0x29: // SO_SYSTEM_QUIT Quit game
// shutDown(0);
// break;
default:
error("o8_system: default case 0x%x", subOp);
}
}
void Scumm_v8::o8_startVideo() {
int len = resStrLen(_scriptPointer);
warning("o8_startVideo(%s/%s)", getGameDataPath(), (char*)_scriptPointer);
SmushPlayer *sp = new SmushPlayer(this, 83333, !_noSubtitles);
sp->play((char*)_scriptPointer, getGameDataPath());
delete sp;
_scriptPointer += len + 1;
}
void Scumm_v8::o8_kernelSetFunctions() {
// TODO
Actor *a;
int args[30];
int len = getStackList(args, sizeof(args) / sizeof(args[0]));
switch (args[0]) {
case 11: { // lockObject
int objidx = getObjectIndex(args[1]);
if (objidx == -1) {
warning("Cannot find object %d to lock\n", args[1]);
break;
}
lock(rtFlObject, objidx);
// if (ObjData.field28 != 0) {
// ObjData.field32 = 1;
// }
break;
}
case 12: { // unlockObject
int objidx = getObjectIndex(args[1]);
if (objidx == -1) {
warning("Cannot find object %d to unlock\n", args[1]);
break;
}
unlock(rtFlObject, objidx);
// if (ObjData.field28 != 0) {
// ObjData.field32 = 0;
// }
break;
}
case 13: // remapCostume
a = derefActorSafe(args[1], "o8_kernelSetFunctions:remapCostume");
assert(a);
a->remapActorPalette(args[2], args[3], args[4], -1);
break;
case 14: // remapCostumeInsert
a = derefActorSafe(args[1], "o8_kernelSetFunctions:remapCostumeInsert");
assert(a);
a->remapActorPalette(args[2], args[3], args[4], args[5]);
break;
case 15: // setVideoFrameRate
// not used anymore (was smush frame rate)
break;
case 20: // setBoxScaleSlot
setBoxScaleSlot(args[1], args[2]);
break;
case 21: // setScaleSlot
setScaleSlot(args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
break;
case 22: // setBannerColors
// warning("o8_kernelSetFunctions: setBannerColors(%d, %d, %d, %d)", args[1], args[2], args[3], args[4]);
break;
case 23: // setActorChoreLimbFrame
// FIXME: This is critical, and is the cause of the Cannon "too many scripts" crash
// This opcode is used a lot in script 28.
// The problem here is that args[4] is always 0, as it is computed from
// lipSyncWidth and lipSyncHeight, which we currently don't support. As a result,
// actors will currently not move their mouth at all!
// warning("o8_kernelSetFunctions: setActorChoreLimbFrame(%d, %d, %d, %d)", args[1], args[2], args[3], args[4]);
a = derefActorSafe(args[1], "o8_kernelSetFunctions:setActorChoreLimbFrame");
assert(a);
a->startAnimActor(args[2]);
a->animateLimb(args[3], args[4]);
break;
case 24: // clearTextQueue
// TODO - clearTextQueue
// warning("o8_kernelSetFunctions: clearTextQueue()");
break;
case 25: { // saveGameReadName
SaveFileManager *mgr = _system->get_savefile_manager();
char *address = (char*)getStringAddress(args[2]);
char name[30];
if (!address) {
warning("o8_kernelSetFunctions: saveGameReadName failed finding slot string %d", args[2]);
break;
}
getSavegameName(args[1] - 1, name, mgr);
if (strlen(name) > 0 && strlen(name) < 30)
strcpy(address, name);
break;
}
case 26: { // saveGame?
//SaveFileManager *mgr = _system->get_savefile_manager();
//char *address = (char*)getStringAddress(args[2]);
char address[30];
warning("o8_kernelSetFunctions: saveGame?(%d, %s)", args[1], address);
break;
}
case 27: { // FIXME: This doesn't work
// saveGameRead
_saveLoadSlot = args[1];
_saveLoadFlag = 2;
_saveLoadCompatible = false;
warning("Sgl: %d\n", args[1]);
break;
}
case 28: // saveGameStampScreenshot
warning("o8_kernelSetFunctions: saveGameStampScreenshot(%d, %d, %d, %d, %d, %d)", args[1], args[2], args[3], args[4], args[5], args[6]);
break;
case 29: // setKeyScript
_keyScriptKey = args[1];
_keyScriptNo = args[2];
break;
case 30: // killAllScriptsButMe
warning("o8_kernelSetFunctions: killAllScriptsButMe()");
killAllScriptsExceptCurrent();
break;
case 31: // stopAllVideo
warning("o8_kernelSetFunctions: stopAllVideo()");
break;
case 32: // writeRegistryValue
warning("o8_kernelSetFunctions: writeRegistryValue(%d, %d)", args[1], args[2]);
break;
case 33: // paletteSetIntensity
warning("o8_kernelSetFunctions: paletteSetIntensity(%d, %d)", args[1], args[2]);
break;
case 34: // queryQuit
warning("o8_kernelSetFunctions: queryQuit()");
break;
case 108: // buildPaletteShadow
setupShadowPalette(args[1], args[2], args[3], args[4], args[5], args[6]);
break;
case 109: // setPaletteShadow
setupShadowPalette(0, args[1], args[2], args[3], args[4], args[5]);
break;
case 118: // blastShadowObject
enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 3);
break;
case 119: // superBlastObject
enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 0);
break;
default:
warning("o8_kernelSetFunctions: default case 0x%x (len = %d)", args[0], len);
}
}
void Scumm_v8::o8_kernelGetFunctions() {
// TODO
int args[30];
int len = getStackList(args, sizeof(args) / sizeof(args[0]));
switch (args[0]) {
case 0x73: // getWalkBoxAt
push(getSpecialBox(args[1], args[2]));
break;
case 0x74: // isPointInBox
push(checkXYInBoxBounds(args[3], args[1], args[2]));
break;
case 0xD3: // getKeyState
switch(args[1]) {
// Stub out a few specific cases, just to make things less noisy
// To actually implement this, we may need a new OSystem call to do
// asyncronous keyboard state checking...
case 0x14B: // Left Arrow depressed?
case 0x14D: // Right Arrow depressed?
case 0x09: // Tab depressed (fire in ship combat)
push(0);
break;
default:
warning("getKeyState(0x%X)\n", args[1]);
push(0);
break;
}
break;
case 0xCE: // getRGBSlot
case 0xD7: // getBox
push(0);
warning("o8_kernelGetFunctions: default case 0x%x (len = %d)", args[0], len);
break;
case 0xD8: { // findBlastObject
int x = args[1] + (camera._cur.x & 7);
int y = args[2] + (camera._cur.y - (_screenHeight /2));;
BlastObject *eo;
for (int i = _blastObjectQueuePos - 1; i >= 0; i--) {
eo = &_blastObjectQueue[i];
if (eo->posX <= x && eo->width + eo->posX > x &&
eo->posY <= y && eo->height + eo->posY > y) {
if (!getClass(eo->number, 32)) {
push(eo->number);
return;
}
}
}
push(0);
break;
}
case 0xD9: { // actorHit - used, for example, to detect ship collision
// during ship-to-ship combat.
#if 0
Actor *a = derefActorSafe(args[1], "actorHit");
assert(a);
int x = args[2];
int y = args[3];
// TODO: this should perform a collision test, i.e. check if
// point (x,y) lies on the given actor or not.
// To achieve this, one needs to consider the current costume
// etc. What the original code seems to do is to draw the
// actor/costume (but maybe in a custom buffer?), and let the
// draw code perform the hit test.
// But I am not 100% clear on this. For example, it probably
// shouldn't touch the gfx usage bits, and some other things...
// So maybe we need dedicated code for this after all?
warning("actorHit(%d, %d, %d) NYI", args[1], x, y);
#endif
push(1); // FIXME - for now always return 1
/*
// Rough sketch, thanks to DanielFox and ludde
struct SomeStruct {
int RoomHeight, RoomWidth;
byte *ScreenBuffer;
}
dword_4FC150 = args[3]; // X
dword_4FC154 = args[2]; // Y
Actor &a = pActors[args[1]];
Point rel = GetScreenCoordsRelativeToRoom(), pt, scale;
SomeStruct tmp;
pt.x = a.x + a.field_18 - rel.x; // 18/1C are some kind of
pt.y = a.y + a.field_1C - rel.y; // X/Y offsets...
scale.x = a.scale_x;
scale.y = a.scale_y;
dword_4FC148 = 2;
graphics_getBuffer1Info(&tmp); // Some kind of get_virtscreen?
chore_drawActor(tmp, actor_nr, &pt, &scale);
if (dword_4FC148 != 1) // Guess this is changed by
dword_4FC148 = 0; // chore_drawActor
push(dword_4FC148);
*/
break;
}
case 0xDA: // lipSyncWidth
case 0xDB: // lipSyncHeight
// TODO - get lip sync data for the currently active voice
// HACK - return random values for now, to make things look half decent
push(_rnd.getRandomNumber(255));
break;
case 0xDC: // actorTalkAnimation
{
Actor *a = derefActorSafe(args[1], "actorTalkAnimation");
assert(a);
push(a->talkFrame1);
}
break;
case 0xDD: // getMasterSFXVol
push(_sound->_sound_volume_sfx / 2);
break;
case 0xDE: // getMasterVoiceVol
push(_sound->_sound_volume_sfx / 2);
break;
case 0xDF: // getMasterMusicVol
push(_sound->_sound_volume_music / 2);
break;
case 0xE0: // readRegistryValue
{
int array = args[1];
// FIXME - hack: for some reasons the wrong variable ID arrives here, compared to the
// scripts. Probably a wrong push/pop somewhere. For now override to correct value.
array = 658;
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
if (!strcmp((char *)ah->data, "Saveload Page") || !strcmp((char *)ah->data, "Object Names"))
push(1);
else
push(0);
}
break;
case 0xE1: // imGetMusicPosition
warning("o8_kernelGetFunctions: imGetMusicPosition(stub)\n");
// FIXME - get this stuff to be properly implemented
push(_sound->_bundleMusicPosition);
break;
case 0xE2: // musicLipSyncWidth
case 0xE3: // musicLipSyncHeight
// TODO - get lip sync data for the currently active music
//warning("o8_kernelGetFunctions: musicLipSync(case 0x%x, len = %d)", args[0], len);
push(_rnd.getRandomNumber(255));
break;
default:
error("o8_kernelGetFunctions: default case 0x%x (len = %d)", args[0], len);
}
}
void Scumm_v8::o8_getActorChore() {
int actnum = pop();
Actor *a = derefActorSafe(actnum, "o8_getActorChore");
assert(a);
// FIXME: This is a hack for the cannon scene, as something isn't quite right
// here yet..
if ((_roomResource == 10) && (vm.slot[_currentScript].number == 2021)) {
push(11);
return;
}
push(a->frame);
}
void Scumm_v8::o8_getActorZPlane() {
int actnum = pop();
Actor *a = derefActorSafe(actnum, "o8_getActorChore");
assert(a);
int z = a->forceClip;
if (z == 100) {
z = getMaskFromBox(a->walkbox);
if (z > gdi._numZBuffer)
z = gdi._numZBuffer;
}
push(z);
}
void Scumm_v8::o8_getObjectImageX() {
int i = getObjectIndex(pop());
push(_objs[i].x_pos);
}
void Scumm_v8::o8_getObjectImageY() {
int i = getObjectIndex(pop());
push(_objs[i].y_pos);
}
void Scumm_v8::o8_getObjectImageWidth() {
int i = getObjectIndex(pop());
push(_objs[i].width);
}
void Scumm_v8::o8_getObjectImageHeight() {
int i = getObjectIndex(pop());
push(_objs[i].height);
}
void Scumm_v8::o8_getStringWidth() {
int charset = pop();
int len = resStrLen(_scriptPointer);
int oldID = _charset->getCurID();
int width;
// Temporary set the specified charset id
_charset->setCurID(charset);
width = _charset->getStringWidth(0, _scriptPointer);
_charset->setCurID(oldID);
push(width);
_scriptPointer += len + 1;
}
void Scumm_v8::o8_drawObject() {
int state = pop(), y = pop(), x = pop(), obj = pop(), objnum = getObjectIndex(obj);
ObjectData *od;
if (!objnum)
return;
od = &_objs[objnum];
if (x != 0x7FFFFFFF) {
od->x_pos = x;
od->y_pos = y;
debug(1, "setting position: 0x%X b 0x%X\n", x, y);
}
addObjectToDrawQue(objnum);
if (state == 255 || state == 254)
warning("o8_drawObject(%d, %d, %d, %d): extended attributes unimplemented", x, y, objnum, state);
else
warning("o8_drawObject(%d, %d, %d, %d)", x, y, objnum, state);
putState(obj, state);
}