/* ScummVM - Scumm Interpreter * Copyright (C) 2001 Ludvig Strigeus * Copyright (C) 2001/2002 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 "smush/player.h" #include "smush/scumm_renderer.h" #include "sound/mididrv.h" #include "scumm/sound.h" #include "scumm/imuse.h" void Scumm::setupOpcodes2() { static const OpcodeProc opcode_list[256] = { /* 00 */ &Scumm::o6_pushByte, &Scumm::o6_pushWord, &Scumm::o6_pushByteVar, &Scumm::o6_pushWordVar, /* 04 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_byteArrayRead, &Scumm::o6_wordArrayRead, /* 08 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_byteArrayIndexedRead, &Scumm::o6_wordArrayIndexedRead, /* 0C */ &Scumm::o6_dup, &Scumm::o6_zero, &Scumm::o6_eq, &Scumm::o6_neq, /* 10 */ &Scumm::o6_gt, &Scumm::o6_lt, &Scumm::o6_le, &Scumm::o6_ge, /* 14 */ &Scumm::o6_add, &Scumm::o6_sub, &Scumm::o6_mul, &Scumm::o6_div, /* 18 */ &Scumm::o6_land, &Scumm::o6_lor, &Scumm::o6_kill, &Scumm::o6_invalid, /* 1C */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* 20 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* 24 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* 28 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* 2C */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* 30 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* 34 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* 38 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* 3C */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* 40 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_writeByteVar, &Scumm::o6_writeWordVar, /* 44 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_byteArrayWrite, &Scumm::o6_wordArrayWrite, /* 48 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_byteArrayIndexedWrite, &Scumm::o6_wordArrayIndexedWrite, /* 4C */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_byteVarInc, &Scumm::o6_wordVarInc, /* 50 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_byteArrayInc, &Scumm::o6_wordArrayInc, /* 54 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_byteVarDec, &Scumm::o6_wordVarDec, /* 58 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_byteArrayDec, &Scumm::o6_wordArrayDec, /* 5C */ &Scumm::o6_jumpTrue, &Scumm::o6_jumpFalse, &Scumm::o6_startScriptEx, &Scumm::o6_startScript, /* 60 */ &Scumm::o6_startObject, &Scumm::o6_setObjectState, &Scumm::o6_setObjectXY, &Scumm::o6_drawBlastObject, /* 64 */ &Scumm::o6_setBlastObjectWindow, &Scumm::o6_stopObjectCode, &Scumm::o6_stopObjectCode, &Scumm::o6_endCutscene, /* 68 */ &Scumm::o6_cutScene, &Scumm::o6_stopMusic, &Scumm::o6_freezeUnfreeze, &Scumm::o6_cursorCommand, /* 6C */ &Scumm::o6_breakHere, &Scumm::o6_ifClassOfIs, &Scumm::o6_setClass, &Scumm::o6_getState, /* 70 */ &Scumm::o6_setState, &Scumm::o6_setOwner, &Scumm::o6_getOwner, &Scumm::o6_jump, /* 74 */ &Scumm::o6_startSound, &Scumm::o6_stopSound, &Scumm::o6_startMusic, &Scumm::o6_stopObjectScript, /* 78 */ &Scumm::o6_panCameraTo, &Scumm::o6_actorFollowCamera, &Scumm::o6_setCameraAt, &Scumm::o6_loadRoom, /* 7C */ &Scumm::o6_stopScript, &Scumm::o6_walkActorToObj, &Scumm::o6_walkActorTo, &Scumm::o6_putActorInRoom, /* 80 */ &Scumm::o6_putActorAtObject, &Scumm::o6_faceActor, &Scumm::o6_animateActor, &Scumm::o6_doSentence, /* 84 */ &Scumm::o6_pickupObject, &Scumm::o6_loadRoomWithEgo, &Scumm::o6_invalid, &Scumm::o6_getRandomNumber, /* 88 */ &Scumm::o6_getRandomNumberRange, &Scumm::o6_invalid, &Scumm::o6_getActorMoving, &Scumm::o6_getScriptRunning, /* 8C */ &Scumm::o6_getActorRoom, &Scumm::o6_getObjectX, &Scumm::o6_getObjectY, &Scumm::o6_getObjectOldDir, /* 90 */ &Scumm::o6_getActorWalkBox, &Scumm::o6_getActorCostume, &Scumm::o6_findInventory, &Scumm::o6_getInventoryCount, /* 94 */ &Scumm::o6_getVerbFromXY, &Scumm::o6_beginOverride, &Scumm::o6_endOverride, &Scumm::o6_setObjectName, /* 98 */ &Scumm::o6_isSoundRunning, &Scumm::o6_setBoxFlags, &Scumm::o6_createBoxMatrix, &Scumm::o6_resourceRoutines, /* 9C */ &Scumm::o6_roomOps, &Scumm::o6_actorSet, &Scumm::o6_verbOps, &Scumm::o6_getActorFromXY, /* A0 */ &Scumm::o6_findObject, &Scumm::o6_pseudoRoom, &Scumm::o6_getActorElevation, &Scumm::o6_getVerbEntrypoint, /* A4 */ &Scumm::o6_arrayOps, &Scumm::o6_saveRestoreVerbs, &Scumm::o6_drawBox, &Scumm::o6_invalid, /* A8 */ &Scumm::o6_getActorWidth, &Scumm::o6_wait, &Scumm::o6_getActorScaleX, &Scumm::o6_getActorAnimCounter1, /* AC */ &Scumm::o6_soundKludge, &Scumm::o6_isAnyOf, &Scumm::o6_quitPauseRestart, &Scumm::o6_isActorInBox, /* B0 */ &Scumm::o6_delay, &Scumm::o6_delayLonger, &Scumm::o6_delayVeryLong, &Scumm::o6_stopSentence, /* B4 */ &Scumm::o6_print_0, &Scumm::o6_print_1, &Scumm::o6_print_2, &Scumm::o6_print_3, /* B8 */ &Scumm::o6_printActor, &Scumm::o6_printEgo, &Scumm::o6_talkActor, &Scumm::o6_talkEgo, /* BC */ &Scumm::o6_dim, &Scumm::o5_dummy, &Scumm::o6_runVerbCodeQuick, &Scumm::o6_runScriptQuick, /* C0 */ &Scumm::o6_dim2, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* C4 */ &Scumm::o6_abs, &Scumm::o6_distObjectObject, &Scumm::o6_distObjectPt, &Scumm::o6_distPtPt, /* C8 */ &Scumm::o6_kernelFunction, &Scumm::o6_miscOps, &Scumm::o6_delayFrames, &Scumm::o6_pickOneOf, /* CC */ &Scumm::o6_pickOneOfDefault, &Scumm::o6_unknownCD, &Scumm::o6_invalid, &Scumm::o6_invalid, /* D0 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_getAnimateVariable, &Scumm::o6_invalid, /* D4 */ &Scumm::o6_invalid, &Scumm::o6_jumpToScript, &Scumm::o6_invalid, &Scumm::o6_invalid, /* D8 */ &Scumm::o6_isRoomScriptRunning, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* DC */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* E0 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* E4 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* E8 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* EC */ &Scumm::o6_getActorPriority, &Scumm::o6_getObjectNewDir, &Scumm::o6_invalid, &Scumm::o6_invalid, /* F0 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* F4 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* F8 */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, /* FC */ &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, &Scumm::o6_invalid, }; static const char *opcode_lookup[] = { /* 00 */ "o6_pushByte", "o6_pushWord", "o6_pushByteVar", "o6_pushWordVar", /* 04 */ "o6_invalid", "o6_invalid", "o6_byteArrayRead", "o6_wordArrayRead", /* 08 */ "o6_invalid", "o6_invalid", "o6_byteArrayIndexedRead", "o6_wordArrayIndexedRead", /* 0C */ "o6_dup", "o6_zero", "o6_eq", "o6_neq", /* 10 */ "o6_gt", "o6_lt", "o6_le", "o6_ge", /* 14 */ "o6_add", "o6_sub", "o6_mul", "o6_div", /* 18 */ "o6_land", "o6_lor", "o6_kill", "o6_invalid", /* 1C */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* 20 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* 24 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* 28 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* 2C */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* 30 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* 34 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* 38 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* 3C */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* 40 */ "o6_invalid", "o6_invalid", "o6_writeByteVar", "o6_writeWordVar", /* 44 */ "o6_invalid", "o6_invalid", "o6_byteArrayWrite", "o6_wordArrayWrite", /* 48 */ "o6_invalid", "o6_invalid", "o6_byteArrayIndexedWrite", "o6_wordArrayIndexedWrite", /* 4C */ "o6_invalid", "o6_invalid", "o6_byteVarInc", "o6_wordVarInc", /* 50 */ "o6_invalid", "o6_invalid", "o6_byteArrayInc", "o6_wordArrayInc", /* 54 */ "o6_invalid", "o6_invalid", "o6_byteVarDec", "o6_wordVarDec", /* 58 */ "o6_invalid", "o6_invalid", "o6_byteArrayDec", "o6_wordArrayDec", /* 5C */ "o6_jumpTrue", "o6_jumpFalse", "o6_startScriptEx", "o6_startScript", /* 60 */ "o6_startObject", "o6_setObjectState", "o6_setObjectXY", "o6_drawBlastObject", /* 64 */ "o6_invalid", "o6_stopObjectCode", "o6_stopObjectCode", "o6_endCutscene", /* 68 */ "o6_cutScene", "o6_stopMusic", "o6_freezeUnfreeze", "o6_cursorCommand", /* 6C */ "o6_breakHere", "o6_ifClassOfIs", "o6_setClass", "o6_getState", /* 70 */ "o6_setState", "o6_setOwner", "o6_getOwner", "o6_jump", /* 74 */ "o6_startSound", "o6_stopSound", "o6_startMusic", "o6_stopObjectScript", /* 78 */ "o6_panCameraTo", "o6_actorFollowCamera", "o6_setCameraAt", "o6_loadRoom", /* 7C */ "o6_stopScript", "o6_walkActorToObj", "o6_walkActorTo", "o6_putActorInRoom", /* 80 */ "o6_putActorAtObject", "o6_faceActor", "o6_animateActor", "o6_doSentence", /* 84 */ "o6_pickupObject", "o6_loadRoomWithEgo", "o6_invalid", "o6_getRandomNumber", /* 88 */ "o6_getRandomNumberRange", "o6_invalid", "o6_getActorMoving", "o6_getScriptRunning", /* 8C */ "o6_getActorRoom", "o6_getObjectX", "o6_getObjectY", "o6_getObjectOldDir", /* 90 */ "o6_getActorWalkBox", "o6_getActorCostume", "o6_findInventory", "o6_getInventoryCount", /* 94 */ "o6_getVerbFromXY", "o6_beginOverride", "o6_endOverride", "o6_setObjectName", /* 98 */ "o6_isSoundRunning", "o6_setBoxFlags", "o6_createBoxMatrix", "o6_resourceRoutines", /* 9C */ "o6_roomOps", "o6_actorSet", "o6_verbOps", "o6_getActorFromXY", /* A0 */ "o6_findObject", "o6_pseudoRoom", "o6_getActorElevation", "o6_getVerbEntrypoint", /* A4 */ "o6_arrayOps", "o6_saveRestoreVerbs", "o6_drawBox", "o6_invalid", /* A8 */ "o6_getActorWidth", "o6_wait", "o6_getActorScaleX", "o6_getActorAnimCounter1", /* AC */ "o6_soundKludge", "o6_isAnyOf", "o6_quitPauseRestart", "o6_isActorInBox", /* B0 */ "o6_delay", "o6_delayLonger", "o6_delayVeryLong", "o6_stopSentence", /* B4 */ "o6_print_0", "o6_print_1", "o6_print_2", "o6_print_3", /* B8 */ "o6_printActor", "o6_printEgo", "o6_talkActor", "o6_talkEgo", /* BC */ "o6_dim", "o6_invalid", "o6_runVerbCodeQuick", "o6_runScriptQuick", /* C0 */ "o6_dim2", "o6_invalid", "o6_invalid", "o6_invalid", /* C4 */ "o6_abs", "o6_distObjectObject", "o6_distObjectPt", "o6_distPtPt", /* C8 */ "o6_kernelFunction", "o6_miscOps", "o6_breakMaybe", "o6_pickOneOf", /* CC */ "o6_pickOneOfDefault", "o6_invalid", "o6_invalid", "o6_invalid", /* D0 */ "o6_invalid", "o6_invalid", "o6_getAnimateVariable", "o6_invalid", /* D4 */ "o6_invalid", "o6_jumpToScript", "o6_invalid", "o6_invalid", /* D8 */ "o6_isRoomScriptRunning", "o6_invalid", "o6_invalid", "o6_invalid", /* DC */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* E0 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* E4 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* E8 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* EC */ "o6_invalid", "o6_getObjectNewDir", "o6_invalid", "o6_invalid", /* F0 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* F4 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* F8 */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid", /* FC */ "o6_invalid", "o6_invalid", "o6_invalid", "o6_invalid" }; _opcodes = opcode_list; _opcodes_lookup = opcode_lookup; } void Scumm::o6_setBlastObjectWindow() { // Set BOMP processing window int a, b, c, d; d = pop(); c = pop(); b = pop(); a = pop(); warning("o6_bompWindow(%d, %d, %d, %d)", a, b, c, d); // sub_274EF(a, b, c, d); } int Scumm::popRoomAndObj(int *room) { int obj; if (_features & GF_HAS_ROOMTABLE) { obj = pop(); *room = getObjectRoom(obj); } else { *room = pop(); obj = pop(); } return obj; } int Scumm::readArray(int array, int idx, int base) { ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array)); if (ah == NULL) { error("readArray: invalid array %d (%d)", array, readVar(array)); } base += idx * ah->dim1_size; assert(base >= 0 && base < ah->dim1_size * ah->dim2_size); if (ah->type == 4) { return ah->data[base]; } else { return (int16)READ_LE_UINT16(ah->data + base * 2); } } void Scumm::writeArray(int array, int idx, int base, int value) { ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array)); if (!ah) return; base += idx * ah->dim1_size; assert(base >= 0 && base < ah->dim1_size * ah->dim2_size); if (ah->type == 4) { ah->data[base] = value; } else { ((uint16 *)ah->data)[base] = TO_LE_16(value); } } int Scumm::getStackList(int16 *args, uint maxnum) { uint num, i; for (i = 0; i < maxnum; i++) args[i] = 0; num = pop(); if (num > maxnum) error("Too many items %d in stack list, max %d", num, maxnum); i = num; while (((int)--i) >= 0) { args[i] = pop(); } return num; } void Scumm::o6_pushByte() { push(fetchScriptByte()); } void Scumm::o6_pushWord() { push((int16)fetchScriptWord()); } void Scumm::o6_pushByteVar() { push(readVar(fetchScriptByte())); } void Scumm::o6_pushWordVar() { push(readVar(fetchScriptWord())); } void Scumm::o6_invalid() { error("Invalid opcode '%x' at %x", _opcode, _scriptPointer - _scriptOrgPointer); } void Scumm::o6_byteArrayRead() { int base = pop(); push(readArray(fetchScriptByte(), 0, base)); } void Scumm::o6_wordArrayRead() { int base = pop(); push(readArray(fetchScriptWord(), 0, base)); } void Scumm::o6_byteArrayIndexedRead() { int base = pop(); int idx = pop(); push(readArray(fetchScriptByte(), idx, base)); } void Scumm::o6_wordArrayIndexedRead() { int base = pop(); int idx = pop(); push(readArray(fetchScriptWord(), idx, base)); } void Scumm::o6_dup() { int a = pop(); push(a); push(a); } void Scumm::o6_zero() { push(pop() == 0); } void Scumm::o6_eq() { push(pop() == pop()); } void Scumm::o6_neq() { push(pop() != pop()); } void Scumm::o6_gt() { int a = pop(); push(pop() > a); } void Scumm::o6_lt() { int a = pop(); push(pop() < a); } void Scumm::o6_le() { int a = pop(); push(pop() <= a); } void Scumm::o6_ge() { int a = pop(); push(pop() >= a); } void Scumm::o6_add() { int a = pop(); push(pop() + a); } void Scumm::o6_sub() { int a = pop(); push(pop() - a); } void Scumm::o6_mul() { int a = pop(); push(pop() * a); } void Scumm::o6_div() { int a = pop(); if (a == 0) error("division by zero"); push(pop() / a); } void Scumm::o6_land() { int a = pop(); push(pop() && a); } void Scumm::o6_lor() { int a = pop(); push(pop() || a); } void Scumm::o6_kill() { pop(); } void Scumm::o6_writeByteVar() { writeVar(fetchScriptByte(), pop()); } void Scumm::o6_writeWordVar() { writeVar(fetchScriptWord(), pop()); } void Scumm::o6_byteArrayWrite() { int a = pop(); writeArray(fetchScriptByte(), 0, pop(), a); } void Scumm::o6_wordArrayWrite() { int a = pop(); writeArray(fetchScriptWord(), 0, pop(), a); } void Scumm::o6_byteArrayIndexedWrite() { int val = pop(); int base = pop(); writeArray(fetchScriptByte(), pop(), base, val); } void Scumm::o6_wordArrayIndexedWrite() { int val = pop(); int base = pop(); writeArray(fetchScriptWord(), pop(), base, val); } void Scumm::o6_byteVarInc() { int var = fetchScriptByte(); writeVar(var, readVar(var) + 1); } void Scumm::o6_wordVarInc() { int var = fetchScriptWord(); writeVar(var, readVar(var) + 1); } void Scumm::o6_byteArrayInc() { int var = fetchScriptByte(); int base = pop(); writeArray(var, 0, base, readArray(var, 0, base) + 1); } void Scumm::o6_wordArrayInc() { int var = fetchScriptWord(); int base = pop(); writeArray(var, 0, base, readArray(var, 0, base) + 1); } void Scumm::o6_byteVarDec() { int var = fetchScriptByte(); writeVar(var, readVar(var) - 1); } void Scumm::o6_wordVarDec() { int var = fetchScriptWord(); writeVar(var, readVar(var) - 1); } void Scumm::o6_byteArrayDec() { int var = fetchScriptByte(); int base = pop(); writeArray(var, 0, base, readArray(var, 0, base) - 1); } void Scumm::o6_wordArrayDec() { int var = fetchScriptWord(); int base = pop(); writeArray(var, 0, base, readArray(var, 0, base) - 1); } void Scumm::o6_jumpTrue() { if (pop()) o6_jump(); else fetchScriptWord(); } void Scumm::o6_jumpFalse() { if (!pop()) o6_jump(); else fetchScriptWord(); } void Scumm::o6_jump() { _scriptPointer += (int16)fetchScriptWord(); } void Scumm::o6_startScriptEx() { int16 args[16]; int script, flags; getStackList(args, sizeof(args) / sizeof(args[0])); script = pop(); flags = pop(); runScript(script, flags & 1, flags & 2, args); } void Scumm::o6_jumpToScript() { int16 args[16]; int script, flags; getStackList(args, sizeof(args) / sizeof(args[0])); script = pop(); flags = pop(); o6_stopObjectCode(); runScript(script, flags & 1, flags & 2, args); } void Scumm::o6_startScript() { int16 args[16]; int script; getStackList(args, sizeof(args) / sizeof(args[0])); script = pop(); runScript(script, 0, 0, args); } void Scumm::o6_startObject() { int16 args[16]; int script, entryp; int flags; getStackList(args, sizeof(args) / sizeof(args[0])); entryp = pop(); script = pop(); flags = pop(); runVerbCode(script, entryp, flags & 1, flags & 2, args); } void Scumm::o6_setObjectState() { int a = pop(); int b = pop(); if (a == 0) a = 1; // debug(1, "setObjectState(%d,%d)", a, b); setObjectState(b, a, -1, -1); } void Scumm::o6_setObjectXY() { int y = pop(); int x = pop(); int obj = pop(); // debug(1, "setObjectXY(%d,%d,%d)", obj, x, y); setObjectState(obj, 1, x, y); } void Scumm::o6_stopObjectCode() { stopObjectCode(); } void Scumm::o6_endCutscene() { endCutscene(); } void Scumm::o6_cutScene() { int16 args[16]; getStackList(args, sizeof(args) / sizeof(args[0])); cutscene(args); } void Scumm::o6_stopMusic() { _sound->stopAllSounds(); } void Scumm::o6_freezeUnfreeze() { int a = pop(); if (a) freezeScripts(a); else unfreezeScripts(); } void Scumm::o6_cursorCommand() { int a, i; int16 args[16]; switch (fetchScriptByte()) { case 0x90: _cursorState = 1; verbMouseOver(0); break; case 0x91: _cursorState = 0; verbMouseOver(0); break; case 0x92: _userPut = 1; break; case 0x93: _userPut = 0; break; case 0x94: _cursorState++; if (_cursorState > 1) error("Cursor state greater than 1 in script"); verbMouseOver(0); break; case 0x95: _cursorState--; verbMouseOver(0); break; case 0x96: _userPut++; break; case 0x97: _userPut--; break; case 0x99:{ int room, obj = popRoomAndObj(&room); setCursorImg(obj, room, 1); break; } case 0x9A: a = pop(); setCursorHotspot2(pop(), a); break; case 0x9C: /* init charset */ initCharset(pop()); break; case 0x9D: /* set charset colors */ getStackList(args, sizeof(args) / sizeof(args[0])); for (i = 0; i < 16; i++) charset._colorMap[i] = _charsetData[_string[1].t_charset][i] = (unsigned char)args[i]; break; case 0xD6: makeCursorColorTransparent(pop()); break; default: error("o6_cursorCommand: default case"); } _vars[VAR_CURSORSTATE] = _cursorState; _vars[VAR_USERPUT] = _userPut; } void Scumm::o6_breakHere() { updateScriptPtr(); _currentScript = 0xFF; } void Scumm::o6_ifClassOfIs() { int16 args[16]; int num, obj, cls; bool b; int cond = 1; num = getStackList(args, sizeof(args) / sizeof(args[0])); obj = pop(); while (--num >= 0) { cls = args[num]; b = getClass(obj, cls); if (cls & 0x80 && !b || !(cls & 0x80) && b) cond = 0; } push(cond); } void Scumm::o6_setClass() { int16 args[16]; int num, obj, cls; num = getStackList(args, sizeof(args) / sizeof(args[0])); obj = pop(); while (--num >= 0) { cls = args[num]; if (cls == 0) _classData[num] = 0; else if (cls & 0x80) putClass(obj, cls, 1); else putClass(obj, cls, 0); } } void Scumm::o6_getState() { push(getState(pop())); } void Scumm::o6_setState() { int state = pop(); int obj = pop(); // debug(1, "setState(%d,%d)", obj, state); putState(obj, state); removeObjectFromRoom(obj); if (_BgNeedsRedraw) clearDrawObjectQueue(); } void Scumm::o6_setOwner() { int owner = pop(); int obj = pop(); setOwnerOf(obj, owner); } void Scumm::o6_getOwner() { push(getOwner(pop())); } void Scumm::o6_startSound() { _sound->addSoundToQueue(pop()); } void Scumm::o6_stopSound() { _sound->stopSound(pop()); } void Scumm::o6_startMusic() { _sound->addSoundToQueue(pop()); } void Scumm::o6_stopObjectScript() { stopObjectScript(pop()); } void Scumm::o6_panCameraTo() { if (_features & GF_AFTER_V7) { int y = pop(); int x = pop(); panCameraTo(x, y); } else { panCameraTo(pop(), 0); } } void Scumm::o6_actorFollowCamera() { if (_features & GF_AFTER_V7) setCameraFollows(derefActorSafe(pop(), "actorFollowCamera")); else actorFollowCamera(pop()); } void Scumm::o6_setCameraAt() { if (_features & GF_AFTER_V7) { int x, y; camera._follows = 0; y = pop(); x = pop(); setCameraAt(x, y); } else { setCameraAtEx(pop()); } } void Scumm::o6_loadRoom() { int room; /* Begin: Autosave _saveLoadSlot = 0; sprintf(_saveLoadName, "Autosave", _saveLoadSlot); _saveLoadFlag = 1; _saveLoadCompatible = false; End: Autosave */ room = pop(); startScene(room, 0, 0); _fullRedraw = 1; } void Scumm::o6_stopScript() { int script = pop(); if (script == 0) stopObjectCode(); else stopScriptNr(script); } void Scumm::o6_walkActorToObj() { int obj, dist; Actor *a, *a2; int x; dist = pop(); obj = pop(); a = derefActorSafe(pop(), "o6_walkActorToObj"); if (!a) return; if (obj >= NUM_ACTORS) { if (whereIsObject(obj) == WIO_NOT_FOUND) return; int x, y, dir; getObjectXYPos(obj, x, y, dir); a->startWalkActor(x, y, dir); } else { a2 = derefActorSafe(obj, "o6_walkActorToObj(2)"); if (!a2) return; if (!a->isInCurrentRoom() || !a2->isInCurrentRoom()) return; if (dist == 0) { dist = a2->scalex * a2->width / 0xFF; dist += dist >> 1; } x = a2->x; if (x < a->x) x += dist; else x -= dist; a->startWalkActor(x, a2->y, -1); } } void Scumm::o6_walkActorTo() { int x, y; y = pop(); x = pop(); derefActorSafe(pop(), "o6_walkActorTo")->startWalkActor(x, y, -1); } void Scumm::o6_putActorInRoom() { int room, x, y; Actor *a; room = pop(); y = pop(); x = pop(); a = derefActorSafe(pop(), "o6_putActorInRoom"); if (!a) return; if (room == 0xFF) { room = a->room; } else { if (a->visible && _currentRoom != room && _vars[VAR_TALK_ACTOR] == a->number) { clearMsgQueue(); } if (room != 0) a->room = room; } a->putActor(x, y, room); } void Scumm::o6_putActorAtObject() { int room, obj, x, y; Actor *a; obj = popRoomAndObj(&room); a = derefActorSafe(pop(), "o6_putActorAtObject"); if (whereIsObject(obj) != WIO_NOT_FOUND) { getObjectXYPos(obj, x, y); } else { x = (_realWidth / 2); y = 120; } if (room == 0xFF) room = a->room; a->putActor(x, y, room); } void Scumm::o6_faceActor() { int act, obj; obj = pop(); act = pop(); faceActorToObj(act, obj); } void Scumm::o6_animateActor() { int anim = pop(); int act = pop(); Actor *a = derefActorSafe(act, "o6_animateActor"); if (!a) return; a->animateActor(anim); } void Scumm::o6_doSentence() { int a, b, c; a = pop(); pop(); //dummy pop b = pop(); c = pop(); doSentence(c, b, a); } void Scumm::o6_pickupObject() { int obj, room; int i; obj = popRoomAndObj(&room); if (room == 0) room = _roomResource; for (i = 1; i < _maxInventoryItems; i++) { if (_inventory[i] == (uint16)obj) { putOwner(obj, _vars[VAR_EGO]); runHook(obj); return; } } addObjectToInventory(obj, room); putOwner(obj, _vars[VAR_EGO]); putClass(obj, 32, 1); putState(obj, 1); removeObjectFromRoom(obj); clearDrawObjectQueue(); runHook(obj); /* Difference */ } void Scumm::o6_loadRoomWithEgo() { Actor *a; int obj, room, x, y; y = pop(); x = pop(); obj = popRoomAndObj(&room); a = derefActorSafe(_vars[VAR_EGO], "o6_loadRoomWithEgo"); a->putActor(0, 0, room); _egoPositioned = false; _vars[VAR_WALKTO_OBJ] = obj; startScene(a->room, a, obj); _vars[VAR_WALKTO_OBJ] = 0; /* startScene maybe modifies VAR_EGO, i hope not */ if (!(_features & GF_AFTER_V7)) { setCameraAt(a->x, a->y); setCameraFollows(a); } _fullRedraw = 1; if (x != -1) { a->startWalkActor(x, y, -1); } } void Scumm::o6_getRandomNumber() { int rnd; rnd = getRandomNumber(pop()); _vars[VAR_V6_RANDOM_NR] = rnd; push(rnd); } void Scumm::o6_getRandomNumberRange() { int max = pop(); int min = pop(); int rnd = getRandomNumberRng(min, max); _vars[VAR_V6_RANDOM_NR] = rnd; push(rnd); } void Scumm::o6_getActorMoving() { push(derefActorSafe(pop(), "o6_getActorMoving")->moving); } void Scumm::o6_getScriptRunning() { push(isScriptRunning(pop())); } void Scumm::o6_isRoomScriptRunning() { push(isRoomScriptRunning(pop())); } void Scumm::o6_getActorRoom() { push(derefActorSafe(pop(), "o6_getActorRoom")->room); } void Scumm::o6_getObjectX() { push(getObjX(pop())); } void Scumm::o6_getObjectY() { push(getObjY(pop())); } void Scumm::o6_getObjectOldDir() { push(getObjOldDir(pop())); } void Scumm::o6_getObjectNewDir() { push(getObjNewDir(pop())); } void Scumm::o6_getActorWalkBox() { Actor *a = derefActorSafe(pop(), "o6_getActorWalkBox"); push(a->ignoreBoxes ? 0 : a->walkbox); } void Scumm::o6_getActorCostume() { push(derefActorSafe(pop(), "o6_getActorCostume")->costume); } void Scumm::o6_findInventory() { int idx = pop(); int owner = pop(); push(findInventory(owner, idx)); } void Scumm::o6_getInventoryCount() { push(getInventoryCount(pop())); } void Scumm::o6_getVerbFromXY() { int y = pop(); int x = pop(); int over = checkMouseOver(x, y); if (over) over = _verbs[over].verbid; push(over); } void Scumm::o6_beginOverride() { beginOverride(); } void Scumm::o6_endOverride() { endOverride(); } void Scumm::o6_setObjectName() { int obj = pop(); int i; if (obj < NUM_ACTORS) error("Can't set actor %d name with new-name-of", obj); if (!(_features & GF_AFTER_V7) && !getOBCDFromObject(obj)) error("Can't set name of object %d", obj); for (i = 1; i < 50; i++) { if (_newNames[i] == obj) { nukeResource(rtObjectName, i); _newNames[i] = 0; break; } } for (i = 1; i < 50; i++) { if (_newNames[i] == 0) { loadPtrToResource(rtObjectName, i, NULL); _newNames[i] = obj; runHook(0); return; } } error("New name of %d overflows name table (max = %d)", obj, 50); } void Scumm::o6_isSoundRunning() { int snd = pop(); // FIXME: This fixes wak-a-rat until we correctly implement // sam and max iMUSE if (_gameId == GID_SAMNMAX && _currentRoom == 18 && snd == 23) { _sound->stopSound(snd); push(0); return; } if (snd) snd = _sound->isSoundRunning(snd); push(snd); } void Scumm::o6_setBoxFlags() { int16 table[65]; int num, value; value = pop(); num = getStackList(table, sizeof(table) / sizeof(table[0])); while (--num >= 0) { setBoxFlags(table[num], value); } } void Scumm::o6_createBoxMatrix() { createBoxMatrix(); } void Scumm::o6_resourceRoutines() { int resid; switch (fetchScriptByte()) { case 100: /* load script */ resid = pop(); if (_features & GF_AFTER_V7) if (resid >= _numGlobalScripts) break; ensureResourceLoaded(rtScript, resid); break; case 101: /* load sound */ resid = pop(); ensureResourceLoaded(rtSound, resid); break; case 102: /* load costume */ resid = pop(); ensureResourceLoaded(rtCostume, resid); break; case 103: /* load room */ resid = pop(); ensureResourceLoaded(rtRoom, resid); break; case 104: /* nuke script */ resid = pop(); if (_features & GF_AFTER_V7) if (resid >= _numGlobalScripts) break; setResourceCounter(rtScript, resid, 0x7F); debug(5, "nuke script %d", resid); break; case 105: /* nuke sound */ resid = pop(); setResourceCounter(rtSound, resid, 0x7F); break; case 106: /* nuke costume */ resid = pop(); setResourceCounter(rtCostume, resid, 0x7F); break; case 107: /* nuke room */ resid = pop(); setResourceCounter(rtRoom, resid, 0x7F); break; case 108: /* lock script */ resid = pop(); if (resid >= _numGlobalScripts) break; lock(rtScript, resid); break; case 109: /* lock sound */ resid = pop(); lock(rtSound, resid); break; case 110: /* lock costume */ resid = pop(); lock(rtCostume, resid); break; case 111: /* lock room */ resid = pop(); if (resid > 0x7F) resid = _resourceMapper[resid & 0x7F]; lock(rtRoom, resid); break; case 112: /* unlock script */ resid = pop(); if (resid >= _numGlobalScripts) break; unlock(rtScript, resid); break; case 113: /* unlock sound */ resid = pop(); unlock(rtSound, resid); break; case 114: /* unlock costume */ resid = pop(); unlock(rtCostume, resid); break; case 115: /* unlock room */ resid = pop(); if (resid > 0x7F) resid = _resourceMapper[resid & 0x7F]; unlock(rtRoom, resid); break; case 116: /* clear heap */ /* this is actually a scumm message */ error("clear heap not working yet"); break; case 117: /* load charset */ resid = pop(); loadCharset(resid); break; case 118: /* nuke charset */ resid = pop(); nukeCharset(resid); break; case 119:{ /* load fl object */ int room, obj = popRoomAndObj(&room); loadFlObject(obj, room); break; } default: error("o6_resourceRoutines: default case"); } } void Scumm::o6_roomOps() { int a, b, c, d, e; switch (fetchScriptByte()) { case 172: /* room scroll */ b = pop(); a = pop(); if (a < (_realWidth / 2)) a = (_realWidth / 2); if (b < (_realWidth / 2)) b = (_realWidth / 2); if (a > _scrWidth - (_realWidth / 2)) a = _scrWidth - (_realWidth / 2); if (b > _scrWidth - (_realWidth / 2)) b = _scrWidth - (_realWidth / 2); _vars[VAR_CAMERA_MIN_X] = a; _vars[VAR_CAMERA_MAX_X] = b; break; case 174: /* set screen */ b = pop(); a = pop(); initScreens(0, a, _realWidth, b); break; case 175: /* set palette color */ d = pop(); c = pop(); b = pop(); a = pop(); setPalColor(d, a, b, c); break; case 176: /* shake on */ setShake(1); break; case 177: /* shake off */ setShake(0); break; case 179: c = pop(); b = pop(); a = pop(); darkenPalette(b, c, a, a, a); break; case 180: _saveLoadCompatible = true; _saveLoadSlot = pop(); _saveLoadFlag = pop(); warning("o6_roomops:180: partially unimplemented"); break; case 181: a = pop(); if (a) { _switchRoomEffect = (byte)(a); _switchRoomEffect2 = (byte)(a >> 8); } else { fadeIn(_newEffect); } break; case 182: e = pop(); d = pop(); c = pop(); b = pop(); a = pop(); darkenPalette(d, e, a, b, c); break; case 183: e = pop(); d = pop(); c = pop(); b = pop(); a = pop(); unkRoomFunc3(d, e, a, b, c); break; case 184: error("save string not implemented"); break; case 185: error("load string not implemented"); break; case 186: /* palmanip? */ d = pop(); c = pop(); b = pop(); a = pop(); palManipulateInit(a, b, c, d); break; case 187: /* color cycle delay */ b = pop(); a = pop(); checkRange(16, 1, a, "o6_roomOps: 187: color cycle out of range (%d)"); _colorCycle[a - 1].delay = (b != 0) ? 0x4000 / (b * 0x4C) : 0; break; case 213: /* set palette */ // One case where this is used is to turn off Sam & Max film // noir mode. Unfortunately it only restores color to the // palette, it doesn't take palette manipulation (darkening, // etc.) into account. So, for instance, if you turn on film // noir mode in Sam & Max's office, turn off the light and turn // off film noir mode, the room will no longer look dark. // // This bug is present in the original interpreter, so it may // not be worth the trouble fixing it. setPalette(pop()); break; default: error("o6_roomOps: default case"); } } void Scumm::o6_actorSet() { Actor *a; int i, j, k; int16 args[8]; byte b; b = fetchScriptByte(); if (b == 197) { _curActor = pop(); return; } a = derefActorSafe(_curActor, "o6_actorSet"); if (!a) return; switch (b) { case 76: /* actor-costume */ a->setActorCostume(pop()); break; case 77: /* actor-speed */ j = pop(); i = pop(); a->setActorWalkSpeed(i, j); break; case 78: /* actor-sound */ k = getStackList(args, sizeof(args) / sizeof(args[0])); for (i = 0; i < k; i++) a->sound[i] = args[i]; break; case 79: /* actor-walkframe */ a->walkFrame = pop(); break; case 80: /* actor-talkframe */ a->talkFrame2 = pop(); a->talkFrame1 = pop(); break; case 81: /* actor-standframe */ a->standFrame = pop(); break; case 82: pop(); pop(); pop(); break; case 83: a->initActor(0); break; case 84: /* actor-elevation */ a->elevation = pop(); a->needRedraw = true; a->needBgReset = true; break; case 85: /* actor-defaultanims */ a->initFrame = 1; a->walkFrame = 2; a->standFrame = 3; a->talkFrame1 = 4; a->talkFrame2 = 5; break; case 86: /* actor-palette */ j = pop(); i = pop(); checkRange(31, 0, i, "Illegal palet slot %d"); a->palette[i] = j; a->needRedraw = true; break; case 87: /* actor-talkcolor */ a->talkColor = pop(); break; case 88: /* actor-name */ loadPtrToResource(rtActorName, a->number, NULL); break; case 89: /* actor-initframe */ a->initFrame = pop(); break; case 91: a->width = pop(); break; case 92: a->scalex = a->scaley = pop(); a->needRedraw = true; a->needBgReset = true; break; case 93: a->forceClip = 0; break; case 225: case 94: a->forceClip = pop(); break; case 95: a->ignoreBoxes = 1; if (_features & GF_AFTER_V7) // yazoo: I don't know if it's supposed to be 100 in other games too... a->forceClip = 100; else a->forceClip = 0; FixRooms:; if (a->isInCurrentRoom()) a->putActor(a->x, a->y, a->room); break; case 96: a->ignoreBoxes = 0; if (_features & GF_AFTER_V7) // yazoo: I don't know if it's supposed to be 100 in other games too... a->forceClip = 100; else a->forceClip = 0; goto FixRooms; case 97: a->animSpeed = pop(); a->animProgress = 0; break; case 98: a->shadow_mode = pop(); break; case 99: a->new_1 = pop(); a->new_2 = pop(); break; case 198: /* set anim variable */ i = pop(); /* value */ a->setAnimVar(pop(), i); break; case 215: a->new_3 = 1; break; case 216: a->new_3 = 0; break; case 217: a->initActor(2); break; case 227: /* actor_layer */ a->layer = pop(); break; case 228: /* walk script */ a->walk_script = pop(); break; case 235: /* talk_script */ a->talk_script = pop(); break; case 229: /* stand */ a->stopActorMoving(); a->startAnimActor(a->standFrame); break; case 230: /* set direction */ a->moving &= ~MF_TURN; a->setDirection(pop()); break; case 231: /* turn to direction */ a->turnToDirection(pop()); break; case 233: /* freeze actor */ a->moving |= 0x80; break; case 234: /* unfreeze actor */ a->moving &= ~0x7f; break; default: error("o6_actorset: default case %d", b); } } void Scumm::o6_verbOps() { int slot, a, b; VerbSlot *vs; byte *ptr, op; // Full Throttle implements conversation by creating new verbs, one // for each option, but it never tells when to actually draw them. if (_gameId == GID_FT) _verbRedraw = true; op = fetchScriptByte(); if (op == 196) { _curVerb = pop(); _curVerbSlot = getVerbSlot(_curVerb, 0); checkRange(_maxVerbs - 1, 0, _curVerbSlot, "Illegal new verb slot %d"); return; } vs = &_verbs[_curVerbSlot]; slot = _curVerbSlot; switch (op) { case 124: /* load img */ a = pop(); if (_curVerbSlot) { setVerbObject(_roomResource, a, slot); vs->type = 1; } break; case 125: loadPtrToResource(rtVerb, slot, NULL); vs->type = 0; vs->imgindex = 0; break; case 126: vs->color = pop(); break; case 127: vs->hicolor = pop(); break; case 128: vs->y = pop(); vs->x = pop(); break; case 129: vs->curmode = 1; break; case 130: vs->curmode = 0; break; case 131: killVerb(slot); break; case 132: slot = getVerbSlot(_curVerb, 0); if (slot == 0) { for (slot = 1; slot < _maxVerbs; slot++) { if (_verbs[slot].verbid == 0) break; } if (slot == _maxVerbs) error("Too many verbs"); _curVerbSlot = slot; } vs = &_verbs[slot]; vs->verbid = _curVerb; vs->color = 2; vs->hicolor = 0; vs->dimcolor = 8; vs->type = 0; vs->charset_nr = _string[0].t_charset; vs->curmode = 0; vs->saveid = 0; vs->key = 0; vs->center = 0; vs->imgindex = 0; break; case 133: vs->dimcolor = pop(); break; case 134: vs->curmode = 2; break; case 135: vs->key = pop(); break; case 136: vs->center = 1; break; case 137: a = pop(); if (a == 0) { ptr = (byte *)""; } else { ptr = getStringAddress(a); } loadPtrToResource(rtVerb, slot, ptr); vs->type = 0; vs->imgindex = 0; break; case 139: b = pop(); a = pop(); if (slot && a != vs->imgindex) { setVerbObject(b, a, slot); vs->type = 1; vs->imgindex = a; } break; case 140: vs->bkcolor = pop(); break; case 255: drawVerb(slot, 0); verbMouseOver(0); break; default: error("o6_verbops: default case"); } } void Scumm::o6_getActorFromXY() { int y = pop(); int x = pop(); push(getActorFromPos(x, y)); } void Scumm::o6_findObject() { int y = pop(); int x = pop(); int r = findObject(x, y); push(r); } void Scumm::o6_pseudoRoom() { int16 list[100]; int num, a, value; num = getStackList(list, sizeof(list) / sizeof(list[0])); value = pop(); while (--num >= 0) { a = list[num]; if (a > 0x7F) _resourceMapper[a & 0x7F] = value; } } void Scumm::o6_getActorElevation() { push(derefActorSafe(pop(), "o6_getActorElevation")->elevation); } void Scumm::o6_getVerbEntrypoint() { int e = pop(); int v = pop(); push(getVerbEntrypoint(v, e)); } void Scumm::o6_arrayOps() { int a, b, c, d, num; int16 list[128]; switch (fetchScriptByte()) { case 205: a = fetchScriptWord(); pop(); arrayop_1(a, NULL); break; case 208: a = fetchScriptWord(); b = pop(); c = pop(); d = readVar(a); if (d == 0) { defineArray(a, 5, 0, b + c); } while (c--) { writeArray(a, 0, b + c, pop()); } break; case 212: a = fetchScriptWord(); b = pop(); num = getStackList(list, sizeof(list) / sizeof(list[0])); d = readVar(a); if (d == 0) error("Must DIM a two dimensional array before assigning"); c = pop(); while (--num >= 0) { writeArray(a, c, b + num, list[num]); } break; default: error("o6_arrayOps: default case"); } } void Scumm::o6_saveRestoreVerbs() { int a, b, c; int slot, slot2; c = pop(); b = pop(); a = pop(); switch (fetchScriptByte()) { case 141: while (a <= b) { slot = getVerbSlot(a, 0); if (slot && _verbs[slot].saveid == 0) { _verbs[slot].saveid = c; drawVerb(slot, 0); verbMouseOver(0); } a++; } break; case 142: while (a <= b) { slot = getVerbSlot(a, c); if (slot) { slot2 = getVerbSlot(a, 0); if (slot2) killVerb(slot2); slot = getVerbSlot(a, c); _verbs[slot].saveid = 0; drawVerb(slot, 0); verbMouseOver(0); } a++; } break; case 143: while (a <= b) { slot = getVerbSlot(a, c); if (slot) killVerb(slot); a++; } break; default: error("o6_saveRestoreVerbs: default case"); } } void Scumm::o6_drawBox() { int x, y, x2, y2, color; color = pop(); y2 = pop(); x2 = pop(); y = pop(); x = pop(); drawBox(x, y, x2, y2, color); } void Scumm::o6_getActorWidth() { push(derefActorSafe(pop(), "o6_getActorWidth")->width); } void Scumm::o6_wait() { switch (fetchScriptByte()) { case 168:{ int offs = (int16)fetchScriptWord(); if (derefActorSafe(pop(), "o6_wait")->moving) { _scriptPointer += offs; o6_breakHere(); } return; } case 169: //printf("waiting for message: %d\n", _vars[VAR_HAVE_MSG]); if (_vars[VAR_HAVE_MSG]) break; return; case 170: if (!(_features & GF_AFTER_V7)) { if (camera._cur.x >> 3 != camera._dest.x >> 3) break; } else { if (camera._dest.x != camera._cur.x || camera._dest.y != camera._cur.y) break; } return; case 171: printf("wait for sentence"); if (_sentenceNum) { if (_sentence[_sentenceNum - 1].unk && !isScriptInUse(_vars[VAR_SENTENCE_SCRIPT])) return; break; } if (!isScriptInUse(_vars[VAR_SENTENCE_SCRIPT])) return; break; case 226:{ /* wait until actor drawn */ int actnum = pop(); Actor *a = derefActorSafe(actnum, "o6_wait:226"); int offs = (int16)fetchScriptWord(); if (a && a->isInCurrentRoom() && a->needRedraw) { _scriptPointer += offs; o6_breakHere(); } return; } case 232:{ /* wait until actor stops turning */ Actor *a = derefActorSafe(pop(), "o6_wait:226"); int offs = (int16)fetchScriptWord(); if (a && a->isInCurrentRoom() && a->moving & MF_TURN) { _scriptPointer += offs; o6_breakHere(); } return; } default: error("o6_wait: default case"); } _scriptPointer -= 2; o6_breakHere(); } void Scumm::o6_getActorScaleX() { push(derefActorSafe(pop(), "o6_getActorScale")->scalex); } void Scumm::o6_getActorAnimCounter1() { push(derefActorSafe(pop(), "o6_getActorAnimCounter")->cost.animCounter1); } void Scumm::o6_getAnimateVariable() { int var = pop(); push(derefActorSafe(pop(), "o6_getAnimateVariable")->getAnimVar(var)); } void Scumm::o6_soundKludge() { int16 list[16]; getStackList(list, sizeof(list) / sizeof(list[0])); _sound->soundKludge(list); } void Scumm::o6_isAnyOf() { int16 list[100]; int num; int16 val; num = getStackList(list, sizeof(list) / sizeof(list[0])); val = pop(); while (--num >= 0) { if (list[num] == val) { push(1); return; } } push(0); return; } void Scumm::o6_quitPauseRestart() { switch (fetchScriptByte()) { case 158: pauseGame(false); break; case 160: shutDown(0); break; default: error("o6_quitPauseRestart: invalid case"); } } void Scumm::o6_isActorInBox() { int box = pop(); Actor *a = derefActorSafe(pop(), "o6_isActorInBox"); push(checkXYInBoxBounds(box, a->x, a->y)); } void Scumm::o6_delay() { uint32 delay = (uint16)pop(); vm.slot[_currentScript].delay = delay; vm.slot[_currentScript].status = 1; o6_breakHere(); } void Scumm::o6_delayLonger() { uint32 delay = (uint16)pop() * 60; vm.slot[_currentScript].delay = delay; vm.slot[_currentScript].status = 1; o6_breakHere(); } void Scumm::o6_delayVeryLong() { uint32 delay = (uint16)pop() * 3600; vm.slot[_currentScript].delay = delay; vm.slot[_currentScript].status = 1; o6_breakHere(); } void Scumm::o6_stopSentence() { _sentenceNum = 0; stopScriptNr(_vars[VAR_SENTENCE_SCRIPT]); clearClickedStatus(); } void Scumm::o6_print_0() { _actorToPrintStrFor = 0xFF; decodeParseString2(0, 0); } void Scumm::o6_print_1() { decodeParseString2(1, 0); } void Scumm::o6_print_2() { decodeParseString2(2, 0); } void Scumm::o6_print_3() { decodeParseString2(3, 0); } void Scumm::o6_printActor() { decodeParseString2(0, 1); } void Scumm::o6_printEgo() { push(_vars[VAR_EGO]); decodeParseString2(0, 1); } void Scumm::o6_talkActor() { _actorToPrintStrFor = pop(); _messagePtr = _scriptPointer; if ((_gameId == GID_DIG) && (_messagePtr[0] == '/')) { char pointer[20]; int i, j; _scriptPointer += resStrLen((char*)_scriptPointer)+ 1; translateText((char*)_messagePtr, (char*)&transText); for (i = 0, j = 0; (_messagePtr[i] != '/' || j == 0) && j < 19; i++) { if (_messagePtr[i] != '/') pointer[j++] = _messagePtr[i]; } pointer[j] = 0; _sound->_talkChannel = _sound->playBundleSound(pointer); _messagePtr = (byte*)&transText; setStringVars(0); actorTalk(); } else { setStringVars(0); actorTalk(); _scriptPointer = _messagePtr; } } void Scumm::o6_talkEgo() { _actorToPrintStrFor = (unsigned char)_vars[VAR_EGO]; _messagePtr = _scriptPointer; if ((_gameId == GID_DIG) && (_messagePtr[0] == '/')) { char pointer[20]; int i, j; _scriptPointer += resStrLen((char*)_scriptPointer) + 1; translateText((char*)_messagePtr, (char*)&transText); for (i = 0, j = 0; (_messagePtr[i] != '/' || j == 0) && j < 19; i++) { if (_messagePtr[i] != '/') pointer[j++] = _messagePtr[i]; } pointer[j] = 0; _sound->_talkChannel = _sound->playBundleSound(pointer); _messagePtr = (byte*)&transText; setStringVars(0); actorTalk(); } else { setStringVars(0); actorTalk(); _scriptPointer = _messagePtr; } } void Scumm::o6_dim() { int data; switch (fetchScriptByte()) { case 199: data = 5; break; case 200: data = 1; break; case 201: data = 2; break; case 202: data = 3; break; case 203: data = 4; break; case 204: nukeArray(fetchScriptWord()); return; default: error("o6_dim : default case"); } defineArray(fetchScriptWord(), data, 0, pop()); } void Scumm::o6_runVerbCodeQuick() { int16 args[16]; int script, entryp; getStackList(args, sizeof(args) / sizeof(args[0])); entryp = pop(); script = pop(); runVerbCode(script, entryp, 0, 1, args); } void Scumm::o6_runScriptQuick() { int16 args[16]; int script; getStackList(args, sizeof(args) / sizeof(args[0])); script = pop(); runScript(script, 0, 1, args); } void Scumm::o6_dim2() { int a, b, data; switch (fetchScriptByte()) { case 199: data = 5; break; case 200: data = 1; break; case 201: data = 2; break; case 202: data = 3; break; case 203: data = 4; break; default: error("o6_dim2: default case"); } b = pop(); a = pop(); defineArray(fetchScriptWord(), data, a, b); } void Scumm::o6_abs() { push(abs(pop())); } void Scumm::o6_distObjectObject() { int a, b; b = pop(); a = pop(); push(getDistanceBetween(true, a, 0, true, b, 0)); } void Scumm::o6_distObjectPt() { int a, b, c; c = pop(); b = pop(); a = pop(); push(getDistanceBetween(true, a, 0, false, b, c)); } void Scumm::o6_distPtPt() { int a, b, c, d; d = pop(); c = pop(); b = pop(); a = pop(); push(getDistanceBetween(false, a, b, false, c, d)); } void Scumm::o6_dummy_stacklist() { error("opcode o6_dummy_stacklist invalid"); } void Scumm::o6_drawBlastObject() { int16 args[16]; int a, b, c, d, e; getStackList(args, sizeof(args) / sizeof(args[0])); e = pop(); d = pop(); c = pop(); b = pop(); a = pop(); enqueueObject(a, b, c, d, e, 0xFF, 0xFF, 1, 0); } void Scumm::o6_miscOps() { int16 args[30]; int i; Actor *a; IMuse *se = _imuse; //yazoo: not very nice getStackList(args, sizeof(args) / sizeof(args[0])); if (_features & GF_AFTER_V7) { switch (args[0]) { case 4: grabCursor(args[1], args[2], args[3], args[4]); break; case 6: { // if (_imuseDigital) { // _imuseDigital->stopAll(); // } uint32 speed; if (strcmp((char*)getStringAddressVar(VAR_VIDEONAME), "sq3.san") == 0) speed = 71; else speed = 1000 / _insaneFlag; debug(1, "INSANE Arg: %d", args[1]); ScummRenderer * sr = new ScummRenderer(this, speed); SmushPlayer * sp = new SmushPlayer(sr); // INSANE mode 0: SMUSH movie playback if (args[1] == 0) { sp->play((char*)getStringAddressVar(VAR_VIDEONAME), getGameDataPath()); } else if (_gameId == GID_FT) { int insaneMode = readArray(233,0,0); debug(1, "FT_INSANE Mode: %d", insaneMode); switch (insaneMode) { case 0: sp->play("minedriv.san", getGameDataPath()); break; case 1: sp->play("tovista2.san", getGameDataPath()); break; case 2: sp->play("tovista1.san", getGameDataPath()); break; case 3: sp->play("minefite.san", getGameDataPath()); break; case 4: sp->play("rottopen.san", getGameDataPath()); break; case 5: case 6: case 7: case 8: warning("FT_INSANE mode %d: Stub", args[1]); break; case 9: sp->play("credits.san", getGameDataPath()); default: // Other INSANE modes warning("Unknown FT_INSANE mode for %d", args[1]); sp->play((char*)getStringAddressVar(VAR_VIDEONAME), getGameDataPath()); } } else { // Other INSANE modes warning("Unknown insane mode for %d", args[1]); sp->play((char*)getStringAddressVar(VAR_VIDEONAME), getGameDataPath()); } delete sp; delete sr; } break; case 7: warning("o6_miscOps: stub7()"); break; case 10: warning("o6_miscOps: stub10(%d,%d,%d,%d)", args[1], args[2], args[3], args[4]); break; case 11: warning("o6_miscOps: stub11(%d)", args[1]); break; case 12: setCursorImg(args[1], (uint) - 1, args[2]); break; case 13: derefActorSafe(args[1], "o6_miscOps:14")->remapActorPalette(args[2], args[3], args[4], -1); break; case 14: derefActorSafe(args[1], "o6_miscOps:14")->remapActorPalette(args[2], args[3], args[4], args[5]); break; case 15: _insaneFlag = args[1]; break; case 16: if (_gameId == GID_DIG) { byte buf[200]; _msgPtrToAdd = buf; setStringVars(0); addMessageToStack(getStringAddressVar(VAR_STRING2DRAW)); if (strncmp("/SYSTEM.007/ /", (char*)&buf, 14) == 0) { translateText((char*)&buf + 13, (char*)&charset._buffer); //description(); } else if (strncmp("/SYSTEM.007/ ", (char*)&buf, 13) == 0) { strcpy((char*)&charset._buffer, (char*)&buf + 13); //description(); } } else { setStringVars(0); _string[0].charset = (byte)args[1]; _string[0].color = (byte)args[2]; _string[0].xpos = args[3]; _string[0].ypos = args[4]; drawDescString(getStringAddressVar(VAR_STRING2DRAW)); } break; case 17: warning("o6_miscOps: stub17(%d,%d,%d,%d)", args[1], args[2], args[3], args[4]); break; case 18: warning("o6_miscOps: stub18(%d,%d)", args[1], args[2]); break; case 107: a = derefActorSafe(args[1], "o6_miscops: 107"); a->scalex = (unsigned char)args[2]; a->needBgReset = true; a->needRedraw = true; break; case 108: setupShadowPalette(args[1], args[2], args[3], args[4], args[5], args[6]); break; case 109: setupShadowPalette(0, args[1], args[2], args[3], args[4], args[5]); break; case 114: warning("o6_miscOps: stub114()"); break; case 117: freezeScripts(2); break; case 118: enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 3); break; case 119: enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 0); break; case 120: warning("o6_miscOps: stub120(%d,%d)", args[1], args[2]); break; case 124: warning("o6_miscOps: stub124(%d)", args[1]); break; } } else { switch (args[0]) { case 3: warning("o6_miscOps: nothing in 3"); break; case 4: grabCursor(args[1], args[2], args[3], args[4]); break; case 5: fadeOut(args[1]); break; case 6: _fullRedraw = 1; redrawBGAreas(); for (i = 0; i < NUM_ACTORS; i++) derefActor(i)->needRedraw = true; processActors(); fadeIn(args[1]); break; case 8: startManiac(); break; case 9: unkMiscOp9(); break; case 104: /* samnmax */ nukeFlObjects(args[2], args[3]); break; case 106: error("stub o6_miscOps_106()"); break; case 107: /* set actor scale */ a = derefActorSafe(args[1], "o6_miscops: 107"); a->scalex = (unsigned char)args[2]; a->needBgReset = true; a->needRedraw = true; break; case 108: /* create proc_special_palette */ case 109: createSpecialPalette(args[1], args[2], args[3], args[4], args[5], 0, 256); break; case 110: gdi.clearUpperMask(); break; case 111: a = derefActorSafe(args[1], "o6_miscops: 111"); a->shadow_mode = args[2] + args[3]; break; case 112: /* palette shift? */ createSpecialPalette(args[1], args[2], args[3], args[4], args[5], args[6], args[7]); break; case 114: // Sam & Max film noir mode if (_gameId == GID_SAMNMAX) { // At this point ScummVM will already have set // variable 0x8000 to indicate that the game is // in film noir mode. All we have to do here is // to mark the palette as "dirty", because // updatePalette() will desaturate the colors // as they are uploaded to the backend. // // This actually works better than the original // interpreter, where actors would sometimes // still be drawn in color. setDirtyColors(0, 255); } else warning("stub o6_miscOps_114()"); break; case 117: warning("stub o6_miscOps_117()"); break; case 118: error("stub o6_miscOps_118(%d,%d,%d,%d,%d,%d,%d)", args[1], args[2], args[3], args[4], args[5], args[6], args[7]); break; case 119: enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 0); break; case 120: swapPalColors(args[1], args[2]); break; case 121: error("stub o6_miscOps_121(%d)", args[1]); break; case 122: _vars[VAR_SOUNDRESULT] = (short)se->do_command(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8] ); break; case 123: copyPalColor(args[2], args[1]); break; case 124: /* samnmax */ warning("o6_miscOps: _saveSound=%d", args[1]); _saveSound = args[1]; break; default: error("o6_miscOps: default case %d", args[0]); } } } void Scumm::o6_kernelFunction() { int16 args[30]; int i; Actor *a; getStackList(args, sizeof(args) / sizeof(args[0])); switch (args[0]) { case 113: // This is used for the Sam & Max paint-by-numbers mini-game // to find out what color to change. I think that what we have // the virtual mouse coordinates, because that's what used // everywhere else in the script. if (args[1] != -1 && args[2] != -1) { VirtScreen *vs = &virtscr[0]; push(vs->screenPtr[args[1] + args[2] * vs->width]); } else push(0); break; case 115: push(getSpecialBox(args[1], args[2])); break; case 116: push(checkXYInBoxBounds(args[3], args[1], args[2])); break; case 206: push(remapPaletteColor(args[1], args[2], args[3], (uint) - 1)); break; case 207: i = getObjectIndex(pop()); push(_objs[i].x_pos); break; case 208: i = getObjectIndex(pop()); push(_objs[i].y_pos); break; case 209: i = getObjectIndex(pop()); push(_objs[i].width); break; case 210: i = getObjectIndex(pop()); push(_objs[i].height); break; case 211: /* 13 = thrust 336 = thrust 328 = thrust 27 = abord 97 = left 331 = left 115 = right 333 = tight */ //FIXME: this is hack to allow turn on light bridge, //How to check is left button pressed ? if ((_gameId == GID_DIG) && (args[1] == 13)) { push(1); break; } warning("o6_kernelFunction: getInput(%d)", args[1]); push(0); break; case 212: a = derefActorSafe(args[1], "o6_kernelFunction:212"); push(a->frame); break; case 215: if ((_extraBoxFlags[args[1]] & 0x00FF) == 0x00C0) { push(_extraBoxFlags[args[1]]); } else { byte *temp = (byte *)getBoxBaseAddr(args[1]); push((byte)(*(temp + 17))); } break; default: error("o6_kernelFunction: default case %d", args[0]); } } void Scumm::o6_delayFrames() { ScriptSlot *ss = &vm.slot[_currentScript]; if (ss->delayFrameCount== 0) { ss->delayFrameCount = pop(); } else { ss->delayFrameCount--; } if (ss->delayFrameCount) { _scriptPointer--; o6_breakHere(); } } void Scumm::o6_pickOneOf() { int16 args[100]; int i, num; num = getStackList(args, sizeof(args) / sizeof(args[0])); i = pop(); if (i < 0 || i >= num) error("o6_pickOneOf: out of range"); push(args[i]); } void Scumm::o6_pickOneOfDefault() { int16 args[100]; int i, num, def; def = pop(); num = getStackList(args, sizeof(args) / sizeof(args[0])); i = pop(); if (i < 0 || i >= num) i = def; else i = args[i]; push(i); } void Scumm::o6_getActorPriority() { Actor *a; a = derefActorSafe(pop(), "getActorPriority"); push(a->layer); } void Scumm::o6_unknownCD() { // Full Throttle: FIXME // Opcode is used when placing the bunny into the // mine field. Some kind of drawBlastObject() wrapper int a, b, c, d; a = pop(); b = pop(); c = pop(); d = pop(); warning("o6_unknownCD: stub(%d, %d, %d, %d)", a, b, c, d); } void Scumm::decodeParseString2(int m, int n) { byte b; b = fetchScriptByte(); switch (b) { case 65: _string[m].ypos = pop(); _string[m].xpos = pop(); _string[m].overhead = false; break; case 66: _string[m].color = pop(); break; case 67: _string[m].right = pop(); break; case 69: _string[m].center = true; _string[m].overhead = false; break; case 71: _string[m].center = false; _string[m].overhead = false; break; case 72: _string[m].overhead = true; _string[m].no_talk_anim = false; break; case 73: error("decodeParseString2: case 73"); break; case 74: _string[m].no_talk_anim = true; break; case 75:{ _messagePtr = _scriptPointer; if ((_messagePtr[0] == '/') && (_gameId == GID_DIG)) { char pointer[20]; int i, j; _scriptPointer += resStrLen((char*)_scriptPointer)+ 1; translateText((char*)_messagePtr, (char*)&transText); for (i = 0, j = 0; (_messagePtr[i] != '/' || j == 0) && j < 19; i++) { if (_messagePtr[i] != '/') pointer[j++] = _messagePtr[i]; } pointer[j] = 0; _sound->_talkChannel = _sound->playBundleSound(pointer); _messagePtr = (byte*)&transText; switch (m) { case 0: actorTalk(); break; case 1: drawString(1); break; case 2: unkMessage1(); break; case 3: unkMessage2(); break; } return; } else { switch (m) { case 0: actorTalk(); break; case 1: drawString(1); break; case 2: unkMessage1(); break; case 3: unkMessage2(); break; } _scriptPointer = _messagePtr; return; } } case 0xFE: setStringVars(m); if (n) _actorToPrintStrFor = pop(); return; case 0xFF: _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; return; default: error("decodeParseString: default case"); } } void Scumm::setupShadowPalette(int slot, int rfact, int gfact, int bfact, int from, int to) { byte *table; int i, num; byte *curpal; if (slot < 0 || slot > 7) error("setupShadowPalette: invalid slot %d", slot); if (from < 0 || from > 255 || to < 0 || from > 255 || to < from) error("setupShadowPalette: invalid range from %d to %d", from, to); table = _shadowPalette + slot * 256; for (i = 0; i < 256; i++) table[i] = i; table += from; curpal = _currentPalette + from * 3; num = to - from + 1; do { *table++ = remapPaletteColor((curpal[0] * rfact) >> 8, curpal[1] * gfact >> 8, curpal[2] * bfact >> 8, (uint) - 1); curpal += 3; } while (--num); }