/* 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; // FIXME: It isn't enough for the Indy3 intro to make the // little trains ignore boxes (class 22), they have to always // clip (class 21) as well. Is this yet another walkbox 0 // error? if (_gameId == GID_INDY3_256 && cls == 22 && _currentRoom == 76) putClass(obj, 21, set); } 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->OBIMoffset; } assert(ptr); imhd = (ImageHeader *)findResourceData(MKID('IMHD'), ptr); if (_features & GF_AFTER_V8) { 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); } else 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; 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->OBIMoffset; } 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; // Sam & Max needs this to fix object-layering problems with // the inventory and conversation icons. if ((_features & GF_AFTER_V7 || _gameId == GID_SAMNMAX) && getClass(od->obj_nr, 22)) flags |= Gdi::dbDrawMaskOnAll; gdi.drawBitmap(ptr, &virtscr[0], x, ypos, height, x - xpos, numstrip, flags); } } void Scumm::loadRoomObjects() { int i, j; ObjectData *od; byte *ptr; uint16 obim_id; byte *room, *searchptr, *rootptr; ImageHeader *imhd; RoomHeader *roomhdr; CodeHeader *cdhd; CHECK_HEAP room = getResourceAddress(rtRoom, _roomResource); roomhdr = (RoomHeader *)findResourceData(MKID('RMHD'), room); if (_features & GF_AFTER_V8) _numObjectsInRoom = (byte)READ_LE_UINT32(&(roomhdr->v8.numObjects)); else 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]; if (_features & GF_AFTER_V8) searchptr = rootptr = getResourceAddress(rtRoomScripts, _roomResource); else searchptr = rootptr = room; assert(searchptr); 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->OBCDoffset = ptr - rootptr; 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); if (_features & GF_AFTER_V8) // TODO - maybe V8 is not the only that needs this? ptr = findResource(MKID('VERB'), ptr, 0); 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_V8) // 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. obim_id = _objectIDMap[imhd->v8.name]; else 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].OBIMoffset = ptr - room; } searchptr = NULL; } for (i = 1; i <= _numObjectsInRoom; i++) { setupRoomObject(&_objs[i], 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->OBCDoffset = 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].OBIMoffset = ptr - room; } searchptr = NULL; } for (i = 1; i <= _numObjectsInRoom; i++) { setupRoomObject(&_objs[i], room); } CHECK_HEAP } void Scumm::setupRoomObject(ObjectData *od, byte *room, byte *searchptr) { CodeHeader *cdhd = NULL; ImageHeader *imhd = NULL; assert(room); if (_features & GF_SMALL_HEADER) { byte *ptr = room + od->OBCDoffset; 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; } if (searchptr == NULL) { if (_features & GF_AFTER_V8) searchptr = getResourceAddress(rtRoomScripts, _roomResource); else searchptr = room; } cdhd = (CodeHeader *)findResourceData(MKID('CDHD'), searchptr + od->OBCDoffset); if (cdhd == NULL) error("Room %d missing CDHD blocks(s)", _roomResource); if (_features & GF_AFTER_V8) { od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id)); od->parent = cdhd->v7.parent; od->parentstate = cdhd->v7.parentstate; imhd = (ImageHeader *)findResourceData(MKID('IMHD'), room + od->OBIMoffset); 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); od->actordir = (byte)READ_LE_UINT32(&imhd->v8.actordir); } else if (_features & GF_AFTER_V7) { od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id)); od->parent = cdhd->v7.parent; od->parentstate = cdhd->v7.parentstate; imhd = (ImageHeader *)findResourceData(MKID('IMHD'), room + od->OBIMoffset); 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); } else if (_features & GF_AFTER_V6) { 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; } else { od->obj_nr = READ_LE_UINT16(&(cdhd->v5.obj_id)); 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; } 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].OBCDoffset; } } 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; if (_features & GF_AFTER_V8) return getResourceAddress(rtRoomScripts, _roomResource) + _objs[i].OBCDoffset; else return getResourceAddress(rtRoom, _roomResource) + _objs[i].OBCDoffset; } } } return 0; } void Scumm::addObjectToInventory(uint obj, uint room) { int i, slot; uint32 size; byte *ptr, *dst; 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); } 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); ptr = foir.obcd; } slot = getInventorySlot(); _inventory[slot] = obj; createResource(rtInventory, slot, size); dst = getResourceAddress(rtInventory, slot); assert(dst); memcpy(dst, ptr, 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_V8) numobj = READ_LE_UINT32(&(roomhdr->v8.numObjects)); else 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 (_features & GF_AFTER_V8) { roomptr = getResourceAddress(rtRoomScripts, room); } if (findWhat & foCodeHeader) { searchptr = roomptr; assert(searchptr); 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); cdhd = (CodeHeader *)findResourceData(MKID('CDHD'), obcdptr); if (_features & GF_SMALL_HEADER) id2 = READ_LE_UINT16(obcdptr + 6); else 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; } } roomptr = fo->roomptr; if (findWhat & foImageHeader) { searchptr = roomptr; assert(searchptr); 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) id3 = READ_LE_UINT16(obimptr + 6); else if (_features & GF_AFTER_V8) // 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. id3 = _objectIDMap[imhd->v8.name]; 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_V8) { setCursorHotspot2(READ_LE_UINT32(&foir.imhd->v8.hotspot[0].x), READ_LE_UINT32(&foir.imhd->v8.hotspot[0].y)); w = READ_LE_UINT32(&foir.imhd->v8.width) >> 3; h = READ_LE_UINT32(&foir.imhd->v8.height) >> 3; } else if (_features & GF_AFTER_V7) { setCursorHotspot2(READ_LE_UINT16(&foir.imhd->v7.hotspot[0].x), READ_LE_UINT16(&foir.imhd->v7.hotspot[0].y)); w = READ_LE_UINT16(&foir.imhd->v7.width) >> 3; h = READ_LE_UINT16(&foir.imhd->v7.height) >> 3; } else { setCursorHotspot2(READ_LE_UINT16(&foir.imhd->old.hotspot[0].x), READ_LE_UINT16(&foir.imhd->old.hotspot[0].y)); w = READ_LE_UINT16(&foir.cdhd->v6.w) >> 3; h = READ_LE_UINT16(&foir.cdhd->v6.h) >> 3; } // TODO - for V8 don't use IMxx_tags. Rather, we do something similiar to the V8 // code in drawBlastObject. It would be *much* nicer if we could aggregate this // common code into some helper functions, instead of having long convuluted // cases scattered all over the place. if (_features & GF_AFTER_V8) { dataptr = findResource(MKID('IMAG'), foir.obim); assert(dataptr); dataptr = findResource(MKID('WRAP'), dataptr); assert(dataptr); dataptr = findResource(MKID('OFFS'), dataptr); assert(dataptr); dataptr += READ_LE_UINT32(dataptr + 4 + 4*imgindex); // TODO - distinguish between SMAP and BOMP here? // HACK - adjust dataptr here until bomp code gets adjusted for V8 bomp = dataptr; } else { 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"); bomp = findResource(MKID('BOMP'), dataptr); } if (bomp != 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 scaleX, int scaleY, int image, int mode) { BlastObject *eo; ObjectData *od; if (_enqueuePos == sizeof(_enqueuedObjects) / sizeof(_enqueuedObjects[0])) { warning("enqueueObject: overflow"); return; } eo = &_enqueuedObjects[_enqueuePos++]; eo->number = objectNumber; eo->posX = objectX + (camera._cur.x & 7); eo->posY = objectY + (camera._cur.y - (_realHeight / 2)); 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->scaleX = scaleX; eo->scaleY = scaleY; 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]; 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(rtRoom, _roomResource) + _objs[idx].OBIMoffset; } if (!ptr) error("BlastObject object %d (%d) image not found", eo->number, idx); if (_features & GF_AFTER_V8) { // The OBIM contains an IMAG, which in turn contains a WRAP, which contains // an OFFS chunk and multiple BOMP chunks. To find the right BOMP, we can // either use the offsets in the OFFS chunk, or iterate over all BOMPs we find. // Here we use the first method. img = findResource(MKID('IMAG'), ptr); assert(img); img = findResource(MKID('WRAP'), img); assert(img); img = findResource(MKID('OFFS'), img); assert(img); bomp = img + READ_LE_UINT32(img + 4 + 4*eo->image) + 8; } else { 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); //hexdump(bomp,32); if (_features & GF_AFTER_V8) { bdd.srcwidth = READ_LE_UINT32(&((BompHeader *)bomp)->v8.width); bdd.srcheight = READ_LE_UINT32(&((BompHeader *)bomp)->v8.height); } else { bdd.srcwidth = READ_LE_UINT16(&((BompHeader *)bomp)->old.width); bdd.srcheight = READ_LE_UINT16(&((BompHeader *)bomp)->old.height); } bdd.out = vs->screenPtr + vs->xstart; bdd.outwidth = vs->width; bdd.outheight = vs->height; if (_features & GF_AFTER_V8) { bdd.dataptr = bomp + 8; // Why this? See also useBompCursor } else { bdd.dataptr = bomp + 10; // Why this? See also useBompCursor } bdd.x = eo->posX; bdd.y = eo->posY; bdd.scale_x = (byte)eo->scaleX; bdd.scale_y = (byte)eo->scaleY; byte bomp_scalling_x[64], bomp_scalling_y[64]; if ((bdd.scale_x != 255) || (bdd.scale_y != 255)) { _bompScallingXPtr = bomp_scalling_x; _bompScallingYPtr = bomp_scalling_y; _bompScaleRight = setupBompScale(_bompScallingXPtr, bdd.srcwidth, bdd.scale_x); _bompScaleBottom = setupBompScale(_bompScallingYPtr, bdd.srcheight, bdd.scale_y); bdd.shadowMode = 0; drawBomp(&bdd, 1, 3); } else { bdd.shadowMode = eo->mode; drawBomp(&bdd, 1, 0); } _bompScallingXPtr = NULL; _bompScallingYPtr = NULL; _bompScaleRight = 0; _bompScaleBottom = 0; updateDirtyRect(vs->number, bdd.x, bdd.x + bdd.srcwidth, bdd.y, bdd.y + bdd.srcheight, 0); } byte _bompScaleTable[] = { 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255, 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255, }; byte _bompBitsTable[] = { 8, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 4, 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0, }; int32 Scumm::setupBompScale(byte * scalling, int32 size, byte scale) { uint32 tmp; int32 count = (size + 7) >> 3; byte * tmp_ptr = _bompScaleTable + (256 - (size >> 1)); byte * tmp_scalling = scalling; byte a = 0; while((count--) != 0) { tmp = *(tmp_ptr + 3); a <<= 1; if (scale < tmp) { a |= 1; } tmp = *(tmp_ptr + 2); a <<= 1; if (scale < tmp) { a |= 1; } tmp = *(tmp_ptr + 1); a <<= 1; if (scale < tmp) { a |= 1; } tmp = *(tmp_ptr + 0); a <<= 1; if (scale < tmp) { a |= 1; } tmp_ptr += 4; tmp = *(tmp_ptr + 3); a <<= 1; if (scale < tmp) { a |= 1; } tmp = *(tmp_ptr + 2); a <<= 1; if (scale < tmp) { a |= 1; } tmp = *(tmp_ptr + 1); a <<= 1; if (scale < tmp) { a |= 1; } tmp = *(tmp_ptr + 0); a <<= 1; if (scale < tmp) { a |= 1; } tmp_ptr += 4; *(tmp_scalling++) = a; } if ((size & 7) != 0) { *(tmp_scalling - 1) |= revBitMask[size & 7]; } count = (size + 7) >> 3; byte ret_value = 0; while((count--) != 0) { ret_value += *(*(scalling++) + _bompBitsTable); } return ret_value; } void Scumm::removeBlastObjects() { BlastObject *eo; int i; eo = _enqueuedObjects; for (i = 0; i < _enqueuePos; i++, eo++) { removeBlastObject(eo); } clearEnqueue(); } void Scumm::removeBlastObject(BlastObject *eo) { VirtScreen *vs = &virtscr[0]; int top, bottom, left, right; int left_strip, right_strip; int i; top = eo->posY; bottom = eo->posY + eo->height; left = eo->posX; right = eo->posX + eo->width; if (bottom < 0 || right < 0 || top > vs->height || left > vs->width) return; if (top < 0) top = 0; if (bottom > vs->height) bottom = vs->height; if (left < 0) left = 0; if (right > vs->width) right = vs->width; left_strip = left >> 3; right_strip = (right >> 3) + 1; if (left_strip < 0) left_strip = 0; if (_features & GF_AFTER_V7) { if (right_strip > 409) right_strip = 409; } else { if (right_strip >= 200) right_strip = 200; } for (i = left_strip; i <= right_strip; i++) gdi.resetBackground(top, bottom, i); updateDirtyRect(0, left, right, top, bottom, 0x40000000); } 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->OBCDoffset = 8; od->OBIMoffset = obcd_size + 8; obim_size = READ_BE_UINT32_UNALIGNED(foir.obim + 4); flob_size = obcd_size + obim_size + 8; // Get room pointer roomptr = getResourceAddress(rtRoom, room); assert(roomptr); // Allocate slot & memory for floating object slot = findFlObjectSlot(); createResource(rtFlObject, slot, flob_size); flob = getResourceAddress(rtFlObject, slot); 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, 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, flob); od->fl_object_index = slot; }