Files
scummvm-cursorfix/engines/scumm/he/script_v90he.cpp
2026-02-02 04:50:13 +01:00

2509 lines
59 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef ENABLE_HE
#include "scumm/actor.h"
#include "scumm/charset.h"
#include "scumm/he/animation_he.h"
#include "scumm/he/font_he.h"
#include "scumm/he/intern_he.h"
#include "scumm/he/logic_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
#include "scumm/he/sprite_he.h"
#include "scumm/util.h"
namespace Scumm {
#define OPCODE(i, x) _opcodes[i]._OPCODE(ScummEngine_v90he, x)
void ScummEngine_v90he::setupOpcodes() {
ScummEngine_v80he::setupOpcodes();
OPCODE(0x0a, o90_dup_n);
/* 1C */
OPCODE(0x1c, o90_wizImageOps);
OPCODE(0x1d, o90_min);
OPCODE(0x1e, o90_max);
OPCODE(0x1f, o90_sin);
/* 20 */
OPCODE(0x20, o90_cos);
OPCODE(0x21, o90_sqrt);
OPCODE(0x22, o90_getAngleFromDelta);
OPCODE(0x23, o90_getAngleFromLine);
/* 24 */
OPCODE(0x24, o90_getDistanceBetweenPoints);
OPCODE(0x25, o90_getSpriteInfo);
OPCODE(0x26, o90_setSpriteInfo);
OPCODE(0x27, o90_getSpriteGroupInfo);
/* 28 */
OPCODE(0x28, o90_setSpriteGroupInfo);
OPCODE(0x29, o90_getWizData);
OPCODE(0x2a, o90_getActorData);
OPCODE(0x2b, o90_priorityStartScript);
/* 2C */
OPCODE(0x2c, o90_priorityChainScript);
OPCODE(0x2d, o90_videoOps);
OPCODE(0x2e, o90_getVideoData);
OPCODE(0x2f, o90_floodFill);
/* 30 */
OPCODE(0x30, o90_mod);
OPCODE(0x31, o90_shl);
OPCODE(0x32, o90_shr);
OPCODE(0x33, o90_xor);
/* 34 */
OPCODE(0x34, o90_findAllObjectsWithClassOf);
OPCODE(0x35, o90_getOverlap);
OPCODE(0x36, o90_cond);
OPCODE(0x37, o90_dim2dim2Array);
/* 38 */
OPCODE(0x38, o90_redim2dimArray);
OPCODE(0x39, o90_getLinesIntersectionPoint);
OPCODE(0x3a, o90_sortArray);
OPCODE(0x44, o90_getObjectData);
OPCODE(0x94, o90_getPaletteData);
OPCODE(0x9e, o90_paletteOps);
OPCODE(0xa5, o90_fontEnum);
OPCODE(0xab, o90_getActorAnimProgress);
OPCODE(0xc8, o90_kernelGetFunctions);
OPCODE(0xc9, o90_kernelSetFunctions);
}
void ScummEngine_v90he::o90_dup_n() {
int num;
int args[16];
push(fetchScriptWord());
num = getStackList(args, ARRAYSIZE(args));
for (int i = 0; i < 2; i++) {
for (int j = 0; j < num; j++)
push(args[j]);
}
}
void ScummEngine_v90he::o90_wizImageOps() {
int a, b;
int subOp = fetchScriptByte();
switch (subOp) {
case SO_WIDTH: // 32, HE99+
_wizImageCommand.actionFlags |= kWAFWidth;
_wizImageCommand.width = pop();
break;
case SO_HEIGHT: // 33, HE99+
_wizImageCommand.actionFlags |= kWAFHeight;
_wizImageCommand.height = pop();
break;
case SO_GENERAL_CLIP_STATE: // 46
// Dummy case
pop();
break;
case SO_GENERAL_CLIP_RECT: // 47
_wizImageCommand.box.bottom = pop();
_wizImageCommand.box.right = pop();
_wizImageCommand.box.top = pop();
_wizImageCommand.box.left = pop();
break;
case SO_DRAW: // 48
_wizImageCommand.actionType = kWADraw;
break;
case SO_LOAD: // 49
_wizImageCommand.actionFlags |= kWAFFilename;
_wizImageCommand.actionType = kWALoad;
copyScriptString(_wizImageCommand.filename, sizeof(_wizImageCommand.filename));
break;
case SO_SAVE: // 50
_wizImageCommand.actionFlags |= kWAFFilename;
_wizImageCommand.actionType = kWASave;
copyScriptString(_wizImageCommand.filename, sizeof(_wizImageCommand.filename));
_wizImageCommand.fileType = pop();
break;
case SO_CAPTURE: // 51
_wizImageCommand.actionFlags |= kWAFRect | kWAFCompressionType;
_wizImageCommand.actionType = kWACapture;
_wizImageCommand.box.bottom = pop();
_wizImageCommand.box.right = pop();
_wizImageCommand.box.top = pop();
_wizImageCommand.box.left = pop();
_wizImageCommand.compressionType = pop();
break;
case SO_STATE: // 52
_wizImageCommand.actionFlags |= kWAFState;
_wizImageCommand.state = pop();
break;
case SO_ANGLE: // 53
_wizImageCommand.actionFlags |= kWAFAngle;
_wizImageCommand.angle = pop();
break;
case SO_SET_FLAGS: // 54
_wizImageCommand.actionFlags |= kWAFFlags;
_wizImageCommand.flags |= pop();
break;
case SO_NOW: // 56
_wizImageCommand.flags = pop();
_wizImageCommand.state = pop();
_wizImageCommand.yPos = pop();
_wizImageCommand.xPos = pop();
_wizImageCommand.image = pop();
_wiz->simpleDrawAWiz(
_wizImageCommand.image,
_wizImageCommand.state,
_wizImageCommand.xPos,
_wizImageCommand.yPos,
_wizImageCommand.flags
);
break;
case SO_INIT: // 57
_wizImageCommand.image = pop();
_wizImageCommand.actionType = kWAUnknown;
_wizImageCommand.actionFlags = 0;
_wizImageCommand.remapCount = 0;
_wizImageCommand.flags = 0;
_wizImageCommand.propertyValue = 0;
_wizImageCommand.propertyNumber = 0;
_wizImageCommand.extendedRenderInfo.sprite = 0;
_wizImageCommand.extendedRenderInfo.group = 0;
break;
case SO_AT_IMAGE: // 62, HE99+
_wizImageCommand.actionFlags |= kWAFSourceImage;
_wizImageCommand.sourceImage = pop();
break;
case SO_AT: // 65
case SO_CURSOR_HOTSPOT: // 154
_wizImageCommand.actionFlags |= kWAFSpot;
_wizImageCommand.yPos = pop();
_wizImageCommand.xPos = pop();
break;
case SO_COLOR: // 66, HE95
case SO_COLOR_LIST: // 249, HE98+
b = pop();
a = pop();
_wizImageCommand.actionFlags |= kWAFRemapList;
_wizImageCommand.actionType = kWAModify;
if (_wizImageCommand.remapCount == 0) {
memset(_wizImageCommand.remapList, 0, sizeof(_wizImageCommand.remapList));
}
assert(_wizImageCommand.remapCount < ARRAYSIZE(_wizImageCommand.remapList));
_wizImageCommand.remapList[_wizImageCommand.remapCount] = a;
_wizImageCommand.remapTable[a] = b;
_wizImageCommand.remapCount++;
break;
case SO_CLIPPED: // 67
_wizImageCommand.actionFlags |= kWAFRect;
_wizImageCommand.box.bottom = pop();
_wizImageCommand.box.right = pop();
_wizImageCommand.box.top = pop();
_wizImageCommand.box.left = pop();
break;
case SO_PALETTE: // 86, HE99+
_wizImageCommand.actionFlags |= kWAFPalette;
_wizImageCommand.palette = pop();
break;
case SO_SCALE: // 92
_wizImageCommand.actionFlags |= kWAFScale;
_wizImageCommand.scale = pop();
break;
case SO_SHADOW: // 98
_wizImageCommand.actionFlags |= kWAFShadow;
_wizImageCommand.shadow = pop();
break;
case SO_POLY_POLYGON: // 131, HE99+
_wizImageCommand.actionFlags |= kWAFPolygon2 | kWAFCompressionType | kWAFPolygon;
_wizImageCommand.actionType = kWAPolyCapture;
_wizImageCommand.polygon2 = pop();
_wizImageCommand.polygon = pop();
_wizImageCommand.compressionType = pop();
break;
case SO_RENDER_RECTANGLE: // 133, HE99+
_wizImageCommand.actionFlags |= kWAFColor | kWAFRenderCoords;
_wizImageCommand.actionType = kWARenderRectangle;
_wizImageCommand.colorValue = pop();
_wizImageCommand.renderCoords.bottom = pop();
_wizImageCommand.renderCoords.right = pop();
_wizImageCommand.renderCoords.top = pop();
_wizImageCommand.renderCoords.left = pop();
break;
case SO_RENDER_LINE: // 134, HE99+
_wizImageCommand.actionFlags |= kWAFColor | kWAFRenderCoords;
_wizImageCommand.actionType = kWARenderLine;
_wizImageCommand.colorValue = pop();
_wizImageCommand.renderCoords.bottom = pop();
_wizImageCommand.renderCoords.right = pop();
_wizImageCommand.renderCoords.top = pop();
_wizImageCommand.renderCoords.left = pop();
break;
case SO_RENDER_PIXEL: // 135, HE99+
_wizImageCommand.actionFlags |= kWAFColor | kWAFRenderCoords;
_wizImageCommand.actionType = kWARenderPixel;
_wizImageCommand.colorValue = pop();
_wizImageCommand.renderCoords.top = _wizImageCommand.renderCoords.bottom = pop();
_wizImageCommand.renderCoords.left = _wizImageCommand.renderCoords.right = pop();
break;
case SO_RENDER_FLOOD_FILL: // 136, HE99+
_wizImageCommand.actionFlags |= kWAFColor | kWAFRenderCoords;
_wizImageCommand.actionType = kWARenderFloodFill;
_wizImageCommand.colorValue = pop();
_wizImageCommand.renderCoords.top = _wizImageCommand.renderCoords.bottom = pop();
_wizImageCommand.renderCoords.left = _wizImageCommand.renderCoords.right = pop();
break;
case SO_RENDER_INTO_IMAGE: // 137, HE99+
_wizImageCommand.actionFlags |= kWAFDestImage;
_wizImageCommand.destImageNumber = pop();
break;
case SO_NEW_GENERAL_PROPERTY: // 139, HE99+
_wizImageCommand.actionFlags |= kWAFProperty;
_wizImageCommand.propertyValue = pop();
_wizImageCommand.propertyNumber = pop();
break;
case SO_FONT_START: // 141, HE99+
_wizImageCommand.actionType = kWAFontStart;
break;
case SO_FONT_CREATE: // 142, HE99+
_wizImageCommand.actionType = kWAFontCreate;
_wizImageCommand.fontProperties.bgColor = pop();
_wizImageCommand.fontProperties.fgColor = pop();
_wizImageCommand.fontProperties.size = pop();
_wizImageCommand.fontProperties.style = pop();
copyScriptString(_wizImageCommand.fontProperties.fontName, sizeof(_wizImageCommand.fontProperties.fontName));
break;
case SO_FONT_RENDER: // 143, HE99+
_wizImageCommand.actionType = kWAFontRender;
_wizImageCommand.fontProperties.yPos = pop();
_wizImageCommand.fontProperties.xPos = pop();
copyScriptString(_wizImageCommand.fontProperties.string, sizeof(_wizImageCommand.fontProperties.string));
break;
case SO_RENDER_ELLIPSE: // 189, HE99+
_wizImageCommand.actionType = kWARenderEllipse;
_wizImageCommand.ellipseProperties.color = pop();
_wizImageCommand.ellipseProperties.lod = pop();
_wizImageCommand.ellipseProperties.ky = pop();
_wizImageCommand.ellipseProperties.kx = pop();
_wizImageCommand.ellipseProperties.qy = pop();
_wizImageCommand.ellipseProperties.qx = pop();
_wizImageCommand.ellipseProperties.py = pop();
_wizImageCommand.ellipseProperties.px = pop();
break;
case SO_FONT_END: // 196, HE99+
_wizImageCommand.actionType = kWAFontEnd;
break;
case SO_NEW: // 217, HE99+
_wizImageCommand.actionType = kWANew;
break;
case SO_SET_POLYGON: // 246
_wizImageCommand.actionFlags |= kWAFFlags | kWAFSpot | kWAFPolygon;
_wizImageCommand.flags |= kWRFPolygon;
_wizImageCommand.polygon = _wizImageCommand.yPos = _wizImageCommand.xPos = pop();
break;
case SO_END: // 255
if (_wizImageCommand.image)
_wiz->processWizImageCmd(&_wizImageCommand);
break;
default:
error("o90_wizImageOps: unhandled case %d", subOp);
}
}
void ScummEngine_v90he::o90_min() {
int a = pop();
int b = pop();
push((a <= b) ? a : b);
}
void ScummEngine_v90he::o90_max() {
int a = pop();
int b = pop();
push((a >= b) ? a : b);
}
void ScummEngine_v90he::o90_sin() {
push(scummMathSin(pop()));
}
void ScummEngine_v90he::o90_cos() {
push(scummMathCos(pop()));
}
void ScummEngine_v90he::o90_sqrt() {
push(scummMathSqrt(pop()));
}
void ScummEngine_v90he::o90_getAngleFromDelta() {
int dy = pop();
int dx = pop();
push(scummMathAngleFromDelta(dx, dy));
}
void ScummEngine_v90he::o90_getAngleFromLine() {
int y2 = pop();
int x2 = pop();
int y1 = pop();
int x1 = pop();
push(scummMathAngleFromDelta((x2 - x1), (y2 - y1)));
}
void ScummEngine_v90he::o90_getDistanceBetweenPoints() {
int x1, y1, z1, x2, y2, z2, dx, dy, dz;
byte subOp = fetchScriptByte();
switch (subOp) {
case ScummEngine_v100he::SO_COORD_2D: // 23
case SO_COORD_2D: // 28
y2 = pop();
x2 = pop();
y1 = pop();
x1 = pop();
dx = (x2 - x1) * (x2 - x1);
dy = (y2 - y1) * (y2 - y1);
push(scummMathSqrt(dx + dy));
break;
case ScummEngine_v100he::SO_COORD_3D: // 24
case SO_COORD_3D: // 29
z2 = pop();
y2 = pop();
x2 = pop();
z1 = pop();
y1 = pop();
x1 = pop();
dx = (x2 - x1) * (x2 - x1);
dy = (y2 - y1) * (y2 - y1);
dz = (z2 - z1) * (z2 - z1);
push(scummMathSqrt(dx + dy + dz));
break;
default:
error("o90_getDistanceBetweenPoints: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_getSpriteInfo() {
int args[16];
int spriteId, flags, groupId, type, count;
int32 x, y;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_XPOS: // 30
spriteId = pop();
if (spriteId) {
_sprite->getSpritePosition(spriteId, x, y);
push(x);
} else {
push(0);
}
break;
case SO_YPOS: // 31
spriteId = pop();
if (spriteId) {
_sprite->getSpritePosition(spriteId, x, y);
push(y);
} else {
push(0);
}
break;
case SO_WIDTH: // 32
spriteId = pop();
if (spriteId) {
_sprite->getSpriteImageDim(spriteId, x, y);
push(x);
} else {
push(0);
}
break;
case SO_HEIGHT: // 33
spriteId = pop();
if (spriteId) {
_sprite->getSpriteImageDim(spriteId, x, y);
push(y);
} else {
push(0);
}
break;
case SO_STEP_DIST_X: // 34
spriteId = pop();
if (spriteId) {
_sprite->getDelta(spriteId, x, y);
push(x);
} else {
push(0);
}
break;
case SO_STEP_DIST_Y: // 35
spriteId = pop();
if (spriteId) {
_sprite->getDelta(spriteId, x, y);
push(y);
} else {
push(0);
}
break;
case SO_COUNT: // 36
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteImageStateCount(spriteId));
else
push(0);
break;
case SO_GROUP: // 37
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteGroup(spriteId));
else
push(0);
break;
case SO_DRAW_XPOS: // 38
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteDisplayX(spriteId));
else
push(0);
break;
case SO_DRAW_YPOS: // 39
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteDisplayY(spriteId));
else
push(0);
break;
case SO_PROPERTY: // 42
flags = pop();
spriteId = pop();
if (spriteId) {
switch (flags) {
case SPRPROP_HFLIP: // 0
push(_sprite->getSpriteHorzFlip(spriteId));
break;
case SPRPROP_VFLIP: // 1
push(_sprite->getSpriteVertFlip(spriteId));
break;
case SPRPROP_ACTIVE: // 2
push(_sprite->getSpriteActiveFlag(spriteId));
break;
case SPRPROP_BACKGROUND_RENDER: // 3
push(_sprite->getSpriteRenderToBackground(spriteId));
break;
case SPRPROP_USE_IMAGE_REMAP_TABLE: // 4
push(_sprite->getSpriteImageRemapFlag(spriteId));
break;
default:
push(0);
}
} else {
push(0);
}
break;
case SO_PRIORITY: // 43
spriteId = pop();
if (spriteId)
push(_sprite->getSpritePriority(spriteId));
else
push(0);
break;
case SO_FIND: // 45
if (_game.heversion == 99) {
flags = getStackList(args, ARRAYSIZE(args));
type = pop();
groupId = pop();
y = pop();
x = pop();
push(_sprite->spriteFromPoint(x, y, groupId, type, flags, args));
} else if (_game.heversion == 98) {
type = pop();
groupId = pop();
y = pop();
x = pop();
push(_sprite->spriteFromPoint(x, y, groupId, type, 0, 0));
} else {
groupId = pop();
y = pop();
x = pop();
push(_sprite->spriteFromPoint(x, y, groupId, 0, 0, 0));
}
break;
case SO_STATE: // 52
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteImageState(spriteId));
else
push(0);
break;
case SO_AT_IMAGE: // 62
spriteId = pop();
if (spriteId)
push(_sprite->getSourceImage(spriteId));
else
push(0);
break;
case SO_IMAGE: // 63
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteImage(spriteId));
else
push(0);
break;
case SO_ERASE: // 68
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteEraseType(spriteId));
else
push(1);
break;
case SO_ANIMATION: // 82
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteAutoAnimFlag(spriteId));
else
push(0);
break;
case SO_PALETTE: // 86
spriteId = pop();
if (spriteId)
push(_sprite->getSpritePalette(spriteId));
else
push(0);
break;
case SO_SCALE: // 92
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteScale(spriteId));
else
push(0);
break;
case SO_ANIMATION_SPEED: // 97
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteAnimSpeed(spriteId));
else
push(1);
break;
case SO_SHADOW: // 98
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteShadow(spriteId));
else
push(0);
break;
case SO_UPDATE: // 124
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteUpdateType(spriteId));
else
push(0);
break;
case SO_CLASS: // 125
count = getStackList(args, ARRAYSIZE(args));
spriteId = pop();
if (spriteId) {
if (!count) {
push(_sprite->getSpriteClass(spriteId, -1));
} else {
if (_game.heversion > 98) {
push(_sprite->checkSpriteClassAgaintClassSet(spriteId, count, args));
} else {
bool stillTrue = true;
while (count--) {
int classID = args[count];
int classbit = _sprite->getSpriteClass(spriteId, (classID & 0x7f));
if (((classID & 0x80) == 0x80) && (classbit == 0))
stillTrue = false;
if (((classID & 0x80) == 0x00) && (classbit != 0))
stillTrue = false;
}
if (!stillTrue) {
push(0);
} else {
push(1);
}
}
}
} else {
push(0);
}
break;
case SO_NEW_GENERAL_PROPERTY: // 139
flags = pop();
spriteId = pop();
if (spriteId)
push(_sprite->getSpriteGeneralProperty(spriteId, flags));
else
push(0);
break;
case SO_MASK: // 140
spriteId = pop();
if (spriteId)
push(_sprite->getMaskImage(spriteId));
else
push(0);
break;
case SO_ACTOR_VARIABLE: // 198
pop();
spriteId = pop();
if (spriteId)
push(_sprite->getUserValue(spriteId));
else
push(0);
break;
default:
error("o90_getSpriteInfo: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_setSpriteInfo() {
int args[16];
int spriteId;
int32 tmp[2];
int n;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_STEP_DIST_X: // 34
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++) {
_sprite->getDelta(spriteId, tmp[0], tmp[1]);
_sprite->setDelta(spriteId, args[0], tmp[1]);
}
break;
case SO_STEP_DIST_Y: // 35
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++) {
_sprite->getDelta(spriteId, tmp[0], tmp[1]);
_sprite->setDelta(spriteId, tmp[0], args[0]);
}
break;
case SO_GROUP: // 37
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpriteGroup(spriteId, args[0]);
break;
case SO_PROPERTY: // 42
args[1] = pop();
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
switch (args[1]) {
case SPRPROP_HFLIP: // 0
_sprite->setSpriteHorzFlip(spriteId, args[0]);
break;
case SPRPROP_VFLIP: // 1
_sprite->setSpriteVertFlip(spriteId, args[0]);
break;
case SPRPROP_ACTIVE: // 2
_sprite->setSpriteActiveFlag(spriteId, args[0]);
break;
case SPRPROP_BACKGROUND_RENDER: // 3
_sprite->setSpriteRenderToBackground(spriteId, args[0]);
break;
case SPRPROP_USE_IMAGE_REMAP_TABLE: // 4
_sprite->setSpriteImageRemapFlag(spriteId, args[0]);
break;
default:
break;
}
break;
case SO_PRIORITY: // 43
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpritePriority(spriteId, args[0]);
break;
case SO_MOVE: // 44
args[1] = pop();
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->moveSprite(spriteId, args[0], args[1]);
break;
case SO_STATE: // 52
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpriteImageState(spriteId, args[0]);
break;
case SO_ANGLE: // 53
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpriteAngle(spriteId, args[0]);
break;
case SO_INIT: // 57
if (_game.features & GF_HE_985 || _game.heversion >= 99) {
_maxSpriteNum = pop();
_minSpriteNum = pop();
if (_minSpriteNum > _maxSpriteNum)
SWAP(_minSpriteNum, _maxSpriteNum);
} else { // HE95
_minSpriteNum = pop();
_maxSpriteNum = _minSpriteNum; // to make all functions happy
}
break;
case SO_AT_IMAGE: // 62, HE99+
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSourceImage(spriteId, args[0]);
break;
case SO_IMAGE: // 63
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpriteImage(spriteId, args[0]);
break;
case SO_AT: // 65
args[1] = pop();
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpritePosition(spriteId, args[0], args[1]);
break;
case SO_ERASE: // 68
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpriteEraseType(spriteId, args[0]);
break;
case SO_STEP_DIST: // 77
args[1] = pop();
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setDelta(spriteId, args[0], args[1]);
break;
case SO_ANIMATION: // 82
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpriteAutoAnimFlag(spriteId, args[0]);
break;
case SO_PALETTE: // 86, HE98+
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpritePalette(spriteId, args[0]);
break;
case SO_SCALE: // 92, HE99+
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpriteScale(spriteId, args[0]);
break;
case SO_ANIMATION_SPEED: // 97, HE98+
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpriteAnimSpeed(spriteId, args[0]);
break;
case SO_SHADOW: // 98
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpriteShadow(spriteId, args[0]);
break;
case SO_UPDATE: // 124
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpriteUpdateType(spriteId, args[0]);
break;
case SO_CLASS: // 125
n = getStackList(args, ARRAYSIZE(args));
if (_minSpriteNum != 0 && _maxSpriteNum != 0 && n != 0) {
int *p = &args[n - 1];
do {
int code = *p;
if (code == 0) {
for (int i = _minSpriteNum; i <= _maxSpriteNum; ++i) {
_sprite->clearSpriteClasses(i);
}
} else if (code & 0x80) {
for (int i = _minSpriteNum; i <= _maxSpriteNum; ++i) {
_sprite->setSpriteClass(i, code & 0x7F, 1);
}
} else {
for (int i = _minSpriteNum; i <= _maxSpriteNum; ++i) {
_sprite->setSpriteClass(i, code & 0x7F, 0);
}
}
--p;
} while (--n);
}
break;
case SO_NEW_GENERAL_PROPERTY: // 139, HE99+
args[1] = pop();
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setSpriteGeneralProperty(spriteId, args[0], args[1]);
break;
case SO_MASK: // 140, HE99+
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setMaskImage(spriteId, args[0]);
break;
case SO_RESTART: // 158
_sprite->resetSpriteSystem(true);
break;
case SO_ACTOR_VARIABLE: // 198
args[1] = pop();
args[0] = pop();
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->setUserValue(spriteId, args[0], args[1]);
break;
case SO_NEW: // 217
if (_minSpriteNum > _maxSpriteNum)
break;
spriteId = _minSpriteNum;
if (!spriteId)
spriteId++;
for (; spriteId <= _maxSpriteNum; spriteId++)
_sprite->newSprite(spriteId);
break;
default:
error("o90_setSpriteInfo: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_getSpriteGroupInfo() {
int32 tx, ty;
int spriteGroupId, type;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_ARRAY: // 8, HE99+
spriteGroupId = pop();
if (spriteGroupId)
push(getGroupSpriteArray(spriteGroupId));
else
push(0);
break;
case SO_XPOS: // 30
spriteGroupId = pop();
if (spriteGroupId) {
_sprite->getGroupPoint(spriteGroupId, tx, ty);
push(tx);
} else {
push(0);
}
break;
case SO_YPOS: // 31
spriteGroupId = pop();
if (spriteGroupId) {
_sprite->getGroupPoint(spriteGroupId, tx, ty);
push(ty);
} else {
push(0);
}
break;
case SO_PROPERTY: // 42, HE99+
type = pop();
spriteGroupId = pop();
if (spriteGroupId) {
switch (type) {
case SPRGRPPROP_XMUL: // 0
push(_sprite->getGroupXMul(spriteGroupId));
break;
case SPRGRPPROP_XDIV: // 1
push(_sprite->getGroupXDiv(spriteGroupId));
break;
case SPRGRPPROP_YMUL: // 2
push(_sprite->getGroupYMul(spriteGroupId));
break;
case SPRGRPPROP_YDIV: // 3
push(_sprite->getGroupYDiv(spriteGroupId));
break;
default:
push(0);
}
} else {
push(0);
}
break;
case SO_PRIORITY: // 43
spriteGroupId = pop();
if (spriteGroupId)
push(_sprite->getGroupPriority(spriteGroupId));
else
push(0);
break;
case SO_IMAGE: // 63, HE99+
spriteGroupId = pop();
if (spriteGroupId)
push(_sprite->getGroupImage(spriteGroupId));
else
push(0);
break;
case SO_NEW_GENERAL_PROPERTY: // 139, HE99+
pop(); // propertyCode
pop(); // groupNum
// The original just checked if groupNum was within
// bounds and just pushed 0 to the stack
push(0);
break;
default:
error("o90_getSpriteGroupInfo: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_setSpriteGroupInfo() {
int type, value1, value2, value3, value4;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_GROUP: // 37
type = pop();
switch (type) {
case SPRGRPOP_MOVE: // 1
value2 = pop();
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->moveGroupMembers(_curSpriteGroupId, value1, value2);
break;
case SPRGRPOP_ORDER: // 2
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->setGroupMembersPriority(_curSpriteGroupId, value1);
break;
case SPRGRPOP_NEW_GROUP: // 3
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->changeGroupMembersGroup(_curSpriteGroupId, value1);
break;
case SPRGRPOP_UPDATE_TYPE: // 4
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->setGroupMembersUpdateType(_curSpriteGroupId, value1);
break;
case SPRGRPOP_NEW: // 5
if (!_curSpriteGroupId)
break;
_sprite->performNewOnGroupMembers(_curSpriteGroupId);
break;
case SPRGRPOP_ANIMATION_SPEED: // 6
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->setGroupMembersAnimationSpeed(_curSpriteGroupId, value1);
break;
case SPRGRPOP_ANIMATION_TYPE: // 7
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->setGroupMembersAutoAnimFlag(_curSpriteGroupId, value1);
break;
case SPRGRPOP_SHADOW: // 8
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->setGroupMembersShadow(_curSpriteGroupId, value1);
break;
default:
error("o90_setSpriteGroupInfo checkType 0: Unknown case %d", subOp);
}
break;
case SO_PROPERTY: // 42
type = pop();
value1 = pop();
if (!_curSpriteGroupId)
break;
switch (type) {
case SPRGRPPROP_XMUL: // 0
_sprite->setGroupXMul(_curSpriteGroupId, value1);
break;
case SPRGRPPROP_XDIV: // 1
_sprite->setGroupXDiv(_curSpriteGroupId, value1);
break;
case SPRGRPPROP_YMUL: // 2
_sprite->setGroupYMul(_curSpriteGroupId, value1);
break;
case SPRGRPPROP_YDIV: // 3
_sprite->setGroupYDiv(_curSpriteGroupId, value1);
break;
default:
error("o90_setSpriteGroupInfo checkType 5: Unknown case %d", subOp);
}
break;
case SO_PRIORITY: // 43
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->setGroupPriority(_curSpriteGroupId, value1);
break;
case SO_MOVE: // 44
value2 = pop();
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->moveGroup(_curSpriteGroupId, value1, value2);
break;
case SO_INIT: // 57
_curSpriteGroupId = pop();
break;
case SO_IMAGE: // 63
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->setGroupImage(_curSpriteGroupId, value1);
break;
case SO_AT: // 65
value2 = pop();
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->setGroupPoint(_curSpriteGroupId, value1, value2);
break;
case SO_CLIPPED: // 67
value4 = pop();
value3 = pop();
value2 = pop();
value1 = pop();
if (!_curSpriteGroupId)
break;
_sprite->setGroupClipRect(_curSpriteGroupId, value1, value2, value3, value4);
break;
case SO_NEVER_ZCLIP: // 93
if (!_curSpriteGroupId)
break;
_sprite->clearGroupClipRect(_curSpriteGroupId);
break;
case SO_NEW: // 217
if (!_curSpriteGroupId)
break;
_sprite->newGroup(_curSpriteGroupId);
break;
default:
error("o90_setSpriteGroupInfo: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_getWizData() {
byte filename[4096];
int resId, state, type;
int fontImageNum, fontProperty;
int32 w, h;
int32 x, y;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_XPOS: // 30
state = pop();
resId = pop();
_wiz->getWizSpot(resId, state, x, y);
push(x);
break;
case SO_YPOS: // 31
state = pop();
resId = pop();
_wiz->getWizSpot(resId, state, x, y);
push(y);
break;
case SO_WIDTH: // 32
state = pop();
resId = pop();
_wiz->getWizImageDim(resId, state, w, h);
push(w);
break;
case SO_HEIGHT: // 33
state = pop();
resId = pop();
_wiz->getWizImageDim(resId, state, w, h);
push(h);
break;
case SO_COUNT: // 36
resId = pop();
push(_wiz->getWizStateCount(resId));
break;
case SO_FIND: // 45
y = pop();
x = pop();
state = pop();
resId = pop();
push(_wiz->hitTestWiz(resId, state, x, y, 0));
break;
case SO_COLOR: // 66
y = pop();
x = pop();
state = pop();
resId = pop();
push(_wiz->pixelHitTestWiz(resId, state, x, y, 0));
break;
case SO_HISTOGRAM: // 130
{
Common::Rect clipRect;
clipRect.bottom = pop();
clipRect.right = pop();
clipRect.top = pop();
clipRect.left = pop();
state = pop();
resId = pop();
if (clipRect.left == -1 && clipRect.top == -1 && clipRect.right == -1 && clipRect.bottom == -1) {
_wiz->getWizImageDim(resId, state, w, h);
_wiz->makeSizedRect(&clipRect, w, h);
}
push(_wiz->createHistogramArrayForImage(resId, state, &clipRect));
break;
}
case SO_NEW_GENERAL_PROPERTY: // 139
if (_game.heversion > 99 || _isHE995) {
type = pop();
} else {
type = 0;
}
state = pop();
resId = pop();
push(_wiz->dwGetImageGeneralProperty(resId, state, type));
break;
case SO_FONT_START: // 141
fontProperty = pop();
copyScriptString(filename, sizeof(filename));
fontImageNum = pop();
if (fontImageNum) {
switch (fontProperty) {
case 2: // PFONT_EXTENT_X
push(((ScummEngine_v99he *)this)->_heFont->getStringWidth(fontImageNum, Common::String((char *)filename).c_str()));
break;
case 3: // PFONT_EXTENT_Y
push(((ScummEngine_v99he *)this)->_heFont->getStringHeight(fontImageNum, Common::String((char *)filename).c_str()));
break;
default:
// No default case in the original...
break;
}
} else {
push(0);
}
break;
default:
error("o90_getWizData: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_getActorData() {
ActorHE *a;
int subOp = pop();
int val = pop();
int act = pop();
a = (ActorHE *)derefActor(act, "o90_getActorData");
switch (subOp) {
case 1:
push(a->isUserConditionSet(val));
break;
case 2:
assertRange(0, val, 15, "o90_getActorData: Limb");
push(a->_cost.frame[val] * 4);
break;
case 3:
push(a->getAnimSpeed());
break;
case 4:
push(a->_shadowMode);
break;
case 5:
push(a->_layer);
break;
case 6:
push(a->_hePaletteNum);
break;
default:
error("o90_getActorData: Unknown actor property %d", subOp);
}
}
void ScummEngine_v90he::o90_priorityStartScript() {
int args[25];
int script, cycle;
byte flags;
getStackList(args, ARRAYSIZE(args));
cycle = pop();
script = pop();
flags = fetchScriptByte();
runScript(script, (flags == SO_BAK || flags == SO_BAKREC), (flags == SO_REC || flags == SO_BAKREC), args, cycle);
}
void ScummEngine_v90he::o90_priorityChainScript() {
int args[25];
int script, cycle;
byte flags;
getStackList(args, ARRAYSIZE(args));
cycle = pop();
script = pop();
flags = fetchScriptByte();
stopObjectCode();
runScript(script, (flags == SO_BAK || flags == SO_BAKREC), (flags == SO_REC || flags == SO_BAKREC), args, cycle);
}
void ScummEngine_v90he::o90_videoOps() {
// Uses Smacker video
int status = fetchScriptByte();
int subOp = status;
switch (subOp) {
case SO_LOAD: // 49
copyScriptString(_videoParams.filename, sizeof(_videoParams.filename));
_videoParams.status = status;
break;
case SO_SET_FLAGS: // 54
_videoParams.flags |= pop();
break;
case SO_INIT: // 57
memset(_videoParams.filename, 0, sizeof(_videoParams.filename));
_videoParams.status = 0;
_videoParams.flags = 0;
_videoParams.number = pop();
_videoParams.wizResNum = 0;
break;
case SO_IMAGE: // 63
_videoParams.wizResNum = pop();
if (_videoParams.wizResNum)
_videoParams.flags |= MoviePlayer::vfImageSurface;
break;
case SO_CLOSE: // 165
_videoParams.status = status;
break;
case SO_END: // 255
if (_videoParams.status == SO_LOAD) {
// Start video
if (_videoParams.flags == 0)
_videoParams.flags = MoviePlayer::vfDefault;
if (_videoParams.flags & MoviePlayer::vfImageSurface) {
VAR(VAR_OPERATION_FAILURE) = _moviePlay->load(convertFilePath(_videoParams.filename), _videoParams.flags, _videoParams.wizResNum);
} else {
VAR(VAR_OPERATION_FAILURE) = _moviePlay->load(convertFilePath(_videoParams.filename), _videoParams.flags);
}
} else if (_videoParams.status == SO_CLOSE) {
// Stop video
_moviePlay->close();
}
break;
default:
error("o90_videoOps: unhandled case %d", subOp);
}
}
void ScummEngine_v90he::o90_getVideoData() {
// Uses Smacker video
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_WIDTH: // 32
pop();
push(_moviePlay->getWidth());
break;
case SO_HEIGHT: // 33
pop();
push(_moviePlay->getHeight());
break;
case SO_COUNT: // 36
pop();
push(_moviePlay->getFrameCount());
break;
case SO_STATE: // 52
pop();
push(_moviePlay->getCurFrame());
break;
case SO_IMAGE: // 63
pop();
push(_moviePlay->getImageNum());
break;
case SO_NEW_GENERAL_PROPERTY: // 139
debug(0, "o90_getVideoData: checkType 107 stub (%d, %d)", pop(), pop());
push(0);
break;
default:
error("o90_getVideoData: unhandled case %d", subOp);
}
}
void ScummEngine_v90he::o90_floodFill() {
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_SET_FLAGS: // 54
_floodFillCommand.flags |= pop();
break;
case SO_INIT: // 57
_floodFillCommand.reset();
_floodFillCommand.box.left = 0;
_floodFillCommand.box.top = 0;
_floodFillCommand.box.right = 639;
_floodFillCommand.box.bottom = 479;
break;
case SO_AT: // 65
_floodFillCommand.y = pop();
_floodFillCommand.x = pop();
break;
case SO_COLOR: // 66
_floodFillCommand.color = pop();
break;
case SO_CLIPPED: // 67
_floodFillCommand.box.bottom = pop();
_floodFillCommand.box.right = pop();
_floodFillCommand.box.top = pop();
_floodFillCommand.box.left = pop();
break;
case SO_END: // 255
_wiz->pgFloodFillCmd(_floodFillCommand.x, _floodFillCommand.y, _floodFillCommand.color, &_floodFillCommand.box);
break;
default:
error("o90_floodFill: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_mod() {
int a = pop();
if (a == 0)
error("modulus by zero");
push(pop() % a);
}
void ScummEngine_v90he::o90_shl() {
int a = pop();
push(pop() << a);
}
void ScummEngine_v90he::o90_shr() {
int a = pop();
push(pop() >> a);
}
void ScummEngine_v90he::o90_xor() {
int a = pop();
push(pop() ^ a);
}
void ScummEngine_v90he::o90_findAllObjectsWithClassOf() {
int args[16];
int cond, num, cls, tmp;
bool b;
num = getStackList(args, ARRAYSIZE(args));
int room = pop();
int numObjs = 0;
if (room != _currentRoom)
error("o90_findAllObjectsWithClassOf: current room is not %d", room);
writeVar(0, 0);
defineArray(0, kDwordArray, 0, 0, 0, _numLocalObjects);
for (int i = 1; i < _numLocalObjects; i++) {
cond = 1;
tmp = num;
while (--tmp >= 0) {
cls = args[tmp];
b = getClass(_objs[i].obj_nr, cls);
if ((cls & 0x80 && !b) || (!(cls & 0x80) && b))
cond = 0;
}
if (cond) {
numObjs++;
writeArray(0, 0, numObjs, _objs[i].obj_nr);
}
}
writeArray(0, 0, 0, numObjs);
push(readVar(0));
}
int auxRectsOverlap(const Common::Rect *destRectPtr, const Common::Rect *sourceRectPtr) {
if (destRectPtr->left > sourceRectPtr->right) {
return 0;
}
if (destRectPtr->top > sourceRectPtr->bottom) {
return 0;
}
if (destRectPtr->right < sourceRectPtr->left) {
return 0;
}
if (destRectPtr->bottom < sourceRectPtr->top) {
return 0;
}
return 1;
}
void ScummEngine_v90he::o90_getOverlap() {
int firstCount, lastCount, checkType, firstRadius, ax, ay, bx, by;
int nVerts, index, lastRadius, distance;
Common::Point lastCenterPoint, firstCenterPoint;
Common::Rect firstRect, lastRect;
int firstList[32], lastList[32];
Common::Point polyPoints[16];
// Get the info
lastCount = getStackList(lastList, ARRAYSIZE(lastList));
firstCount = getStackList(firstList, ARRAYSIZE(firstList));
checkType = pop();
// Check the info
switch (checkType) {
default:
error("o90_getOverlap: Unknown overlap type %d", checkType);
break;
case OVERLAP_SPRITE_TO_SPRITE_PIXEL_PERFECT:
// Get the adjustments...
if (firstCount == 3) {
ax = firstList[1];
ay = firstList[2];
} else {
ax = 0;
ay = 0;
}
if (lastCount == 3) {
bx = lastList[1];
by = lastList[2];
} else {
bx = 0;
by = 0;
}
// Do the command.
push(_sprite->pixelPerfectSpriteCollisionCheck(firstList[0], ax, ay, lastList[0], bx, by));
break;
case OVERLAP_SPRITE_TO_SPRITE:
// Get the positions and check to see if either rect is invalid...
_sprite->getSpriteLogicalRect(firstList[0], &firstRect);
_sprite->getSpriteLogicalRect(lastList[0], &lastRect);
if (!_wiz->isRectValid(firstRect)) {
push(0);
break;
}
if (firstCount == 3)
_wiz->moveRect(&firstRect, firstList[1], firstList[2]);
if (lastCount == 3)
_wiz->moveRect(&lastRect, lastList[1], lastList[2]);
push(auxRectsOverlap(&firstRect, &lastRect));
break;
case OVERLAP_DRAW_POS_SPRITE_TO_SPRITE:
// Get the positions and check to see if either rect is invalid...
_sprite->getSpriteDrawRect(firstList[0], &firstRect);
_sprite->getSpriteDrawRect(lastList[0], &lastRect);
if (!_wiz->isRectValid(firstRect)) {
push(0);
break;
}
if (firstCount == 3)
_wiz->moveRect(&firstRect, firstList[1], firstList[2]);
if (lastCount == 3)
_wiz->moveRect(&lastRect, lastList[1], lastList[2]);
push(auxRectsOverlap(&firstRect, &lastRect));
break;
case OVERLAP_SPRITE_TO_RECT:
// Get the positions and check to see if either rect is invalid...
_sprite->getSpriteLogicalRect(firstList[0], &firstRect);
lastRect.left = lastList[0];
lastRect.top = lastList[1];
lastRect.right = lastList[2];
lastRect.bottom = lastList[3];
if (!_wiz->isRectValid(firstRect)) {
push(0);
break;
}
if (firstCount == 3)
_wiz->moveRect(&firstRect, firstList[1], firstList[2]);
push(auxRectsOverlap(&firstRect, &lastRect));
break;
case OVERLAP_DRAW_POS_SPRITE_TO_RECT:
// Get the positions and check to see if either rect is invalid...
_sprite->getSpriteDrawRect(firstList[0], &firstRect);
lastRect.left = lastList[0];
lastRect.top = lastList[1];
lastRect.right = lastList[2];
lastRect.bottom = lastList[3];
if (!_wiz->isRectValid(firstRect)) {
push(0);
break;
}
if (firstCount == 3)
_wiz->moveRect(&firstRect, firstList[1], firstList[2]);
push(auxRectsOverlap(&firstRect, &lastRect));
break;
case OVERLAP_POINT_TO_RECT:
firstCenterPoint.x = firstList[0];
firstCenterPoint.y = firstList[1];
lastRect.left = lastList[0];
lastRect.top = lastList[1];
lastRect.right = lastList[2];
lastRect.bottom = lastList[3];
push(_wiz->isPointInRect(&lastRect, &firstCenterPoint));
break;
case OVERLAP_POINT_TO_CIRCLE:
firstCenterPoint.x = firstList[0];
firstCenterPoint.y = firstList[1];
lastCenterPoint.x = lastList[0];
lastCenterPoint.y = lastList[1];
lastRadius = lastList[2];
distance = scummMathDist2D(
firstCenterPoint.x, firstCenterPoint.y, lastCenterPoint.x, lastCenterPoint.y);
push((distance <= lastRadius));
break;
case OVERLAP_RECT_TO_RECT:
firstRect.left = firstList[0];
firstRect.top = firstList[1];
firstRect.right = firstList[2];
firstRect.bottom = firstList[3];
lastRect.left = lastList[0];
lastRect.top = lastList[1];
lastRect.right = lastList[2];
lastRect.bottom = lastList[3];
push(auxRectsOverlap(&firstRect, &lastRect));
break;
case OVERLAP_CIRCLE_TO_CIRCLE:
firstCenterPoint.x = firstList[0];
firstCenterPoint.y = firstList[1];
firstRadius = firstList[2];
lastCenterPoint.x = lastList[0];
lastCenterPoint.y = lastList[1];
lastRadius = lastList[2];
distance = scummMathDist2D(
firstCenterPoint.x, firstCenterPoint.y, lastCenterPoint.x, lastCenterPoint.y);
push(distance < (firstRadius + lastRadius));
break;
case OVERLAP_POINT_N_SIDED_POLYGON:
firstCenterPoint.x = firstList[0];
firstCenterPoint.y = firstList[1];
nVerts = lastCount / 2;
if (nVerts) {
index = 0;
for (int i = 0; i < nVerts; i++) {
polyPoints[i].x = lastList[index++];
polyPoints[i].y = lastList[index++];
}
push(_wiz->polyIsPointInsidePoly(polyPoints, nVerts, &firstCenterPoint) ? 1 : 0);
} else {
push(0);
}
break;
}
}
void ScummEngine_v90he::o90_cond() {
int a = pop();
int b = pop();
int c = pop();
if (!c)
b = a;
push(b);
}
void ScummEngine_v90he::o90_dim2dim2Array() {
int data, acrossMin, acrossMax, downMin, downMax;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_BIT: // 2
data = kBitArray;
break;
case SO_NIBBLE: // 3
data = kNibbleArray;
break;
case SO_BYTE: // 4
data = kByteArray;
break;
case SO_INT: // 5
data = kIntArray;
break;
case SO_DWORD: // 6
data = kDwordArray;
break;
case SO_STRING: // 7
data = kStringArray;
break;
default:
error("o90_dim2dim2Array: default case %d", subOp);
}
if (pop() == 2) {
acrossMax = pop();
acrossMin = pop();
downMax = pop();
downMin = pop();
} else {
downMax = pop();
downMin = pop();
acrossMax = pop();
acrossMin = pop();
}
defineArray(fetchScriptWord(), data, downMin, downMax, acrossMin, acrossMax);
}
void ScummEngine_v90he::o90_redim2dimArray() {
int a, b, c, d;
d = pop();
c = pop();
b = pop();
a = pop();
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_BYTE: // 4
redimArray(fetchScriptWord(), a, b, c, d, kByteArray);
break;
case SO_INT: // 5
redimArray(fetchScriptWord(), a, b, c, d, kIntArray);
break;
case SO_DWORD: // 6
redimArray(fetchScriptWord(), a, b, c, d, kDwordArray);
break;
default:
error("o90_redim2dimArray: default type %d", subOp);
}
}
void ScummEngine_v90he::o90_getLinesIntersectionPoint() {
int x1, y1, x2, y2, x3, y3, x4, y4, x, y;
int dv, xVariable, yVariable, ta, tb, tc, td;
float ua, ub, oodv;
bool segAIsAPoint;
bool segBIsAPoint;
xVariable = fetchScriptWord();
yVariable = fetchScriptWord();
// Get the line segment coords off the stack...
y4 = pop();
x4 = pop();
y3 = pop();
x3 = pop();
y2 = pop();
x2 = pop();
y1 = pop();
x1 = pop();
// Check to see if both segments are points...
segAIsAPoint = ((x1 == x2) && (y1 == y2));
segBIsAPoint = ((x3 == x4) && (y3 == y4));
if (segAIsAPoint && segBIsAPoint) {
if ((x1 == x3) && (y1 == y3) && (x2 == x4) && (y2 == y4)) {
// The points are the same....
writeVar(xVariable, x1);
writeVar(yVariable, y1);
push(1);
return;
} else {
// No intersection...
writeVar(xVariable, 0);
writeVar(yVariable, 0);
push(0);
return;
}
} else {
// Check to see if we need to special case to point on a line...
if (segAIsAPoint) {
int dx, py;
dx = (x4 - x3);
if (dx != 0) {
float m = (float)(y4 - y3) / (float)dx;
py = (((float)(x1 - x3) * m) + 0.5) + y3;
if (y1 == py) {
writeVar(xVariable, x1);
writeVar(yVariable, y1);
push(1);
return;
}
} else {
if ((x3 == x1) && ((y3 <= y4) ? ((y1 >= y3) && (y1 <= y4)) : ((y1 >= y4) && (y1 <= y3)))) {
writeVar(xVariable, x1);
writeVar(yVariable, y1);
push(1);
return;
}
}
// There was no intersection...
writeVar(xVariable, 0);
writeVar(yVariable, 0);
push(0);
return;
} else if (segBIsAPoint) {
int dx, py;
dx = (x2 - x1);
if (dx != 0) {
float m = (float)(y2 - y1) / (float)dx;
py = (((float)(x3 - x1) * m) + 0.5) + y1;
if (y3 == py) {
writeVar(xVariable, x3);
writeVar(yVariable, y3);
push(1);
return;
}
} else {
if ((x3 == x1) && ((y1 <= y2) ? ((y3 >= y1) && (y3 <= y2)) : ((y3 >= y2) && (y3 <= y1)))) {
writeVar(xVariable, x3);
writeVar(yVariable, y3);
push(1);
return;
}
}
// There was no intersection...
writeVar(xVariable, 0);
writeVar(yVariable, 0);
push(0);
return;
}
}
// Do the intersection test...
dv = (((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1)));
ta = (y1 - y3);
tb = (x1 - x3);
tc = ((x4 - x3) * ta) - ((y4 - y3) * tb);
td = ((x2 - x1) * ta) - ((y2 - y1) * tb);
if (dv != 0) {
oodv = 1.0 / (float)dv;
ua = (float)tc * oodv;
ub = (float)td * oodv;
if ((ua >= 0) && (ub >= 0) && (ua <= 1.0) && (ub <= 1.0)) {
x = (int)(x1 + (0.5 + (ua * (x2 - x1))));
y = (int)(y1 + (0.5 + (ua * (y2 - y1))));
writeVar(xVariable, x);
writeVar(yVariable, y);
push(1);
return;
}
} else {
if (tc == 0) {
writeVar(xVariable, ((x3 + x4) / 2));
writeVar(yVariable, ((y3 + y4) / 2));
push(2);
return;
}
}
// No intersection...
writeVar(xVariable, 0);
writeVar(yVariable, 0);
push(0);
}
void ScummEngine_v90he::getArrayDim(int array, int *downMin, int *downMax, int *acrossMin, int *acrossMax) {
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
assert(ah);
if (downMin && *downMin == -1) {
*downMin = FROM_LE_32(ah->downMin);
}
if (downMax && *downMax == -1) {
*downMax = FROM_LE_32(ah->downMax);
}
if (acrossMin && *acrossMin == -1) {
*acrossMin = FROM_LE_32(ah->acrossMin);
}
if (acrossMax && *acrossMax == -1) {
*acrossMax = FROM_LE_32(ah->acrossMax);
}
}
static int sortArrayOffset;
static int compareByteArray(const void *a, const void *b) {
int va = *((const uint8 *)a + sortArrayOffset);
int vb = *((const uint8 *)b + sortArrayOffset);
return va - vb;
}
static int compareByteArrayReverse(const void *a, const void *b) {
int va = *((const uint8 *)a + sortArrayOffset);
int vb = *((const uint8 *)b + sortArrayOffset);
return vb - va;
}
static int compareIntArray(const void *a, const void *b) {
int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2);
int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2);
return va - vb;
}
static int compareIntArrayReverse(const void *a, const void *b) {
int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2);
int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2);
return vb - va;
}
static int compareDwordArray(const void *a, const void *b) {
int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4);
int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4);
return va - vb;
}
static int compareDwordArrayReverse(const void *a, const void *b) {
int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4);
int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4);
return vb - va;
}
/**
* Sort a row range in a two-dimensional array by the value in a given column.
*
* We sort the data in the row range [downMin..downMax], according to the value
* in column acrossMin == acrossMax.
*/
void ScummEngine_v90he::sortArray(int array, int downMin, int downMax, int acrossMin, int acrossMax, int sortOrder) {
debug(9, "sortArray(%d, [%d,%d,%d,%d], %d)", array, downMin, downMax, acrossMin, acrossMax, sortOrder);
assert(acrossMin == acrossMax);
checkArrayLimits(array, downMin, downMax, acrossMin, acrossMax);
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
assert(ah);
const int num = downMax - downMin + 1; // number of rows to sort
const int pitch = FROM_LE_32(ah->acrossMax) - FROM_LE_32(ah->acrossMin) + 1; // length of a row = number of columns in it
const int offset = pitch * (downMin - FROM_LE_32(ah->downMin)); // memory offset to the first row to be sorted
sortArrayOffset = acrossMin - FROM_LE_32(ah->acrossMin); // offset to the column by which we sort
// Now we just have to invoke qsort on the appropriate row range. We
// need to pass sortArrayOffset as an implicit parameter to the
// comparison functions, which makes it necessary to use a global
// (albeit local to this file) variable.
// This could be avoided by using qsort_r or a self-written portable
// analog (this function passes an additional, user determined
// parameter to the comparison function).
// Another idea would be to use Common::sort, but that only is
// suitable if you sort objects of fixed size, which must be known
// during compilation time; clearly this not the case here.
switch (FROM_LE_32(ah->type)) {
case kByteArray:
case kStringArray:
if (sortOrder <= 0) {
qsort(ah->data + offset, num, pitch, compareByteArray);
} else {
qsort(ah->data + offset, num, pitch, compareByteArrayReverse);
}
break;
case kIntArray:
if (sortOrder <= 0) {
qsort(ah->data + offset * 2, num, pitch * 2, compareIntArray);
} else {
qsort(ah->data + offset * 2, num, pitch * 2, compareIntArrayReverse);
}
break;
case kDwordArray:
if (sortOrder <= 0) {
qsort(ah->data + offset * 4, num, pitch * 4, compareDwordArray);
} else {
qsort(ah->data + offset * 4, num, pitch * 4, compareDwordArrayReverse);
}
break;
default:
error("Invalid array type %d", FROM_LE_32(ah->type));
}
}
void ScummEngine_v90he::o90_sortArray() {
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_SORT:
case ScummEngine_v100he::SO_SORT: // HE100
{
int array = fetchScriptWord();
int sortOrder = pop();
int acrossMax = pop();
int acrossMin = pop();
int downMax = pop();
int downMin = pop();
getArrayDim(array, &downMin, &downMax, &acrossMin, &acrossMax);
sortArray(array, downMin, downMax, acrossMin, acrossMax, sortOrder);
}
break;
default:
error("o90_sortArray: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_getObjectData() {
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_WIDTH:
if (_heObjectNum == -1)
push(0);
else
push(_objs[_heObjectNum].width);
break;
case SO_HEIGHT:
if (_heObjectNum == -1)
push(0);
else
push(_objs[_heObjectNum].height);
break;
case SO_COUNT:
if (_heObjectNum == -1)
push(0);
else
push(getObjectImageCount(_heObject));
break;
case SO_DRAW_XPOS:
if (_heObjectNum == -1)
push(0);
else
push(_objs[_heObjectNum].x_pos);
break;
case SO_DRAW_YPOS:
if (_heObjectNum == -1)
push(0);
else
push(_objs[_heObjectNum].y_pos);
break;
case SO_STATE:
push(getState(_heObject));
break;
case SO_INIT:
_heObject = pop();
_heObjectNum = getObjectIndex(_heObject);
break;
case SO_NEW_GENERAL_PROPERTY:
// Dummy case, as in the original
pop();
push(0);
break;
default:
error("o90_getObjectData: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_getPaletteData() {
int c, d, e;
int r, g, b;
int palSlot, color;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_FIND:
e = pop();
d = pop();
palSlot = pop();
b = pop();
g = pop();
r = pop();
push(getHEPaletteSimilarColor(palSlot, r, g, d, e));
break;
case SO_STATE:
c = pop();
b = pop();
palSlot = pop();
push(getHEPaletteColorComponent(palSlot, b, c));
break;
case SO_COLOR:
color = pop();
palSlot = pop();
push(getHEPaletteColor(palSlot, color));
break;
case SO_CHANNEL:
c = pop();
b = pop();
if (_game.features & GF_16BIT_COLOR)
push(getHEPalette16BitColorComponent(b, c));
else
push(getHEPaletteColorComponent(1, b, c));
break;
case SO_NEW:
b = pop();
b = MAX(0, b);
b = MIN(b, 255);
g = pop();
g = MAX(0, g);
g = MIN(g, 255);
r = pop();
r = MAX(0, r);
r = MIN(r, 255);
if (_game.features & GF_16BIT_COLOR) {
push(get16BitColor(r, g, b));
} else {
push(getHEPaletteSimilarColor(1, r, g, 10, 245));
}
break;
default:
error("o90_getPaletteData: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_paletteOps() {
int a, b, c, d, e;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_INIT: // 57
_hePaletteNum = pop();
break;
case SO_IMAGE: // 63
b = pop();
a = pop();
if (_hePaletteNum != 0) {
setHEPaletteFromImage(_hePaletteNum, a, b);
}
break;
case SO_COLOR: // 66
e = pop();
d = pop();
c = pop();
b = pop();
a = pop();
if (_hePaletteNum != 0) {
for (; a <= b; ++a) {
setHEPaletteColor(_hePaletteNum, a, c, d, e);
}
}
break;
case SO_TO: // 70
c = pop();
b = pop();
a = pop();
if (_hePaletteNum != 0) {
for (; a <= b; ++a) {
copyHEPaletteColor(_hePaletteNum, a, c);
}
}
break;
case SO_COSTUME: // 76, HE99+
a = pop();
if (_hePaletteNum != 0) {
setHEPaletteFromCostume(_hePaletteNum, a);
}
break;
case SO_PALETTE: // 86
a = pop();
if (_hePaletteNum != 0) {
copyHEPalette(_hePaletteNum, a);
}
break;
case SO_ROOM_PALETTE: // 175
b = pop();
a = pop();
if (_hePaletteNum != 0) {
setHEPaletteFromRoom(_hePaletteNum, a, b);
}
break;
case SO_NEW: // 217
if (_hePaletteNum != 0) {
restoreHEPalette(_hePaletteNum);
}
break;
case SO_END: // 255
_hePaletteNum = 0;
break;
default:
error("o90_paletteOps: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_fontEnum() {
byte resultString[80];
byte subOp = fetchScriptByte();
switch (subOp) {
case ScummEngine_v100he::SO_INIT: // HE100
case SO_INIT:
push(((ScummEngine_v99he *)this)->_heFont->enumInit());
break;
case ScummEngine_v100he::SO_PROPERTY: // HE100
case SO_PROPERTY:
switch (pop()) {
case 1: // FONT_ENUM_GET
{
_scummVars[0] = 0;
const char *fontName = ((ScummEngine_v99he *)this)->_heFont->enumGet(pop());
if (!fontName) {
fontName = "";
}
int len = strlen(fontName);
byte *ptr = defineArray(0, kStringArray, 0, 0, 0, len);
if (ptr) {
memcpy(ptr, fontName, len);
}
push(_scummVars[0]);
break;
}
case 2: // FONT_ENUM_FIND
copyScriptString(resultString, sizeof(resultString));
push(((ScummEngine_v99he *)this)->_heFont->enumFind((char *)resultString));
break;
}
break;
default:
error("o90_fontEnum: Unknown case %d", subOp);
}
}
void ScummEngine_v90he::o90_getActorAnimProgress() {
Actor *a = derefActor(pop(), "o90_getActorAnimProgress");
push(a->getAnimProgress());
}
void ScummEngine_v90he::o90_kernelGetFunctions() {
int args[29];
int num, tmp;
Actor *a;
num = getStackList(args, ARRAYSIZE(args));
switch (args[0]) {
case 1001:
push(scummMathSin(args[1]));
break;
case 1002:
push(scummMathCos(args[1]));
break;
case 1003:
push(scummMathSqrt(args[1]));
break;
case 1004:
push(scummMathDist2D(args[1], args[2], args[3], args[4]));
break;
case 1005:
push(scummMathAngleFromDelta(args[1], args[2]));
break;
case 1006:
push(scummMathAngleOfLineSegment(args[1], args[2], args[3], args[4]));
break;
case 1969:
a = derefActor(args[1], "o90_kernelGetFunctions: 1969");
tmp = a->_heCondMask;
tmp &= 0x7FFF0000;
push(tmp);
break;
case 2001:
if (_logicHE)
push(_logicHE->dispatch(args[1], num - 2, (int32 *)&args[2]));
else
push(0);
break;
default:
error("o90_kernelGetFunctions: default case %d", args[0]);
}
}
void ScummEngine_v90he::o90_kernelSetFunctions() {
int args[29];
int num, tmp;
ActorHE *a;
num = getStackList(args, ARRAYSIZE(args));
switch (args[0]) {
case 20:
a = (ActorHE *)derefActor(args[1], "o90_kernelSetFunctions: 20");
heQueueEraseAuxActor(a);
break;
case 21:
_skipDrawObject = 1;
break;
case 22:
_skipDrawObject = 0;
break;
case 23:
clearCharsetMask();
_fullRedraw = true;
break;
case 24:
_disableActorDrawingFlag = 1;
redrawAllActors();
break;
case 25:
_disableActorDrawingFlag = 0;
redrawAllActors();
break;
case 27:
// Used in readdemo
break;
case 32:
// Used by MegaCat Studios for rich presence in Backyard games
break;
case 42:
_wiz->_useWizClipRect = true;
_wiz->_wizClipRect.left = args[1];
_wiz->_wizClipRect.top = args[2];
_wiz->_wizClipRect.right = args[3];
_wiz->_wizClipRect.bottom = args[4];
break;
case 43:
_wiz->_useWizClipRect = false;
break;
case 714:
setResourceOffHeap(args[1], args[2], args[3]);
break;
case 1492:
// Remote start script function
break;
case 1969:
a = (ActorHE *)derefActor(args[1], "o90_kernelSetFunctions: 1969");
tmp = a->_heCondMask;
tmp ^= args[2];
tmp &= 0x7FFF0000;
a->_heCondMask ^= tmp;
break;
case 2001:
if (_logicHE)
_logicHE->dispatch(args[1], num - 2, (int32 *)&args[2]);
break;
case 201102: // Used in puttzoo iOS
case 20111014: // Used in spyfox iOS
break;
default:
error("o90_kernelSetFunctions: default case %d (param count %d)", args[0], num);
}
}
} // End of namespace Scumm
#endif // ENABLE_HE