/* 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 . * */ #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