From ce46866403fdcc479cf9d67e4d430409b15dadc3 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 21 Aug 2002 16:07:07 +0000 Subject: Initial revision svn-id: r4785 --- scumm/object.cpp | 1326 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1326 insertions(+) create mode 100644 scumm/object.cpp (limited to 'scumm/object.cpp') diff --git a/scumm/object.cpp b/scumm/object.cpp new file mode 100644 index 0000000000..319328c0ee --- /dev/null +++ b/scumm/object.cpp @@ -0,0 +1,1326 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001/2002 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "stdafx.h" +#include "scumm.h" +#include "actor.h" +#include "object.h" +#include "resource.h" + +bool Scumm::getClass(int obj, int cls) +{ + 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) { + if (cls == 31) // CLASS_PLAYERONLY + cls = 23; + + if (cls == 32) // CLASS_TOUCHABLE + cls = 24; + } + return (_classData[obj] & (1 << (cls - 1))) != 0; +} + +void Scumm::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) { + if (cls == 31) // CLASS_PLAYERONLY + cls = 23; + + if (cls == 32) // CLASS_TOUCHABLE + cls = 24; + } + + if (set) + _classData[obj] |= (1 << (cls - 1)); + else + _classData[obj] &= ~(1 << (cls - 1)); + + if (1 <= obj && obj < NUM_ACTORS) { + _actors[obj].classChanged(cls, set); + } +} + +int Scumm::getOwner(int obj) +{ + checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in getOwner"); + return _objectOwnerTable[obj]; +} + +void Scumm::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 Scumm::getState(int obj) +{ + checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in getState"); + return _objectStateTable[obj]; +} + +void Scumm::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 Scumm::getObjectRoom(int obj) +{ + checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in getObjectRoom"); + return _objectRoomTable[obj]; +} + +int Scumm::getObjectIndex(int object) +{ + int i; + + /* OF_OWNER_ROOM should be 0xFF for full throttle, else 0xF */ + if (_objectOwnerTable[object] != OF_OWNER_ROOM) { + for (i = 0; i < _maxInventoryItems; i++) + if (_inventory[i] == object) + return i; + return -1; + } else { + for (i = _numObjectsInRoom; i > 0; i--) { + if (_objs[i].obj_nr == object) + return i; + } + return -1; + } +} + +int Scumm::whereIsObject(int object) +{ + int i; + + if (object >= _numGlobalObjects) + return WIO_NOT_FOUND; + + if (_objectOwnerTable[object] != OF_OWNER_ROOM) { + for (i = 0; i < _maxInventoryItems; i++) + if (_inventory[i] == object) + return WIO_INVENTORY; + return WIO_NOT_FOUND; + } + + for (i = _numObjectsInRoom; 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 Scumm::getObjectOrActorXY(int object, int &x, int &y) +{ + if (object < NUM_ACTORS) { + Actor *act = derefActorSafe(object, "getObjectOrActorXY"); + if (!act) + return 0; + else + return act->getActorXYPos(x, y); + } + + switch (whereIsObject(object)) { + case WIO_NOT_FOUND: + return -1; + case WIO_INVENTORY: + if (_objectOwnerTable[object] < NUM_ACTORS) + return derefActorSafe(_objectOwnerTable[object], "getObjectOrActorXY(2)")->getActorXYPos(x, y); + else + return 0xFF; + } + getObjectXYPos(object, x, y); + return 0; +} + +/* Return the position of an object. + Returns X, Y and direction in angles + */ +void Scumm::getObjectXYPos(int object, int &x, int &y, int &dir) +{ + ObjectData *od = &_objs[getObjectIndex(object)]; + int state; + byte *ptr; + ImageHeader *imhd; + + if (!(_features & GF_SMALL_HEADER)) { + if (_features & GF_AFTER_V6) { + state = getState(object) - 1; + if (state < 0) + state = 0; + + if (od->fl_object_index) { + ptr = getResourceAddress(rtFlObject, od->fl_object_index); + ptr = findResource(MKID('OBIM'), ptr); + } else { + ptr = getResourceAddress(rtRoom, _roomResource); + ptr += od->offs_obim_to_room; + } + assert(ptr); + imhd = (ImageHeader *)findResourceData(MKID('IMHD'), ptr); + if (_features & GF_AFTER_V7) { + 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; + } + dir = oldDirToNewDir(od->actordir & 3); + } else { + x = od->walk_x; + y = od->walk_y; + dir = oldDirToNewDir(od->actordir & 3); + } +} + +int Scumm::getObjActToObjActDist(int a, int b) +{ + int x, y, x2, y2; + Actor *acta = NULL; + Actor *actb = NULL; + + if (a < NUM_ACTORS) + acta = derefActorSafe(a, "getObjActToObjActDist"); + + if (b < NUM_ACTORS) + 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; + + if (acta) { + AdjustBoxResult r = acta->adjustXYToBeInBox(x2, y2, -1); + x2 = r.x; + y2 = r.y; + } + + y = abs(y - y2); + x = abs(x - x2); + + if (y > x) + x = y; + return x; +} + +int Scumm::findObject(int x, int y) +{ + int i, b; + byte a; + + for (i = 1; i <= _numObjectsInRoom; i++) { + if (!_objs[i].obj_nr || getClass(_objs[i].obj_nr, 32)) + continue; + b = i; + do { + a = _objs[b].parentstate; + b = _objs[b].parent; + if (b == 0) { + 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 == a); + } + return 0; +} + +void Scumm::drawRoomObject(int i, int arg) +{ + ObjectData *od; + byte a; + + od = &_objs[i]; + if (!od->obj_nr || !od->state) + return; + + do { + a = od->parentstate; + if (!od->parent) { + drawObject(i, arg); + break; + } + od = &_objs[od->parent]; + } while (od->state == a); +} + +void Scumm::drawRoomObjects(int arg) +{ + int i; + + if (_features & GF_DRAWOBJ_OTHER_ORDER) { + for (i = 1; i <= _numObjectsInRoom; i++) + drawRoomObject(i, arg); + } else { + for (i = _numObjectsInRoom; i != 0; i--) + drawRoomObject(i, arg); + } +} + +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') +}; + +void Scumm::drawObject(int obj, int arg) +{ + ObjectData *od; + int xpos, ypos, height, width; + byte *ptr; + int x, a, numstrip; + int tmp; + + if (_BgNeedsRedraw) + arg = 0; + + _curVirtScreen = &virtscr[0]; + + od = &_objs[obj]; + + xpos = od->x_pos >> 3; + ypos = od->y_pos; + + width = od->width >> 3; + height = od->height &= 0xF8; // Ender + + if (width == 0 || xpos > _screenEndStrip || xpos + width < _screenStartStrip) + return; + + if (od->fl_object_index) { + ptr = getResourceAddress(rtFlObject, od->fl_object_index); + ptr = findResource(MKID('OBIM'), ptr); + } else { + ptr = getResourceAddress(rtRoom, _roomResource); + ptr = ptr + od->offs_obim_to_room; + } + + if (_features & GF_SMALL_HEADER) + ptr += 8; + else + ptr = findResource(IMxx_tags[getState(od->obj_nr)], ptr); + if (!ptr) + return; + + x = 0xFFFF; + + for (a = numstrip = 0; a < width; a++) { + tmp = xpos + a; + if (arg == 1 && _screenStartStrip != tmp) + continue; + if (arg == 2 && _screenEndStrip != tmp) + continue; + if (tmp < _screenStartStrip || tmp > _screenEndStrip) + continue; + gfxUsageBits[tmp] |= 0x80000000; + if (tmp < x) + x = tmp; + numstrip++; + } + + if (numstrip != 0) { + byte flags = Gdi::dbAllowMaskOr; + if (_features & GF_AFTER_V7 && getClass(od->obj_nr, 22)) + flags |= Gdi::dbDrawMaskOnBoth; + gdi.drawBitmap(ptr, _curVirtScreen, x, ypos, height, x - xpos, numstrip, flags); + } +} + +void Scumm::loadRoomObjects() +{ + int i, j; + ObjectData *od; + byte *ptr; + uint16 obim_id; + byte *room, *searchptr; + ImageHeader *imhd; + RoomHeader *roomhdr; + CodeHeader *cdhd; + + CHECK_HEAP room = getResourceAddress(rtRoom, _roomResource); + roomhdr = (RoomHeader *)findResourceData(MKID('RMHD'), room); + + if (_features & GF_AFTER_V7) + _numObjectsInRoom = READ_LE_UINT16(&(roomhdr->v7.numObjects)); + else + _numObjectsInRoom = READ_LE_UINT16(&(roomhdr->old.numObjects)); + + if (_numObjectsInRoom == 0) + return; + + if (_numObjectsInRoom > _numLocalObjects) + error("More than %d objects in room %d", _numLocalObjects, _roomResource); + + od = &_objs[1]; + searchptr = room; + for (i = 0; i < _numObjectsInRoom; i++, od++) { + ptr = findResource(MKID('OBCD'), searchptr); + if (ptr == NULL) + error("Room %d missing object code block(s)", _roomResource); + + od->offs_obcd_to_room = ptr - room; + cdhd = (CodeHeader *)findResourceData(MKID('CDHD'), ptr); + + if (_features & GF_AFTER_V7) + od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id)); + else if (_features & GF_AFTER_V6) + od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id)); + else + od->obj_nr = READ_LE_UINT16(&(cdhd->v5.obj_id)); + +#ifdef DUMP_SCRIPTS + do { + char buf[32]; + sprintf(buf, "roomobj-%d-", _roomResource); + dumpResource(buf, od->obj_nr, ptr); + } while (0); +#endif + searchptr = NULL; + } + + searchptr = room; + for (i = 0; i < _numObjectsInRoom; i++) { + ptr = findResource(MKID('OBIM'), searchptr); + if (ptr == NULL) + error("Room %d missing image blocks(s)", _roomResource); + + imhd = (ImageHeader *)findResourceData(MKID('IMHD'), ptr); + if (_features & GF_AFTER_V7) + obim_id = READ_LE_UINT16(&imhd->v7.obj_id); + else + obim_id = READ_LE_UINT16(&imhd->old.obj_id); + + for (j = 1; j <= _numObjectsInRoom; j++) { + if (_objs[j].obj_nr == obim_id) + _objs[j].offs_obim_to_room = ptr - room; + } + searchptr = NULL; + } + + od = &_objs[1]; + for (i = 1; i <= _numObjectsInRoom; i++, od++) { + setupRoomObject(od, room); + } + +CHECK_HEAP} + +void Scumm::loadRoomObjectsSmall() +{ + int i, j; + ObjectData *od; + byte *ptr; + uint16 obim_id; + byte *room, *searchptr; + RoomHeader *roomhdr; + + CHECK_HEAP room = getResourceAddress(rtRoom, _roomResource); + roomhdr = (RoomHeader *)findResourceData(MKID('RMHD'), room); + + _numObjectsInRoom = READ_LE_UINT16(&(roomhdr->old.numObjects)); + + if (_numObjectsInRoom == 0) + return; + + if (_numObjectsInRoom > _numLocalObjects) + error("More than %d objects in room %d", _numLocalObjects, _roomResource); + + od = &_objs[1]; + searchptr = room; + for (i = 0; i < _numObjectsInRoom; i++, od++) { + ptr = findResourceSmall(MKID('OBCD'), searchptr); + if (ptr == NULL) + error("Room %d missing object code block(s)", _roomResource); + + od->offs_obcd_to_room = ptr - room; + od->obj_nr = READ_LE_UINT16(ptr + 6); + +#ifdef DUMP_SCRIPTS + do { + char buf[32]; + sprintf(buf, "roomobj-%d-", _roomResource); + dumpResource(buf, od->obj_nr, ptr); + } while (0); +#endif + searchptr = NULL; + } + + searchptr = room; + for (i = 0; i < _numObjectsInRoom; i++) { + ptr = findResourceSmall(MKID('OBIM'), searchptr); + if (ptr == NULL) + error("Room %d missing image blocks(s)", _roomResource); + + obim_id = READ_LE_UINT16(ptr + 6); + + for (j = 1; j <= _numObjectsInRoom; j++) { + if (_objs[j].obj_nr == obim_id) + _objs[j].offs_obim_to_room = ptr - room; + } + searchptr = NULL; + } + + od = &_objs[1]; + for (i = 1; i <= _numObjectsInRoom; i++, od++) { + setupRoomObject(od, room); + } + +CHECK_HEAP} + +void Scumm::setupRoomObject(ObjectData *od, byte *room) +{ + CodeHeader *cdhd; + ImageHeader *imhd; + + if (_features & GF_SMALL_HEADER) { + + byte *ptr = room + od->offs_obcd_to_room; + + od->obj_nr = READ_LE_UINT16(ptr + 6); // ok + + od->width = *(ptr + 11) << 3; // ok + od->x_pos = *(ptr + 9) << 3; // ok + + if (*(ptr + 10) & 0x80) { + od->parentstate = 1; // it's 0x10 in the original code + } else { + od->parentstate = 0; + } + + od->y_pos = ((*(ptr + 10)) & 0x7F) << 3; + + od->parent = *(ptr + 12); + 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); // ok + + + + return; + } + + cdhd = (CodeHeader *)findResourceData(MKID('CDHD'), room + od->offs_obcd_to_room); + if (_features & GF_AFTER_V7) + od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id)); + else if (_features & GF_AFTER_V6) + od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id)); + else + od->obj_nr = READ_LE_UINT16(&(cdhd->v5.obj_id)); + + if (!(_features & GF_AFTER_V7)) { + if (_features & GF_AFTER_V6) { + 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; + } else { + od->width = cdhd->v5.w << 3; + od->height = cdhd->v5.h << 3; + od->x_pos = cdhd->v5.x << 3; + od->y_pos = cdhd->v5.y << 3; + 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; + } + } else { + od->parent = cdhd->v7.parent; + od->parentstate = cdhd->v7.parentstate; + + imhd = (ImageHeader *)findResourceData(MKID('IMHD'), room + od->offs_obim_to_room); + 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 = READ_LE_UINT16(&imhd->v7.actordir); + + } + od->fl_object_index = 0; +} + +void Scumm::fixObjectFlags() +{ + int i; + ObjectData *od = &_objs[1]; + for (i = 1; i <= _numObjectsInRoom; i++, od++) { + od->state = _objectStateTable[od->obj_nr]; + } +} + +void Scumm::processDrawQue() +{ + int i, j; + for (i = 0; i < _drawObjectQueNr; i++) { + j = _drawObjectQue[i]; + if (j) + drawObject(j, 0); + } + _drawObjectQueNr = 0; +} + +void Scumm::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; + nukeResource(rtFlObject, _objs[i].fl_object_index); + _objs[i].obj_nr = 0; + _objs[i].fl_object_index = 0; + } + } while (++i <= _numObjectsInRoom); + return; + } + + for (i = 1; i < _maxInventoryItems; i++) { + if (_inventory[i] == obj) { + j = whereIsObject(obj); + if (j == WIO_INVENTORY) { + nukeResource(rtInventory, i); + _inventory[i] = 0; + } + a = &_inventory[1]; + for (i = 1; i < _maxInventoryItems - 1; i++, a++) { + if (!a[0] && a[1]) { + a[0] = a[1]; + a[1] = 0; + _baseInventoryItems[i] = _baseInventoryItems[i + 1]; + _baseInventoryItems[i + 1] = NULL; + } + } + return; + } + } +} + +void Scumm::removeObjectFromRoom(int obj) +{ + int i, cnt; + uint32 *ptr; + + for (i = 1; i <= _numObjectsInRoom; i++) { + if (_objs[i].obj_nr == (uint16)obj) { + if (_objs[i].width != 0) { + ptr = &gfxUsageBits[_objs[i].x_pos >> 3]; + cnt = _objs[i].width >> 3; + do { + *ptr++ |= 0x80000000; + } while (--cnt); + } + _BgNeedsRedraw = true; + return; + } + } +} + +void Scumm::addObjectToDrawQue(int object) +{ + _drawObjectQue[_drawObjectQueNr++] = object; + if ((unsigned int)_drawObjectQueNr > sizeof(_drawObjectQue) / sizeof(_drawObjectQue[0])) + error("Draw Object Que overflow"); +} + +void Scumm::clearDrawObjectQueue() +{ + _drawObjectQueNr = 0; +} + +byte *Scumm::getObjOrActorName(int obj) +{ + byte *objptr; + int i; + + if (obj < NUM_ACTORS) + return derefActorSafe(obj, "getObjOrActorName")->getActorName(); + + if (_features & GF_SMALL_HEADER) { + byte offset = 0; + + objptr = getOBCDFromObject(obj); + if (objptr) + offset = READ_LE_UINT16(objptr + 18); + return (objptr + offset); + } + + if (_features & GF_AFTER_V6) { + for (i = 1; i < 50; i++) { + if (_newNames[i] == obj) { + debug(5, "Found new name for object %d at _newNames[i]", obj, i); + return getResourceAddress(rtObjectName, i); + break; + } + } + } + + objptr = getOBCDFromObject(obj); + if (objptr == NULL) + return (byte *)" "; + + return findResourceData(MKID('OBNA'), objptr); +} + +uint32 Scumm::getOBCDOffs(int object) +{ + int i; + + if (_objectOwnerTable[object] != OF_OWNER_ROOM) + return 0; + for (i = _numObjectsInRoom; i > 0; i--) { + if (_objs[i].obj_nr == object) { + if (_objs[i].fl_object_index != 0) + return 8; + return _objs[i].offs_obcd_to_room; + } + } + return 0; +} + +byte *Scumm::getOBCDFromObject(int obj) +{ + int i; + + if (_objectOwnerTable[obj] != OF_OWNER_ROOM) { + for (i = 0; i < _maxInventoryItems; i++) { + if (_inventory[i] == obj) + return getResourceAddress(rtInventory, i); + } + } else { + for (i = _numObjectsInRoom; i > 0; --i) { + if (_objs[i].obj_nr == obj) { + if (_objs[i].fl_object_index) + return getResourceAddress(rtFlObject, _objs[i].fl_object_index) + 8; + return getResourceAddress(rtRoom, _roomResource) + _objs[i].offs_obcd_to_room; + } + } + } + return 0; +} + +void Scumm::addObjectToInventory(uint obj, uint room) +{ + int i, slot; + uint32 size; + byte *obcdptr, *ptr; + FindObjectInRoom foir; + + debug(1, "Adding object %d from room %d into inventory", obj, room); + + CHECK_HEAP if (whereIsObject(obj) == WIO_FLOBJECT) { + i = getObjectIndex(obj); + ptr = getResourceAddress(rtFlObject, _objs[i].fl_object_index) + 8; + size = READ_BE_UINT32_UNALIGNED(ptr + 4); + slot = getInventorySlot(); + _inventory[slot] = obj; + createResource(rtInventory, slot, size); + ptr = getResourceAddress(rtFlObject, _objs[i].fl_object_index) + 8; + memcpy(getResourceAddress(rtInventory, slot), ptr, size); + } else { + findObjectInRoom(&foir, foCodeHeader, obj, room); + if (_features & GF_SMALL_HEADER) + size = READ_LE_UINT32(foir.obcd); + else + size = READ_BE_UINT32_UNALIGNED(foir.obcd + 4); + slot = getInventorySlot(); + _inventory[slot] = obj; + createResource(rtInventory, slot, size); + obcdptr = getResourceAddress(rtRoom, room) - foir.roomptr + foir.obcd; + memcpy(getResourceAddress(rtInventory, slot), obcdptr, size); + } + +CHECK_HEAP} + +void Scumm::findObjectInRoom(FindObjectInRoom *fo, byte findWhat, uint id, uint room) +{ + CodeHeader *cdhd; + int i, numobj; + byte *roomptr, *obcdptr, *obimptr, *searchptr; + RoomHeader *roomhdr; + ImageHeader *imhd; + int id2; + int id3; + + if (findWhat & foCheckAlreadyLoaded && getObjectIndex(id) != -1) { + fo->obcd = obcdptr = getOBCDFromObject(id); + assert((byte *)obcdptr > (byte *)256); + fo->obim = obimptr = obcdptr + RES_SIZE(obcdptr); + fo->cdhd = (CodeHeader *)findResourceData(MKID('CDHD'), obcdptr); + fo->imhd = (ImageHeader *)findResourceData(MKID('IMHD'), obimptr); + return; + } + + fo->roomptr = roomptr = getResourceAddress(rtRoom, room); + if (!roomptr) + error("findObjectInRoom: failed getting roomptr to %d", room); + + roomhdr = (RoomHeader *)findResourceData(MKID('RMHD'), roomptr); + + if (_features & GF_AFTER_V7) + 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 (findWhat & foCodeHeader) { + searchptr = roomptr; + for (i = 0;;) { + if (_features & GF_SMALL_HEADER) + obcdptr = findResourceSmall(MKID('OBCD'), searchptr); + else + obcdptr = findResource(MKID('OBCD'), searchptr); + if (obcdptr == NULL) + error("findObjectInRoom: Not enough code blocks in room %d", room); + if (_features & GF_SMALL_HEADER) { + if (READ_LE_UINT16(obcdptr + 6) == (uint16)id) { + fo->cdhd = NULL; + fo->obcd = obcdptr; + break; + } + } else { + cdhd = (CodeHeader *)findResourceData(MKID('CDHD'), obcdptr); + if (_features & GF_AFTER_V7) + id2 = READ_LE_UINT16(&(cdhd->v7.obj_id)); + else if (_features & GF_AFTER_V6) + id2 = READ_LE_UINT16(&(cdhd->v6.obj_id)); + else + id2 = READ_LE_UINT16(&(cdhd->v5.obj_id)); + + if (id2 == (uint16)id) { + fo->cdhd = cdhd; + fo->obcd = obcdptr; + break; + } + } + if (++i == numobj) + error("findObjectInRoom: Object %d not found in room %d", id, room); + searchptr = NULL; + } + } + + if (findWhat & foImageHeader) { + searchptr = roomptr; + for (i = 0;;) { + if (_features & GF_SMALL_HEADER) + obimptr = findResourceSmall(MKID('OBIM'), searchptr); + else + obimptr = findResource(MKID('OBIM'), searchptr); + if (obimptr == NULL) + error("findObjectInRoom: Not enough image blocks in room %d", room); + imhd = (ImageHeader *)findResourceData(MKID('IMHD'), obimptr); + if (_features & GF_SMALL_HEADER) { + if (READ_LE_UINT16(obimptr + 6) == (uint16)id) { + fo->obim = obimptr; + fo->imhd = imhd; + break; + } + } else { + if (_features & GF_AFTER_V7) + id3 = READ_LE_UINT16(&imhd->v7.obj_id); + else + id3 = READ_LE_UINT16(&imhd->old.obj_id); + if (id3 == (uint16)id) { + fo->obim = obimptr; + fo->imhd = imhd; + break; + } + } + if (++i == numobj) + error("findObjectInRoom: Object %d image not found in room %d", id, room); + searchptr = NULL; + } + } +} + +int Scumm::getInventorySlot() +{ + int i; + for (i = 1; i <= _maxInventoryItems; i++) { + if (_inventory[i] == 0) + return i; + } + error("Inventory full, %d max items", _maxInventoryItems); + return -1; +} + +void Scumm::SamInventoryHack(int obj) +{ // FIXME: Sam and Max hack + int base = 6; + + while (base < 80) { + int value = readArray(178, 0, base); + if (value == obj) + return; + if (value == 0) { + _vars[179]++; + writeArray(178, 0, base, obj); + return; + } + base++; + } +} + +void Scumm::setOwnerOf(int obj, int owner) +{ + ScriptSlot *ss; + if (owner == 0) { + clearOwnerOf(obj); + ss = &vm.slot[_currentScript]; + if (ss->where == WIO_INVENTORY && _inventory[ss->number] == obj) { + putOwner(obj, 0); + runHook(0); + stopObjectCode(); + return; + } + } + if ((owner == 2) && (_gameId == GID_SAMNMAX)) + SamInventoryHack(obj); + + putOwner(obj, owner); + runHook(0); +} + +int Scumm::getObjX(int obj) +{ + if (obj < NUM_ACTORS) { + if (obj < 1) + return 0; /* fix for indy4's map */ + return derefActorSafe(obj, "getObjX")->x; + } else { + if (whereIsObject(obj) == WIO_NOT_FOUND) + return -1; + int x, y; + getObjectOrActorXY(obj, x, y); + return x; + } +} + +int Scumm::getObjY(int obj) +{ + if (obj < NUM_ACTORS) { + if (obj < 1) + return 0; /* fix for indy4's map */ + return derefActorSafe(obj, "getObjY")->y; + } else { + if (whereIsObject(obj) == WIO_NOT_FOUND) + return -1; + int x, y; + getObjectOrActorXY(obj, x, y); + return y; + } +} + +int Scumm::getObjOldDir(int obj) +{ + if (obj < NUM_ACTORS) { + return newDirToOldDir(derefActorSafe(obj, "getObjOldDir")->facing); + } else { + int x, y, dir; + getObjectXYPos(obj, x, y, dir); + return dir; + } +} + +int Scumm::getObjNewDir(int obj) +{ + if (obj < NUM_ACTORS) { + return derefActorSafe(obj, "getObjNewDir")->facing; + } else { + int x, y, dir; + getObjectXYPos(obj, x, y, dir); + return oldDirToNewDir(dir); + } +} + +int Scumm::findInventory(int owner, int idx) +{ + int count = 1, i, obj; + for (i = 0; i != _maxInventoryItems; i++) { + obj = _inventory[i]; + if (obj && getOwner(obj) == owner && count++ == idx) + return obj; + } + return 0; +} + +int Scumm::getInventoryCount(int owner) +{ + int i, obj; + int count = 0; + for (i = 0; i != _maxInventoryItems; i++) { + obj = _inventory[i]; + if (obj && getOwner(obj) == owner) + count++; + } + return count; +} + +void Scumm::setObjectState(int obj, int state, int x, int y) +{ + int i; + + i = getObjectIndex(obj); + if (i == -1) { + warning("setObjectState: no such object"); + return; + } + + if (x != -1) { + _objs[i].x_pos = x << 3; + _objs[i].y_pos = y << 3; + } + + addObjectToDrawQue(i); + putState(obj, state); +} + +static int getDist(int x, int y, int x2, int y2) +{ + int a = abs(y - y2); + int b = abs(x - x2); + if (a > b) + return a; + return b; +} + +int Scumm::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 < NUM_ACTORS) + i = derefActorSafe(b, "unkObjProc1")->scalex; + } else { + x = b; + y = c; + } + + if (is_obj_2) { + if (getObjectOrActorXY(e, x2, y2) == -1) + return -1; + if (e < NUM_ACTORS) + j = derefActorSafe(e, "unkObjProc1(2)")->scalex; + } else { + x2 = e; + y2 = f; + } + + return getDist(x, y, x2, y2) * 0xFF / ((i + j) >> 1); +} + +void Scumm::setCursorImg(uint img, uint room, uint imgindex) +{ + int w, h; + byte *dataptr, *bomp; + uint32 size; + FindObjectInRoom foir; + + if (room == (uint) - 1) + room = getObjectRoom(img); + + findObjectInRoom(&foir, foCodeHeader | foImageHeader | foCheckAlreadyLoaded, img, room); + + if (_features & GF_AFTER_V7) + setCursorHotspot2(READ_LE_UINT16(&foir.imhd->v7.hotspot[0].x), + READ_LE_UINT16(&foir.imhd->v7.hotspot[0].y)); + else + setCursorHotspot2(READ_LE_UINT16(&foir.imhd->old.hotspot[0].x), + READ_LE_UINT16(&foir.imhd->old.hotspot[0].y)); + + + if (!(_features & GF_AFTER_V7)) { + w = READ_LE_UINT16(&foir.cdhd->v6.w) >> 3; + h = READ_LE_UINT16(&foir.cdhd->v6.h) >> 3; + } else { + w = READ_LE_UINT16(&foir.imhd->v7.width) >> 3; + h = READ_LE_UINT16(&foir.imhd->v7.height) >> 3; + } + + dataptr = findResource(IMxx_tags[imgindex], foir.obim); + if (dataptr == NULL) + error("setCursorImg: No such image"); + + size = READ_BE_UINT32_UNALIGNED(dataptr + 4); + if (size > sizeof(_grabbedCursor)) + error("setCursorImg: Cursor image too large"); + + if ((bomp = findResource(MKID('BOMP'), dataptr)) != NULL) + useBompCursor(bomp, w, h); + else + useIm01Cursor(dataptr, w, h); + +} + +void Scumm::nukeFlObjects(int min, int max) +{ + ObjectData *od; + int i; + + warning("nukeFlObjects(%d,%d)", min, max); + + for (i = _numObjectsInRoom, od = _objs; --i >= 0; od++) + if (od->fl_object_index && od->obj_nr >= min && od->obj_nr <= max) { + nukeResource(rtFlObject, od->fl_object_index); + od->obj_nr = 0; + od->fl_object_index = 0; + } +} + +void Scumm::enqueueObject(int objectNumber, int objectX, int objectY, int objectWidth, + int objectHeight, int f, int g, int image, int mode) +{ + BlastObject *eo; + ObjectData *od; + + if (_enqueuePos == sizeof(_enqueuedObjects) / sizeof(_enqueuedObjects[0])) + error("enqueueObject: overflow"); + + eo = &_enqueuedObjects[_enqueuePos++]; + eo->number = objectNumber; + eo->areaX = _enqueue_b; + eo->areaY = _enqueue_c; + eo->areaWidth = _enqueue_d; + eo->areaHeight = _enqueue_e; + eo->posX = objectX + (camera._cur.x & 7); + eo->posY = objectY + (camera._cur.y - 100); + if (objectWidth == 0) { + od = &_objs[getObjectIndex(objectNumber)]; + eo->width = od->width; + } else { + eo->width = objectWidth; + } + if (objectHeight == 0) { + od = &_objs[getObjectIndex(objectNumber)]; + eo->height = od->height; + } else { + eo->height = objectHeight; + } + + eo->unk3 = f; + eo->unk4 = g; + eo->image = image; + + eo->mode = mode; +} + +void Scumm::drawBlastObjects() +{ + BlastObject *eo; + int i; + + eo = _enqueuedObjects; + for (i = 0; i < _enqueuePos; i++, eo++) { + drawBlastObject(eo); + } +} + + +void Scumm::drawBlastObject(BlastObject *eo) +{ + VirtScreen *vs; + byte *bomp, *ptr, *img; + int idx; + BompDrawData bdd; + + vs = &virtscr[0]; + + _lastXstart = vs->xstart; + + checkRange(_numGlobalObjects - 1, 30, eo->number, "Illegal Blast object %d"); + + idx = _objs[getObjectIndex(eo->number)].fl_object_index; + + if (idx) { + ptr = getResourceAddress(rtFlObject, idx); + ptr = findResource(MKID('OBIM'), ptr); + } else { + idx = getObjectIndex(eo->number); + assert(idx != -1); + ptr = getResourceAddress(1, _roomResource) + _objs[idx].offs_obim_to_room; + } + if (!ptr) + error("BlastObject object %d image not found", eo->number); + + img = findResource(IMxx_tags[eo->image], ptr); + if (!img) + img = findResource(IMxx_tags[1], ptr); // Backward compatibility with samnmax blast objects + if (!img) + error("blast-object %d invalid image %d (1-x)", eo->number, eo->image); + + bomp = findResourceData(MKID('BOMP'), img); + if (!bomp) + error("object %d is not a blast object", eo->number); + + bdd.srcwidth = READ_LE_UINT16(&((BompHeader *)bomp)->width); + bdd.srcheight = READ_LE_UINT16(&((BompHeader *)bomp)->height); + + bdd.out = vs->screenPtr + vs->xstart; + bdd.outwidth = vs->width; + bdd.outheight = vs->height; + bdd.dataptr = bomp + 10; + bdd.x = eo->posX; + bdd.y = eo->posY; + bdd.scale_x = (unsigned char)eo->unk3; + bdd.scale_y = (unsigned char)eo->unk4; + + drawBomp(&bdd, 0, bdd.dataptr, 1, 0); + updateDirtyRect(vs->number, bdd.x, bdd.x + bdd.srcwidth, bdd.y, bdd.y + bdd.srcheight, 0); +} + +void Scumm::removeBlastObjects() +{ + BlastObject *eo; + int i; + + eo = _enqueuedObjects; + for (i = 0; i < _enqueuePos; i++, eo++) { + removeBlastObject(eo); + } + + clearEnqueue(); +} + +void Scumm::removeBlastObject(BlastObject *eo) +{ + restoreBG(eo->posX, eo->posY, eo->posX + eo->width, eo->posY + eo->height); +} + +int Scumm::findFlObjectSlot() +{ + int i; + for (i = 1; i < _maxFLObject; i++) { + if (_baseFLObject[i] == NULL) + return i; + } + error("findFlObjectSlot: Out of FLObject slots"); + return -1; +} + +void Scumm::loadFlObject(uint object, uint room) +{ + FindObjectInRoom foir; + int slot; + ObjectData *od; + byte *flob, *roomptr; + uint32 obcd_size, obim_size, flob_size; + + /* Don't load an already loaded object */ + if (whereIsObject(object) != WIO_NOT_FOUND) + 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 */ + if (++_numObjectsInRoom > _numLocalObjects) + error("loadFlObject: Local Object Table overflow"); + od = &_objs[_numObjectsInRoom]; + + /* Setup sizes */ + obcd_size = READ_BE_UINT32_UNALIGNED(foir.obcd + 4); + od->offs_obcd_to_room = 8; + od->offs_obim_to_room = obcd_size + 8; + obim_size = READ_BE_UINT32_UNALIGNED(foir.obim + 4); + flob_size = obcd_size + obim_size + 8; + + /* Allocate slot & memory for floating object */ + slot = findFlObjectSlot(); + createResource(rtFlObject, slot, flob_size); + + /* Copy object code + object image to floating object */ + roomptr = getResourceAddress(rtRoom, room); + flob = getResourceAddress(rtFlObject, slot); + ((uint32 *)flob)[0] = MKID('FLOB'); + ((uint32 *)flob)[1] = TO_BE_32(flob_size); + memcpy(flob + 8, roomptr - foir.roomptr + foir.obcd, obcd_size); + memcpy(flob + 8 + obcd_size, roomptr - foir.roomptr + foir.obim, obim_size); + + /* Setup local object flags */ + setupRoomObject(od, flob); + + od->fl_object_index = slot; +} -- cgit v1.2.3