diff options
Diffstat (limited to 'engines/scumm/object.cpp')
-rw-r--r-- | engines/scumm/object.cpp | 1779 |
1 files changed, 1779 insertions, 0 deletions
diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp new file mode 100644 index 0000000000..0472edcc15 --- /dev/null +++ b/engines/scumm/object.cpp @@ -0,0 +1,1779 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "scumm/scumm.h" +#include "scumm/actor.h" +#include "scumm/bomp.h" +#include "scumm/intern.h" +#ifndef DISABLE_HE +#include "scumm/intern_he.h" +#endif +#include "scumm/object.h" +#include "scumm/resource.h" +#include "scumm/usage_bits.h" +#include "scumm/util.h" + +namespace Scumm { + +#if !defined(__GNUC__) + #pragma START_PACK_STRUCTS +#endif + +struct BompHeader { /* Bomp header */ + union { + struct { + uint16 unk; + uint16 width, height; + } GCC_PACK old; + + struct { + uint32 width, height; + } GCC_PACK v8; + } GCC_PACK; +} GCC_PACK; + +#if !defined(__GNUC__) + #pragma END_PACK_STRUCTS +#endif + + +bool ScummEngine::getClass(int obj, int cls) const { + checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in getClass"); + cls &= 0x7F; + checkRange(32, 1, cls, "Class %d out of range in getClass"); + + if (_features & GF_SMALL_HEADER) { + // Translate the new (V5) object classes to the old classes + // (for those which differ). + switch (cls) { + case kObjectClassUntouchable: + cls = 24; + break; + case kObjectClassPlayer: + cls = 23; + break; + case kObjectClassXFlip: + cls = 19; + break; + case kObjectClassYFlip: + cls = 18; + break; + } + } + + return (_classData[obj] & (1 << (cls - 1))) != 0; +} + +void ScummEngine::putClass(int obj, int cls, bool set) { + checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in putClass"); + cls &= 0x7F; + checkRange(32, 1, cls, "Class %d out of range in putClass"); + + if (_features & GF_SMALL_HEADER) { + // Translate the new (V5) object classes to the old classes + // (for those which differ). + switch (cls) { + case kObjectClassUntouchable: + cls = 24; + break; + case kObjectClassPlayer: + cls = 23; + break; + case kObjectClassXFlip: + cls = 19; + break; + case kObjectClassYFlip: + cls = 18; + break; + } + } + + if (set) + _classData[obj] |= (1 << (cls - 1)); + else + _classData[obj] &= ~(1 << (cls - 1)); + + if (_version <= 4 && obj >= 1 && obj < _numActors) { + _actors[obj].classChanged(cls, set); + } +} + +int ScummEngine::getOwner(int obj) const { + checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in getOwner"); + return _objectOwnerTable[obj]; +} + +void ScummEngine::putOwner(int obj, int owner) { + checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in putOwner"); + checkRange(0xFF, 0, owner, "Owner %d out of range in putOwner"); + _objectOwnerTable[obj] = owner; +} + +int ScummEngine::getState(int obj) { + checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in getState"); + + if (!_copyProtection) { + // I knew LucasArts sold cracked copies of the original Maniac Mansion, + // at least as part of Day of the Tentacle. Apparently they also sold + // cracked versions of the enhanced version. At least in Germany. + // + // This will keep the security door open at all times. I can only + // assume that 182 and 193 each correspond to one particular side of + // the it. Fortunately it does not prevent frustrated players from + // blowing up the mansion, should they feel the urge to. + + if (_gameId == GID_MANIAC && (obj == 182 || obj == 193)) + _objectStateTable[obj] |= 0x08; + } + + return _objectStateTable[obj]; +} + +void ScummEngine::putState(int obj, int state) { + checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in putState"); + checkRange(0xFF, 0, state, "State %d out of range in putState"); + _objectStateTable[obj] = state; +} + +int ScummEngine::getObjectRoom(int obj) const { + checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in getObjectRoom"); + return _objectRoomTable[obj]; +} + +int ScummEngine::getObjectIndex(int object) const { + int i; + + if (object < 1) + return -1; + + for (i = (_numLocalObjects-1); i > 0; i--) { + if (_objs[i].obj_nr == object) + return i; + } + return -1; +} + +int ScummEngine::whereIsObject(int object) const { + int i; + + if (object >= _numGlobalObjects) + return WIO_NOT_FOUND; + + if (object < 1) + return WIO_NOT_FOUND; + + if (_objectOwnerTable[object] != OF_OWNER_ROOM) { + for (i = 0; i < _numInventory; i++) + if (_inventory[i] == object) + return WIO_INVENTORY; + return WIO_NOT_FOUND; + } + + for (i = (_numLocalObjects-1); i > 0; i--) + if (_objs[i].obj_nr == object) { + if (_objs[i].fl_object_index) + return WIO_FLOBJECT; + return WIO_ROOM; + } + + return WIO_NOT_FOUND; +} + +int ScummEngine::getObjectOrActorXY(int object, int &x, int &y) { + if (object < _numActors) { + Actor *act = derefActorSafe(object, "getObjectOrActorXY"); + if (act) + return act->getActorXYPos(x, y); + else + return -1; + } + + switch (whereIsObject(object)) { + case WIO_NOT_FOUND: + return -1; + case WIO_INVENTORY: + if (_objectOwnerTable[object] < _numActors) + return derefActor(_objectOwnerTable[object], "getObjectOrActorXY(2)")->getActorXYPos(x, y); + else + return -1; + } + getObjectXYPos(object, x, y); + return 0; +} + +/** + * Return the position of an object. + * Returns X, Y and direction in angles + */ +void ScummEngine::getObjectXYPos(int object, int &x, int &y, int &dir) { + int idx = getObjectIndex(object); + assert(idx >= 0); + ObjectData &od = _objs[idx]; + int state; + const byte *ptr; + const ImageHeader *imhd; + + if (_version >= 6) { + state = getState(object) - 1; + if (state < 0) + state = 0; + + ptr = getOBIMFromObjectData(od); + if (!ptr) { + // FIXME: We used to assert here, but it seems that in the nexus + // in The Dig, this can happen, at least with old savegames, and + // it's safe to continue... + debug(0, "getObjectXYPos: Can't find object %d", object); + return; + } + imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), ptr); + assert(imhd); + if (_version == 8) { + switch (FROM_LE_32(imhd->v8.version)) { + case 800: + x = od.x_pos + (int32)READ_LE_UINT32((const byte *)imhd + 8 * state + 0x44); + y = od.y_pos + (int32)READ_LE_UINT32((const byte *)imhd + 8 * state + 0x48); + break; + case 801: + x = od.x_pos + (int32)READ_LE_UINT32(&imhd->v8.hotspot[state].x); + y = od.y_pos + (int32)READ_LE_UINT32(&imhd->v8.hotspot[state].y); + break; + default: + error("Unsupported image header version %d\n", FROM_LE_32(imhd->v8.version)); + } + } else if (_version == 7) { + x = od.x_pos + (int16)READ_LE_UINT16(&imhd->v7.hotspot[state].x); + y = od.y_pos + (int16)READ_LE_UINT16(&imhd->v7.hotspot[state].y); + } else { + x = od.x_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].x); + y = od.y_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].y); + } + } else { + x = od.walk_x; + y = od.walk_y; + } + if (_version == 8) + dir = fromSimpleDir(1, od.actordir); + else + dir = oldDirToNewDir(od.actordir & 3); +} + +static int getDist(int x, int y, int x2, int y2) { + int a = ABS(y - y2); + int b = ABS(x - x2); + return MAX(a, b); +} + +int ScummEngine::getObjActToObjActDist(int a, int b) { + int x, y, x2, y2; + Actor *acta = NULL; + Actor *actb = NULL; + + if (a < _numActors) + acta = derefActorSafe(a, "getObjActToObjActDist"); + + if (b < _numActors) + actb = derefActorSafe(b, "getObjActToObjActDist(2)"); + + if (acta && actb && acta->getRoom() == actb->getRoom() && acta->getRoom() && !acta->isInCurrentRoom()) + return 0; + + if (getObjectOrActorXY(a, x, y) == -1) + return 0xFF; + + if (getObjectOrActorXY(b, x2, y2) == -1) + return 0xFF; + + // Perform adjustXYToBeInBox() *only* if the first item is an + // actor and the second is an object. This used to not check + // whether the second item is a non-actor, which caused bug + // #853874). + if (acta && !actb) { + AdjustBoxResult r = acta->adjustXYToBeInBox(x2, y2); + x2 = r.x; + y2 = r.y; + } + + // Now compute the distance between the two points + if (_version <= 2) { + // For V1/V2 games, distances are measured in the original "character" + // based coordinate system, instead of pixels. Otherwise various scripts + // will break. See bugs #853874, #774529 + x /= 8; + y /= 2; + x2 /= 8; + y2 /= 2; + } + + return getDist(x, y, x2, y2); +} + +int ScummEngine::findObject(int x, int y) { + int i, b; + byte a; + const int mask = (_version <= 2) ? 0x8 : 0xF; + + for (i = 1; i < _numLocalObjects; i++) { + if ((_objs[i].obj_nr < 1) || getClass(_objs[i].obj_nr, kObjectClassUntouchable)) + continue; + + if (_platform == Common::kPlatformC64 && _gameId == GID_MANIAC) { + if (_objs[i].flags == 0 && _objs[i].state & 0x2) + continue; + } else { + if (_version <= 2 && _objs[i].state & 0x2) + continue; + } + + b = i; + do { + a = _objs[b].parentstate; + b = _objs[b].parent; + if (b == 0) { +#ifndef DISABLE_HE + if (_heversion >= 70) { + if (((ScummEngine_v70he *)this)->_wiz->polygonHit(_objs[i].obj_nr, x, y)) + return _objs[i].obj_nr; + } +#endif + if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x && + _objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y) + return _objs[i].obj_nr; + break; + } + } while ((_objs[b].state & mask) == a); + } + + return 0; +} + +void ScummEngine::drawRoomObject(int i, int arg) { + ObjectData *od; + byte a; + const int mask = (_version <= 2) ? 0x8 : 0xF; + + od = &_objs[i]; + if ((i < 1) || (od->obj_nr < 1) || !od->state) + return; + + do { + a = od->parentstate; + if (!od->parent) { + if (_version <= 6 || od->fl_object_index == 0) + drawObject(i, arg); + break; + } + od = &_objs[od->parent]; + } while ((od->state & mask) == a); +} + +void ScummEngine::drawRoomObjects(int arg) { + int i; + const int mask = (_version <= 2) ? 0x8 : 0xF; + + if (_heversion >= 60) { + // In HE games, normal objects are drawn, followed by FlObjects. + for (i = (_numLocalObjects-1); i > 0; i--) { + if (_objs[i].obj_nr > 0 && (_objs[i].state & mask) && _objs[i].fl_object_index == 0) + drawRoomObject(i, arg); + } + for (i = (_numLocalObjects-1); i > 0; i--) { + if (_objs[i].obj_nr > 0 && (_objs[i].state & mask) && _objs[i].fl_object_index != 0) + drawRoomObject(i, arg); + } + } else if (_gameId == GID_SAMNMAX) { + // In Sam & Max, objects are drawn in reverse order. + for (i = 1; i < _numLocalObjects; i++) + if (_objs[i].obj_nr > 0) + drawRoomObject(i, arg); + } else { + for (i = (_numLocalObjects-1); i > 0; i--) + if (_objs[i].obj_nr > 0 && (_objs[i].state & mask)) { + drawRoomObject(i, arg); + } + } +} + +static const uint32 IMxx_tags[] = { + MKID('IM00'), + MKID('IM01'), + MKID('IM02'), + MKID('IM03'), + MKID('IM04'), + MKID('IM05'), + MKID('IM06'), + MKID('IM07'), + MKID('IM08'), + MKID('IM09'), + MKID('IM0A'), + MKID('IM0B'), + MKID('IM0C'), + MKID('IM0D'), + MKID('IM0E'), + MKID('IM0F'), + MKID('IM10') +}; + +void ScummEngine::drawObject(int obj, int arg) { + if (_skipDrawObject) + return; + + ObjectData &od = _objs[obj]; + int height, width; + const byte *ptr; + int x, a, numstrip; + int tmp; + + if (_bgNeedsRedraw) + arg = 0; + + if (od.obj_nr == 0) + return; + + checkRange(_numGlobalObjects - 1, 0, od.obj_nr, "Object %d out of range in drawObject"); + + const int xpos = od.x_pos / 8; + const int ypos = od.y_pos; + + width = od.width / 8; + height = od.height &= 0xFFFFFFF8; // Mask out last 3 bits + + // Short circuit for objects which aren't visible at all. + if (width == 0 || xpos > _screenEndStrip || xpos + width < _screenStartStrip) + return; + + ptr = getOBIMFromObjectData(od); + + if (_features & GF_OLD_BUNDLE) + ptr += 0; + else + ptr = getObjectImage(ptr, getState(od.obj_nr)); + + if (!ptr) + return; + + x = 0xFFFF; + + for (a = numstrip = 0; a < width; a++) { + tmp = xpos + a; + if (tmp < _screenStartStrip || _screenEndStrip < tmp) + continue; + if (arg > 0 && _screenStartStrip + arg <= tmp) + continue; + if (arg < 0 && tmp <= _screenEndStrip + arg) + continue; + setGfxUsageBit(tmp, USAGE_BIT_DIRTY); + if (tmp < x) + x = tmp; + numstrip++; + } + + if (numstrip != 0) { + byte flags = od.flags | Gdi::dbObjectMode; + + // Sam & Max needs this to fix object-layering problems with + // the inventory and conversation icons. + if ((_gameId == GID_SAMNMAX && getClass(od.obj_nr, kObjectClassIgnoreBoxes)) || + (_gameId == GID_FT && getClass(od.obj_nr, kObjectClassPlayer))) + flags |= Gdi::dbDrawMaskOnAll; + + if (_heversion >= 70 && findResource(MKID('SMAP'), ptr) == NULL) + gdi.drawBMAPObject(ptr, &virtscr[0], obj, od.x_pos, od.y_pos, od.width, od.height); + else + gdi.drawBitmap(ptr, &virtscr[0], x, ypos, width * 8, height, x - xpos, numstrip, flags); + } +} + +void ScummEngine::clearRoomObjects() { + int i; + + if (_features & GF_SMALL_HEADER) { + for (i = 0; i < _numLocalObjects; i++) { + _objs[i].obj_nr = 0; + } + } else { + storeFlObject(-1); + + for (i = 0; i < _numLocalObjects; i++) { + if (_objs[i].obj_nr < 1) // Optimise codepath + continue; + + // Nuke all non-flObjects (flObjects are nuked in script.cpp) + if (_objs[i].fl_object_index == 0) { + _objs[i].obj_nr = 0; + } else { + // Nuke all unlocked flObjects + if (!res.isLocked(rtFlObject, _objs[i].fl_object_index)) { + res.nukeResource(rtFlObject, _objs[i].fl_object_index); + _objs[i].obj_nr = 0; + _objs[i].fl_object_index = 0; + } else if (_heversion >= 70) { + storeFlObject(i); + _objs[i].obj_nr = 0; + _objs[i].fl_object_index = 0; + } + } + } + } +} + +void ScummEngine::storeFlObject(int slot) { + if (slot == -1) { + _numStoredFlObjects = 0; + } else { + memcpy(&_storedFlObjects[_numStoredFlObjects], &_objs[slot], sizeof(_objs[slot])); + _numStoredFlObjects++; + if (_numStoredFlObjects > 100) + error("Too many flobjects saved on room transition."); + } +} + +void ScummEngine::restoreFlObjects() { + if (!_numStoredFlObjects) + return; + + int i, slot; + + for (i = 0; i < _numStoredFlObjects; i++) { + slot = findLocalObjectSlot(); + memcpy(&_objs[slot], &_storedFlObjects[i], sizeof(_objs[slot])); + } + + _numStoredFlObjects = 0; +} + +void ScummEngine::loadRoomObjects() { + int i, j; + ObjectData *od; + const byte *ptr; + uint16 obim_id; + const byte *room, *searchptr, *rootptr; + const CodeHeader *cdhd; + + CHECK_HEAP + room = getResourceAddress(rtRoom, _roomResource); + + if (_numObjectsInRoom == 0) + return; + + if (_numObjectsInRoom > _numLocalObjects) + error("More than %d objects in room %d", _numLocalObjects, _roomResource); + + if (_version == 8) + searchptr = rootptr = getResourceAddress(rtRoomScripts, _roomResource); + else + searchptr = rootptr = room; + assert(searchptr); + + // Load in new room objects + ResourceIterator obcds(searchptr, false); + for (i = 0; i < _numObjectsInRoom; i++) { + od = &_objs[findLocalObjectSlot()]; + + ptr = obcds.findNext(MKID('OBCD')); + if (ptr == NULL) + error("Room %d missing object code block(s)", _roomResource); + + od->OBCDoffset = ptr - rootptr; + cdhd = (const CodeHeader *)findResourceData(MKID('CDHD'), ptr); + + if (_version >= 7) + od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id)); + else if (_version == 6) + od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id)); + else + od->obj_nr = READ_LE_UINT16(&(cdhd->v5.obj_id)); + + if (_dumpScripts) { + char buf[32]; + sprintf(buf, "roomobj-%d-", _roomResource); + ptr = findResource(MKID('VERB'), ptr); + dumpResource(buf, od->obj_nr, ptr); + } + + } + + searchptr = room; + ResourceIterator obims(room, false); + for (i = 0; i < _numObjectsInRoom; i++) { + ptr = obims.findNext(MKID('OBIM')); + if (ptr == NULL) + error("Room %d missing image blocks(s)", _roomResource); + + obim_id = getObjectIdFromOBIM(ptr); + + for (j = 1; j < _numLocalObjects; j++) { + if (_objs[j].obj_nr == obim_id) + _objs[j].OBIMoffset = ptr - room; + } + } + + for (i = 1; i < _numLocalObjects; i++) { + if (_objs[i].obj_nr && !_objs[i].fl_object_index) + setupRoomObject(&_objs[i], room); + } + + CHECK_HEAP +} + +void ScummEngine_v3old::loadRoomObjects() { + int i; + ObjectData *od; + const byte *room, *ptr; + + CHECK_HEAP + room = getResourceAddress(rtRoom, _roomResource); + + if (_numObjectsInRoom == 0) + return; + + if (_numObjectsInRoom > _numLocalObjects) + error("More than %d objects in room %d", _numLocalObjects, _roomResource); + + if (_version <= 2) + ptr = room + 28; + else + ptr = room + 29; + + for (i = 0; i < _numObjectsInRoom; i++) { + od = &_objs[findLocalObjectSlot()]; + + od->OBIMoffset = READ_LE_UINT16(ptr); + od->OBCDoffset = READ_LE_UINT16(ptr + 2 * _numObjectsInRoom); + setupRoomObject(od, room); + + ptr += 2; + + if (_dumpScripts) { + char buf[32]; + sprintf(buf, "roomobj-%d-", _roomResource); + dumpResource(buf, od->obj_nr, room + od->OBCDoffset); + } + } + + CHECK_HEAP +} + +void ScummEngine_v4::loadRoomObjects() { + int i, j; + ObjectData *od; + const byte *ptr; + uint16 obim_id; + const byte *room; + + CHECK_HEAP + room = getResourceAddress(rtRoom, _roomResource); + + if (_numObjectsInRoom == 0) + return; + + if (_numObjectsInRoom > _numLocalObjects) + error("More than %d objects in room %d", _numLocalObjects, _roomResource); + + ResourceIterator obcds(room, true); + for (i = 0; i < _numObjectsInRoom; i++) { + od = &_objs[findLocalObjectSlot()]; + + ptr = obcds.findNext(MKID('OBCD')); + if (ptr == NULL) + error("Room %d missing object code block(s)", _roomResource); + + od->OBCDoffset = ptr - room; + od->obj_nr = READ_LE_UINT16(ptr + 6); + if (_dumpScripts) { + char buf[32]; + sprintf(buf, "roomobj-%d-", _roomResource); + dumpResource(buf, od->obj_nr, ptr); + } + } + + ResourceIterator obims(room, true); + for (i = 0; i < _numObjectsInRoom; i++) { + ptr = obims.findNext(MKID('OBIM')); + if (ptr == NULL) + error("Room %d missing image blocks(s)", _roomResource); + + obim_id = READ_LE_UINT16(ptr + 6); + + for (j = 1; j < _numLocalObjects; j++) { + if (_objs[j].obj_nr == obim_id) + _objs[j].OBIMoffset = ptr - room; + } + } + + for (i = 1; i < _numLocalObjects; i++) { + if (_objs[i].obj_nr && !_objs[i].fl_object_index) { + setupRoomObject(&_objs[i], room); + } + } + + CHECK_HEAP +} + +void ScummEngine_c64::setupRoomObject(ObjectData *od, const byte *room, const byte *searchptr) { + assert(room); + const byte *ptr = room + od->OBCDoffset; + ptr -= 2; + + od->obj_nr = *(ptr + 6); + od->flags = *(ptr + 7); + + od->x_pos = *(ptr + 8) * 8; + od->y_pos = ((*(ptr + 9)) & 0x7F) * 8; + + od->parentstate = (*(ptr + 9) & 0x80) ? 1 : 0; + od->parentstate *= 8; + + od->width = *(ptr + 10) * 8; + + od->parent = *(ptr + 11); + + od->walk_x = *(ptr + 12) * 8; + od->walk_y = (*(ptr + 13) & 0x1f) * 8; + od->actordir = (*(ptr + 14)) & 7; + od->height = *(ptr + 14) & 0xf8; +} + +void ScummEngine_v4::setupRoomObject(ObjectData *od, const byte *room, const byte *searchptr) { + assert(room); + const byte *ptr = room + od->OBCDoffset; + + if (_features & GF_OLD_BUNDLE) + ptr -= 2; + + od->obj_nr = READ_LE_UINT16(ptr + 6); + + od->x_pos = *(ptr + 9) * 8; + od->y_pos = ((*(ptr + 10)) & 0x7F) * 8; + + od->parentstate = (*(ptr + 10) & 0x80) ? 1 : 0; + if (_version <= 2) + od->parentstate *= 8; + + od->width = *(ptr + 11) * 8; + + od->parent = *(ptr + 12); + + if (_version <= 2) { + od->walk_x = *(ptr + 13) * 8; + od->walk_y = (*(ptr + 14) & 0x1f) * 8; + od->actordir = (*(ptr + 15)) & 7; + od->height = *(ptr + 15) & 0xf8; + } else { + od->walk_x = READ_LE_UINT16(ptr + 13); + od->walk_y = READ_LE_UINT16(ptr + 15); + od->actordir = (*(ptr + 17)) & 7; + od->height = *(ptr + 17) & 0xf8; + } +} + +void ScummEngine::setupRoomObject(ObjectData *od, const byte *room, const byte *searchptr) { + const CodeHeader *cdhd = NULL; + const ImageHeader *imhd = NULL; + + assert(room); + + if (searchptr == NULL) { + if (_version == 8) + searchptr = getResourceAddress(rtRoomScripts, _roomResource); + else + searchptr = room; + } + + cdhd = (const CodeHeader *)findResourceData(MKID('CDHD'), searchptr + od->OBCDoffset); + if (cdhd == NULL) + error("Room %d missing CDHD blocks(s)", _roomResource); + if (od->OBIMoffset) + imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), room + od->OBIMoffset); + + od->flags = Gdi::dbAllowMaskOr; + + if (_version == 8) { + od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id)); + + od->parent = cdhd->v7.parent; + od->parentstate = cdhd->v7.parentstate; + + od->x_pos = (int)READ_LE_UINT32(&imhd->v8.x_pos); + od->y_pos = (int)READ_LE_UINT32(&imhd->v8.y_pos); + od->width = (uint)READ_LE_UINT32(&imhd->v8.width); + od->height = (uint)READ_LE_UINT32(&imhd->v8.height); + // HACK: This is done since an angle doesn't fit into a byte (360 > 256) + od->actordir = toSimpleDir(1, READ_LE_UINT32(&imhd->v8.actordir)); + if (FROM_LE_32(imhd->v8.version) == 801) + od->flags = ((((byte)READ_LE_UINT32(&imhd->v8.flags)) & 16) == 0) ? Gdi::dbAllowMaskOr : 0; + + } else if (_version == 7) { + od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id)); + + od->parent = cdhd->v7.parent; + od->parentstate = cdhd->v7.parentstate; + + od->x_pos = READ_LE_UINT16(&imhd->v7.x_pos); + od->y_pos = READ_LE_UINT16(&imhd->v7.y_pos); + od->width = READ_LE_UINT16(&imhd->v7.width); + od->height = READ_LE_UINT16(&imhd->v7.height); + od->actordir = (byte)READ_LE_UINT16(&imhd->v7.actordir); + + } else if (_version == 6) { + od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id)); + + od->width = READ_LE_UINT16(&cdhd->v6.w); + od->height = READ_LE_UINT16(&cdhd->v6.h); + od->x_pos = ((int16)READ_LE_UINT16(&cdhd->v6.x)); + od->y_pos = ((int16)READ_LE_UINT16(&cdhd->v6.y)); + if (cdhd->v6.flags == 0x80) { + od->parentstate = 1; + } else { + od->parentstate = (cdhd->v6.flags & 0xF); + } + od->parent = cdhd->v6.parent; + od->actordir = cdhd->v6.actordir; + + if (_heversion >= 60 && imhd) + od->flags = ((imhd->old.flags & 1) != 0) ? Gdi::dbAllowMaskOr : 0; + + } else { + od->obj_nr = READ_LE_UINT16(&(cdhd->v5.obj_id)); + + od->width = cdhd->v5.w * 8; + od->height = cdhd->v5.h * 8; + od->x_pos = cdhd->v5.x * 8; + od->y_pos = cdhd->v5.y * 8; + if (cdhd->v5.flags == 0x80) { + od->parentstate = 1; + } else { + od->parentstate = (cdhd->v5.flags & 0xF); + } + od->parent = cdhd->v5.parent; + od->walk_x = READ_LE_UINT16(&cdhd->v5.walk_x); + od->walk_y = READ_LE_UINT16(&cdhd->v5.walk_y); + od->actordir = cdhd->v5.actordir; + } + + od->fl_object_index = 0; +} + +void ScummEngine::updateObjectStates() { + int i; + ObjectData *od = &_objs[1]; + for (i = 1; i < _numLocalObjects; i++, od++) { + if (od->obj_nr > 0) + od->state = getState(od->obj_nr); + } +} + +void ScummEngine::processDrawQue() { + int i, j; + for (i = 0; i < _drawObjectQueNr; i++) { + j = _drawObjectQue[i]; + if (j) + drawObject(j, 0); + } + _drawObjectQueNr = 0; +} + +void ScummEngine::addObjectToDrawQue(int object) { + if ((unsigned int)_drawObjectQueNr >= ARRAYSIZE(_drawObjectQue)) + error("Draw Object Que overflow"); + _drawObjectQue[_drawObjectQueNr++] = object; +} + +void ScummEngine::removeObjectFromDrawQue(int object) { + if (_drawObjectQueNr <= 0) + return; + + int i; + for (i = 0; i < _drawObjectQueNr; i++) { + if (_drawObjectQue[i] == object) + _drawObjectQue[i] = 0; + } +} + +void ScummEngine::clearDrawObjectQueue() { + _drawObjectQueNr = 0; +} + +void ScummEngine::clearDrawQueues() { + clearDrawObjectQueue(); +} + +void ScummEngine_v6::clearDrawQueues() { + ScummEngine::clearDrawQueues(); + + _blastObjectQueuePos = 0; +} + +#ifndef DISABLE_HE +void ScummEngine_v70he::clearDrawQueues() { + ScummEngine_v6::clearDrawQueues(); + + _wiz->polygonClear(); +} + +void ScummEngine_v80he::clearDrawQueues() { + ScummEngine_v70he::clearDrawQueues(); + + _wiz->clearWizBuffer(); +} +#endif + +void ScummEngine::clearOwnerOf(int obj) { + int i, j; + uint16 *a; + + stopObjectScript(obj); + + if (getOwner(obj) == OF_OWNER_ROOM) { + i = 0; + do { + if (_objs[i].obj_nr == obj) { + if (!_objs[i].fl_object_index) + return; + res.nukeResource(rtFlObject, _objs[i].fl_object_index); + _objs[i].obj_nr = 0; + _objs[i].fl_object_index = 0; + } + } while (++i < _numLocalObjects); + return; + } + + for (i = 0; i < _numInventory; i++) { + if (_inventory[i] == obj) { + j = whereIsObject(obj); + if (j == WIO_INVENTORY) { + res.nukeResource(rtInventory, i); + _inventory[i] = 0; + } + a = _inventory; + for (i = 0; i < _numInventory - 1; i++, a++) { + if (!a[0] && a[1]) { + a[0] = a[1]; + a[1] = 0; + res.address[rtInventory][i] = res.address[rtInventory][i + 1]; + res.address[rtInventory][i + 1] = NULL; + } + } + return; + } + } +} + +/** + * Mark the rectangle covered by the given object as dirty, thus eventually + * ensuring a redraw of that area. This function is typically invoked when an + * object gets removed from the current room, or when its state changed. + */ +void ScummEngine::markObjectRectAsDirty(int obj) { + int i, strip; + + for (i = 1; i < _numLocalObjects; i++) { + if (_objs[i].obj_nr == (uint16)obj) { + if (_objs[i].width != 0) { + const int minStrip = MAX(_screenStartStrip, _objs[i].x_pos / 8); + const int maxStrip = MIN(_screenEndStrip+1, _objs[i].x_pos / 8 + _objs[i].width / 8); + for (strip = minStrip; strip < maxStrip; strip++) { + setGfxUsageBit(strip, USAGE_BIT_DIRTY); + } + } + _bgNeedsRedraw = true; + return; + } + } +} + +const byte *ScummEngine::getObjOrActorName(int obj) { + byte *objptr; + int i; + + if (obj < _numActors) + return derefActor(obj, "getObjOrActorName")->getActorName(); + + for (i = 0; i < _numNewNames; i++) { + if (_newNames[i] == obj) { + debug(5, "Found new name for object %d at _newNames[%d]", obj, i); + return getResourceAddress(rtObjectName, i); + } + } + + objptr = getOBCDFromObject(obj); + if (objptr == NULL) + return NULL; + + if (_features & GF_SMALL_HEADER) { + byte offset = 0; + + if (_version <= 2) + offset = *(objptr + 14); + else if (_features & GF_OLD_BUNDLE) + offset = *(objptr + 16); + else + offset = *(objptr + 18); + + return (objptr + offset); + } + + return findResourceData(MKID('OBNA'), objptr); +} + +void ScummEngine::setObjectName(int obj) { + int i; + + if (obj < _numActors) + error("Can't set actor %d name with new-name-of", obj); + + for (i = 0; i < _numNewNames; i++) { + if (_newNames[i] == obj) { + res.nukeResource(rtObjectName, i); + _newNames[i] = 0; + break; + } + } + + for (i = 0; i < _numNewNames; i++) { + if (_newNames[i] == 0) { + loadPtrToResource(rtObjectName, i, NULL); + _newNames[i] = obj; + runInventoryScript(0); + return; + } + } + + error("New name of %d overflows name table (max = %d)", obj, _numNewNames); +} + +uint32 ScummEngine::getOBCDOffs(int object) const { + int i; + + if (_objectOwnerTable[object] != OF_OWNER_ROOM) + return 0; + for (i = (_numLocalObjects-1); i > 0; i--) { + if (_objs[i].obj_nr == object) { + if (_objs[i].fl_object_index != 0) + return 8; + return _objs[i].OBCDoffset; + } + } + return 0; +} + +byte *ScummEngine::getOBCDFromObject(int obj) { + int i; + byte *ptr; + + if (_objectOwnerTable[obj] != OF_OWNER_ROOM) { + for (i = 0; i < _numInventory; i++) { + if (_inventory[i] == obj) + return getResourceAddress(rtInventory, i); + } + } else { + for (i = (_numLocalObjects-1); i > 0; --i) { + if (_objs[i].obj_nr == obj) { + if (_objs[i].fl_object_index) { + assert(_objs[i].OBCDoffset == 8); + ptr = getResourceAddress(rtFlObject, _objs[i].fl_object_index); + } else if (_version == 8) + ptr = getResourceAddress(rtRoomScripts, _roomResource); + else + ptr = getResourceAddress(rtRoom, _roomResource); + assert(ptr); + return ptr + _objs[i].OBCDoffset; + } + } + } + return 0; +} + +const byte *ScummEngine::getOBIMFromObjectData(const ObjectData &od) { + const byte *ptr; + + if (od.fl_object_index) { + ptr = getResourceAddress(rtFlObject, od.fl_object_index); + ptr = findResource(MKID('OBIM'), ptr); + } else { + ptr = getResourceAddress(rtRoom, _roomResource); + if (ptr) + ptr += od.OBIMoffset; + } + return ptr; +} + +const byte *ScummEngine::getObjectImage(const byte *ptr, int state) { + assert(ptr); + if (_features & GF_OLD_BUNDLE) + ptr += 0; + else if (_features & GF_SMALL_HEADER) { + ptr += 8; + } else if (_version == 8) { + // The OBIM contains an IMAG, which in turn contains a WRAP, which contains + // an OFFS chunk and multiple BOMP/SMAP chunks. To find the right BOMP/SMAP, + // we use the offsets in the OFFS chunk, + ptr = findResource(MKID('IMAG'), ptr); + if (!ptr) + return 0; + + ptr = findResource(MKID('WRAP'), ptr); + if (!ptr) + return 0; + + ptr = findResource(MKID('OFFS'), ptr); + if (!ptr) + return 0; + + // Get the address of the specified SMAP (corresponding to IMxx) + ptr += READ_LE_UINT32(ptr + 4 + 4*state); + } else { + ptr = findResource(IMxx_tags[state], ptr); + } + + return ptr; +} + +int ScummEngine::getObjectImageCount(int object) { + const byte *ptr; + const ImageHeader *imhd; + int objnum; + + objnum = getObjectIndex(object); + if (objnum == -1) + return 0; + + ptr = getOBIMFromObjectData(_objs[objnum]); + imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), ptr); + if (!imhd) + return 0; + + if (_version == 8) { + return (READ_LE_UINT32(&imhd->v8.image_count)); + } else if (_version == 7) { + return(READ_LE_UINT16(&imhd->v7.image_count)); + } else { + return (READ_LE_UINT16(&imhd->old.image_count)); + } +} + +#ifndef DISABLE_SCUMM_7_8 +int ScummEngine_v8::getObjectIdFromOBIM(const byte *obim) { + // In V8, IMHD has no obj_id, but rather a name string. We map the name + // back to an object id using a table derived from the DOBJ resource. + const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), obim); + ObjectNameId *found = (ObjectNameId *)bsearch(imhd->v8.name, _objectIDMap, _objectIDMapSize, + sizeof(ObjectNameId), (int (*)(const void*, const void*))strcmp); + assert(found); + return found->id; +} + +int ScummEngine_v7::getObjectIdFromOBIM(const byte *obim) { + const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), obim); + return READ_LE_UINT16(&imhd->v7.obj_id); +} +#endif + +int ScummEngine::getObjectIdFromOBIM(const byte *obim) { + if (_features & GF_SMALL_HEADER) + return READ_LE_UINT16(obim + 6); + + const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), obim); + return READ_LE_UINT16(&imhd->old.obj_id); +} + +void ScummEngine::addObjectToInventory(uint obj, uint room) { + int idx, slot; + uint32 size; + const byte *ptr; + byte *dst; + FindObjectInRoom foir; + + debug(1, "Adding object %d from room %d into inventory", obj, room); + + CHECK_HEAP + if (whereIsObject(obj) == WIO_FLOBJECT) { + idx = getObjectIndex(obj); + assert(idx >= 0); + ptr = getResourceAddress(rtFlObject, _objs[idx].fl_object_index) + 8; + size = READ_BE_UINT32(ptr + 4); + } else { + findObjectInRoom(&foir, foCodeHeader, obj, room); + if (_features & GF_OLD_BUNDLE) + size = READ_LE_UINT16(foir.obcd); + else if (_features & GF_SMALL_HEADER) + size = READ_LE_UINT32(foir.obcd); + else + size = READ_BE_UINT32(foir.obcd + 4); + ptr = foir.obcd; + } + + slot = getInventorySlot(); + _inventory[slot] = obj; + dst = res.createResource(rtInventory, slot, size); + assert(dst); + memcpy(dst, ptr, size); + + CHECK_HEAP +} + +void ScummEngine::findObjectInRoom(FindObjectInRoom *fo, byte findWhat, uint id, uint room) { + + const CodeHeader *cdhd; + int i, numobj; + const byte *roomptr, *obcdptr, *obimptr, *searchptr; + int id2; + int obim_id; + + id2 = getObjectIndex(id); + if (findWhat & foCheckAlreadyLoaded && id2 != -1) { + assert(_version >= 6); + if (findWhat & foCodeHeader) { + fo->obcd = obcdptr = getOBCDFromObject(id); + assert(obcdptr); + fo->cdhd = (const CodeHeader *)findResourceData(MKID('CDHD'), obcdptr); + } + if (findWhat & foImageHeader) { + fo->obim = obimptr = getOBIMFromObjectData(_objs[id2]); + assert(obimptr); + } + return; + } + + fo->roomptr = roomptr = getResourceAddress(rtRoom, room); + if (!roomptr) + error("findObjectInRoom: failed getting roomptr to %d", room); + + if (_features & GF_OLD_BUNDLE) { + numobj = roomptr[20]; + } else { + const RoomHeader *roomhdr = (const RoomHeader *)findResourceData(MKID('RMHD'), roomptr); + + if (_version == 8) + numobj = READ_LE_UINT32(&(roomhdr->v8.numObjects)); + else if (_version == 7) + numobj = READ_LE_UINT16(&(roomhdr->v7.numObjects)); + else + numobj = READ_LE_UINT16(&(roomhdr->old.numObjects)); + } + + if (numobj == 0) + error("findObjectInRoom: No object found in room %d", room); + if (numobj > _numLocalObjects) + error("findObjectInRoom: More (%d) than %d objects in room %d", numobj, _numLocalObjects, room); + + if (_features & GF_OLD_BUNDLE) { + if (_version <= 2) + searchptr = roomptr + 28; + else + searchptr = roomptr + 29; + + for (i = 0; i < numobj; i++) { + obimptr = roomptr + READ_LE_UINT16(searchptr); + obcdptr = roomptr + READ_LE_UINT16(searchptr + 2 * numobj); + id2 = READ_LE_UINT16(obcdptr + 4); + + if (id2 == (uint16)id) { + if (findWhat & foCodeHeader) { + fo->obcd = obcdptr; + fo->cdhd = (const CodeHeader *)(obcdptr + 10); // TODO - FIXME + } + if (findWhat & foImageHeader) { + fo->obim = obimptr; + } + break; + } + searchptr += 2; + } + return; + } + + if (findWhat & foCodeHeader) { + if (_version == 8) + searchptr = getResourceAddress(rtRoomScripts, room); + else + searchptr = roomptr; + assert(searchptr); + ResourceIterator obcds(searchptr, (_features & GF_SMALL_HEADER) != 0); + for (i = 0; i < numobj; i++) { + obcdptr = obcds.findNext(MKID('OBCD')); + if (obcdptr == NULL) + error("findObjectInRoom: Not enough code blocks in room %d", room); + cdhd = (const CodeHeader *)findResourceData(MKID('CDHD'), obcdptr); + + if (_features & GF_SMALL_HEADER) + id2 = READ_LE_UINT16(obcdptr + 6); + else if (_version >= 7) + id2 = READ_LE_UINT16(&(cdhd->v7.obj_id)); + else if (_version == 6) + id2 = READ_LE_UINT16(&(cdhd->v6.obj_id)); + else + id2 = READ_LE_UINT16(&(cdhd->v5.obj_id)); + + if (id2 == (uint16)id) { + fo->obcd = obcdptr; + fo->cdhd = cdhd; + break; + } + } + if (i == numobj) + error("findObjectInRoom: Object %d not found in room %d", id, room); + } + + roomptr = fo->roomptr; + if (findWhat & foImageHeader) { + ResourceIterator obims(roomptr, (_features & GF_SMALL_HEADER) != 0); + for (i = 0; i < numobj; i++) { + obimptr = obims.findNext(MKID('OBIM')); + if (obimptr == NULL) + error("findObjectInRoom: Not enough image blocks in room %d", room); + obim_id = getObjectIdFromOBIM(obimptr); + + if (obim_id == (uint16)id) { + fo->obim = obimptr; + break; + } + } + if (i == numobj) + error("findObjectInRoom: Object %d image not found in room %d", id, room); + } +} + +int ScummEngine::getInventorySlot() { + int i; + for (i = 0; i < _numInventory; i++) { + if (_inventory[i] == 0) + return i; + } + error("Inventory full, %d max items", _numInventory); + return -1; +} + +void ScummEngine::setOwnerOf(int obj, int owner) { + ScriptSlot *ss; + + // In Sam & Max this is necessary, or you won't get your stuff back + // from the Lost and Found tent after riding the Cone of Tragedy. But + // it probably applies to all V6+ games. See bugs #493153 and #907113. + // FT disassembly is checked, behaviour is correct. [sev] + + int arg = (_version >= 6) ? obj : 0; + + if (owner == 0) { + clearOwnerOf(obj); + ss = &vm.slot[_currentScript]; + if (ss->where == WIO_INVENTORY && _inventory[ss->number] == obj) { + putOwner(obj, 0); + runInventoryScript(arg); + stopObjectCode(); + return; + } + } + + putOwner(obj, owner); + runInventoryScript(arg); +} + +int ScummEngine::getObjX(int obj) { + if (obj < _numActors) { + if (obj < 1) + return 0; /* fix for indy4's map */ + return derefActor(obj, "getObjX")->_pos.x; + } else { + if (whereIsObject(obj) == WIO_NOT_FOUND) + return -1; + int x, y; + getObjectOrActorXY(obj, x, y); + return x; + } +} + +int ScummEngine::getObjY(int obj) { + if (obj < _numActors) { + if (obj < 1) + return 0; /* fix for indy4's map */ + return derefActor(obj, "getObjY")->_pos.y; + } else { + if (whereIsObject(obj) == WIO_NOT_FOUND) + return -1; + int x, y; + getObjectOrActorXY(obj, x, y); + return y; + } +} + +int ScummEngine::getObjOldDir(int obj) { + return newDirToOldDir(getObjNewDir(obj)); +} + +int ScummEngine::getObjNewDir(int obj) { + int dir; + if (obj < _numActors) { + dir = derefActor(obj, "getObjNewDir")->getFacing(); + } else { + int x, y; + getObjectXYPos(obj, x, y, dir); + } + return dir; +} + +int ScummEngine::findInventory(int owner, int idx) { + int count = 1, i, obj; + for (i = 0; i < _numInventory; i++) { + obj = _inventory[i]; + if (obj && getOwner(obj) == owner && count++ == idx) + return obj; + } + return 0; +} + +int ScummEngine::getInventoryCount(int owner) { + int i, obj; + int count = 0; + for (i = 0; i < _numInventory; i++) { + obj = _inventory[i]; + if (obj && getOwner(obj) == owner) + count++; + } + return count; +} + +void ScummEngine::setObjectState(int obj, int state, int x, int y) { + int i; + + i = getObjectIndex(obj); + if (i == -1) { + debug(0, "setObjectState: no such object %d", obj); + return; + } + + if (x != -1 && x != 0x7FFFFFFF) { + _objs[i].x_pos = x * 8; + _objs[i].y_pos = y * 8; + } + + addObjectToDrawQue(i); + if (_version >= 7) { + int imagecount; + if (state == 0xFF) { + state = getState(obj); + imagecount = getObjectImageCount(obj); + + if (state < imagecount) + state++; + else + state = 1; + } + + if (state == 0xFE) + state = _rnd.getRandomNumber(getObjectImageCount(obj)); + } + putState(obj, state); +} + +int ScummEngine::getDistanceBetween(bool is_obj_1, int b, int c, bool is_obj_2, int e, int f) { + int i, j; + int x, y; + int x2, y2; + + j = i = 0xFF; + + if (is_obj_1) { + if (getObjectOrActorXY(b, x, y) == -1) + return -1; + if (b < _numActors) + i = derefActor(b, "getDistanceBetween_is_obj_1")->_scalex; + } else { + x = b; + y = c; + } + + if (is_obj_2) { + if (getObjectOrActorXY(e, x2, y2) == -1) + return -1; + if (e < _numActors) + j = derefActor(e, "getDistanceBetween_is_obj_2")->_scalex; + } else { + x2 = e; + y2 = f; + } + + return getDist(x, y, x2, y2) * 0xFF / ((i + j) / 2); +} + +void ScummEngine::nukeFlObjects(int min, int max) { + ObjectData *od; + int i; + + debug(0, "nukeFlObjects(%d,%d)", min, max); + + for (i = (_numLocalObjects-1), od = _objs; --i >= 0; od++) + if (od->fl_object_index && od->obj_nr >= min && od->obj_nr <= max) { + res.nukeResource(rtFlObject, od->fl_object_index); + od->obj_nr = 0; + od->fl_object_index = 0; + } +} + +void ScummEngine_v6::enqueueObject(int objectNumber, int objectX, int objectY, int objectWidth, + int objectHeight, int scaleX, int scaleY, int image, int mode) { + BlastObject *eo; + + if (_blastObjectQueuePos >= (int)ARRAYSIZE(_blastObjectQueue)) { + error("enqueueObject: overflow"); + } + + int idx = getObjectIndex(objectNumber); + assert(idx >= 0); + + eo = &_blastObjectQueue[_blastObjectQueuePos++]; + eo->number = objectNumber; + eo->rect.left = objectX; + eo->rect.top = objectY + _screenTop; + if (objectWidth == 0) { + eo->rect.right = eo->rect.left + _objs[idx].width; + } else { + eo->rect.right = eo->rect.left + objectWidth; + } + if (objectHeight == 0) { + eo->rect.bottom = eo->rect.top + _objs[idx].height; + } else { + eo->rect.bottom = eo->rect.top + objectHeight; + } + + eo->scaleX = scaleX; + eo->scaleY = scaleY; + eo->image = image; + + eo->mode = mode; +} + +void ScummEngine_v6::drawBlastObjects() { + BlastObject *eo; + int i; + + eo = _blastObjectQueue; + for (i = 0; i < _blastObjectQueuePos; i++, eo++) { + drawBlastObject(eo); + } +} + +void ScummEngine_v6::drawBlastObject(BlastObject *eo) { + VirtScreen *vs; + const byte *bomp, *ptr; + int objnum; + BompDrawData bdd; + + vs = &virtscr[0]; + + checkRange(_numGlobalObjects - 1, 30, eo->number, "Illegal Blast object %d"); + + objnum = getObjectIndex(eo->number); + if (objnum == -1) + error("drawBlastObject: getObjectIndex on BlastObject %d failed", eo->number); + + ptr = getOBIMFromObjectData(_objs[objnum]); + if (!ptr) + error("BlastObject object %d image not found", eo->number); + + const byte *img = getObjectImage(ptr, eo->image); + if (_version == 8) { + assert(img); + bomp = img + 8; + } else { + if (!img) + img = getObjectImage(ptr, 1); // Backward compatibility with samnmax blast objects + assert(img); + bomp = findResourceData(MKID('BOMP'), img); + } + + if (!bomp) + error("object %d is not a blast object", eo->number); + + if (_version == 8) { + bdd.srcwidth = READ_LE_UINT32(&((const BompHeader *)bomp)->v8.width); + bdd.srcheight = READ_LE_UINT32(&((const BompHeader *)bomp)->v8.height); + } else { + bdd.srcwidth = READ_LE_UINT16(&((const BompHeader *)bomp)->old.width); + bdd.srcheight = READ_LE_UINT16(&((const BompHeader *)bomp)->old.height); + } + + bdd.dst = *vs; + bdd.dst.pixels = vs->getPixels(0, 0); + // Skip the bomp header + if (_version == 8) { + bdd.dataptr = bomp + 8; + } else { + bdd.dataptr = bomp + 10; + } + bdd.x = eo->rect.left; + bdd.y = eo->rect.top; + bdd.scale_x = (byte)eo->scaleX; + bdd.scale_y = (byte)eo->scaleY; + bdd.maskPtr = NULL; + + if ((bdd.scale_x != 255) || (bdd.scale_y != 255)) { + bdd.shadowMode = 0; + } else { + bdd.shadowMode = eo->mode; + } + drawBomp(bdd, false); + + markRectAsDirty(vs->number, bdd.x, bdd.x + bdd.srcwidth, bdd.y, bdd.y + bdd.srcheight); +} + +void ScummEngine_v6::removeBlastObjects() { + BlastObject *eo; + int i; + + eo = _blastObjectQueue; + for (i = 0; i < _blastObjectQueuePos; i++, eo++) { + removeBlastObject(eo); + } + _blastObjectQueuePos = 0; +} + +void ScummEngine_v6::removeBlastObject(BlastObject *eo) { + VirtScreen *vs = &virtscr[0]; + + Common::Rect r; + int left_strip, right_strip; + int i; + + r = eo->rect; + + r.clip(Common::Rect(vs->w, vs->h)); + + if (r.width() <= 0 || r.height() <= 0) + return; + + left_strip = r.left / 8; + right_strip = (r.right + (vs->xstart % 8)) / 8; + + if (left_strip < 0) + left_strip = 0; + if (right_strip > gdi._numStrips - 1) + right_strip = gdi._numStrips - 1; + for (i = left_strip; i <= right_strip; i++) + gdi.resetBackground(r.top, r.bottom, i); + + markRectAsDirty(kMainVirtScreen, r, USAGE_BIT_RESTORED); +} + +int ScummEngine::findLocalObjectSlot() { + int i; + + for (i = 1; i < _numLocalObjects; i++) { + if (!_objs[i].obj_nr) { + memset(&_objs[i], 0, sizeof(_objs[i])); + return i; + } + } + + return -1; +} + +int ScummEngine::findFlObjectSlot() { + int i; + for (i = 1; i < _numFlObject; i++) { + if (res.address[rtFlObject][i] == NULL) + return i; + } + error("findFlObjectSlot: Out of FLObject slots"); + return -1; +} + +void ScummEngine::loadFlObject(uint object, uint room) { + FindObjectInRoom foir; + int i, slot, objslot; + ObjectData *od; + byte *flob; + uint32 obcd_size, obim_size, flob_size; + bool isRoomLocked, isRoomScriptsLocked; + + // Don't load an already loaded object + if (getObjectIndex(object) != -1) + return; + + // Don't load an already stored object + for (i = 0; i < _numStoredFlObjects; i++) { + if (_storedFlObjects[i].obj_nr == object) + return; + } + + // Locate the object in the room resource + findObjectInRoom(&foir, foImageHeader | foCodeHeader, object, room); + + // Add an entry for the new floating object in the local object table + objslot = findLocalObjectSlot(); + if (objslot == -1) + error("loadFlObject: Local Object Table overflow"); + + od = &_objs[objslot]; + + // Dump object script + if (_dumpScripts) { + char buf[32]; + const byte *ptr = foir.obcd; + sprintf(buf, "roomobj-%d-", room); + ptr = findResource(MKID('VERB'), ptr); + dumpResource(buf, object, ptr); + } + + // Setup sizes + obcd_size = READ_BE_UINT32(foir.obcd + 4); + od->OBCDoffset = 8; + od->OBIMoffset = obcd_size + 8; + obim_size = READ_BE_UINT32(foir.obim + 4); + flob_size = obcd_size + obim_size + 8; + + // Lock room/roomScripts for the given room. They contains the OBCD/OBIM + // data, and a call to createResource might expire them, hence we lock them. + isRoomLocked = res.isLocked(rtRoom, room); + isRoomScriptsLocked = res.isLocked(rtRoomScripts, room); + if (!isRoomLocked) + res.lock(rtRoom, room); + if (_version == 8 && !isRoomScriptsLocked) + res.lock(rtRoomScripts, room); + + // Allocate slot & memory for floating object + slot = findFlObjectSlot(); + flob = res.createResource(rtFlObject, slot, flob_size); + assert(flob); + + // Copy object code + object image to floating object + ((uint32 *)flob)[0] = MKID('FLOB'); + ((uint32 *)flob)[1] = TO_BE_32(flob_size); + + memcpy(flob + 8, foir.obcd, obcd_size); + memcpy(flob + 8 + obcd_size, foir.obim, obim_size); + + // Unlock room/roomScripts + if (!isRoomLocked) + res.unlock(rtRoom, room); + if (_version == 8 && !isRoomScriptsLocked) + res.unlock(rtRoomScripts, room); + + // Setup local object flags + setupRoomObject(od, flob, flob); + + od->fl_object_index = slot; +} + +} // End of namespace Scumm |