mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
logic misinterpretation. I assume these were added for readability, but
(a) names suggestet that they're class variables but they didn't
(b) their use wasn't consistent, i.e. there were places where variables were
used directly, not by means of the macros
(c) they didn't save that much space because they are used in few places.
svn-id: r15179
3168 lines
68 KiB
C++
3168 lines
68 KiB
C++
|
|
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2001 Ludvig Strigeus
|
|
* Copyright (C) 2001-2004 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 "common/config-manager.h"
|
|
|
|
#include "scumm/actor.h"
|
|
#include "scumm/charset.h"
|
|
#include "scumm/imuse.h"
|
|
#include "scumm/imuse_digi/dimuse.h"
|
|
#include "scumm/intern.h"
|
|
#include "scumm/object.h"
|
|
#include "scumm/resource.h"
|
|
#include "scumm/scumm.h"
|
|
#include "scumm/sound.h"
|
|
#include "scumm/verbs.h"
|
|
#include "scumm/smush/smush_player.h"
|
|
|
|
#include "sound/mididrv.h"
|
|
#include "sound/mixer.h"
|
|
|
|
#include "scumm/insane/insane.h"
|
|
|
|
namespace Scumm {
|
|
|
|
#define OPCODE(x) { &ScummEngine_v6::x, #x }
|
|
|
|
void ScummEngine_v6::setupOpcodes() {
|
|
static const OpcodeEntryV6 opcodes[256] = {
|
|
/* 00 */
|
|
OPCODE(o6_pushByte),
|
|
OPCODE(o6_pushWord),
|
|
OPCODE(o6_pushByteVar),
|
|
OPCODE(o6_pushWordVar),
|
|
/* 04 */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_byteArrayRead),
|
|
OPCODE(o6_wordArrayRead),
|
|
/* 08 */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_byteArrayIndexedRead),
|
|
OPCODE(o6_wordArrayIndexedRead),
|
|
/* 0C */
|
|
OPCODE(o6_dup),
|
|
OPCODE(o6_not),
|
|
OPCODE(o6_eq),
|
|
OPCODE(o6_neq),
|
|
/* 10 */
|
|
OPCODE(o6_gt),
|
|
OPCODE(o6_lt),
|
|
OPCODE(o6_le),
|
|
OPCODE(o6_ge),
|
|
/* 14 */
|
|
OPCODE(o6_add),
|
|
OPCODE(o6_sub),
|
|
OPCODE(o6_mul),
|
|
OPCODE(o6_div),
|
|
/* 18 */
|
|
OPCODE(o6_land),
|
|
OPCODE(o6_lor),
|
|
OPCODE(o6_pop),
|
|
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_writeByteVar),
|
|
OPCODE(o6_writeWordVar),
|
|
/* 44 */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_byteArrayWrite),
|
|
OPCODE(o6_wordArrayWrite),
|
|
/* 48 */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_byteArrayIndexedWrite),
|
|
OPCODE(o6_wordArrayIndexedWrite),
|
|
/* 4C */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_byteVarInc),
|
|
OPCODE(o6_wordVarInc),
|
|
/* 50 */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_byteArrayInc),
|
|
OPCODE(o6_wordArrayInc),
|
|
/* 54 */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_byteVarDec),
|
|
OPCODE(o6_wordVarDec),
|
|
/* 58 */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_byteArrayDec),
|
|
OPCODE(o6_wordArrayDec),
|
|
/* 5C */
|
|
OPCODE(o6_if),
|
|
OPCODE(o6_ifNot),
|
|
OPCODE(o6_startScript),
|
|
OPCODE(o6_startScriptQuick),
|
|
/* 60 */
|
|
OPCODE(o6_startObject),
|
|
OPCODE(o6_drawObject),
|
|
OPCODE(o6_drawObjectAt),
|
|
OPCODE(o6_drawBlastObject),
|
|
/* 64 */
|
|
OPCODE(o6_setBlastObjectWindow),
|
|
OPCODE(o6_stopObjectCode),
|
|
OPCODE(o6_stopObjectCode),
|
|
OPCODE(o6_endCutscene),
|
|
/* 68 */
|
|
OPCODE(o6_cutscene),
|
|
OPCODE(o6_stopMusic),
|
|
OPCODE(o6_freezeUnfreeze),
|
|
OPCODE(o6_cursorCommand),
|
|
/* 6C */
|
|
OPCODE(o6_breakHere),
|
|
OPCODE(o6_ifClassOfIs),
|
|
OPCODE(o6_setClass),
|
|
OPCODE(o6_getState),
|
|
/* 70 */
|
|
OPCODE(o6_setState),
|
|
OPCODE(o6_setOwner),
|
|
OPCODE(o6_getOwner),
|
|
OPCODE(o6_jump),
|
|
/* 74 */
|
|
OPCODE(o6_startSound),
|
|
OPCODE(o6_stopSound),
|
|
OPCODE(o6_startMusic),
|
|
OPCODE(o6_stopObjectScript),
|
|
/* 78 */
|
|
OPCODE(o6_panCameraTo),
|
|
OPCODE(o6_actorFollowCamera),
|
|
OPCODE(o6_setCameraAt),
|
|
OPCODE(o6_loadRoom),
|
|
/* 7C */
|
|
OPCODE(o6_stopScript),
|
|
OPCODE(o6_walkActorToObj),
|
|
OPCODE(o6_walkActorTo),
|
|
OPCODE(o6_putActorAtXY),
|
|
/* 80 */
|
|
OPCODE(o6_putActorAtObject),
|
|
OPCODE(o6_faceActor),
|
|
OPCODE(o6_animateActor),
|
|
OPCODE(o6_doSentence),
|
|
/* 84 */
|
|
OPCODE(o6_pickupObject),
|
|
OPCODE(o6_loadRoomWithEgo),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_getRandomNumber),
|
|
/* 88 */
|
|
OPCODE(o6_getRandomNumberRange),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_getActorMoving),
|
|
OPCODE(o6_isScriptRunning),
|
|
/* 8C */
|
|
OPCODE(o6_getActorRoom),
|
|
OPCODE(o6_getObjectX),
|
|
OPCODE(o6_getObjectY),
|
|
OPCODE(o6_getObjectOldDir),
|
|
/* 90 */
|
|
OPCODE(o6_getActorWalkBox),
|
|
OPCODE(o6_getActorCostume),
|
|
OPCODE(o6_findInventory),
|
|
OPCODE(o6_getInventoryCount),
|
|
/* 94 */
|
|
OPCODE(o6_getVerbFromXY),
|
|
OPCODE(o6_beginOverride),
|
|
OPCODE(o6_endOverride),
|
|
OPCODE(o6_setObjectName),
|
|
/* 98 */
|
|
OPCODE(o6_isSoundRunning),
|
|
OPCODE(o6_setBoxFlags),
|
|
OPCODE(o6_createBoxMatrix),
|
|
OPCODE(o6_resourceRoutines),
|
|
/* 9C */
|
|
OPCODE(o6_roomOps),
|
|
OPCODE(o6_actorOps),
|
|
OPCODE(o6_verbOps),
|
|
OPCODE(o6_getActorFromXY),
|
|
/* A0 */
|
|
OPCODE(o6_findObject),
|
|
OPCODE(o6_pseudoRoom),
|
|
OPCODE(o6_getActorElevation),
|
|
OPCODE(o6_getVerbEntrypoint),
|
|
/* A4 */
|
|
OPCODE(o6_arrayOps),
|
|
OPCODE(o6_saveRestoreVerbs),
|
|
OPCODE(o6_drawBox),
|
|
OPCODE(o6_pop),
|
|
/* A8 */
|
|
OPCODE(o6_getActorWidth),
|
|
OPCODE(o6_wait),
|
|
OPCODE(o6_getActorScaleX),
|
|
OPCODE(o6_getActorAnimCounter1),
|
|
/* AC */
|
|
OPCODE(o6_soundKludge),
|
|
OPCODE(o6_isAnyOf),
|
|
OPCODE(o6_quitPauseRestart),
|
|
OPCODE(o6_isActorInBox),
|
|
/* B0 */
|
|
OPCODE(o6_delay),
|
|
OPCODE(o6_delaySeconds),
|
|
OPCODE(o6_delayMinutes),
|
|
OPCODE(o6_stopSentence),
|
|
/* B4 */
|
|
OPCODE(o6_printLine),
|
|
OPCODE(o6_printCursor),
|
|
OPCODE(o6_printDebug),
|
|
OPCODE(o6_printSystem),
|
|
/* B8 */
|
|
OPCODE(o6_printActor),
|
|
OPCODE(o6_printEgo),
|
|
OPCODE(o6_talkActor),
|
|
OPCODE(o6_talkEgo),
|
|
/* BC */
|
|
OPCODE(o6_dimArray),
|
|
OPCODE(o6_dummy),
|
|
OPCODE(o6_startObjectQuick),
|
|
OPCODE(o6_startScriptQuick2),
|
|
/* C0 */
|
|
OPCODE(o6_dim2dimArray),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
/* C4 */
|
|
OPCODE(o6_abs),
|
|
OPCODE(o6_distObjectObject),
|
|
OPCODE(o6_distObjectPt),
|
|
OPCODE(o6_distPtPt),
|
|
/* C8 */
|
|
OPCODE(o6_kernelGetFunctions),
|
|
OPCODE(o6_kernelSetFunctions),
|
|
OPCODE(o6_delayFrames),
|
|
OPCODE(o6_pickOneOf),
|
|
/* CC */
|
|
OPCODE(o6_pickOneOfDefault),
|
|
OPCODE(o6_stampObject),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
/* D0 */
|
|
OPCODE(o6_getDateTime),
|
|
OPCODE(o6_stopTalking),
|
|
OPCODE(o6_getAnimateVariable),
|
|
OPCODE(o6_invalid),
|
|
/* D4 */
|
|
OPCODE(o6_shuffle),
|
|
OPCODE(o6_jumpToScript),
|
|
OPCODE(o6_band),
|
|
OPCODE(o6_bor),
|
|
/* D8 */
|
|
OPCODE(o6_isRoomScriptRunning),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
/* DC */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_findAllObjects),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
/* E0 */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_getPixel),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_pickVarRandom),
|
|
/* E4 */
|
|
OPCODE(o6_setBoxSet),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
/* E8 */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
/* EC */
|
|
OPCODE(o6_getActorLayer),
|
|
OPCODE(o6_getObjectNewDir),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
/* F0 */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
/* F4 */
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
OPCODE(o6_invalid),
|
|
/* 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),
|
|
};
|
|
|
|
_opcodesV6 = opcodes;
|
|
}
|
|
|
|
void ScummEngine_v6::executeOpcode(byte i) {
|
|
OpcodeProcV6 op = _opcodesV6[i].proc;
|
|
(this->*op) ();
|
|
}
|
|
|
|
const char *ScummEngine_v6::getOpcodeDesc(byte i) {
|
|
return _opcodesV6[i].desc;
|
|
}
|
|
|
|
int ScummEngine_v6::popRoomAndObj(int *room) {
|
|
int obj;
|
|
|
|
if (_version >= 7 || _heversion >= 70) {
|
|
obj = pop();
|
|
*room = getObjectRoom(obj);
|
|
} else {
|
|
*room = pop();
|
|
obj = pop();
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
ArrayHeader *ScummEngine_v6::defineArray(int array, int type, int dim2, int dim1) {
|
|
if (_heversion >= 72)
|
|
error("Call to old defineArray");
|
|
|
|
int id;
|
|
int size;
|
|
ArrayHeader *ah;
|
|
|
|
assert(0 <= type && type <= 5);
|
|
|
|
|
|
if (_heversion >= 60) {
|
|
if (type == kBitArray || type == kNibbleArray)
|
|
type = kByteArray;
|
|
} else {
|
|
// NOTE: The following code turns all arrays except string arrays into
|
|
// integer arrays. There seems to be no reason for this, and it wastes
|
|
// space. However, we can't just remove this either, as that would
|
|
// break savegame compatibility. So do not touch this unless you are
|
|
// also adding code which updated old savegames, too. And of course
|
|
// readArray() and writeArray() would have to be updated, too...
|
|
if (type != kStringArray)
|
|
type = kIntArray;
|
|
}
|
|
|
|
nukeArray(array);
|
|
|
|
id = findFreeArrayId();
|
|
|
|
if (_version == 8) {
|
|
if (array & 0x40000000) {
|
|
}
|
|
|
|
if (array & 0x80000000) {
|
|
error("Can't define bit variable as array pointer");
|
|
}
|
|
|
|
size = (type == kIntArray) ? 4 : 1;
|
|
} else {
|
|
if (array & 0x4000) {
|
|
}
|
|
|
|
if (array & 0x8000) {
|
|
error("Can't define bit variable as array pointer");
|
|
}
|
|
|
|
size = (type == kIntArray) ? 2 : 1;
|
|
}
|
|
|
|
writeVar(array, id);
|
|
|
|
size *= dim2 + 1;
|
|
size *= dim1 + 1;
|
|
|
|
ah = (ArrayHeader *)createResource(rtString, id, size + sizeof(ArrayHeader));
|
|
|
|
ah->type = TO_LE_16(type);
|
|
ah->dim1 = TO_LE_16(dim1 + 1);
|
|
ah->dim2 = TO_LE_16(dim2 + 1);
|
|
|
|
return ah;
|
|
}
|
|
|
|
void ScummEngine_v6::nukeArray(int a) {
|
|
int data;
|
|
|
|
data = readVar(a);
|
|
|
|
if (data)
|
|
nukeResource(rtString, data);
|
|
if (_features & GF_HUMONGOUS)
|
|
_arraySlot[data] = 0;
|
|
|
|
writeVar(a, 0);
|
|
}
|
|
|
|
int ScummEngine_v6::findFreeArrayId() {
|
|
byte **addr = res.address[rtString];
|
|
int i;
|
|
|
|
for (i = 1; i < _numArray; i++) {
|
|
if (!addr[i])
|
|
return i;
|
|
}
|
|
error("Out of array pointers, %d max", _numArray);
|
|
return -1;
|
|
}
|
|
|
|
#define SWAP16(x) x = SWAP_BYTES_16(x)
|
|
|
|
ArrayHeader *ScummEngine_v6::getArray(int array) {
|
|
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
|
|
if (!ah)
|
|
return 0;
|
|
|
|
// Workaround for a long standing bug where we save array headers in native
|
|
// endianness, instead of a fixed endianness. We try to detect savegames
|
|
// which were created on a big endian system and convert them to little
|
|
// endian.
|
|
if ((FROM_LE_16(ah->dim1) & 0xF000) || (FROM_LE_16(ah->dim2) & 0xF000) || (FROM_LE_16(ah->type) & 0xFF00)) {
|
|
SWAP16(ah->dim1);
|
|
SWAP16(ah->dim2);
|
|
SWAP16(ah->type);
|
|
}
|
|
|
|
return ah;
|
|
}
|
|
|
|
int ScummEngine_v6::readArray(int array, int idx, int base) {
|
|
if (_heversion >= 72)
|
|
error("Call to old readArray");
|
|
|
|
ArrayHeader *ah = getArray(array);
|
|
|
|
if (ah == NULL || ah->data == NULL)
|
|
error("readArray: invalid array %d (%d)", array, readVar(array));
|
|
|
|
// WORKAROUND bug #645711. This is clearly a script bug, as this script
|
|
// excerpt shows nicely:
|
|
// ...
|
|
// [03A7] (5D) if (isAnyOf(array-447[localvar13][localvar14],[0,4])) {
|
|
// [03BD] (5D) if ((localvar13 != -1) && (localvar14 != -1)) {
|
|
// [03CF] (B6) printDebug.begin()
|
|
// ...
|
|
if (_gameId == GID_FT && array == 447 && _currentRoom == 95 && vm.slot[_currentScript].number == 2010 && idx == -1 && base == -1) {
|
|
return 0;
|
|
}
|
|
|
|
const int offset = base + idx * FROM_LE_16(ah->dim1);
|
|
|
|
if (offset < 0 || offset >= FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2)) {
|
|
error("readArray: array %d out of bounds: [%d,%d] exceeds [%d,%d]",
|
|
array, base, idx, FROM_LE_16(ah->dim1), FROM_LE_16(ah->dim2));
|
|
}
|
|
|
|
if (FROM_LE_16(ah->type) != kIntArray) {
|
|
return ah->data[offset];
|
|
} else if (_version == 8) {
|
|
return (int32)READ_LE_UINT32(ah->data + offset * 4);
|
|
} else {
|
|
return (int16)READ_LE_UINT16(ah->data + offset * 2);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::writeArray(int array, int idx, int base, int value) {
|
|
if (_heversion >= 72)
|
|
error("Call to old writeArray");
|
|
|
|
ArrayHeader *ah = getArray(array);
|
|
if (!ah)
|
|
return;
|
|
|
|
const int offset = base + idx * FROM_LE_16(ah->dim1);
|
|
|
|
if (offset < 0 || offset >= FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2)) {
|
|
error("writeArray: array %d out of bounds: [%d,%d] exceeds [%d,%d]",
|
|
array, base, idx, FROM_LE_16(ah->dim1), FROM_LE_16(ah->dim2));
|
|
}
|
|
|
|
if (FROM_LE_16(ah->type) != kIntArray) {
|
|
ah->data[offset] = value;
|
|
} else if (_version == 8) {
|
|
WRITE_LE_UINT32(ah->data + offset * 4, value);
|
|
} else {
|
|
WRITE_LE_UINT16(ah->data + offset * 2, value);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::readArrayFromIndexFile() {
|
|
int num;
|
|
int a, b, c;
|
|
|
|
while ((num = _fileHandle.readUint16LE()) != 0) {
|
|
a = _fileHandle.readUint16LE();
|
|
b = _fileHandle.readUint16LE();
|
|
c = _fileHandle.readUint16LE();
|
|
if (c == kBitArray)
|
|
defineArray(num, kBitArray, a, b);
|
|
else
|
|
defineArray(num, kIntArray, a, b);
|
|
}
|
|
}
|
|
|
|
int ScummEngine_v6::getStackList(int *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 (i--) {
|
|
args[i] = pop();
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
void ScummEngine_v6::o6_pushByte() {
|
|
push(fetchScriptByte());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_pushWord() {
|
|
push(fetchScriptWordSigned());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_pushByteVar() {
|
|
push(readVar(fetchScriptByte()));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_pushWordVar() {
|
|
push(readVar(fetchScriptWord()));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_invalid() {
|
|
error("Invalid opcode '%x' at %x", _opcode, _scriptPointer - _scriptOrgPointer);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_byteArrayRead() {
|
|
int base = pop();
|
|
push(readArray(fetchScriptByte(), 0, base));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_wordArrayRead() {
|
|
int base = pop();
|
|
push(readArray(fetchScriptWord(), 0, base));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_byteArrayIndexedRead() {
|
|
int base = pop();
|
|
int idx = pop();
|
|
push(readArray(fetchScriptByte(), idx, base));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_wordArrayIndexedRead() {
|
|
int base = pop();
|
|
int idx = pop();
|
|
push(readArray(fetchScriptWord(), idx, base));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_dup() {
|
|
int a = pop();
|
|
push(a);
|
|
push(a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_not() {
|
|
push(pop() == 0);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_eq() {
|
|
push(pop() == pop());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_neq() {
|
|
push(pop() != pop());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_gt() {
|
|
int a = pop();
|
|
push(pop() > a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_lt() {
|
|
int a = pop();
|
|
push(pop() < a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_le() {
|
|
int a = pop();
|
|
push(pop() <= a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_ge() {
|
|
int a = pop();
|
|
push(pop() >= a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_add() {
|
|
int a = pop();
|
|
push(pop() + a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_sub() {
|
|
int a = pop();
|
|
push(pop() - a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_mul() {
|
|
int a = pop();
|
|
push(pop() * a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_div() {
|
|
int a = pop();
|
|
if (a == 0)
|
|
error("division by zero");
|
|
push(pop() / a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_land() {
|
|
int a = pop();
|
|
push(pop() && a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_lor() {
|
|
int a = pop();
|
|
push(pop() || a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_bor() {
|
|
int a = pop();
|
|
push(pop() | a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_band() {
|
|
int a = pop();
|
|
push(pop() & a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_pop() {
|
|
pop();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_writeByteVar() {
|
|
writeVar(fetchScriptByte(), pop());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_writeWordVar() {
|
|
writeVar(fetchScriptWord(), pop());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_byteArrayWrite() {
|
|
int a = pop();
|
|
writeArray(fetchScriptByte(), 0, pop(), a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_wordArrayWrite() {
|
|
int a = pop();
|
|
writeArray(fetchScriptWord(), 0, pop(), a);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_byteArrayIndexedWrite() {
|
|
int val = pop();
|
|
int base = pop();
|
|
writeArray(fetchScriptByte(), pop(), base, val);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_wordArrayIndexedWrite() {
|
|
int val = pop();
|
|
int base = pop();
|
|
writeArray(fetchScriptWord(), pop(), base, val);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_byteVarInc() {
|
|
int var = fetchScriptByte();
|
|
writeVar(var, readVar(var) + 1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_wordVarInc() {
|
|
int var = fetchScriptWord();
|
|
writeVar(var, readVar(var) + 1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_byteArrayInc() {
|
|
int var = fetchScriptByte();
|
|
int base = pop();
|
|
writeArray(var, 0, base, readArray(var, 0, base) + 1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_wordArrayInc() {
|
|
int var = fetchScriptWord();
|
|
int base = pop();
|
|
writeArray(var, 0, base, readArray(var, 0, base) + 1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_byteVarDec() {
|
|
int var = fetchScriptByte();
|
|
writeVar(var, readVar(var) - 1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_wordVarDec() {
|
|
int var = fetchScriptWord();
|
|
writeVar(var, readVar(var) - 1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_byteArrayDec() {
|
|
int var = fetchScriptByte();
|
|
int base = pop();
|
|
writeArray(var, 0, base, readArray(var, 0, base) - 1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_wordArrayDec() {
|
|
int var = fetchScriptWord();
|
|
int base = pop();
|
|
writeArray(var, 0, base, readArray(var, 0, base) - 1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_if() {
|
|
if (pop())
|
|
o6_jump();
|
|
else
|
|
fetchScriptWord();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_ifNot() {
|
|
if (!pop())
|
|
o6_jump();
|
|
else
|
|
fetchScriptWord();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_jump() {
|
|
int offset = fetchScriptWordSigned();
|
|
_scriptPointer += offset;
|
|
}
|
|
|
|
void ScummEngine_v6::o6_startScript() {
|
|
int args[16];
|
|
int script, flags;
|
|
|
|
getStackList(args, ARRAYSIZE(args));
|
|
script = pop();
|
|
flags = pop();
|
|
|
|
// WORKAROUND bug #556558: At Dino Bungee National Memorial, the buttons for
|
|
// the Wally and Rex dinosaurs will always restart their speech, instead of
|
|
// stopping and starting their speech. This was a script bug in the original
|
|
// game.
|
|
if (_gameId == GID_SAMNMAX && _roomResource == 59 &&
|
|
vm.slot[_currentScript].number == 201 && script == 48) {
|
|
o6_breakHere();
|
|
}
|
|
|
|
// WORKAROUND bug #903223: In Puerto Pollo, if you have Guybrush examine
|
|
// the church clock, he'll read out the current time. Nice touch, only that
|
|
// it sounds crap in the german version (and maybe others, too). It seems
|
|
// the original engine of the german version played just a simple fixed
|
|
// text in this spot, for the above reason. Since the data files are
|
|
// unchanged, it must have been an engine hack job. No idea how they did
|
|
// it exactly, but this here is how we do it :-)
|
|
if (_gameId == GID_CMI && script == 204 &&
|
|
_currentRoom == 15 && vm.slot[_currentScript].number == 421 &&
|
|
_language == Common::DE_DEU) {
|
|
|
|
_actorToPrintStrFor = 1;
|
|
_string[0].loadDefault();
|
|
actorTalk((const byte *)"/VDSO325/Whoa! Look at the time. Gotta scoot.");
|
|
|
|
return;
|
|
}
|
|
|
|
runScript(script, (flags & 1) != 0, (flags & 2) != 0, args);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_jumpToScript() {
|
|
int args[16];
|
|
int script, flags;
|
|
|
|
getStackList(args, ARRAYSIZE(args));
|
|
script = pop();
|
|
flags = pop();
|
|
stopObjectCode();
|
|
runScript(script, (flags & 1) != 0, (flags & 2) != 0, args);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_startScriptQuick() {
|
|
int args[16];
|
|
int script;
|
|
getStackList(args, ARRAYSIZE(args));
|
|
script = pop();
|
|
runScript(script, 0, 0, args);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_startScriptQuick2() {
|
|
int args[16];
|
|
int script;
|
|
getStackList(args, ARRAYSIZE(args));
|
|
script = pop();
|
|
runScript(script, 0, 1, args);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_startObject() {
|
|
int args[16];
|
|
int script, entryp;
|
|
int flags;
|
|
getStackList(args, ARRAYSIZE(args));
|
|
entryp = pop();
|
|
script = pop();
|
|
flags = pop();
|
|
runObjectScript(script, entryp, (flags & 1) != 0, (flags & 2) != 0, args);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_startObjectQuick() {
|
|
int args[16];
|
|
int script, entryp;
|
|
getStackList(args, ARRAYSIZE(args));
|
|
entryp = pop();
|
|
script = pop();
|
|
runObjectScript(script, entryp, 0, 1, args);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_drawObject() {
|
|
int state = pop();
|
|
int obj = pop();
|
|
|
|
// This is based on disassembly
|
|
if (state == 0)
|
|
state = 1;
|
|
|
|
setObjectState(obj, state, -1, -1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_drawObjectAt() {
|
|
int y = pop();
|
|
int x = pop();
|
|
int obj = pop();
|
|
setObjectState(obj, 1, x, y);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_stopObjectCode() {
|
|
stopObjectCode();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_endCutscene() {
|
|
endCutscene();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_cutscene() {
|
|
int args[16];
|
|
getStackList(args, ARRAYSIZE(args));
|
|
beginCutscene(args);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_stopMusic() {
|
|
_sound->stopAllSounds();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_freezeUnfreeze() {
|
|
int a = pop();
|
|
|
|
if (a)
|
|
freezeScripts(a);
|
|
else
|
|
unfreezeScripts();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_cursorCommand() {
|
|
int a, i;
|
|
int args[16];
|
|
int subOp = fetchScriptByte();
|
|
|
|
switch (subOp) {
|
|
case 0x90: // SO_CURSOR_ON Turn cursor on
|
|
_cursor.state = 1;
|
|
verbMouseOver(0);
|
|
break;
|
|
case 0x91: // SO_CURSOR_OFF Turn cursor off
|
|
_cursor.state = 0;
|
|
verbMouseOver(0);
|
|
break;
|
|
case 0x92: // SO_USERPUT_ON
|
|
_userPut = 1;
|
|
break;
|
|
case 0x93: // SO_USERPUT_OFF
|
|
_userPut = 0;
|
|
break;
|
|
case 0x94: // 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 0x95: // SO_CURSOR_SOFT_OFF Turn soft cursor off
|
|
_cursor.state--;
|
|
verbMouseOver(0);
|
|
break;
|
|
case 0x96: // SO_USERPUT_SOFT_ON
|
|
_userPut++;
|
|
break;
|
|
case 0x97: // SO_USERPUT_SOFT_OFF
|
|
_userPut--;
|
|
break;
|
|
case 0x99: // SO_CURSOR_IMAGE Set cursor image
|
|
{
|
|
int room, obj = popRoomAndObj(&room);
|
|
setCursorFromImg(obj, room, 1);
|
|
break;
|
|
}
|
|
case 0x9A: // SO_CURSOR_HOTSPOT Set cursor hotspot
|
|
a = pop();
|
|
setCursorHotspot(pop(), a);
|
|
break;
|
|
case 0x9C: // SO_CHARSET_SET
|
|
initCharset(pop());
|
|
break;
|
|
case 0x9D: // SO_CHARSET_COLOR
|
|
getStackList(args, ARRAYSIZE(args));
|
|
for (i = 0; i < 16; i++)
|
|
_charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
|
|
break;
|
|
case 0xD6: // SO_CURSOR_TRANSPARENT Set cursor transparent color
|
|
setCursorTransparency(pop());
|
|
break;
|
|
default:
|
|
error("o6_cursorCommand: default case %x", subOp);
|
|
}
|
|
|
|
VAR(VAR_CURSORSTATE) = _cursor.state;
|
|
VAR(VAR_USERPUT) = _userPut;
|
|
}
|
|
|
|
void ScummEngine_v6::o6_breakHere() {
|
|
updateScriptPtr();
|
|
_currentScript = 0xFF;
|
|
}
|
|
|
|
void ScummEngine_v6::o6_ifClassOfIs() {
|
|
int args[16];
|
|
int num, obj, cls;
|
|
bool b;
|
|
int cond = 1;
|
|
|
|
num = getStackList(args, ARRAYSIZE(args));
|
|
obj = pop();
|
|
|
|
while (--num >= 0) {
|
|
cls = args[num];
|
|
b = getClass(obj, cls);
|
|
if ((cls & 0x80 && !b) || (!(cls & 0x80) && b))
|
|
cond = 0;
|
|
}
|
|
push(cond);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_setClass() {
|
|
int args[16];
|
|
int num, obj, cls;
|
|
|
|
num = getStackList(args, ARRAYSIZE(args));
|
|
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 ScummEngine_v6::o6_getState() {
|
|
push(getState(pop()));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_setState() {
|
|
int state = pop();
|
|
int obj = pop();
|
|
|
|
putState(obj, state);
|
|
markObjectRectAsDirty(obj);
|
|
if (_bgNeedsRedraw)
|
|
clearDrawObjectQueue();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_setOwner() {
|
|
int owner = pop();
|
|
int obj = pop();
|
|
setOwnerOf(obj, owner);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getOwner() {
|
|
push(getOwner(pop()));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_startSound() {
|
|
int offset = 0;
|
|
|
|
// In Fatty Bear's Birthday Surprise the piano uses offsets 1 - 23 to
|
|
// indicate which note to play, but only when using the standard piano
|
|
// sound. See also o6_soundOps()
|
|
if ((_features & GF_HUMONGOUS) && (_gameId != GID_PUTTDEMO))
|
|
offset = pop();
|
|
|
|
if (_features & GF_DIGI_IMUSE)
|
|
_imuseDigital->startSfx(pop(), 64);
|
|
else
|
|
_sound->addSoundToQueue(pop(), offset);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_stopSound() {
|
|
_sound->stopSound(pop());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_startMusic() {
|
|
if (_features & GF_DIGI_IMUSE)
|
|
error("o6_startMusic() It shouldn't be called here for imuse digital");
|
|
|
|
_sound->addSoundToQueue(pop());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_stopObjectScript() {
|
|
stopObjectScript(pop());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_panCameraTo() {
|
|
if (_version >= 7) {
|
|
int y = pop();
|
|
int x = pop();
|
|
panCameraTo(x, y);
|
|
} else {
|
|
panCameraTo(pop(), 0);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_actorFollowCamera() {
|
|
if (_version >= 7)
|
|
setCameraFollows(derefActor(pop(), "actorFollowCamera"));
|
|
else
|
|
actorFollowCamera(pop());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_setCameraAt() {
|
|
if (_version >= 7) {
|
|
int x, y;
|
|
|
|
camera._follows = 0;
|
|
VAR(VAR_CAMERA_FOLLOWED_ACTOR) = 0;
|
|
|
|
y = pop();
|
|
x = pop();
|
|
|
|
setCameraAt(x, y);
|
|
} else {
|
|
setCameraAtEx(pop());
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_loadRoom() {
|
|
int room = pop();
|
|
startScene(room, 0, 0);
|
|
_fullRedraw = 1;
|
|
}
|
|
|
|
void ScummEngine_v6::o6_stopScript() {
|
|
int script = pop();
|
|
if (script == 0)
|
|
stopObjectCode();
|
|
else
|
|
stopScript(script);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_walkActorToObj() {
|
|
int act, obj, dist;
|
|
Actor *a, *a2;
|
|
int x, y;
|
|
|
|
dist = pop();
|
|
obj = pop();
|
|
act = pop();
|
|
a = derefActor(act, "o6_walkActorToObj");
|
|
|
|
if (obj >= _numActors) {
|
|
int wio = whereIsObject(obj);
|
|
|
|
if (wio != WIO_FLOBJECT && wio != WIO_ROOM)
|
|
return;
|
|
|
|
int dir;
|
|
getObjectXYPos(obj, x, y, dir);
|
|
a->startWalkActor(x, y, dir);
|
|
} else {
|
|
a2 = derefActorSafe(obj, "o6_walkActorToObj(2)");
|
|
if (_gameId == GID_SAMNMAX && a2 == 0) {
|
|
// FIXME: This is a hack to work around bug #742676 SAM: Fish Farm.
|
|
// Note quite sure why it happens, though, if it's normal or due to
|
|
// a bug in the ScummVM code.
|
|
warning("o6_walkActorToObj: invalid actor %d", obj);
|
|
return;
|
|
}
|
|
if (!a->isInCurrentRoom() || !a2->isInCurrentRoom())
|
|
return;
|
|
if (dist == 0) {
|
|
dist = a2->scalex * a2->width / 0xFF;
|
|
dist += dist / 2;
|
|
}
|
|
x = a2->_pos.x;
|
|
y = a2->_pos.y;
|
|
if (x < a->_pos.x)
|
|
x += dist;
|
|
else
|
|
x -= dist;
|
|
a->startWalkActor(x, y, -1);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_walkActorTo() {
|
|
int x, y;
|
|
y = pop();
|
|
x = pop();
|
|
Actor *a = derefActor(pop(), "o6_walkActorTo");
|
|
a->startWalkActor(x, y, -1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_putActorAtXY() {
|
|
int room, x, y, act;
|
|
Actor *a;
|
|
|
|
room = pop();
|
|
y = pop();
|
|
x = pop();
|
|
act = pop();
|
|
a = derefActor(act, "o6_putActorAtXY");
|
|
|
|
if (room == 0xFF || room == 0x7FFFFFFF) {
|
|
room = a->room;
|
|
} else {
|
|
if (a->visible && _currentRoom != room && getTalkingActor() == a->number) {
|
|
stopTalk();
|
|
}
|
|
if (room != 0)
|
|
a->room = room;
|
|
}
|
|
a->putActor(x, y, room);
|
|
}
|
|
|
|
|
|
void ScummEngine_v6::o6_putActorAtObject() {
|
|
int room, obj, x, y;
|
|
Actor *a;
|
|
|
|
if (_features & GF_HUMONGOUS) {
|
|
room = pop();
|
|
obj = pop();
|
|
} else
|
|
obj = popRoomAndObj(&room);
|
|
|
|
a = derefActor(pop(), "o6_putActorAtObject");
|
|
if (whereIsObject(obj) != WIO_NOT_FOUND) {
|
|
getObjectXYPos(obj, x, y);
|
|
} else {
|
|
x = 160;
|
|
y = 120;
|
|
}
|
|
if (room == 0xFF)
|
|
room = a->room;
|
|
a->putActor(x, y, room);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_faceActor() {
|
|
int obj = pop();
|
|
Actor *a = derefActor(pop(), "o6_faceActor");
|
|
a->faceToObject(obj);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_animateActor() {
|
|
int anim = pop();
|
|
int act = pop();
|
|
if (_gameId == GID_TENTACLE && _roomResource == 57 &&
|
|
vm.slot[_currentScript].number == 19 && act == 593) {
|
|
// FIXME: This very odd case (animateActor(593,250)) occurs in DOTT, in the
|
|
// cutscene after George cuts down the "cherry tree" and the tree Laverne
|
|
// is trapped in vanishes... see bug #743363.
|
|
// Not sure if this means animateActor somehow also must work for objects
|
|
// (593 is the time machine in room 57), or if this is simply a script bug.
|
|
act = 6;
|
|
}
|
|
Actor *a = derefActor(act, "o6_animateActor");
|
|
a->animateActor(anim);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_doSentence() {
|
|
int verb, objectA, objectB, dummy = 0;
|
|
|
|
objectB = pop();
|
|
if (_version < 8)
|
|
dummy = pop(); // dummy pop (in Sam&Max, seems to be always 0 or 130)
|
|
objectA = pop();
|
|
verb = pop();
|
|
|
|
doSentence(verb, objectA, objectB);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_pickupObject() {
|
|
int obj, room;
|
|
int i;
|
|
|
|
obj = popRoomAndObj(&room);
|
|
if (room == 0)
|
|
room = _roomResource;
|
|
|
|
for (i = 0; i < _numInventory; i++) {
|
|
if (_inventory[i] == (uint16)obj) {
|
|
putOwner(obj, VAR(VAR_EGO));
|
|
runInventoryScript(obj);
|
|
return;
|
|
}
|
|
}
|
|
|
|
addObjectToInventory(obj, room);
|
|
putOwner(obj, VAR(VAR_EGO));
|
|
putClass(obj, kObjectClassUntouchable, 1);
|
|
putState(obj, 1);
|
|
markObjectRectAsDirty(obj);
|
|
clearDrawObjectQueue();
|
|
runInventoryScript(obj); /* Difference */
|
|
}
|
|
|
|
void ScummEngine_v6::o6_loadRoomWithEgo() {
|
|
Actor *a;
|
|
int obj, room, x, y;
|
|
|
|
y = pop();
|
|
x = pop();
|
|
|
|
if (_features & GF_HUMONGOUS) {
|
|
room = pop();
|
|
obj = pop();
|
|
} else
|
|
obj = popRoomAndObj(&room);
|
|
|
|
a = derefActor(VAR(VAR_EGO), "o6_loadRoomWithEgo");
|
|
a->putActor(0, 0, room);
|
|
_egoPositioned = false;
|
|
|
|
if (VAR_WALKTO_OBJ != 0xFF)
|
|
VAR(VAR_WALKTO_OBJ) = obj;
|
|
startScene(a->room, a, obj);
|
|
if (VAR_WALKTO_OBJ != 0xFF)
|
|
VAR(VAR_WALKTO_OBJ) = 0;
|
|
|
|
if (_version == 6) {
|
|
setCameraAt(a->_pos.x, a->_pos.y);
|
|
setCameraFollows(a);
|
|
}
|
|
|
|
_fullRedraw = 1;
|
|
|
|
if (x != -1 && x != 0x7FFFFFFF) {
|
|
a->startWalkActor(x, y, -1);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getRandomNumber() {
|
|
int rnd;
|
|
rnd = _rnd.getRandomNumber(pop());
|
|
if (VAR_RANDOM_NR != 0xFF)
|
|
VAR(VAR_RANDOM_NR) = rnd;
|
|
push(rnd);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getRandomNumberRange() {
|
|
int max = pop();
|
|
int min = pop();
|
|
int rnd = _rnd.getRandomNumberRng(min, max);
|
|
if (VAR_RANDOM_NR != 0xFF)
|
|
VAR(VAR_RANDOM_NR) = rnd;
|
|
push(rnd);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_isScriptRunning() {
|
|
push(isScriptRunning(pop()));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_isRoomScriptRunning() {
|
|
push(isRoomScriptRunning(pop()));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getActorMoving() {
|
|
Actor *a = derefActor(pop(), "o6_getActorMoving");
|
|
push(a->moving);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getActorRoom() {
|
|
int act = pop();
|
|
|
|
if (act == 0) {
|
|
// This case occurs at the very least in COMI. That's because in COMI's script 28,
|
|
// there is a check which looks as follows:
|
|
// if (((VAR_TALK_ACTOR != 0) && (VAR_HAVE_MSG == 1)) &&
|
|
// (getActorRoom(VAR_TALK_ACTOR) == VAR_ROOM))
|
|
// Due to the way this is represented in bytecode, the engine cannot
|
|
// short circuit. Hence, even though this would be perfectly fine code
|
|
// in C/C++, here it can (and does) lead to getActorRoom(0) being
|
|
// invoked. We silently ignore this.
|
|
push(0);
|
|
return;
|
|
}
|
|
|
|
if (act == 255) {
|
|
// This case also occurs in COMI...
|
|
push(0);
|
|
return;
|
|
}
|
|
|
|
Actor *a = derefActor(act, "o6_getActorRoom");
|
|
push(a->room);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getActorWalkBox() {
|
|
Actor *a = derefActor(pop(), "o6_getActorWalkBox");
|
|
push(a->ignoreBoxes ? 0 : a->walkbox);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getActorCostume() {
|
|
Actor *a = derefActor(pop(), "o6_getActorCostume");
|
|
push(a->costume);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getActorElevation() {
|
|
Actor *a = derefActor(pop(), "o6_getActorElevation");
|
|
push(a->getElevation());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getActorWidth() {
|
|
Actor *a = derefActor(pop(), "o6_getActorWidth");
|
|
push(a->width);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getActorScaleX() {
|
|
Actor *a = derefActor(pop(), "o6_getActorScale");
|
|
push(a->scalex);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getActorAnimCounter1() {
|
|
Actor *a = derefActor(pop(), "o6_getActorAnimCounter");
|
|
push(a->cost.animCounter);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getAnimateVariable() {
|
|
int var = pop();
|
|
Actor *a = derefActor(pop(), "o6_getAnimateVariable");
|
|
push(a->getAnimVar(var));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_isActorInBox() {
|
|
int box = pop();
|
|
Actor *a = derefActor(pop(), "o6_isActorInBox");
|
|
push(checkXYInBoxBounds(box, a->_pos.x, a->_pos.y));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getActorLayer() {
|
|
Actor *a = derefActor(pop(), "getActorLayer");
|
|
push(a->layer);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getObjectX() {
|
|
push(getObjX(pop()));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getObjectY() {
|
|
push(getObjY(pop()));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getObjectOldDir() {
|
|
push(getObjOldDir(pop()));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getObjectNewDir() {
|
|
push(getObjNewDir(pop()));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_findInventory() {
|
|
int idx = pop();
|
|
int owner = pop();
|
|
push(findInventory(owner, idx));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getInventoryCount() {
|
|
push(getInventoryCount(pop()));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getVerbFromXY() {
|
|
int y = pop();
|
|
int x = pop();
|
|
int over = checkMouseOver(x, y);
|
|
if (over)
|
|
over = _verbs[over].verbid;
|
|
push(over);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_beginOverride() {
|
|
beginOverride();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_endOverride() {
|
|
endOverride();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_setObjectName() {
|
|
int obj = pop();
|
|
setObjectName(obj);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_isSoundRunning() {
|
|
int snd = pop();
|
|
|
|
if (snd)
|
|
snd = _sound->isSoundRunning(snd);
|
|
|
|
push(snd);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_setBoxFlags() {
|
|
int table[65];
|
|
int num, value;
|
|
|
|
value = pop();
|
|
num = getStackList(table, ARRAYSIZE(table));
|
|
|
|
while (--num >= 0) {
|
|
setBoxFlags(table[num], value);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_createBoxMatrix() {
|
|
createBoxMatrix();
|
|
|
|
if ((_gameId == GID_DIG) || (_gameId == GID_CMI))
|
|
putActors();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_resourceRoutines() {
|
|
int resid, op;
|
|
op = fetchScriptByte();
|
|
|
|
switch (op) {
|
|
case 100: // SO_LOAD_SCRIPT
|
|
resid = pop();
|
|
if (_version >= 7)
|
|
if (resid >= _numGlobalScripts)
|
|
break;
|
|
ensureResourceLoaded(rtScript, resid);
|
|
break;
|
|
case 101: // SO_LOAD_SOUND
|
|
resid = pop();
|
|
ensureResourceLoaded(rtSound, resid);
|
|
break;
|
|
case 102: // SO_LOAD_COSTUME
|
|
resid = pop();
|
|
ensureResourceLoaded(rtCostume, resid);
|
|
break;
|
|
case 103: // SO_LOAD_ROOM
|
|
resid = pop();
|
|
ensureResourceLoaded(rtRoom, resid);
|
|
break;
|
|
case 104: // SO_NUKE_SCRIPT
|
|
resid = pop();
|
|
if (_version >= 7)
|
|
if (resid >= _numGlobalScripts)
|
|
break;
|
|
setResourceCounter(rtScript, resid, 0x7F);
|
|
break;
|
|
case 105: // SO_NUKE_SOUND
|
|
resid = pop();
|
|
setResourceCounter(rtSound, resid, 0x7F);
|
|
break;
|
|
case 106: // SO_NUKE_COSTUME
|
|
resid = pop();
|
|
setResourceCounter(rtCostume, resid, 0x7F);
|
|
break;
|
|
case 107: // SO_NUKE_ROOM
|
|
resid = pop();
|
|
setResourceCounter(rtRoom, resid, 0x7F);
|
|
break;
|
|
case 108: // SO_LOCK_SCRIPT
|
|
resid = pop();
|
|
if (resid >= _numGlobalScripts)
|
|
break;
|
|
lock(rtScript, resid);
|
|
break;
|
|
case 109: // SO_LOCK_SOUND
|
|
resid = pop();
|
|
lock(rtSound, resid);
|
|
break;
|
|
case 110: // SO_LOCK_COSTUME
|
|
resid = pop();
|
|
lock(rtCostume, resid);
|
|
break;
|
|
case 111: // SO_LOCK_ROOM
|
|
resid = pop();
|
|
if (resid > 0x7F)
|
|
resid = _resourceMapper[resid & 0x7F];
|
|
lock(rtRoom, resid);
|
|
break;
|
|
case 112: // SO_UNLOCK_SCRIPT
|
|
resid = pop();
|
|
if (resid >= _numGlobalScripts)
|
|
break;
|
|
unlock(rtScript, resid);
|
|
break;
|
|
case 113: // SO_UNLOCK_SOUND
|
|
resid = pop();
|
|
unlock(rtSound, resid);
|
|
break;
|
|
case 114: // SO_UNLOCK_COSTUME
|
|
resid = pop();
|
|
unlock(rtCostume, resid);
|
|
break;
|
|
case 115: // SO_UNLOCK_ROOM
|
|
resid = pop();
|
|
if (resid > 0x7F)
|
|
resid = _resourceMapper[resid & 0x7F];
|
|
unlock(rtRoom, resid);
|
|
break;
|
|
case 116: // SO_CLEAR_HEAP
|
|
/* this is actually a scumm message */
|
|
error("clear heap not working yet");
|
|
break;
|
|
case 117: // SO_LOAD_CHARSET
|
|
resid = pop();
|
|
loadCharset(resid);
|
|
break;
|
|
case 118: // SO_NUKE_CHARSET
|
|
resid = pop();
|
|
nukeCharset(resid);
|
|
break;
|
|
case 119: // SO_LOAD_OBJECT
|
|
{
|
|
int room, obj = popRoomAndObj(&room);
|
|
loadFlObject(obj, room);
|
|
break;
|
|
}
|
|
default:
|
|
error("o6_resourceRoutines: default case %d", op);
|
|
}
|
|
}
|
|
|
|
|
|
void ScummEngine_v6::o6_roomOps() {
|
|
int a, b, c, d, e;
|
|
byte op;
|
|
|
|
op = fetchScriptByte();
|
|
|
|
switch (op) {
|
|
case 172: // SO_ROOM_SCROLL
|
|
b = pop();
|
|
a = pop();
|
|
if (a < (_screenWidth / 2))
|
|
a = (_screenWidth / 2);
|
|
if (b < (_screenWidth / 2))
|
|
b = (_screenWidth / 2);
|
|
if (a > _roomWidth - (_screenWidth / 2))
|
|
a = _roomWidth - (_screenWidth / 2);
|
|
if (b > _roomWidth - (_screenWidth / 2))
|
|
b = _roomWidth - (_screenWidth / 2);
|
|
VAR(VAR_CAMERA_MIN_X) = a;
|
|
VAR(VAR_CAMERA_MAX_X) = b;
|
|
break;
|
|
|
|
case 174: // SO_ROOM_SCREEN
|
|
b = pop();
|
|
a = pop();
|
|
initScreens(a, b);
|
|
break;
|
|
|
|
case 175: // SO_ROOM_PALETTE
|
|
d = pop();
|
|
c = pop();
|
|
b = pop();
|
|
a = pop();
|
|
setPalColor(d, a, b, c);
|
|
break;
|
|
|
|
case 176: // SO_ROOM_SHAKE_ON
|
|
setShake(1);
|
|
break;
|
|
|
|
case 177: // SO_ROOM_SHAKE_OFF
|
|
setShake(0);
|
|
break;
|
|
|
|
case 179: // SO_ROOM_INTENSITY
|
|
c = pop();
|
|
b = pop();
|
|
a = pop();
|
|
darkenPalette(a, a, a, b, c);
|
|
break;
|
|
|
|
case 180: // SO_ROOM_SAVEGAME
|
|
_saveTemporaryState = true;
|
|
_saveLoadSlot = pop();
|
|
_saveLoadFlag = pop();
|
|
if (_gameId == GID_TENTACLE)
|
|
_saveSound = (_saveLoadSlot != 0);
|
|
break;
|
|
|
|
case 181: // SO_ROOM_FADE
|
|
a = pop();
|
|
if (a) {
|
|
_switchRoomEffect = (byte)(a & 0xFF);
|
|
_switchRoomEffect2 = (byte)(a >> 8);
|
|
} else {
|
|
fadeIn(_newEffect);
|
|
}
|
|
break;
|
|
|
|
case 182: // SO_RGB_ROOM_INTENSITY
|
|
e = pop();
|
|
d = pop();
|
|
c = pop();
|
|
b = pop();
|
|
a = pop();
|
|
darkenPalette(a, b, c, d, e);
|
|
break;
|
|
|
|
case 183: // SO_ROOM_SHADOW
|
|
e = pop();
|
|
d = pop();
|
|
c = pop();
|
|
b = pop();
|
|
a = pop();
|
|
setupShadowPalette(a, b, c, d, e, 0, 256);
|
|
break;
|
|
|
|
case 184: // SO_SAVE_STRING
|
|
error("save string not implemented");
|
|
break;
|
|
|
|
case 185: // SO_LOAD_STRING
|
|
error("load string not implemented");
|
|
break;
|
|
|
|
case 186: // SO_ROOM_TRANSFORM
|
|
d = pop();
|
|
c = pop();
|
|
b = pop();
|
|
a = pop();
|
|
palManipulateInit(a, b, c, d);
|
|
break;
|
|
|
|
case 187: // SO_CYCLE_SPEED
|
|
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: // SO_ROOM_NEW_PALETTE
|
|
a = pop();
|
|
|
|
// This opcode is used when turning off noir mode in Sam & Max,
|
|
// but since our implementation of this feature doesn't change
|
|
// the original palette there's no need to reload it. Doing it
|
|
// this way, we avoid some graphics glitches that the original
|
|
// interpreter had.
|
|
|
|
if (_gameId == GID_SAMNMAX && vm.slot[_currentScript].number == 64)
|
|
setDirtyColors(0, 255);
|
|
else
|
|
setPalette(a);
|
|
break;
|
|
default:
|
|
error("o6_roomOps: default case %d", op);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_actorOps() {
|
|
Actor *a;
|
|
int i, j, k;
|
|
int args[8];
|
|
byte b;
|
|
|
|
b = fetchScriptByte();
|
|
if (b == 197) {
|
|
_curActor = pop();
|
|
return;
|
|
}
|
|
|
|
a = derefActorSafe(_curActor, "o6_actorOps");
|
|
if (!a)
|
|
return;
|
|
|
|
switch (b) {
|
|
case 76: // SO_COSTUME
|
|
a->setActorCostume(pop());
|
|
break;
|
|
case 77: // SO_STEP_DIST
|
|
j = pop();
|
|
i = pop();
|
|
a->setActorWalkSpeed(i, j);
|
|
break;
|
|
case 78: // SO_SOUND
|
|
k = getStackList(args, ARRAYSIZE(args));
|
|
for (i = 0; i < k; i++)
|
|
a->sound[i] = args[i];
|
|
break;
|
|
case 79: // SO_WALK_ANIMATION
|
|
a->walkFrame = pop();
|
|
break;
|
|
case 80: // SO_TALK_ANIMATION
|
|
a->talkStopFrame = pop();
|
|
a->talkStartFrame = pop();
|
|
break;
|
|
case 81: // SO_STAND_ANIMATION
|
|
a->standFrame = pop();
|
|
break;
|
|
case 82: // SO_ANIMATION
|
|
// dummy case in scumm6
|
|
pop();
|
|
pop();
|
|
pop();
|
|
break;
|
|
case 83: // SO_DEFAULT
|
|
a->initActor(0);
|
|
break;
|
|
case 84: // SO_ELEVATION
|
|
a->setElevation(pop());
|
|
break;
|
|
case 85: // SO_ANIMATION_DEFAULT
|
|
a->initFrame = 1;
|
|
a->walkFrame = 2;
|
|
a->standFrame = 3;
|
|
a->talkStartFrame = 4;
|
|
a->talkStopFrame = 5;
|
|
break;
|
|
case 86: // SO_PALETTE
|
|
j = pop();
|
|
i = pop();
|
|
checkRange(255, 0, i, "Illegal palette slot %d");
|
|
a->setPalette(i, j);
|
|
break;
|
|
case 87: // SO_TALK_COLOR
|
|
a->talkColor = pop();
|
|
break;
|
|
case 88: // SO_ACTOR_NAME
|
|
loadPtrToResource(rtActorName, a->number, NULL);
|
|
break;
|
|
case 89: // SO_INIT_ANIMATION
|
|
a->initFrame = pop();
|
|
break;
|
|
case 91: // SO_ACTOR_WIDTH
|
|
a->width = pop();
|
|
break;
|
|
case 92: // SO_SCALE
|
|
i = pop();
|
|
a->setScale(i, i);
|
|
break;
|
|
case 93: // SO_NEVER_ZCLIP
|
|
a->forceClip = 0;
|
|
break;
|
|
case 225: // SO_ALWAYS_ZCLIP
|
|
case 94: // SO_ALWAYS_ZCLIP
|
|
a->forceClip = pop();
|
|
break;
|
|
case 95: // SO_IGNORE_BOXES
|
|
a->ignoreBoxes = 1;
|
|
a->forceClip = (_version >= 7) ? 100 : 0;
|
|
if (a->isInCurrentRoom())
|
|
a->putActor(a->_pos.x, a->_pos.y, a->room);
|
|
break;
|
|
case 96: // SO_FOLLOW_BOXES
|
|
a->ignoreBoxes = 0;
|
|
a->forceClip = (_version >= 7) ? 100 : 0;
|
|
if (a->isInCurrentRoom())
|
|
a->putActor(a->_pos.x, a->_pos.y, a->room);
|
|
break;
|
|
case 97: // SO_ANIMATION_SPEED
|
|
a->setAnimSpeed(pop());
|
|
break;
|
|
case 98: // SO_SHADOW
|
|
a->shadow_mode = pop();
|
|
break;
|
|
case 99: // SO_TEXT_OFFSET
|
|
a->talkPosY = pop();
|
|
a->talkPosX = pop();
|
|
break;
|
|
case 198: // SO_ACTOR_VARIABLE
|
|
i = pop();
|
|
a->setAnimVar(pop(), i);
|
|
break;
|
|
case 215: // SO_ACTOR_IGNORE_TURNS_ON
|
|
a->ignoreTurns = true;
|
|
break;
|
|
case 216: // SO_ACTOR_IGNORE_TURNS_OFF
|
|
a->ignoreTurns = false;
|
|
break;
|
|
case 217: // SO_ACTOR_NEW
|
|
a->initActor(2);
|
|
break;
|
|
case 227: // SO_ACTOR_DEPTH
|
|
a->layer = pop();
|
|
break;
|
|
case 228: // SO_ACTOR_WALK_SCRIPT
|
|
a->walkScript = pop();
|
|
break;
|
|
case 229: // SO_ACTOR_STOP
|
|
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: // SO_ACTOR_WALK_PAUSE
|
|
a->moving |= MF_FROZEN;
|
|
break;
|
|
case 234: // SO_ACTOR_WALK_RESUME
|
|
a->moving &= ~MF_FROZEN;
|
|
break;
|
|
case 235: // SO_ACTOR_TALK_SCRIPT
|
|
a->talkScript = pop();
|
|
break;
|
|
default:
|
|
error("o6_actorOps: default case %d", b);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_verbOps() {
|
|
int slot, a, b;
|
|
VerbSlot *vs;
|
|
byte op;
|
|
|
|
op = fetchScriptByte();
|
|
if (op == 196) {
|
|
_curVerb = pop();
|
|
_curVerbSlot = getVerbSlot(_curVerb, 0);
|
|
checkRange(_numVerbs - 1, 0, _curVerbSlot, "Illegal new verb slot %d");
|
|
return;
|
|
}
|
|
vs = &_verbs[_curVerbSlot];
|
|
slot = _curVerbSlot;
|
|
switch (op) {
|
|
case 124: // SO_VERB_IMAGE
|
|
a = pop();
|
|
if (_curVerbSlot) {
|
|
setVerbObject(_roomResource, a, slot);
|
|
vs->type = kImageVerbType;
|
|
if (_heversion >= 60)
|
|
vs->imgindex = a;
|
|
}
|
|
break;
|
|
case 125: // SO_VERB_NAME
|
|
loadPtrToResource(rtVerb, slot, NULL);
|
|
vs->type = kTextVerbType;
|
|
vs->imgindex = 0;
|
|
break;
|
|
case 126: // SO_VERB_COLOR
|
|
vs->color = pop();
|
|
break;
|
|
case 127: // SO_VERB_HICOLOR
|
|
vs->hicolor = pop();
|
|
break;
|
|
case 128: // SO_VERB_AT
|
|
vs->curRect.top = pop();
|
|
vs->curRect.left = pop();
|
|
break;
|
|
case 129: // SO_VERB_ON
|
|
vs->curmode = 1;
|
|
break;
|
|
case 130: // SO_VERB_OFF
|
|
vs->curmode = 0;
|
|
break;
|
|
case 131: // SO_VERB_DELETE
|
|
if (_features & GF_HUMONGOUS)
|
|
slot = getVerbSlot(pop(), 0);
|
|
killVerb(slot);
|
|
break;
|
|
case 132: // SO_VERB_NEW
|
|
slot = getVerbSlot(_curVerb, 0);
|
|
if (slot == 0) {
|
|
for (slot = 1; slot < _numVerbs; slot++) {
|
|
if (_verbs[slot].verbid == 0)
|
|
break;
|
|
}
|
|
if (slot == _numVerbs)
|
|
error("Too many verbs");
|
|
_curVerbSlot = slot;
|
|
}
|
|
vs = &_verbs[slot];
|
|
vs->verbid = _curVerb;
|
|
vs->color = 2;
|
|
vs->hicolor = 0;
|
|
vs->dimcolor = 8;
|
|
vs->type = kTextVerbType;
|
|
vs->charset_nr = _string[0]._default.charset;
|
|
vs->curmode = 0;
|
|
vs->saveid = 0;
|
|
vs->key = 0;
|
|
vs->center = 0;
|
|
vs->imgindex = 0;
|
|
break;
|
|
case 133: // SO_VERB_DIMCOLOR
|
|
vs->dimcolor = pop();
|
|
break;
|
|
case 134: // SO_VERB_DIM
|
|
vs->curmode = 2;
|
|
break;
|
|
case 135: // SO_VERB_KEY
|
|
vs->key = pop();
|
|
break;
|
|
case 136: // SO_VERB_CENTER
|
|
vs->center = 1;
|
|
break;
|
|
case 137: // SO_VERB_NAME_STR
|
|
a = pop();
|
|
if (a == 0) {
|
|
loadPtrToResource(rtVerb, slot, (const byte *)"");
|
|
} else {
|
|
loadPtrToResource(rtVerb, slot, getStringAddress(a));
|
|
}
|
|
vs->type = kTextVerbType;
|
|
vs->imgindex = 0;
|
|
break;
|
|
case 139: // SO_VERB_IMAGE_IN_ROOM
|
|
b = pop();
|
|
a = pop();
|
|
|
|
if (slot && a != vs->imgindex) {
|
|
setVerbObject(b, a, slot);
|
|
vs->type = kImageVerbType;
|
|
vs->imgindex = a;
|
|
}
|
|
break;
|
|
case 140: // SO_VERB_BAKCOLOR
|
|
vs->bkcolor = pop();
|
|
break;
|
|
case 255:
|
|
drawVerb(slot, 0);
|
|
verbMouseOver(0);
|
|
break;
|
|
default:
|
|
error("o6_verbops: default case %d", op);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getActorFromXY() {
|
|
int y = pop();
|
|
int x = pop();
|
|
push(getActorFromPos(x, y));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_findObject() {
|
|
int y = pop();
|
|
int x = pop();
|
|
int r = findObject(x, y);
|
|
push(r);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_pseudoRoom() {
|
|
int list[100];
|
|
int num, a, value;
|
|
|
|
num = getStackList(list, ARRAYSIZE(list));
|
|
value = pop();
|
|
|
|
while (--num >= 0) {
|
|
a = list[num];
|
|
if (a > 0x7F)
|
|
_resourceMapper[a & 0x7F] = value;
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getVerbEntrypoint() {
|
|
int e = pop();
|
|
int v = pop();
|
|
push(getVerbEntrypoint(v, e));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_arrayOps() {
|
|
byte subOp = fetchScriptByte();
|
|
int array = fetchScriptWord();
|
|
int b, c, d, len;
|
|
ArrayHeader *ah;
|
|
int list[128];
|
|
|
|
switch (subOp) {
|
|
case 205: // SO_ASSIGN_STRING
|
|
b = pop();
|
|
len = resStrLen(_scriptPointer);
|
|
ah = defineArray(array, kStringArray, 0, len + 1);
|
|
copyScriptString(ah->data + b);
|
|
break;
|
|
case 208: // SO_ASSIGN_INT_LIST
|
|
b = pop();
|
|
c = pop();
|
|
d = readVar(array);
|
|
if (d == 0) {
|
|
defineArray(array, kIntArray, 0, b + c);
|
|
}
|
|
while (c--) {
|
|
writeArray(array, 0, b + c, pop());
|
|
}
|
|
break;
|
|
case 212: // SO_ASSIGN_2DIM_LIST
|
|
b = pop();
|
|
len = getStackList(list, ARRAYSIZE(list));
|
|
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("o6_arrayOps: default case %d (array %d)", subOp, array);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_saveRestoreVerbs() {
|
|
int a, b, c;
|
|
int slot, slot2;
|
|
|
|
c = pop();
|
|
b = pop();
|
|
a = pop();
|
|
|
|
byte subOp = fetchScriptByte();
|
|
if (_version == 8) {
|
|
subOp = (subOp - 141) + 0xB4;
|
|
}
|
|
|
|
switch (subOp) {
|
|
case 141: // SO_SAVE_VERBS
|
|
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: // SO_RESTORE_VERBS
|
|
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: // SO_DELETE_VERBS
|
|
while (a <= b) {
|
|
slot = getVerbSlot(a, c);
|
|
if (slot)
|
|
killVerb(slot);
|
|
a++;
|
|
}
|
|
break;
|
|
default:
|
|
error("o6_saveRestoreVerbs: default case");
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::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 ScummEngine_v6::o6_wait() {
|
|
int actnum;
|
|
int offs = -2;
|
|
Actor *a;
|
|
byte subOp = fetchScriptByte();
|
|
|
|
switch (subOp) {
|
|
case 168: // SO_WAIT_FOR_ACTOR Wait for actor
|
|
offs = fetchScriptWordSigned();
|
|
actnum = pop();
|
|
a = derefActor(actnum, "o6_wait:168");
|
|
if (a->isInCurrentRoom() && a->moving)
|
|
break;
|
|
return;
|
|
case 169: // SO_WAIT_FOR_MESSAGE Wait for message
|
|
if (VAR(VAR_HAVE_MSG))
|
|
break;
|
|
return;
|
|
case 170: // SO_WAIT_FOR_CAMERA Wait for camera
|
|
if (_version >= 7) {
|
|
if (camera._dest != camera._cur)
|
|
break;
|
|
} else {
|
|
if (camera._cur.x / 8 != camera._dest.x / 8)
|
|
break;
|
|
}
|
|
|
|
return;
|
|
case 171: // 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 226: // SO_WAIT_FOR_ANIMATION
|
|
offs = fetchScriptWordSigned();
|
|
actnum = pop();
|
|
a = derefActor(actnum, "o6_wait:226");
|
|
if (a->isInCurrentRoom() && a->needRedraw)
|
|
break;
|
|
return;
|
|
case 232: // SO_WAIT_FOR_TURN
|
|
// FIXME: This opcode is really odd. It's used a lot in The Dig.
|
|
// But sometimes it receives the actor ID as params, and sometimes an
|
|
// angle. However in (almost?) all cases, just before calling it, _curActor
|
|
// is set, so we can use it. I tried to add code that detects if an angle
|
|
// is passed, and if so, wait till that angle is reached, but that leads to hangs.
|
|
// It would be very good if somebody could disassmble the original code
|
|
// for this opcode so that we could figure out what's really going on here.
|
|
//
|
|
// For now, if the value passed in is divisible by 45, assume it is an
|
|
// angle, and use _curActor as the actor to wait for.
|
|
offs = fetchScriptWordSigned();
|
|
actnum = pop();
|
|
if (actnum % 45 == 0) {
|
|
actnum = _curActor;
|
|
}
|
|
a = derefActor(actnum, "o6_wait:232b");
|
|
if (a->isInCurrentRoom() && a->moving & MF_TURN)
|
|
break;
|
|
return;
|
|
default:
|
|
error("o6_wait: default case 0x%x", subOp);
|
|
}
|
|
|
|
_scriptPointer += offs;
|
|
o6_breakHere();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_soundKludge() {
|
|
int list[16];
|
|
int num = getStackList(list, ARRAYSIZE(list));
|
|
|
|
_sound->soundKludge(list, num);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_isAnyOf() {
|
|
int list[100];
|
|
int num;
|
|
int16 val;
|
|
|
|
num = getStackList(list, ARRAYSIZE(list));
|
|
val = pop();
|
|
|
|
while (--num >= 0) {
|
|
if (list[num] == val) {
|
|
push(1);
|
|
return;
|
|
}
|
|
}
|
|
push(0);
|
|
return;
|
|
}
|
|
|
|
void ScummEngine_v6::o6_quitPauseRestart() {
|
|
byte subOp = fetchScriptByte();
|
|
switch (subOp) {
|
|
case 158: // SO_RESTART
|
|
restart();
|
|
break;
|
|
case 159: // SO_PAUSE
|
|
pauseGame();
|
|
break;
|
|
case 160: // SO_QUIT
|
|
shutDown();
|
|
break;
|
|
default:
|
|
error("o6_quitPauseRestart invalid case %d", subOp);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_delay() {
|
|
// FIXME - what exactly are we measuring here? In order for the other two
|
|
// delay functions to be right, it should be 1/60th of a second. But for
|
|
// CMI it would seem this should delay for 1/10th of a second...
|
|
uint32 delay = (uint16)pop();
|
|
vm.slot[_currentScript].delay = delay;
|
|
vm.slot[_currentScript].status = ssPaused;
|
|
o6_breakHere();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_delaySeconds() {
|
|
uint32 delay = (uint32)pop();
|
|
if (_gameId != GID_CMI)
|
|
// FIXME - are we really measuring minutes here?
|
|
delay = delay * 60;
|
|
else
|
|
// FIXME - Is this the same in ComI? Seem to need a 1.5 minute
|
|
// multiplier for correct timing - see patch 664893
|
|
delay = delay * 90;
|
|
|
|
vm.slot[_currentScript].delay = delay;
|
|
vm.slot[_currentScript].status = ssPaused;
|
|
o6_breakHere();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_delayMinutes() {
|
|
// FIXME - are we really measuring minutes here?
|
|
uint32 delay = (uint16)pop() * 3600;
|
|
vm.slot[_currentScript].delay = delay;
|
|
vm.slot[_currentScript].status = ssPaused;
|
|
o6_breakHere();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_stopSentence() {
|
|
_sentenceNum = 0;
|
|
stopScript(VAR(VAR_SENTENCE_SCRIPT));
|
|
clearClickedStatus();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_printLine() {
|
|
_actorToPrintStrFor = 0xFF;
|
|
decodeParseString(0, 0);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_printCursor() {
|
|
decodeParseString(1, 0);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_printDebug() {
|
|
decodeParseString(2, 0);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_printSystem() {
|
|
decodeParseString(3, 0);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_printActor() {
|
|
decodeParseString(0, 1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_printEgo() {
|
|
push(VAR(VAR_EGO));
|
|
decodeParseString(0, 1);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_talkActor() {
|
|
_actorToPrintStrFor = pop();
|
|
|
|
_string[0].loadDefault();
|
|
actorTalk(_scriptPointer);
|
|
|
|
_scriptPointer += resStrLen(_scriptPointer) + 1;
|
|
}
|
|
|
|
void ScummEngine_v6::o6_talkEgo() {
|
|
push(VAR(VAR_EGO));
|
|
o6_talkActor();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_dimArray() {
|
|
int data;
|
|
|
|
switch (fetchScriptByte()) {
|
|
case 199: // SO_INT_ARRAY
|
|
data = kIntArray;
|
|
break;
|
|
case 200: // SO_BIT_ARRAY
|
|
data = kBitArray;
|
|
break;
|
|
case 201: // SO_NIBBLE_ARRAY
|
|
data = kNibbleArray;
|
|
break;
|
|
case 202: // SO_BYTE_ARRAY
|
|
data = kByteArray;
|
|
break;
|
|
case 203: // SO_STRING_ARRAY
|
|
data = kStringArray;
|
|
break;
|
|
case 204: // SO_UNDIM_ARRAY
|
|
nukeArray(fetchScriptWord());
|
|
return;
|
|
default:
|
|
error("o6_dimArray: default case");
|
|
}
|
|
|
|
defineArray(fetchScriptWord(), data, 0, pop());
|
|
}
|
|
|
|
void ScummEngine_v6::o6_dummy() {
|
|
if (_features & GF_HUMONGOUS)
|
|
stopObjectCode();
|
|
/* nothing */
|
|
}
|
|
|
|
void ScummEngine_v6::o6_dim2dimArray() {
|
|
int a, b, data;
|
|
switch (fetchScriptByte()) {
|
|
case 199: // SO_INT_ARRAY
|
|
data = kIntArray;
|
|
break;
|
|
case 200: // SO_BIT_ARRAY
|
|
data = kBitArray;
|
|
break;
|
|
case 201: // SO_NIBBLE_ARRAY
|
|
data = kNibbleArray;
|
|
break;
|
|
case 202: // SO_BYTE_ARRAY
|
|
data = kByteArray;
|
|
break;
|
|
case 203: // SO_STRING_ARRAY
|
|
data = kStringArray;
|
|
break;
|
|
default:
|
|
error("o6_dim2dimArray: default case");
|
|
}
|
|
|
|
b = pop();
|
|
a = pop();
|
|
defineArray(fetchScriptWord(), data, a, b);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_abs() {
|
|
int a = pop(); // palmos: prevent multi pop if we use an abs function defined as : #define abs(a) ((a) < 0 ? -(a) : (a))
|
|
push(abs(a));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_distObjectObject() {
|
|
int a, b;
|
|
b = pop();
|
|
a = pop();
|
|
push(getDistanceBetween(true, a, 0, true, b, 0));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_distObjectPt() {
|
|
int a, b, c;
|
|
c = pop();
|
|
b = pop();
|
|
a = pop();
|
|
push(getDistanceBetween(true, a, 0, false, b, c));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_distPtPt() {
|
|
int a, b, c, d;
|
|
d = pop();
|
|
c = pop();
|
|
b = pop();
|
|
a = pop();
|
|
push(getDistanceBetween(false, a, b, false, c, d));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_drawBlastObject() {
|
|
int args[16];
|
|
int a, b, c, d, e;
|
|
|
|
getStackList(args, ARRAYSIZE(args));
|
|
e = pop();
|
|
d = pop();
|
|
c = pop();
|
|
b = pop();
|
|
a = pop();
|
|
enqueueObject(a, b, c, d, e, 0xFF, 0xFF, 1, 0);
|
|
}
|
|
|
|
// Set BOMP processing window
|
|
void ScummEngine_v6::o6_setBlastObjectWindow() {
|
|
// TODO - implement this
|
|
int a, b, c, d;
|
|
|
|
d = pop();
|
|
c = pop();
|
|
b = pop();
|
|
a = pop();
|
|
|
|
warning("o6_setBlastObjectWindow(%d, %d, %d, %d)", a, b, c, d);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_kernelSetFunctions() {
|
|
int args[30];
|
|
int num;
|
|
Actor *a;
|
|
|
|
num = getStackList(args, ARRAYSIZE(args));
|
|
|
|
if (_version >= 7) {
|
|
switch (args[0]) {
|
|
case 4:
|
|
grabCursor(args[1], args[2], args[3], args[4]);
|
|
break;
|
|
case 6: {
|
|
uint32 speed;
|
|
if (_smushFrameRate == 0)
|
|
_smushFrameRate = 14;
|
|
speed = 1000000 / _smushFrameRate;
|
|
|
|
// SMUSH movie playback
|
|
if (args[1] == 0) {
|
|
assert(getStringAddressVar(VAR_VIDEONAME));
|
|
if (strcmp((char *)getStringAddressVar(VAR_VIDEONAME), "sq3.san") == 0)
|
|
speed = 1000000 / 14;
|
|
|
|
SmushPlayer *sp = new SmushPlayer(this, speed);
|
|
|
|
// Correct incorrect smush filename in Macintosh FT demo
|
|
if ((_gameId == GID_FT) && (_features & GF_DEMO) && (_features & GF_MACINTOSH) &&
|
|
(strcmp((char *)getStringAddressVar(VAR_VIDEONAME), "jumpgorge.san") == 0))
|
|
sp->play("jumpgorg.san");
|
|
else
|
|
sp->play((char *)getStringAddressVar(VAR_VIDEONAME));
|
|
delete sp;
|
|
} else if (_gameId == GID_FT) {
|
|
const int insaneVarNum = ((_features & GF_DEMO) && (_features & GF_PC))
|
|
? 232 : 233;
|
|
|
|
_insaneRunning = true;
|
|
_insane->setSmushParams(speed);
|
|
_insane->runScene(insaneVarNum);
|
|
_insaneRunning = false;
|
|
}
|
|
}
|
|
break;
|
|
case 12:
|
|
setCursorFromImg(args[1], (uint) - 1, args[2]);
|
|
break;
|
|
case 13:
|
|
derefActor(args[1], "o6_kernelSetFunctions:13")->remapActorPalette(args[2], args[3], args[4], -1);
|
|
break;
|
|
case 14:
|
|
derefActor(args[1], "o6_kernelSetFunctions:14")->remapActorPalette(args[2], args[3], args[4], args[5]);
|
|
break;
|
|
case 15:
|
|
_smushFrameRate = args[1];
|
|
break;
|
|
case 16:
|
|
case 17:
|
|
enqueueText(getStringAddressVar(VAR_STRING2DRAW), args[3], args[4], args[2], args[1], true);
|
|
break;
|
|
case 20:
|
|
// it's used for turn on/off 'RadioChatter' effect for voice in the dig, but i's not needed
|
|
break;
|
|
case 107:
|
|
a = derefActor(args[1], "o6_kernelSetFunctions: 107");
|
|
a->setScale((unsigned char)args[2], -1);
|
|
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_kernelSetFunctions: 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 124:
|
|
_saveSound = args[1];
|
|
break;
|
|
case 215:
|
|
ConfMan.set("subtitles", args[1] != 0);
|
|
break;
|
|
default:
|
|
error("o6_kernelSetFunctions: default case %d (param count %d)", args[0], num);
|
|
break;
|
|
}
|
|
} else {
|
|
switch (args[0]) {
|
|
case 3:
|
|
// Dummy case
|
|
break;
|
|
case 4:
|
|
grabCursor(args[1], args[2], args[3], args[4]);
|
|
break;
|
|
case 5:
|
|
fadeOut(args[1]);
|
|
break;
|
|
case 6:
|
|
_fullRedraw = 1;
|
|
redrawBGAreas();
|
|
setActorRedrawFlags();
|
|
processActors();
|
|
fadeIn(args[1]);
|
|
break;
|
|
case 8:
|
|
startManiac();
|
|
break;
|
|
case 9:
|
|
killAllScriptsExceptCurrent();
|
|
break;
|
|
case 104: /* samnmax */
|
|
nukeFlObjects(args[2], args[3]);
|
|
break;
|
|
case 107: /* set actor scale */
|
|
a = derefActor(args[1], "o6_kernelSetFunctions: 107");
|
|
a->setScale((unsigned char)args[2], -1);
|
|
break;
|
|
case 108: /* create proc_special_palette */
|
|
case 109:
|
|
// Case 108 and 109 share the same function
|
|
if (num != 6)
|
|
warning("o6_kernelSetFunctions sub op %d: expected 6 params but got %d", args[0], num);
|
|
setupShadowPalette(args[3], args[4], args[5], args[1], args[2], 0, 256);
|
|
break;
|
|
case 110:
|
|
_charset->clearCharsetMask();
|
|
break;
|
|
case 111:
|
|
a = derefActor(args[1], "o6_kernelSetFunctions: 111");
|
|
a->shadow_mode = args[2] + args[3];
|
|
break;
|
|
case 112: /* palette shift? */
|
|
setupShadowPalette(args[3], args[4], args[5], args[1], args[2], 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_kernelSetFunctions_114()");
|
|
break;
|
|
case 117:
|
|
// Sam & Max uses this opcode in script-43, right
|
|
// before a screensaver is selected.
|
|
//
|
|
// Sam & Max uses variable 132 to specify the number of
|
|
// minutes of inactivity (no mouse movements) before
|
|
// starting the screensaver, so setting it to 0 will
|
|
// help in debugging.
|
|
freezeScripts(0x80);
|
|
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 122:
|
|
VAR(VAR_SOUNDRESULT) =
|
|
(short)_imuse->doCommand (num - 1, &args[1]);
|
|
break;
|
|
case 123:
|
|
copyPalColor(args[2], args[1]);
|
|
break;
|
|
case 124:
|
|
_saveSound = args[1];
|
|
break;
|
|
default:
|
|
error("o6_kernelSetFunctions: default case %d (param count %d)", args[0], num);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_kernelGetFunctions() {
|
|
int args[30];
|
|
int i;
|
|
int slot;
|
|
Actor *a;
|
|
|
|
getStackList(args, ARRAYSIZE(args));
|
|
|
|
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.
|
|
|
|
{
|
|
VirtScreen *vs = &virtscr[0];
|
|
if (args[1] < 0 || args[1] >= vs->w || args[2] < 0 || args[2] >= vs->h) {
|
|
// FIXME: Until we know what to do in this case...
|
|
warning("o6_kernelGetFunctions:113: asking for pixel (%d, %d) outside of %dx%d screen", args[1], args[2], vs->w, vs->h);
|
|
push(0);
|
|
} else
|
|
push(*((byte *)vs->pixels + args[1] + args[2] * vs->pitch));
|
|
}
|
|
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(args[1]);
|
|
push(_objs[i].x_pos);
|
|
break;
|
|
case 208:
|
|
i = getObjectIndex(args[1]);
|
|
push(_objs[i].y_pos);
|
|
break;
|
|
case 209:
|
|
i = getObjectIndex(args[1]);
|
|
push(_objs[i].width);
|
|
break;
|
|
case 210:
|
|
i = getObjectIndex(args[1]);
|
|
push(_objs[i].height);
|
|
break;
|
|
case 211:
|
|
/*
|
|
13 = thrust
|
|
336 = thrust
|
|
328 = thrust
|
|
27 = abort
|
|
97 = left
|
|
331 = left
|
|
115 = right
|
|
333 = right
|
|
*/
|
|
|
|
push(getKeyState(args[1]));
|
|
break;
|
|
case 212:
|
|
a = derefActor(args[1], "o6_kernelGetFunctions:212");
|
|
// This is used by walk scripts
|
|
push(a->frame);
|
|
break;
|
|
case 213:
|
|
slot = getVerbSlot(args[1], 0);
|
|
push(_verbs[slot].curRect.left);
|
|
break;
|
|
case 214:
|
|
slot = getVerbSlot(args[1], 0);
|
|
push(_verbs[slot].curRect.top);
|
|
break;
|
|
case 215:
|
|
if ((_extraBoxFlags[args[1]] & 0x00FF) == 0x00C0) {
|
|
push(_extraBoxFlags[args[1]]);
|
|
} else {
|
|
push(getBoxFlags(args[1]));
|
|
}
|
|
break;
|
|
default:
|
|
error("o6_kernelGetFunctions: default case %d", args[0]);
|
|
}
|
|
}
|
|
|
|
// FIXME: check either some warning will trigger. I am not sure that those
|
|
// keys are queried in scripts at all
|
|
int ScummEngine::getKeyState(int key) {
|
|
switch(key) {
|
|
case 0x145:
|
|
warning("ScummEngine::getKeyState(%x) 'numlock' is probed", key);
|
|
return 0;
|
|
break;
|
|
case 0x164:
|
|
warning("ScummEngine::getKeyState(%x) 'left shift' is probed", key);
|
|
return 0;
|
|
break;
|
|
case 0x165:
|
|
warning("ScummEngine::getKeyState(%x) 'right shift' is probed", key);
|
|
return 0;
|
|
break;
|
|
case 0x166:
|
|
case 0x167:
|
|
warning("ScummEngine::getKeyState(%x) 'alt' is probed", key);
|
|
return 0;
|
|
break;
|
|
case 0x168:
|
|
warning("ScummEngine::getKeyState(%x) 'left ctrl' is probed", key);
|
|
return 0;
|
|
break;
|
|
case 0x202a:
|
|
warning("ScummEngine::getKeyState(%x) 'gray *' is probed", key);
|
|
return 0;
|
|
break;
|
|
case 0x202d:
|
|
warning("ScummEngine::getKeyState(%x) 'gray -' is probed", key);
|
|
return 0;
|
|
break;
|
|
case 0x147: // Home
|
|
return (_keyDownMap[0x107] || _keyDownMap[0x115]) ? 1 : 0;
|
|
break;
|
|
case 0x148: // Up
|
|
return (_keyDownMap[0x108] || _keyDownMap[0x111] ||
|
|
_keyDownMap[0x38]) ? 1 : 0;
|
|
break;
|
|
case 0x149: // PgUp
|
|
return (_keyDownMap[0x109] || _keyDownMap[0x118]) ? 1 : 0;
|
|
break;
|
|
case 0x14A: // Gray-
|
|
return (_keyDownMap[0x10d] || _keyDownMap[0x2d]) ? 1 : 0;
|
|
break;
|
|
case 0x14B: // Left
|
|
return (_keyDownMap[0x104] || _keyDownMap[0x114] ||
|
|
_keyDownMap[0x34]) ? 1 : 0;
|
|
break;
|
|
case 0x14C: // 5
|
|
return (_keyDownMap[0x105]) ? 1 : 0;
|
|
break;
|
|
case 0x14D: // Right
|
|
return (_keyDownMap[0x106] || _keyDownMap[0x113] ||
|
|
_keyDownMap[0x36]) ? 1 : 0;
|
|
break;
|
|
case 0x14E: // Gray+
|
|
return (_keyDownMap[0x10e] ||
|
|
(_keyDownMap[0x13d] && _keyDownMap[0x12f])) ? 1 : 0;
|
|
break;
|
|
case 0x14F: // End
|
|
return (_keyDownMap[0x101] || _keyDownMap[0x117]) ? 1 : 0;
|
|
break;
|
|
case 0x150: // Down
|
|
return (_keyDownMap[0x102] || _keyDownMap[0x112] ||
|
|
_keyDownMap[0x32]) ? 1 : 0;
|
|
break;
|
|
case 0x151: // PgDn
|
|
return (_keyDownMap[0x103] || _keyDownMap[0x119]) ? 1 : 0;
|
|
break;
|
|
case 0x152: // Ins
|
|
return (_keyDownMap[0x100] || _keyDownMap[0x115]) ? 1 : 0;
|
|
break;
|
|
case 0x153: // Del
|
|
return (_keyDownMap[0x10a] || _keyDownMap[0x7f]) ? 1 : 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (key >= 0x13b && key <= 0x144) { // F1-F10
|
|
key -= 0x13b - 0x11a;
|
|
} else if (key >= 0x154 && key <= 0x15d) { // Shift+F1-F10
|
|
key -= 0x154 - 0x11a; // map it to just F1-F10
|
|
|
|
warning("ScummEngine::getKeyState(%x) 'Shift-F%d' is probed", key, key-0x153);
|
|
} else if (key > 0x8000) { // Alt
|
|
key -= 0x8000;
|
|
key += 154; // see ScummEngine::parseEvents()
|
|
} else if (key > 0x4000) { // Ctrl
|
|
key -= 0x4000;
|
|
key -= 0x40;
|
|
} else if (key > 0x2000) { // Gray keys
|
|
key -= 0x2000;
|
|
warning("ScummEngine::getKeyState(%x) 'gray key' is probed", key);
|
|
}
|
|
|
|
return (_keyDownMap[key]) ? 1 : 0;
|
|
}
|
|
|
|
void ScummEngine_v6::o6_delayFrames() {
|
|
ScriptSlot *ss = &vm.slot[_currentScript];
|
|
if (ss->delayFrameCount == 0) {
|
|
ss->delayFrameCount = pop();
|
|
} else {
|
|
ss->delayFrameCount--;
|
|
}
|
|
if (ss->delayFrameCount) {
|
|
_scriptPointer--;
|
|
o6_breakHere();
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_pickOneOf() {
|
|
int args[100];
|
|
int i, num;
|
|
|
|
num = getStackList(args, ARRAYSIZE(args));
|
|
i = pop();
|
|
if (i < 0 || i > num)
|
|
error("o6_pickOneOf: %d out of range (0, %d)", i, num - 1);
|
|
push(args[i]);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_pickOneOfDefault() {
|
|
int args[100];
|
|
int i, num, def;
|
|
|
|
def = pop();
|
|
num = getStackList(args, ARRAYSIZE(args));
|
|
i = pop();
|
|
if (i < 0 || i >= num)
|
|
i = def;
|
|
else
|
|
i = args[i];
|
|
push(i);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_stampObject() {
|
|
int object, x, y, state;
|
|
|
|
// dummy opcode in tentacle
|
|
if (_gameId == GID_TENTACLE)
|
|
return;
|
|
|
|
state = pop();
|
|
y = pop();
|
|
x = pop();
|
|
object = pop();
|
|
if (_version >= 7 && object < 30) {
|
|
if (state == 0)
|
|
state = 255;
|
|
|
|
Actor *a = derefActor(object, "o6_stampObject");
|
|
a->scalex = state;
|
|
a->scaley = state;
|
|
a->putActor(x, y, _currentRoom);
|
|
a->drawToBackBuf = true;
|
|
a->drawActorCostume();
|
|
a->drawToBackBuf = false;
|
|
a->drawActorCostume();
|
|
return;
|
|
}
|
|
|
|
if (state == 0)
|
|
state = 1;
|
|
|
|
int objnum = getObjectIndex(object);
|
|
if (objnum == 0)
|
|
return;
|
|
|
|
if (x != -1) {
|
|
_objs[objnum].x_pos = x * 8;
|
|
_objs[objnum].y_pos = y * 8;
|
|
}
|
|
|
|
putState(object, state);
|
|
drawObject(objnum, 0);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_stopTalking() {
|
|
stopTalk();
|
|
}
|
|
|
|
void ScummEngine_v6::o6_findAllObjects() {
|
|
int room = pop();
|
|
int i = 1;
|
|
|
|
if (room != _currentRoom)
|
|
warning("o6_findAllObjects: current room is not %d", room);
|
|
writeVar(0, 0);
|
|
defineArray(0, kIntArray, 0, _numLocalObjects + 1);
|
|
writeArray(0, 0, 0, _numLocalObjects);
|
|
|
|
while (i < _numLocalObjects) {
|
|
writeArray(0, 0, i, _objs[i].obj_nr);
|
|
i++;
|
|
}
|
|
|
|
push(readVar(0));
|
|
}
|
|
|
|
void ScummEngine_v6::shuffleArray(int num, int minIdx, int maxIdx) {
|
|
int range = maxIdx - minIdx;
|
|
int count = range * 2;
|
|
|
|
// Shuffle the array 'num'
|
|
while (count--) {
|
|
// Determine two random elements...
|
|
int rand1 = _rnd.getRandomNumber(range) + minIdx;
|
|
int rand2 = _rnd.getRandomNumber(range) + minIdx;
|
|
|
|
// ...and swap them
|
|
int val1 = readArray(num, 0, rand1);
|
|
int val2 = readArray(num, 0, rand2);
|
|
writeArray(num, 0, rand1, val2);
|
|
writeArray(num, 0, rand2, val1);
|
|
}
|
|
}
|
|
|
|
void ScummEngine_v6::o6_shuffle() {
|
|
int b = pop();
|
|
int a = pop();
|
|
shuffleArray(fetchScriptWord(), a, b);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_pickVarRandom() {
|
|
int num;
|
|
int args[100];
|
|
int dim1;
|
|
|
|
num = getStackList(args, ARRAYSIZE(args));
|
|
int value = fetchScriptWord();
|
|
|
|
if (readVar(value) == 0) {
|
|
defineArray(value, kIntArray, 0, num);
|
|
if (num > 0) {
|
|
int16 counter = 0;
|
|
do {
|
|
writeArray(value, 0, counter + 1, args[counter]);
|
|
} while (++counter < num);
|
|
}
|
|
|
|
shuffleArray(value, 1, num);
|
|
writeArray(value, 0, 0, 2);
|
|
push(readArray(value, 0, 1));
|
|
return;
|
|
}
|
|
|
|
num = readArray(value, 0, 0);
|
|
|
|
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(value));
|
|
dim1 = FROM_LE_16(ah->dim1) - 1;
|
|
|
|
if (dim1 < num) {
|
|
int16 var_2 = readArray(value, 0, num - 1);
|
|
shuffleArray(value, 1, dim1);
|
|
if (readArray(value, 0, 1) == var_2) {
|
|
num = 2;
|
|
} else {
|
|
num = 1;
|
|
}
|
|
}
|
|
|
|
writeArray(value, 0, 0, num + 1);
|
|
push(readArray(value, 0, num));
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getDateTime() {
|
|
struct tm *t;
|
|
time_t now = time(NULL);
|
|
|
|
t = localtime(&now);
|
|
|
|
VAR(VAR_TIMEDATE_YEAR) = t->tm_year;
|
|
VAR(VAR_TIMEDATE_MONTH) = t->tm_mon;
|
|
VAR(VAR_TIMEDATE_DAY) = t->tm_mday;
|
|
VAR(VAR_TIMEDATE_HOUR) = t->tm_hour;
|
|
VAR(VAR_TIMEDATE_MINUTE) = t->tm_min;
|
|
|
|
if (_version == 8)
|
|
VAR(VAR_TIMEDATE_SECOND) = t->tm_sec;
|
|
}
|
|
|
|
void ScummEngine_v6::o6_getPixel() {
|
|
// this opcode check ground area in minigame "Asteroid Lander" in the dig
|
|
int x, y;
|
|
|
|
if (_features & GF_HUMONGOUS) {
|
|
x = pop();
|
|
y = pop();
|
|
} else {
|
|
y = pop();
|
|
x = pop();
|
|
}
|
|
|
|
VirtScreen *vs = findVirtScreen(y);
|
|
|
|
if (vs == NULL || x > _screenWidth - 1 || x < 0) {
|
|
push(-1);
|
|
return;
|
|
}
|
|
|
|
byte area = *vs->getPixels(x, y - vs->topline);
|
|
push(area);
|
|
}
|
|
|
|
void ScummEngine_v6::o6_setBoxSet() {
|
|
//FIXME Causes glitches and game to crash
|
|
// Only used in fbear when leaving the basement
|
|
|
|
int arg = pop();
|
|
return;
|
|
|
|
const byte *room = getResourceAddress(rtRoom, _roomResource);
|
|
const byte *boxd = NULL, *boxm = NULL;
|
|
int32 dboxSize, mboxSize;
|
|
int i;
|
|
|
|
ResourceIterator boxds(room, false);
|
|
for (i = 0; i < arg; i++)
|
|
boxd = boxds.findNext(MKID('BOXD'));
|
|
|
|
if (!boxd)
|
|
error("ScummEngine_v6::o6_setBoxSet: Can't find dboxes for set %d", arg);
|
|
|
|
dboxSize = READ_BE_UINT32(boxd + 4);
|
|
byte *matrix = createResource(rtMatrix, 2, dboxSize);
|
|
|
|
assert(matrix);
|
|
memcpy(matrix, boxd, dboxSize);
|
|
|
|
ResourceIterator boxms(room, false);
|
|
for (i = 0; i < arg; i++)
|
|
boxm = boxms.findNext(MKID('BOXM'));
|
|
|
|
if (!boxm)
|
|
error("ScummEngine_v6::o6_setBoxSet: Can't find mboxes for set %d", arg);
|
|
|
|
mboxSize = READ_BE_UINT32(boxm + 4);
|
|
matrix = createResource(rtMatrix, 1, mboxSize);
|
|
|
|
assert(matrix);
|
|
memcpy(matrix, boxm, mboxSize);
|
|
|
|
if (_version == 7)
|
|
putActors();
|
|
}
|
|
|
|
void ScummEngine_v6::decodeParseString(int m, int n) {
|
|
byte b;
|
|
|
|
b = fetchScriptByte();
|
|
|
|
switch (b) {
|
|
case 65: // SO_AT
|
|
_string[m].ypos = pop();
|
|
_string[m].xpos = pop();
|
|
_string[m].overhead = false;
|
|
break;
|
|
case 66: // SO_COLOR
|
|
_string[m].color = pop();
|
|
break;
|
|
case 67: // SO_CLIPPED
|
|
_string[m].right = pop();
|
|
break;
|
|
case 69: // SO_CENTER
|
|
_string[m].center = true;
|
|
_string[m].overhead = false;
|
|
break;
|
|
case 71: // SO_LEFT
|
|
_string[m].center = false;
|
|
_string[m].overhead = false;
|
|
break;
|
|
case 72: // SO_OVERHEAD
|
|
_string[m].overhead = true;
|
|
_string[m].no_talk_anim = false;
|
|
break;
|
|
case 73: // SO_SAY_VOICE
|
|
error("decodeParseString: case 73");
|
|
break;
|
|
case 74: // SO_MUMBLE
|
|
_string[m].no_talk_anim = true;
|
|
break;
|
|
case 75: // SO_TEXTSTRING
|
|
printString(m, _scriptPointer);
|
|
_scriptPointer += resStrLen(_scriptPointer) + 1;
|
|
break;
|
|
case 0xFE:
|
|
_string[m].loadDefault();
|
|
if (n)
|
|
_actorToPrintStrFor = pop();
|
|
break;
|
|
case 0xFF:
|
|
_string[m].saveDefault();
|
|
break;
|
|
default:
|
|
error("decodeParseString: default case 0x%x", b);
|
|
}
|
|
}
|
|
|
|
} // End of namespace Scumm
|