1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "scumm/actor.h"
24 #include "scumm/bomp.h"
25 #include "scumm/he/intern_he.h"
26 #include "scumm/object.h"
27 #include "scumm/resource.h"
28 #include "scumm/scumm_v0.h"
29 #include "scumm/scumm_v8.h"
30 #include "scumm/usage_bits.h"
31 #include "scumm/util.h"
32 
33 namespace Scumm {
34 
addObjectToInventory(uint obj,uint room)35 void ScummEngine::addObjectToInventory(uint obj, uint room) {
36 	int idx, slot;
37 	uint32 size;
38 	const byte *ptr;
39 	byte *dst;
40 	FindObjectInRoom foir;
41 
42 	debug(1, "Adding object %d from room %d into inventory", obj, room);
43 
44 	if (whereIsObject(obj) == WIO_FLOBJECT) {
45 		idx = getObjectIndex(obj);
46 		assert(idx >= 0);
47 		ptr = getResourceAddress(rtFlObject, _objs[idx].fl_object_index) + 8;
48 		assert(ptr);
49 		size = READ_BE_UINT32(ptr + 4);
50 	} else {
51 		findObjectInRoom(&foir, foCodeHeader, obj, room);
52 		if (_game.features & GF_OLD_BUNDLE)
53 			size = READ_LE_UINT16(foir.obcd);
54 		else if (_game.features & GF_SMALL_HEADER)
55 			size = READ_LE_UINT32(foir.obcd);
56 		else
57 			size = READ_BE_UINT32(foir.obcd + 4);
58 		ptr = foir.obcd;
59 	}
60 
61 	slot = getInventorySlot();
62 	_inventory[slot] = obj;
63 	dst = _res->createResource(rtInventory, slot, size);
64 	assert(dst);
65 	memcpy(dst, ptr, size);
66 }
67 
getInventorySlot()68 int ScummEngine::getInventorySlot() {
69 	int i;
70 	for (i = 0; i < _numInventory; i++) {
71 		if (_inventory[i] == 0)
72 			return i;
73 	}
74 	error("Inventory full, %d max items", _numInventory);
75 	return -1;
76 }
77 
findInventory(int owner,int idx)78 int ScummEngine::findInventory(int owner, int idx) {
79 	int count = 1, i, obj;
80 	for (i = 0; i < _numInventory; i++) {
81 		obj = _inventory[i];
82 		if (obj && getOwner(obj) == owner && count++ == idx)
83 			return obj;
84 	}
85 	return 0;
86 }
87 
getInventoryCount(int owner)88 int ScummEngine::getInventoryCount(int owner) {
89 	int i, obj;
90 	int count = 0;
91 	for (i = 0; i < _numInventory; i++) {
92 		obj = _inventory[i];
93 		if (obj && getOwner(obj) == owner)
94 			count++;
95 	}
96 	return count;
97 }
98 
setOwnerOf(int obj,int owner)99 void ScummEngine::setOwnerOf(int obj, int owner) {
100 	ScriptSlot *ss;
101 
102 	// In Sam & Max this is necessary, or you won't get your stuff back
103 	// from the Lost and Found tent after riding the Cone of Tragedy. But
104 	// it probably applies to all V6+ games. See bugs #52 and #1507.
105 	// FT disassembly is checked, behavior is correct. [sev]
106 
107 	int arg = (_game.version >= 6) ? obj : 0;
108 
109 	// WORKAROUND for bug #3657: Game crash when finishing Indy3 demo.
110 	// Script 94 tries to empty the inventory but does so in a bogus way.
111 	// This causes it to try to remove object 0 from the inventory.
112 	if (_game.id == GID_PASS && obj == 0 && vm.slot[_currentScript].number == 94)
113 		return;
114 
115 	// WORKAROUND for bug #6802: assert() was triggered in freddi2.
116 	// Bug is in room 39. Problem is script 10, in the localvar2==78 case;
117 	// this only sets the obj id if var198 is non-zero, but in the asserting
118 	// case, it is obj 0. That means two setOwnerOf calls are made with obj 0.
119 	// The correct setOwnerOf calls are made afterwards, so just ignoring this
120 	// seems to work just fine.
121 	if (_game.id == GID_HEGAME && obj == 0 && _currentRoom == 39 && vm.slot[_currentScript].number == 10)
122 		return;
123 
124 	assert(obj > 0);
125 
126 	if (owner == 0) {
127 		clearOwnerOf(obj);
128 
129 		// FIXME: See bug #2771 and many others. Essentially, the following
130 		// code, while matching disasm of various versions of the SCUMM engine,
131 		// is total bullocks, and leads to odd crashes due to out-of-bounds
132 		// array (read) access. Three "famous" crashes were caused by this:
133 		//   Monkey Island 1: Using meat with flower
134 		//   FOA: Using ribcage with another item
135 		//   DOTT: Using stamp with contract
136 		//
137 		// The bad code:
138 		//   if (ss->where == WIO_INVENTORY && _inventory[ss->number] == obj) {
139 		// That check makes no sense at all: _inventory only contains 80 items,
140 		// which are in the order the player picked up items. We can only
141 		// guess that the SCUMM coders meant to write
142 		//   if (ss->where == WIO_INVENTORY && ss->number == obj) {
143 		// which would ensure that an object script that nukes itself gets
144 		// stopped. Alas, we can't just make that change, since it could
145 		// lead to new regressions.
146 		// Another fix would be to completely remove this check, which should
147 		// not cause much problems, since it'll only succeed by pure chance.
148 		//
149 		// For now we follow a more defensive route: We perform the check
150 		// if ss->number is small enough.
151 
152 		ss = &vm.slot[_currentScript];
153 		if (ss->where == WIO_INVENTORY) {
154 			if (ss->number < _numInventory && _inventory[ss->number] == obj) {
155 				error("Odd setOwnerOf case #1: Please report to Fingolfin where you encountered this");
156 				putOwner(obj, 0);
157 				runInventoryScript(arg);
158 				stopObjectCode();
159 				return;
160 			}
161 			if (ss->number == obj)
162 				error("Odd setOwnerOf case #2: Please report to Fingolfin where you encountered this");
163 		}
164 	}
165 
166 	putOwner(obj, owner);
167 	runInventoryScript(arg);
168 }
169 
clearOwnerOf(int obj)170 void ScummEngine::clearOwnerOf(int obj) {
171 	int i;
172 
173 	// Stop the associated object script code (else crashes might occurs)
174 	stopObjectScript(obj);
175 
176 	// If the object is "owned" by a the current room, we scan the
177 	// object list and (only if it's a floating object) nuke it.
178 	if (getOwner(obj) == OF_OWNER_ROOM) {
179 		for (i = 0; i < _numLocalObjects; i++)  {
180 			if (_objs[i].obj_nr == obj && _objs[i].fl_object_index) {
181 				// Removing an flObject from a room means we can nuke it
182 				_res->nukeResource(rtFlObject, _objs[i].fl_object_index);
183 				_objs[i].obj_nr = 0;
184 				_objs[i].fl_object_index = 0;
185 			}
186 		}
187 	} else {
188 
189 		// Alternatively, scan the inventory to see if the object is in there...
190 		for (i = 0; i < _numInventory; i++) {
191 			if (_inventory[i] == obj) {
192 				assert(WIO_INVENTORY == whereIsObject(obj));
193 
194 				// Found the object! Nuke it from the inventory.
195 				_res->nukeResource(rtInventory, i);
196 				_inventory[i] = 0;
197 
198 				// Now fill up the gap removing the object from the inventory created.
199 				for (i = 0; i < _numInventory - 1; i++) {
200 					if (!_inventory[i] && _inventory[i+1]) {
201 						_inventory[i] = _inventory[i+1];
202 						_inventory[i+1] = 0;
203 						// FIXME FIXME FIXME: This is incomplete, as we do not touch flags, status... BUG
204 						_res->_types[rtInventory][i]._address = _res->_types[rtInventory][i + 1]._address;
205 						_res->_types[rtInventory][i]._size = _res->_types[rtInventory][i + 1]._size;
206 						_res->_types[rtInventory][i + 1]._address = NULL;
207 						_res->_types[rtInventory][i + 1]._size = 0;
208 					}
209 				}
210 				break;
211 			}
212 		}
213 	}
214 }
215 
getClass(int obj,int cls) const216 bool ScummEngine::getClass(int obj, int cls) const {
217 	if (_game.version == 0)
218 		return false;
219 
220 	assertRange(0, obj, _numGlobalObjects - 1, "object");
221 	cls &= 0x7F;
222 	assertRange(1, cls, 32, "class");
223 
224 	if (_game.features & GF_SMALL_HEADER) {
225 		// Translate the new (V5) object classes to the old classes
226 		// (for those which differ).
227 		switch (cls) {
228 		case kObjectClassUntouchable:
229 			cls = 24;
230 			break;
231 		case kObjectClassPlayer:
232 			cls = 23;
233 			break;
234 		case kObjectClassXFlip:
235 			cls = 19;
236 			break;
237 		case kObjectClassYFlip:
238 			cls = 18;
239 			break;
240 		default:
241 			break;
242 		}
243 	}
244 
245 	return (_classData[obj] & (1 << (cls - 1))) != 0;
246 }
247 
putClass(int obj,int cls,bool set)248 void ScummEngine::putClass(int obj, int cls, bool set) {
249 	if (_game.version == 0)
250 		return;
251 
252 	assertRange(0, obj, _numGlobalObjects - 1, "object");
253 	cls &= 0x7F;
254 	assertRange(1, cls, 32, "class");
255 
256 	if (_game.features & GF_SMALL_HEADER) {
257 		// Translate the new (V5) object classes to the old classes
258 		// (for those which differ).
259 		switch (cls) {
260 		case kObjectClassUntouchable:
261 			cls = 24;
262 			break;
263 		case kObjectClassPlayer:
264 			cls = 23;
265 			break;
266 		case kObjectClassXFlip:
267 			cls = 19;
268 			break;
269 		case kObjectClassYFlip:
270 			cls = 18;
271 			break;
272 		default:
273 			break;
274 		}
275 	}
276 
277 	if (set)
278 		_classData[obj] |= (1 << (cls - 1));
279 	else
280 		_classData[obj] &= ~(1 << (cls - 1));
281 
282 	if (_game.version <= 4 && obj >= 1 && obj < _numActors) {
283 		_actors[obj]->classChanged(cls, set);
284 	}
285 }
286 
getOwner(int obj) const287 int ScummEngine::getOwner(int obj) const {
288 	assertRange(0, obj, _numGlobalObjects - 1, "object");
289 	return _objectOwnerTable[obj];
290 }
291 
putOwner(int obj,int owner)292 void ScummEngine::putOwner(int obj, int owner) {
293 	assertRange(0, obj, _numGlobalObjects - 1, "object");
294 	assertRange(0, owner, 0xFF, "owner");
295 	_objectOwnerTable[obj] = owner;
296 }
297 
getState(int obj)298 int ScummEngine::getState(int obj) {
299 	assertRange(0, obj, _numGlobalObjects - 1, "object");
300 
301 	if (!_copyProtection) {
302 		// I knew LucasArts sold cracked copies of the original Maniac Mansion,
303 		// at least as part of Day of the Tentacle. Apparently they also sold
304 		// cracked versions of the enhanced version. At least in Germany.
305 		//
306 		// This will keep the security door open at all times. I can only
307 		// assume that 182 and 193 each correspond to one particular side of
308 		// it. Fortunately this does not prevent frustrated players from
309 		// blowing up the mansion, should they feel the urge to.
310 
311 		if (_game.id == GID_MANIAC && _game.version != 0 && (obj == 182 || obj == 193))
312 			_objectStateTable[obj] |= kObjectState_08;
313 	}
314 
315 	return _objectStateTable[obj];
316 }
317 
putState(int obj,int state)318 void ScummEngine::putState(int obj, int state) {
319 	assertRange(0, obj, _numGlobalObjects - 1, "object");
320 	assertRange(0, state, 0xFF, "state");
321 	_objectStateTable[obj] = state;
322 }
323 
getObjectRoom(int obj) const324 int ScummEngine::getObjectRoom(int obj) const {
325 	assertRange(0, obj, _numGlobalObjects - 1, "object");
326 	return _objectRoomTable[obj];
327 }
328 
getObjectIndex(int object) const329 int ScummEngine::getObjectIndex(int object) const {
330 	int i;
331 
332 	if (object < 1)
333 		return -1;
334 
335 	for (i = (_numLocalObjects-1); i > 0; i--) {
336 		if (_objs[i].obj_nr == object)
337 			return i;
338 	}
339 	return -1;
340 }
341 
whereIsObject(int object) const342 int ScummEngine::whereIsObject(int object) const {
343 	int i;
344 
345 	// Note: in MM v0 bg objects are greater _numGlobalObjects
346 	if (_game.version != 0 && object >= _numGlobalObjects)
347 		return WIO_NOT_FOUND;
348 
349 	if (object < 1)
350 		return WIO_NOT_FOUND;
351 
352 	if ((_game.version != 0 || OBJECT_V0_TYPE(object) == 0) &&
353 		 _objectOwnerTable[object] != OF_OWNER_ROOM)
354 	{
355 		for (i = 0; i < _numInventory; i++)
356 			if (_inventory[i] == object)
357 				return WIO_INVENTORY;
358 		return WIO_NOT_FOUND;
359 	}
360 
361 	for (i = (_numLocalObjects-1); i > 0; i--) {
362 		if (_objs[i].obj_nr == object) {
363 			if (_objs[i].fl_object_index)
364 				return WIO_FLOBJECT;
365 			return WIO_ROOM;
366 		}
367 	}
368 
369 	return WIO_NOT_FOUND;
370 }
371 
getObjectOrActorXY(int object,int & x,int & y)372 int ScummEngine::getObjectOrActorXY(int object, int &x, int &y) {
373 	Actor *act;
374 
375 	if (objIsActor(object)) {
376 		act = derefActorSafe(objToActor(object), "getObjectOrActorXY");
377 		if (act && act->isInCurrentRoom()) {
378 			x = act->getRealPos().x;
379 			y = act->getRealPos().y;
380 			return 0;
381 		} else
382 			return -1;
383 	}
384 
385 	switch (whereIsObject(object)) {
386 	case WIO_NOT_FOUND:
387 		return -1;
388 	case WIO_INVENTORY:
389 		if (objIsActor(_objectOwnerTable[object])) {
390 			act = derefActor(_objectOwnerTable[object], "getObjectOrActorXY(2)");
391 			if (act && act->isInCurrentRoom()) {
392 				x = act->getRealPos().x;
393 				y = act->getRealPos().y;
394 				return 0;
395 			}
396 		}
397 		return -1;
398 	default:
399 		break;
400 	}
401 	getObjectXYPos(object, x, y);
402 	return 0;
403 }
404 
405 /**
406  * Return the position of an object.
407  * Returns X, Y and direction in angles
408  */
getObjectXYPos(int object,int & x,int & y,int & dir)409 void ScummEngine::getObjectXYPos(int object, int &x, int &y, int &dir) {
410 	int idx = getObjectIndex(object);
411 	assert(idx >= 0);
412 	ObjectData &od = _objs[idx];
413 	int state;
414 	const byte *ptr;
415 	const ImageHeader *imhd;
416 
417 	if (_game.version >= 6) {
418 		state = getState(object) - 1;
419 		if (state < 0)
420 			state = 0;
421 
422 		ptr = getOBIMFromObjectData(od);
423 		if (!ptr) {
424 			// FIXME: We used to assert here, but it seems that in the nexus
425 			// in The Dig, this can happen, at least with old savegames, and
426 			// it's safe to continue...
427 			debug(0, "getObjectXYPos: Can't find object %d", object);
428 			return;
429 		}
430 		imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), ptr);
431 		assert(imhd);
432 		if (_game.version == 8) {
433 			switch (FROM_LE_32(imhd->v8.version)) {
434 			case 800:
435 				x = od.x_pos + (int32)READ_LE_UINT32((const byte *)imhd + 8 * state + 0x44);
436 				y = od.y_pos + (int32)READ_LE_UINT32((const byte *)imhd + 8 * state + 0x48);
437 				break;
438 			case 801:
439 				x = od.x_pos + (int32)READ_LE_UINT32(&imhd->v8.hotspot[state].x);
440 				y = od.y_pos + (int32)READ_LE_UINT32(&imhd->v8.hotspot[state].y);
441 				break;
442 			default:
443 				error("Unsupported image header version %d", FROM_LE_32(imhd->v8.version));
444 			}
445 		} else if (_game.version == 7) {
446 			x = od.x_pos + (int16)READ_LE_UINT16(&imhd->v7.hotspot[state].x);
447 			y = od.y_pos + (int16)READ_LE_UINT16(&imhd->v7.hotspot[state].y);
448 		} else {
449 			x = od.x_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].x);
450 			y = od.y_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].y);
451 		}
452 	} else if (_game.version <= 2) {
453 		x = od.walk_x;
454 		y = od.walk_y;
455 
456 		// Adjust x, y when no actor direction is set, but only perform this
457 		// adjustment for V0 games (e.g. MM C64), otherwise certain scenes in
458 		// newer games are affected as well (e.g. the interior of the Shuttle
459 		// Bus scene in Zak V2, where no actor is present). Refer to bug #6034.
460 		if (!od.actordir && _game.version == 0) {
461 			x = od.x_pos + od.width / 2;
462 			y = od.y_pos + od.height / 2;
463 		}
464 		x = x >> V12_X_SHIFT;
465 		y = y >> V12_Y_SHIFT;
466 	} else {
467 		x = od.walk_x;
468 		y = od.walk_y;
469 	}
470 	if (_game.version == 8)
471 		dir = fromSimpleDir(1, od.actordir);
472 	else
473 		dir = oldDirToNewDir(od.actordir & 3);
474 }
475 
getDist(int x,int y,int x2,int y2)476 int ScummEngine::getDist(int x, int y, int x2, int y2) {
477 	int a = ABS(y - y2);
478 	int b = ABS(x - x2);
479 	return MAX(a, b);
480 }
481 
getObjActToObjActDist(int a,int b)482 int ScummEngine::getObjActToObjActDist(int a, int b) {
483 	int x, y, x2, y2;
484 	Actor *acta = NULL;
485 	Actor *actb = NULL;
486 
487 	if (objIsActor(a))
488 		acta = derefActorSafe(objToActor(a), "getObjActToObjActDist");
489 
490 	if (objIsActor(b))
491 		actb = derefActorSafe(objToActor(b), "getObjActToObjActDist(2)");
492 
493 	if (acta && actb && acta->getRoom() == actb->getRoom() && acta->getRoom() && !acta->isInCurrentRoom())
494 		return 0;
495 
496 	if (getObjectOrActorXY(a, x, y) == -1)
497 		return 0xFF;
498 
499 	if (getObjectOrActorXY(b, x2, y2) == -1)
500 		return 0xFF;
501 
502 	// Perform adjustXYToBeInBox() *only* if the first item is an
503 	// actor and the second is an object. This used to not check
504 	// whether the second item is a non-actor, which caused bug
505 	// #1320).
506 	if (acta && !actb) {
507 		AdjustBoxResult r = acta->adjustXYToBeInBox(x2, y2);
508 		x2 = r.x;
509 		y2 = r.y;
510 	}
511 
512 
513 	// Now compute the distance between the two points
514 	return getDist(x, y, x2, y2);
515 }
516 
findObject(int x,int y)517 int ScummEngine::findObject(int x, int y) {
518 	int i, b;
519 	byte a;
520 	const int mask = (_game.version <= 2) ? kObjectState_08 : 0xF;
521 
522 	for (i = 1; i < _numLocalObjects; i++) {
523 		if ((_objs[i].obj_nr < 1) || getClass(_objs[i].obj_nr, kObjectClassUntouchable))
524 			continue;
525 
526 		if ((_game.version == 0 && OBJECT_V0_TYPE(_objs[i].obj_nr) == kObjectV0TypeFG) ||
527 			(_game.version > 0 && _game.version <= 2)) {
528 			if (_objs[i].state & kObjectStateUntouchable)
529 				continue;
530 		}
531 
532 		b = i;
533 		do {
534 			a = _objs[b].parentstate;
535 			b = _objs[b].parent;
536 			if (b == 0) {
537 #ifdef ENABLE_HE
538 				if (_game.heversion >= 71) {
539 					if (((ScummEngine_v71he *)this)->_wiz->polygonHit(_objs[i].obj_nr, x, y))
540 						return _objs[i].obj_nr;
541 				}
542 #endif
543 				if (_game.id == GID_CMI || _game.id == GID_DIG) {
544 					if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos >= x &&
545 						_objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos >= y)
546 						return _objs[i].obj_nr;
547 				} else {
548 					if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x &&
549 						_objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y)
550 						return _objs[i].obj_nr;
551 				}
552 				break;
553 			}
554 		} while ((_objs[b].state & mask) == a);
555 	}
556 
557 	return 0;
558 }
559 
drawRoomObject(int i,int arg)560 void ScummEngine::drawRoomObject(int i, int arg) {
561 	ObjectData *od;
562 	byte a;
563 	const int mask = (_game.version <= 2) ? kObjectState_08 : 0xF;
564 
565 	od = &_objs[i];
566 	if ((i < 1) || (od->obj_nr < 1) || !od->state)
567 		return;
568 
569 	do {
570 		a = od->parentstate;
571 		if (!od->parent) {
572 			if (_game.version <= 6 || od->fl_object_index == 0)
573 				drawObject(i, arg);
574 			break;
575 		}
576 		od = &_objs[od->parent];
577 	} while ((od->state & mask) == a);
578 }
579 
drawRoomObjects(int arg)580 void ScummEngine::drawRoomObjects(int arg) {
581 	int i;
582 	const int mask = (_game.version <= 2) ? kObjectState_08 : 0xF;
583 
584 	if (_game.heversion >= 60) {
585 		// In HE games, normal objects are drawn, followed by FlObjects.
586 		for (i = (_numLocalObjects-1); i > 0; i--) {
587 			if (_objs[i].obj_nr > 0 && (_objs[i].state & mask) && _objs[i].fl_object_index == 0)
588 				drawRoomObject(i, arg);
589 		}
590 		for (i = (_numLocalObjects-1); i > 0; i--) {
591 			if (_objs[i].obj_nr > 0 && (_objs[i].state & mask) && _objs[i].fl_object_index != 0)
592 				drawRoomObject(i, arg);
593 		}
594 	} else if (_game.id == GID_SAMNMAX) {
595 		// In Sam & Max, objects are drawn in reverse order.
596 		for (i = 1; i < _numLocalObjects; i++)
597 			if (_objs[i].obj_nr > 0)
598 				drawRoomObject(i, arg);
599 	} else {
600 		for (i = (_numLocalObjects-1); i > 0; i--)
601 			if (_objs[i].obj_nr > 0 && (_objs[i].state & mask)) {
602 				drawRoomObject(i, arg);
603 			}
604 	}
605 }
606 
drawObject(int obj,int arg)607 void ScummEngine::drawObject(int obj, int arg) {
608 	if (_skipDrawObject)
609 		return;
610 
611 	ObjectData &od = _objs[obj];
612 	int height, width;
613 	const byte *ptr;
614 	int x, a, numstrip;
615 	int tmp;
616 
617 	if (_bgNeedsRedraw)
618 		arg = 0;
619 
620 	if (od.obj_nr == 0)
621 		return;
622 
623 	assertRange(0, od.obj_nr, _numGlobalObjects - 1, "object");
624 
625 	const int xpos = od.x_pos / 8;
626 	const int ypos = od.y_pos;
627 
628 	width = od.width / 8;
629 	height = od.height &= 0xFFFFFFF8;	// Mask out last 3 bits
630 
631 	// Short circuit for objects which aren't visible at all.
632 	if (width == 0 || xpos > _screenEndStrip || xpos + width < _screenStartStrip)
633 		return;
634 
635 	// For objects without image in Apple II & Commodore 64 versions of Maniac Mansion
636 	if (_game.version == 0 && od.OBIMoffset == 0)
637 		return;
638 
639 	ptr = getObjectImage(getOBIMFromObjectData(od), getState(od.obj_nr));
640 	if (!ptr)
641 		return;
642 
643 	x = 0xFFFF;
644 
645 	for (a = numstrip = 0; a < width; a++) {
646 		tmp = xpos + a;
647 		if (tmp < _screenStartStrip || _screenEndStrip < tmp)
648 			continue;
649 		if (arg > 0 && _screenStartStrip + arg <= tmp)
650 			continue;
651 		if (arg < 0 && tmp <= _screenEndStrip + arg)
652 			continue;
653 		setGfxUsageBit(tmp, USAGE_BIT_DIRTY);
654 		if (tmp < x)
655 			x = tmp;
656 		numstrip++;
657 	}
658 
659 	if (numstrip != 0) {
660 		byte flags = od.flags | Gdi::dbObjectMode;
661 
662 		// Sam & Max needs this to fix object-layering problems with
663 		// the inventory and conversation icons.
664 		if ((_game.id == GID_SAMNMAX && getClass(od.obj_nr, kObjectClassIgnoreBoxes)) ||
665 		    (_game.id == GID_FT && getClass(od.obj_nr, kObjectClassPlayer)))
666 			flags |= Gdi::dbDrawMaskOnAll;
667 
668 #ifdef ENABLE_HE
669 		if (_game.heversion >= 70 && findResource(MKTAG('S','M','A','P'), ptr) == NULL)
670 			_gdi->drawBMAPObject(ptr, &_virtscr[kMainVirtScreen], obj, od.x_pos, od.y_pos, od.width, od.height);
671 		else
672 #endif
673 			_gdi->drawBitmap(ptr, &_virtscr[kMainVirtScreen], x, ypos, width * 8, height, x - xpos, numstrip, flags);
674 	}
675 }
676 
clearRoomObjects()677 void ScummEngine::clearRoomObjects() {
678 	int i;
679 
680 	if (_game.features & GF_SMALL_HEADER) {
681 		for (i = 0; i < _numLocalObjects; i++) {
682 			_objs[i].obj_nr = 0;
683 		}
684 	} else {
685 		for (i = 0; i < _numLocalObjects; i++) {
686 			if (_objs[i].obj_nr < 1)	// Optimise codepath
687 				continue;
688 
689 			// Nuke all non-flObjects (flObjects are nuked in script.cpp)
690 			if (_objs[i].fl_object_index == 0) {
691 				_objs[i].obj_nr = 0;
692 			} else {
693 				// Nuke all unlocked flObjects
694 				if (!_res->isLocked(rtFlObject, _objs[i].fl_object_index)) {
695 					_res->nukeResource(rtFlObject, _objs[i].fl_object_index);
696 					_objs[i].obj_nr = 0;
697 					_objs[i].fl_object_index = 0;
698 				}
699 			}
700 		}
701 	}
702 }
703 
resetRoomObjects()704 void ScummEngine_v70he::resetRoomObjects() {
705 	ScummEngine_v60he::resetRoomObjects();
706 	restoreFlObjects();
707 }
708 
clearRoomObjects()709 void ScummEngine_v70he::clearRoomObjects() {
710 	_numStoredFlObjects = 0;
711 
712 	for (int i = 0; i < _numLocalObjects; i++) {
713 		if (_objs[i].obj_nr < 1)	// Optimise codepath
714 			continue;
715 
716 		if (_objs[i].fl_object_index != 0) {
717 			if (!_res->isLocked(rtFlObject, _objs[i].fl_object_index)) {
718 				_res->nukeResource(rtFlObject, _objs[i].fl_object_index);
719 			} else {
720 				storeFlObject(i);
721 			}
722 		}
723 		_objs[i].fl_object_index = 0;
724 		_objs[i].obj_nr = 0;
725 	}
726 
727 	if (_currentRoom == 0)
728 		restoreFlObjects();
729 }
730 
storeFlObject(int slot)731 void ScummEngine_v70he::storeFlObject(int slot) {
732 	memcpy(&_storedFlObjects[_numStoredFlObjects], &_objs[slot], sizeof(_objs[slot]));
733 	_numStoredFlObjects++;
734 	if (_numStoredFlObjects > 100)
735 		error("Too many flobjects saved on room transition");
736 }
737 
restoreFlObjects()738 void ScummEngine_v70he::restoreFlObjects() {
739 	int i, slot;
740 
741 	for (i = 0; i < _numStoredFlObjects; i++) {
742 		slot = findLocalObjectSlot();
743 		memcpy(&_objs[slot], &_storedFlObjects[i], sizeof(_objs[slot]));
744 	}
745 
746 	_numStoredFlObjects = 0;
747 }
748 
resetRoomObjects()749 void ScummEngine::resetRoomObjects() {
750 	int i, j;
751 	ObjectData *od;
752 	const byte *ptr;
753 	uint16 obim_id;
754 	const byte *room, *searchptr, *rootptr;
755 	const CodeHeader *cdhd;
756 
757 	room = getResourceAddress(rtRoom, _roomResource);
758 	assert(room);
759 
760 	if (_numObjectsInRoom == 0)
761 		return;
762 
763 	if (_numObjectsInRoom > _numLocalObjects)
764 		error("More than %d objects in room %d", _numLocalObjects, _roomResource);
765 
766 	if (_game.version == 8)
767 		searchptr = rootptr = getResourceAddress(rtRoomScripts, _roomResource);
768 	else
769 		searchptr = rootptr = room;
770 	assert(searchptr);
771 
772 	// Load in new room objects
773 	ResourceIterator obcds(searchptr, false);
774 	for (i = 0; i < _numObjectsInRoom; i++) {
775 		od = &_objs[findLocalObjectSlot()];
776 
777 		ptr = obcds.findNext(MKTAG('O','B','C','D'));
778 		if (ptr == NULL)
779 			error("Room %d missing object code block(s)", _roomResource);
780 
781 		od->OBCDoffset = ptr - rootptr;
782 		cdhd = (const CodeHeader *)findResourceData(MKTAG('C','D','H','D'), ptr);
783 
784 		if (_game.version >= 7)
785 			od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id));
786 		else if (_game.version == 6)
787 			od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id));
788 		else
789 			od->obj_nr = READ_LE_UINT16(&(cdhd->v5.obj_id));
790 
791 		if (_dumpScripts) {
792 			char buf[32];
793 			sprintf(buf, "roomobj-%d-", _roomResource);
794 			ptr = findResource(MKTAG('V','E','R','B'), ptr);
795 			dumpResource(buf, od->obj_nr, ptr);
796 		}
797 
798 	}
799 
800 	searchptr = room;
801 	ResourceIterator obims(room, false);
802 	for (i = 0; i < _numObjectsInRoom; i++) {
803 		ptr = obims.findNext(MKTAG('O','B','I','M'));
804 		if (ptr == NULL)
805 			error("Room %d missing image blocks(s)", _roomResource);
806 
807 		obim_id = getObjectIdFromOBIM(ptr);
808 
809 		for (j = 1; j < _numLocalObjects; j++) {
810 			if (_objs[j].obj_nr == obim_id)
811 				_objs[j].OBIMoffset = ptr - room;
812 		}
813 	}
814 
815 	for (i = 1; i < _numLocalObjects; i++) {
816 		if (_objs[i].obj_nr && !_objs[i].fl_object_index)
817 			resetRoomObject(&_objs[i], room);
818 	}
819 }
820 
resetRoomObjects()821 void ScummEngine_v3old::resetRoomObjects() {
822 	int i;
823 	ObjectData *od;
824 	const byte *room, *ptr;
825 
826 	room = getResourceAddress(rtRoom, _roomResource);
827 	assert(room);
828 
829 	if (_numObjectsInRoom == 0)
830 		return;
831 
832 	if (_numObjectsInRoom > _numLocalObjects)
833 		error("More than %d objects in room %d", _numLocalObjects, _roomResource);
834 
835 	if (_game.version <= 2)
836 		ptr = room + 28;
837 	else
838 		ptr = room + 29;
839 
840 	// Default pointer of objects without image, in v0 version of Maniac Mansion
841 	int defaultPtr = READ_LE_UINT16(ptr + 2 * _numObjectsInRoom);
842 
843 	for (i = 0; i < _numObjectsInRoom; i++) {
844 		od = &_objs[findLocalObjectSlot()];
845 
846 		if (_game.version == 0 && READ_LE_UINT16(ptr) == defaultPtr)
847 			od->OBIMoffset = 0;
848 		else
849 			od->OBIMoffset = READ_LE_UINT16(ptr);
850 
851 		od->OBCDoffset = READ_LE_UINT16(ptr + 2 * _numObjectsInRoom);
852 		resetRoomObject(od, room);
853 
854 		ptr += 2;
855 
856 		if (_dumpScripts) {
857 			char buf[32];
858 			sprintf(buf, "roomobj-%d-", _roomResource);
859 			dumpResource(buf, od->obj_nr, room + od->OBCDoffset);
860 		}
861 	}
862 }
863 
resetRoomObjects()864 void ScummEngine_v4::resetRoomObjects() {
865 	int i, j;
866 	ObjectData *od;
867 	const byte *ptr;
868 	uint16 obim_id;
869 	const byte *room;
870 
871 	room = getResourceAddress(rtRoom, _roomResource);
872 	assert(room);
873 
874 	if (_numObjectsInRoom == 0)
875 		return;
876 
877 	if (_numObjectsInRoom > _numLocalObjects)
878 		error("More than %d objects in room %d", _numLocalObjects, _roomResource);
879 
880 	ResourceIterator obcds(room, true);
881 	for (i = 0; i < _numObjectsInRoom; i++) {
882 		od = &_objs[findLocalObjectSlot()];
883 
884 		ptr = obcds.findNext(MKTAG('O','B','C','D'));
885 		if (ptr == NULL)
886 			error("Room %d missing object code block(s)", _roomResource);
887 
888 		od->OBCDoffset = ptr - room;
889 		od->obj_nr = READ_LE_UINT16(ptr + 6);
890 		if (_dumpScripts) {
891 			char buf[32];
892 			sprintf(buf, "roomobj-%d-", _roomResource);
893 			dumpResource(buf, od->obj_nr, ptr);
894 		}
895 	}
896 
897 	ResourceIterator obims(room, true);
898 	for (i = 0; i < _numObjectsInRoom; i++) {
899 		// In the PC Engine version of Loom, there aren't image blocks
900 		// for all objects.
901 		ptr = obims.findNext(MKTAG('O','B','I','M'));
902 		if (ptr == NULL)
903 			break;
904 
905 		obim_id = READ_LE_UINT16(ptr + 6);
906 
907 		for (j = 1; j < _numLocalObjects; j++) {
908 			if (_objs[j].obj_nr == obim_id)
909 				_objs[j].OBIMoffset = ptr - room;
910 		}
911 	}
912 
913 	for (i = 1; i < _numLocalObjects; i++) {
914 		if (_objs[i].obj_nr && !_objs[i].fl_object_index) {
915 			resetRoomObject(&_objs[i], room);
916 		}
917 	}
918 }
919 
resetRoomObject(ObjectData * od,const byte * room,const byte * searchptr)920 void ScummEngine_v0::resetRoomObject(ObjectData *od, const byte *room, const byte *searchptr) {
921 	assert(room);
922 	const byte *ptr = room + od->OBCDoffset;
923 	ptr -= 2;
924 
925 	od->obj_nr = OBJECT_V0(*(ptr + 6), *(ptr + 7));
926 
927 	od->x_pos = *(ptr + 8) * 8;
928 	od->y_pos = ((*(ptr + 9)) & 0x7F) * 8;
929 
930 	od->parentstate = (*(ptr + 9) & 0x80) ? 1 : 0;
931 	od->parentstate *= 8;
932 
933 	od->width = *(ptr + 10) * 8;
934 
935 	od->parent = *(ptr + 11);
936 
937 	od->walk_x = *(ptr + 12) * 8;
938 	od->walk_y = (*(ptr + 13) & 0x1f) * 8;
939 	od->actordir = (*(ptr + 14)) & 7;
940 	od->height = *(ptr + 14) & 0xf8;
941 }
942 
resetRoomObject(ObjectData * od,const byte * room,const byte * searchptr)943 void ScummEngine_v4::resetRoomObject(ObjectData *od, const byte *room, const byte *searchptr) {
944 	assert(room);
945 	const byte *ptr = room + od->OBCDoffset;
946 
947 	if (_game.features & GF_OLD_BUNDLE)
948 		ptr -= 2;
949 
950 	od->obj_nr = READ_LE_UINT16(ptr + 6);
951 
952 	if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
953 		od->x_pos = *(ptr + 8) * 8;
954 		od->y_pos = ((*(ptr + 9)) & 0x7F) * 8;
955 
956 		od->parentstate = (*(ptr + 9) & 0x80) ? 1 : 0;
957 		od->width = *(ptr + 10) * 8;
958 
959 		// TODO: Where is parent data?
960 		od->parent = 0;
961 		od->walk_x = READ_LE_UINT16(ptr + 11);
962 		od->walk_y = READ_LE_UINT16(ptr + 13);
963 		od->actordir = (*(ptr + 15)) & 7;
964 		od->height = *(ptr + 15) & 0xf8;
965 	} else {
966 		od->x_pos = *(ptr + 9) * 8;
967 		od->y_pos = ((*(ptr + 10)) & 0x7F) * 8;
968 
969 		od->parentstate = (*(ptr + 10) & 0x80) ? 1 : 0;
970 		if (_game.version <= 2)
971 			od->parentstate *= 8;
972 
973 		od->width = *(ptr + 11) * 8;
974 
975 		od->parent = *(ptr + 12);
976 
977 		if (_game.version <= 2) {
978 			od->walk_x = *(ptr + 13) * 8;
979 			od->walk_y = (*(ptr + 14) & 0x1f) * 8;
980 			od->actordir = (*(ptr + 15)) & 7;
981 			od->height = *(ptr + 15) & 0xf8;
982 		} else {
983 			od->walk_x = READ_LE_UINT16(ptr + 13);
984 			od->walk_y = READ_LE_UINT16(ptr + 15);
985 			od->actordir = (*(ptr + 17)) & 7;
986 			od->height = *(ptr + 17) & 0xf8;
987 		}
988 	}
989 }
990 
resetRoomObject(ObjectData * od,const byte * room,const byte * searchptr)991 void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte *searchptr) {
992 	const CodeHeader *cdhd = NULL;
993 	const ImageHeader *imhd = NULL;
994 
995 	assert(room);
996 
997 	if (searchptr == NULL) {
998 		if (_game.version == 8) {
999 			searchptr = getResourceAddress(rtRoomScripts, _roomResource);
1000 			assert(searchptr);
1001 		} else {
1002 			searchptr = room;
1003 		}
1004 	}
1005 
1006 	cdhd = (const CodeHeader *)findResourceData(MKTAG('C','D','H','D'), searchptr + od->OBCDoffset);
1007 	if (cdhd == NULL)
1008 		error("Room %d missing CDHD blocks(s)", _roomResource);
1009 	if (od->OBIMoffset)
1010 		imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), room + od->OBIMoffset);
1011 
1012 	od->flags = Gdi::dbAllowMaskOr;
1013 
1014 	if (_game.version == 8) {
1015 		assert(imhd);
1016 		od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id));
1017 
1018 		od->parent = cdhd->v7.parent;
1019 		od->parentstate = cdhd->v7.parentstate;
1020 
1021 		od->x_pos = (int)READ_LE_UINT32(&imhd->v8.x_pos);
1022 		od->y_pos = (int)READ_LE_UINT32(&imhd->v8.y_pos);
1023 		od->width = (uint)READ_LE_UINT32(&imhd->v8.width);
1024 		od->height = (uint)READ_LE_UINT32(&imhd->v8.height);
1025 		// HACK: This is done since an angle doesn't fit into a byte (360 > 256)
1026 		od->actordir = toSimpleDir(1, READ_LE_UINT32(&imhd->v8.actordir));
1027 		if (FROM_LE_32(imhd->v8.version) == 801)
1028 			od->flags = ((((byte)READ_LE_UINT32(&imhd->v8.flags)) & 16) == 0) ? Gdi::dbAllowMaskOr : 0;
1029 
1030 	} else if (_game.version == 7) {
1031 		assert(imhd);
1032 		od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id));
1033 
1034 		od->parent = cdhd->v7.parent;
1035 		od->parentstate = cdhd->v7.parentstate;
1036 
1037 		od->x_pos = READ_LE_UINT16(&imhd->v7.x_pos);
1038 		od->y_pos = READ_LE_UINT16(&imhd->v7.y_pos);
1039 		od->width = READ_LE_UINT16(&imhd->v7.width);
1040 		od->height = READ_LE_UINT16(&imhd->v7.height);
1041 		od->actordir = (byte)READ_LE_UINT16(&imhd->v7.actordir);
1042 
1043 	} else if (_game.version == 6) {
1044 		od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id));
1045 
1046 		od->width = READ_LE_UINT16(&cdhd->v6.w);
1047 		od->height = READ_LE_UINT16(&cdhd->v6.h);
1048 		od->x_pos = ((int16)READ_LE_UINT16(&cdhd->v6.x));
1049 		od->y_pos = ((int16)READ_LE_UINT16(&cdhd->v6.y));
1050 		if (cdhd->v6.flags == 0x80) {
1051 			od->parentstate = 1;
1052 		} else {
1053 			od->parentstate = (cdhd->v6.flags & 0xF);
1054 		}
1055 		od->parent = cdhd->v6.parent;
1056 		od->actordir = cdhd->v6.actordir;
1057 
1058 		if (_game.heversion >= 60 && imhd)
1059 			od->flags = ((imhd->old.flags & 1) != 0) ? Gdi::dbAllowMaskOr : 0;
1060 
1061 	} else {
1062 		od->obj_nr = READ_LE_UINT16(&(cdhd->v5.obj_id));
1063 
1064 		od->width = cdhd->v5.w * 8;
1065 		od->height = cdhd->v5.h * 8;
1066 		od->x_pos = cdhd->v5.x * 8;
1067 		od->y_pos = cdhd->v5.y * 8;
1068 		if (cdhd->v5.flags == 0x80) {
1069 			od->parentstate = 1;
1070 		} else {
1071 			od->parentstate = (cdhd->v5.flags & 0xF);
1072 		}
1073 		od->parent = cdhd->v5.parent;
1074 		od->walk_x = READ_LE_UINT16(&cdhd->v5.walk_x);
1075 		od->walk_y = READ_LE_UINT16(&cdhd->v5.walk_y);
1076 		od->actordir = cdhd->v5.actordir;
1077 	}
1078 
1079 	od->fl_object_index = 0;
1080 }
1081 
updateObjectStates()1082 void ScummEngine::updateObjectStates() {
1083 	int i;
1084 	ObjectData *od = &_objs[1];
1085 	for (i = 1; i < _numLocalObjects; i++, od++) {
1086 		// V0 MM, objects with type == 1 are room objects (room specific objects, non-pickup)
1087 		if (_game.version == 0 && OBJECT_V0_TYPE(od->obj_nr) == kObjectV0TypeBG)
1088 			continue;
1089 
1090 		if (od->obj_nr > 0)
1091 			od->state = getState(od->obj_nr);
1092 	}
1093 }
1094 
processDrawQue()1095 void ScummEngine::processDrawQue() {
1096 	int i, j;
1097 	for (i = 0; i < _drawObjectQueNr; i++) {
1098 		j = _drawObjectQue[i];
1099 		if (j)
1100 			drawObject(j, 0);
1101 	}
1102 	_drawObjectQueNr = 0;
1103 }
1104 
addObjectToDrawQue(int object)1105 void ScummEngine::addObjectToDrawQue(int object) {
1106 	if ((unsigned int)_drawObjectQueNr >= ARRAYSIZE(_drawObjectQue))
1107 		error("Draw Object Que overflow");
1108 	_drawObjectQue[_drawObjectQueNr++] = object;
1109 }
1110 
removeObjectFromDrawQue(int object)1111 void ScummEngine::removeObjectFromDrawQue(int object) {
1112 	if (_drawObjectQueNr <= 0)
1113 		return;
1114 
1115 	int i;
1116 	for (i = 0; i < _drawObjectQueNr; i++) {
1117 		if (_drawObjectQue[i] == object)
1118 			_drawObjectQue[i] = 0;
1119 	}
1120 }
1121 
clearDrawObjectQueue()1122 void ScummEngine::clearDrawObjectQueue() {
1123 	_drawObjectQueNr = 0;
1124 }
1125 
clearDrawQueues()1126 void ScummEngine::clearDrawQueues() {
1127 	clearDrawObjectQueue();
1128 }
1129 
clearDrawQueues()1130 void ScummEngine_v6::clearDrawQueues() {
1131 	ScummEngine::clearDrawQueues();
1132 
1133 	_blastObjectQueuePos = 0;
1134 }
1135 
1136 #ifdef ENABLE_HE
clearDrawQueues()1137 void ScummEngine_v71he::clearDrawQueues() {
1138 	ScummEngine_v6::clearDrawQueues();
1139 
1140 	_wiz->polygonClear();
1141 }
1142 
clearDrawQueues()1143 void ScummEngine_v80he::clearDrawQueues() {
1144 	ScummEngine_v71he::clearDrawQueues();
1145 
1146 	_wiz->clearWizBuffer();
1147 }
1148 #endif
1149 
1150 /**
1151  * Mark the rectangle covered by the given object as dirty, thus eventually
1152  * ensuring a redraw of that area. This function is typically invoked when an
1153  * object gets removed from the current room, or when its state changed.
1154  */
markObjectRectAsDirty(int obj)1155 void ScummEngine::markObjectRectAsDirty(int obj) {
1156 	int i, strip;
1157 	++_V0Delay._objectRedrawCount;
1158 
1159 	for (i = 1; i < _numLocalObjects; i++) {
1160 		if (_objs[i].obj_nr == (uint16)obj) {
1161 			if (_objs[i].width != 0) {
1162 				const int minStrip = MAX(_screenStartStrip, _objs[i].x_pos / 8);
1163 				const int maxStrip = MIN(_screenEndStrip+1, _objs[i].x_pos / 8 + _objs[i].width / 8);
1164 				for (strip = minStrip; strip < maxStrip; strip++) {
1165 					++_V0Delay._objectStripRedrawCount;
1166 					setGfxUsageBit(strip, USAGE_BIT_DIRTY);
1167 				}
1168 			}
1169 			_bgNeedsRedraw = true;
1170 			return;
1171 		}
1172 	}
1173 }
1174 
getObjOrActorName(int obj)1175 const byte *ScummEngine::getObjOrActorName(int obj) {
1176 	byte *objptr;
1177 	int i;
1178 
1179 	if (objIsActor(obj))
1180 		return derefActor(objToActor(obj), "getObjOrActorName")->getActorName();
1181 
1182 	for (i = 0; i < _numNewNames; i++) {
1183 		if (_newNames[i] == obj) {
1184 			debug(5, "Found new name for object %d at _newNames[%d]", obj, i);
1185 			return getResourceAddress(rtObjectName, i);
1186 		}
1187 	}
1188 
1189 	objptr = getOBCDFromObject(obj, true);
1190 	if (objptr == NULL)
1191 		return NULL;
1192 
1193 	if (_game.features & GF_SMALL_HEADER) {
1194 		byte offset = 0;
1195 
1196 		if (_game.version == 0)
1197 			offset = *(objptr + 13);
1198 		else if (_game.version <= 2)
1199 			offset = *(objptr + 14);
1200 		else if (_game.features & GF_OLD_BUNDLE)
1201 			offset = *(objptr + 16);
1202 		else if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine)
1203 			offset = *(objptr + 16) + 17;
1204 		else
1205 			offset = *(objptr + 18);
1206 
1207 		return (objptr + offset);
1208 	}
1209 
1210 	return findResourceData(MKTAG('O','B','N','A'), objptr);
1211 }
1212 
setObjectName(int obj)1213 void ScummEngine::setObjectName(int obj) {
1214 	int i;
1215 
1216 	if (objIsActor(obj))
1217 		error("Can't set actor %d name with new-name-of", obj);
1218 
1219 	for (i = 0; i < _numNewNames; i++) {
1220 		if (_newNames[i] == obj) {
1221 			_res->nukeResource(rtObjectName, i);
1222 			_newNames[i] = 0;
1223 			break;
1224 		}
1225 	}
1226 
1227 	for (i = 0; i < _numNewNames; i++) {
1228 		if (_newNames[i] == 0) {
1229 			loadPtrToResource(rtObjectName, i, NULL);
1230 			_newNames[i] = obj;
1231 			runInventoryScript(0);
1232 			return;
1233 		}
1234 	}
1235 
1236 	error("New name of %d overflows name table (max = %d)", obj, _numNewNames);
1237 }
1238 
getOBCDOffs(int object) const1239 uint32 ScummEngine::getOBCDOffs(int object) const {
1240 	int i;
1241 
1242 	if ((_game.version != 0 || OBJECT_V0_TYPE(object) == 0) &&
1243 		_objectOwnerTable[object] != OF_OWNER_ROOM)
1244 		return 0;
1245 
1246 	for (i = (_numLocalObjects-1); i > 0; i--) {
1247 		if (_objs[i].obj_nr == object) {
1248 			if (_objs[i].fl_object_index != 0)
1249 				return 8;
1250 			return _objs[i].OBCDoffset;
1251 		}
1252 	}
1253 	return 0;
1254 }
1255 
getOBCDFromObject(int obj,bool v0CheckInventory)1256 byte *ScummEngine::getOBCDFromObject(int obj, bool v0CheckInventory) {
1257 	int i;
1258 	byte *ptr;
1259 
1260 	if ((_game.version != 0 || OBJECT_V0_TYPE(obj) == 0) &&
1261 		_objectOwnerTable[obj] != OF_OWNER_ROOM)
1262 	{
1263 		if (_game.version == 0 && !v0CheckInventory)
1264 			return 0;
1265 		for (i = 0; i < _numInventory; i++) {
1266 			if (_inventory[i] == obj)
1267 				return getResourceAddress(rtInventory, i);
1268 		}
1269 	} else {
1270 		for (i = (_numLocalObjects-1); i > 0; --i) {
1271 			if (_objs[i].obj_nr == obj) {
1272 				if (_objs[i].fl_object_index) {
1273 					assert(_objs[i].OBCDoffset == 8);
1274 					ptr = getResourceAddress(rtFlObject, _objs[i].fl_object_index);
1275 				} else if (_game.version == 8)
1276 					ptr = getResourceAddress(rtRoomScripts, _roomResource);
1277 				else
1278 					ptr = getResourceAddress(rtRoom, _roomResource);
1279 				assert(ptr);
1280 				return ptr + _objs[i].OBCDoffset;
1281 			}
1282 		}
1283 	}
1284 	return 0;
1285 }
1286 
getOBIMFromObjectData(const ObjectData & od)1287 const byte *ScummEngine::getOBIMFromObjectData(const ObjectData &od) {
1288 	const byte *ptr;
1289 
1290 	if (od.fl_object_index) {
1291 		ptr = getResourceAddress(rtFlObject, od.fl_object_index);
1292 		ptr = findResource(MKTAG('O','B','I','M'), ptr);
1293 	} else {
1294 		ptr = getResourceAddress(rtRoom, _roomResource);
1295 		if (ptr)
1296 			ptr += od.OBIMoffset;
1297 	}
1298 	return ptr;
1299 }
1300 
1301 static const uint32 IMxx_tags[] = {
1302 	MKTAG('I','M','0','0'),
1303 	MKTAG('I','M','0','1'),
1304 	MKTAG('I','M','0','2'),
1305 	MKTAG('I','M','0','3'),
1306 	MKTAG('I','M','0','4'),
1307 	MKTAG('I','M','0','5'),
1308 	MKTAG('I','M','0','6'),
1309 	MKTAG('I','M','0','7'),
1310 	MKTAG('I','M','0','8'),
1311 	MKTAG('I','M','0','9'),
1312 	MKTAG('I','M','0','A'),
1313 	MKTAG('I','M','0','B'),
1314 	MKTAG('I','M','0','C'),
1315 	MKTAG('I','M','0','D'),
1316 	MKTAG('I','M','0','E'),
1317 	MKTAG('I','M','0','F'),
1318 	MKTAG('I','M','1','0')
1319 };
1320 
getObjectImage(const byte * ptr,int state)1321 const byte *ScummEngine::getObjectImage(const byte *ptr, int state) {
1322 	assert(ptr);
1323 	if (_game.features & GF_OLD_BUNDLE)
1324 		ptr += 0;
1325 	else if (_game.features & GF_SMALL_HEADER) {
1326 		ptr += 8;
1327 	} else if (_game.version == 8) {
1328 		// The OBIM contains an IMAG, which in turn contains a WRAP, which contains
1329 		// an OFFS chunk and multiple BOMP/SMAP chunks. To find the right BOMP/SMAP,
1330 		// we use the offsets in the OFFS chunk,
1331 		ptr = findResource(MKTAG('I','M','A','G'), ptr);
1332 		if (!ptr)
1333 			return 0;
1334 
1335 		ptr = findResource(MKTAG('W','R','A','P'), ptr);
1336 		if (!ptr)
1337 			return 0;
1338 
1339 		ptr = findResource(MKTAG('O','F','F','S'), ptr);
1340 		if (!ptr)
1341 			return 0;
1342 
1343 		// Get the address of the specified SMAP (corresponding to IMxx)
1344 		ptr += READ_LE_UINT32(ptr + 4 + 4*state);
1345 	} else {
1346 		ptr = findResource(IMxx_tags[state], ptr);
1347 	}
1348 
1349 	return ptr;
1350 }
1351 
getObjectImageCount(int object)1352 int ScummEngine::getObjectImageCount(int object) {
1353 	const byte *ptr;
1354 	const ImageHeader *imhd;
1355 	int objnum;
1356 
1357 	objnum = getObjectIndex(object);
1358 	if (objnum == -1)
1359 		return 0;
1360 
1361 	ptr = getOBIMFromObjectData(_objs[objnum]);
1362 	imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), ptr);
1363 	if (!imhd)
1364 		return 0;
1365 
1366 	if (_game.version == 8) {
1367 		return (READ_LE_UINT32(&imhd->v8.image_count));
1368 	} else if (_game.version == 7) {
1369 		return(READ_LE_UINT16(&imhd->v7.image_count));
1370 	} else {
1371 		return (READ_LE_UINT16(&imhd->old.image_count));
1372 	}
1373 }
1374 
1375 #ifdef ENABLE_SCUMM_7_8
getObjectIdFromOBIM(const byte * obim)1376 int ScummEngine_v8::getObjectIdFromOBIM(const byte *obim) {
1377 	// In V8, IMHD has no obj_id, but rather a name string. We map the name
1378 	// back to an object id using a table derived from the DOBJ resource.
1379 	const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), obim);
1380 	ObjectNameId *found = (ObjectNameId *)bsearch(imhd->v8.name, _objectIDMap, _objectIDMapSize,
1381 					sizeof(ObjectNameId), (int (*)(const void*, const void*))strcmp);
1382 	assert(found);
1383 	return found->id;
1384 }
1385 
getObjectIdFromOBIM(const byte * obim)1386 int ScummEngine_v7::getObjectIdFromOBIM(const byte *obim) {
1387 	const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), obim);
1388 	return READ_LE_UINT16(&imhd->v7.obj_id);
1389 }
1390 #endif
1391 
getObjectIdFromOBIM(const byte * obim)1392 int ScummEngine::getObjectIdFromOBIM(const byte *obim) {
1393 	if (_game.features & GF_SMALL_HEADER)
1394 		return READ_LE_UINT16(obim + 6);
1395 
1396 	const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), obim);
1397 	return READ_LE_UINT16(&imhd->old.obj_id);
1398 }
1399 
findObjectInRoom(FindObjectInRoom * fo,byte findWhat,uint id,uint room)1400 void ScummEngine::findObjectInRoom(FindObjectInRoom *fo, byte findWhat, uint id, uint room) {
1401 
1402 	const CodeHeader *cdhd;
1403 	int i, numobj;
1404 	const byte *roomptr, *obcdptr, *obimptr, *searchptr;
1405 	int id2;
1406 	int obim_id;
1407 
1408 	id2 = getObjectIndex(id);
1409 	if (findWhat & foCheckAlreadyLoaded && id2 != -1) {
1410 		assert(_game.version >= 6);
1411 		if (findWhat & foCodeHeader) {
1412 			fo->obcd = obcdptr = getOBCDFromObject(id);
1413 			assert(obcdptr);
1414 			fo->cdhd = (const CodeHeader *)findResourceData(MKTAG('C','D','H','D'), obcdptr);
1415 		}
1416 		if (findWhat & foImageHeader) {
1417 			fo->obim = obimptr = getOBIMFromObjectData(_objs[id2]);
1418 			assert(obimptr);
1419 		}
1420 		return;
1421 	}
1422 
1423 	fo->roomptr = roomptr = getResourceAddress(rtRoom, room);
1424 	if (!roomptr)
1425 		error("findObjectInRoom: failed getting roomptr to %d", room);
1426 
1427 	if (_game.features & GF_OLD_BUNDLE) {
1428 		numobj = roomptr[20];
1429 	} else {
1430 		const RoomHeader *roomhdr = (const RoomHeader *)findResourceData(MKTAG('R','M','H','D'), roomptr);
1431 
1432 		if (_game.version == 8)
1433 			numobj = READ_LE_UINT32(&(roomhdr->v8.numObjects));
1434 		else if (_game.version == 7)
1435 			numobj = READ_LE_UINT16(&(roomhdr->v7.numObjects));
1436 		else
1437 			numobj = READ_LE_UINT16(&(roomhdr->old.numObjects));
1438 	}
1439 
1440 	if (numobj == 0)
1441 		error("findObjectInRoom: No object found in room %d", room);
1442 	if (numobj > _numLocalObjects)
1443 		error("findObjectInRoom: More (%d) than %d objects in room %d", numobj, _numLocalObjects, room);
1444 
1445 	if (_game.features & GF_OLD_BUNDLE) {
1446 		if (_game.version <= 2)
1447 			searchptr = roomptr + 28;
1448 		else
1449 			searchptr = roomptr + 29;
1450 
1451 		for (i = 0; i < numobj; i++) {
1452 			obimptr = roomptr + READ_LE_UINT16(searchptr);
1453 			obcdptr = roomptr + READ_LE_UINT16(searchptr + 2 * numobj);
1454 			id2 = READ_LE_UINT16(obcdptr + 4);
1455 
1456 			if (id2 == (uint16)id) {
1457 				if (findWhat & foCodeHeader) {
1458 					fo->obcd = obcdptr;
1459 					// We assume that the code header starts at a fixed offset.
1460 					// A bit hackish, but works reasonably well.
1461 					fo->cdhd = (const CodeHeader *)(obcdptr + 10);
1462 				}
1463 				if (findWhat & foImageHeader) {
1464 					fo->obim = obimptr;
1465 				}
1466 				break;
1467 			}
1468 			searchptr += 2;
1469 		}
1470 		return;
1471 	}
1472 
1473 	if (findWhat & foCodeHeader) {
1474 		if (_game.version == 8)
1475 			searchptr = getResourceAddress(rtRoomScripts, room);
1476 		else
1477 			searchptr = roomptr;
1478 		assert(searchptr);
1479 		ResourceIterator	obcds(searchptr, (_game.features & GF_SMALL_HEADER) != 0);
1480 		for (i = 0; i < numobj; i++) {
1481 			obcdptr = obcds.findNext(MKTAG('O','B','C','D'));
1482 			if (obcdptr == NULL)
1483 				error("findObjectInRoom: Not enough code blocks in room %d", room);
1484 			cdhd = (const CodeHeader *)findResourceData(MKTAG('C','D','H','D'), obcdptr);
1485 
1486 			if (_game.features & GF_SMALL_HEADER)
1487 				id2 = READ_LE_UINT16(obcdptr + 6);
1488 			else if (_game.version >= 7)
1489 				id2 = READ_LE_UINT16(&(cdhd->v7.obj_id));
1490 			else if (_game.version == 6)
1491 				id2 = READ_LE_UINT16(&(cdhd->v6.obj_id));
1492 			else
1493 				id2 = READ_LE_UINT16(&(cdhd->v5.obj_id));
1494 
1495 			if (id2 == (uint16)id) {
1496 				fo->obcd = obcdptr;
1497 				fo->cdhd = cdhd;
1498 				break;
1499 			}
1500 		}
1501 		if (i == numobj)
1502 			error("findObjectInRoom: Object %d not found in room %d", id, room);
1503 	}
1504 
1505 	roomptr = fo->roomptr;
1506 	if (findWhat & foImageHeader) {
1507 		ResourceIterator	obims(roomptr, (_game.features & GF_SMALL_HEADER) != 0);
1508 		for (i = 0; i < numobj; i++) {
1509 			obimptr = obims.findNext(MKTAG('O','B','I','M'));
1510 			if (obimptr == NULL)
1511 				error("findObjectInRoom: Not enough image blocks in room %d", room);
1512 			obim_id = getObjectIdFromOBIM(obimptr);
1513 
1514 			if (obim_id == (uint16)id) {
1515 				fo->obim = obimptr;
1516 				break;
1517 			}
1518 		}
1519 		if (i == numobj)
1520 			error("findObjectInRoom: Object %d image not found in room %d", id, room);
1521 	}
1522 }
1523 
objIsActor(int obj)1524 bool ScummEngine_v0::objIsActor(int obj) {
1525 	// object IDs < _numActors are used in v0 for objects too (e.g. hamster)
1526 	return OBJECT_V0_TYPE(obj) == kObjectV0TypeActor;
1527 }
1528 
objToActor(int obj)1529 int ScummEngine_v0::objToActor(int obj) {
1530 	return OBJECT_V0_ID(obj);
1531 }
1532 
actorToObj(int actor)1533 int ScummEngine_v0::actorToObj(int actor) {
1534 	return OBJECT_V0(actor, kObjectV0TypeActor);
1535 }
1536 
objIsActor(int obj)1537 bool ScummEngine::objIsActor(int obj) {
1538 	return obj < _numActors;
1539 }
1540 
objToActor(int obj)1541 int ScummEngine::objToActor(int obj) {
1542 	return obj;
1543 }
1544 
actorToObj(int actor)1545 int ScummEngine::actorToObj(int actor) {
1546 	return actor;
1547 }
1548 
getObjX(int obj)1549 int ScummEngine::getObjX(int obj) {
1550 	if (obj < 1)
1551 		return 0;									/* fix for indy4's map */
1552 
1553 	if (objIsActor(obj)) {
1554 		return derefActor(objToActor(obj), "getObjX")->getRealPos().x;
1555 	} else {
1556 		if (whereIsObject(obj) == WIO_NOT_FOUND)
1557 			return -1;
1558 		int x, y;
1559 		if (getObjectOrActorXY(obj, x, y) == -1)
1560 			return -1;
1561 		return x;
1562 	}
1563 }
1564 
getObjY(int obj)1565 int ScummEngine::getObjY(int obj) {
1566 	if (obj < 1)
1567 		return 0;									/* fix for indy4's map */
1568 
1569 	if (objIsActor(obj)) {
1570 		return derefActor(objToActor(obj), "getObjY")->getRealPos().y;
1571 	} else {
1572 		if (whereIsObject(obj) == WIO_NOT_FOUND)
1573 			return -1;
1574 		int x, y;
1575 		if (getObjectOrActorXY(obj, x, y) == -1)
1576 			return -1;
1577 		return y;
1578 	}
1579 }
1580 
getObjOldDir(int obj)1581 int ScummEngine::getObjOldDir(int obj) {
1582 	return newDirToOldDir(getObjNewDir(obj));
1583 }
1584 
getObjNewDir(int obj)1585 int ScummEngine::getObjNewDir(int obj) {
1586 	int dir;
1587 	if (objIsActor(obj)) {
1588 		dir = derefActor(objToActor(obj), "getObjNewDir")->getFacing();
1589 	} else {
1590 		int x, y;
1591 		getObjectXYPos(obj, x, y, dir);
1592 	}
1593 	return dir;
1594 }
1595 
setObjectState(int obj,int state,int x,int y)1596 void ScummEngine::setObjectState(int obj, int state, int x, int y) {
1597 	int i;
1598 
1599 	i = getObjectIndex(obj);
1600 	if (i == -1) {
1601 		debug(0, "setObjectState: no such object %d", obj);
1602 		return;
1603 	}
1604 
1605 	if (x != -1 && x != 0x7FFFFFFF) {
1606 		_objs[i].x_pos = x * 8;
1607 		_objs[i].y_pos = y * 8;
1608 	}
1609 
1610 	addObjectToDrawQue(i);
1611 	if (_game.version >= 7) {
1612 		int imagecount;
1613 		if (state == 0xFF) {
1614 			state = getState(obj);
1615 			imagecount = getObjectImageCount(obj);
1616 
1617 			if (state < imagecount)
1618 				state++;
1619 			else
1620 				state = 1;
1621 		}
1622 
1623 		if (state == 0xFE)
1624 			state = _rnd.getRandomNumber(getObjectImageCount(obj));
1625 	}
1626 	putState(obj, state);
1627 }
1628 
getDistanceBetween(bool is_obj_1,int b,int c,bool is_obj_2,int e,int f)1629 int ScummEngine_v6::getDistanceBetween(bool is_obj_1, int b, int c, bool is_obj_2, int e, int f) {
1630 	int i, j;
1631 	int x, y;
1632 	int x2, y2;
1633 
1634 	j = i = 0xFF;
1635 
1636 	if (is_obj_1) {
1637 		if (getObjectOrActorXY(b, x, y) == -1)
1638 			return -1;
1639 		if (b < _numActors)
1640 			i = derefActor(b, "getDistanceBetween_is_obj_1")->_scalex;
1641 	} else {
1642 		x = b;
1643 		y = c;
1644 	}
1645 
1646 	if (is_obj_2) {
1647 		if (getObjectOrActorXY(e, x2, y2) == -1)
1648 			return -1;
1649 		if (e < _numActors)
1650 			j = derefActor(e, "getDistanceBetween_is_obj_2")->_scalex;
1651 	} else {
1652 		x2 = e;
1653 		y2 = f;
1654 	}
1655 
1656 	return getDist(x, y, x2, y2) * 0xFF / ((i + j) / 2);
1657 }
1658 
nukeFlObjects(int min,int max)1659 void ScummEngine::nukeFlObjects(int min, int max) {
1660 	ObjectData *od;
1661 	int i;
1662 
1663 	debug(0, "nukeFlObjects(%d,%d)", min, max);
1664 
1665 	for (i = (_numLocalObjects-1), od = _objs; --i >= 0; od++)
1666 		if (od->fl_object_index && od->obj_nr >= min && od->obj_nr <= max) {
1667 			_res->nukeResource(rtFlObject, od->fl_object_index);
1668 			od->obj_nr = 0;
1669 			od->fl_object_index = 0;
1670 		}
1671 }
1672 
enqueueObject(int objectNumber,int objectX,int objectY,int objectWidth,int objectHeight,int scaleX,int scaleY,int image,int mode)1673 void ScummEngine_v6::enqueueObject(int objectNumber, int objectX, int objectY, int objectWidth,
1674 								int objectHeight, int scaleX, int scaleY, int image, int mode) {
1675 	BlastObject *eo;
1676 
1677 	if (_blastObjectQueuePos >= (int)ARRAYSIZE(_blastObjectQueue)) {
1678 		error("enqueueObject: overflow");
1679 	}
1680 
1681 	int idx = getObjectIndex(objectNumber);
1682 	assert(idx >= 0);
1683 
1684 	eo = &_blastObjectQueue[_blastObjectQueuePos++];
1685 	eo->number = objectNumber;
1686 	eo->rect.left = objectX;
1687 	eo->rect.top = objectY + _screenTop;
1688 	if (objectWidth == 0) {
1689 		eo->rect.right = eo->rect.left + _objs[idx].width;
1690 	} else {
1691 		eo->rect.right = eo->rect.left + objectWidth;
1692 	}
1693 	if (objectHeight == 0) {
1694 		eo->rect.bottom = eo->rect.top + _objs[idx].height;
1695 	} else {
1696 		eo->rect.bottom = eo->rect.top + objectHeight;
1697 	}
1698 
1699 	eo->scaleX = scaleX;
1700 	eo->scaleY = scaleY;
1701 	eo->image = image;
1702 
1703 	eo->mode = mode;
1704 }
1705 
drawBlastObjects()1706 void ScummEngine_v6::drawBlastObjects() {
1707 	BlastObject *eo;
1708 	int i;
1709 
1710 	eo = _blastObjectQueue;
1711 	for (i = 0; i < _blastObjectQueuePos; i++, eo++) {
1712 		drawBlastObject(eo);
1713 	}
1714 }
1715 
drawBlastObject(BlastObject * eo)1716 void ScummEngine_v6::drawBlastObject(BlastObject *eo) {
1717 	VirtScreen *vs;
1718 	const byte *bomp, *ptr;
1719 	int objnum;
1720 	BompDrawData bdd;
1721 
1722 	vs = &_virtscr[kMainVirtScreen];
1723 
1724 	assertRange(30, eo->number, _numGlobalObjects - 1, "blast object");
1725 
1726 	objnum = getObjectIndex(eo->number);
1727 	if (objnum == -1)
1728 		error("drawBlastObject: getObjectIndex on BlastObject %d failed", eo->number);
1729 
1730 	ptr = getOBIMFromObjectData(_objs[objnum]);
1731 	if (!ptr)
1732 		error("BlastObject object %d image not found", eo->number);
1733 
1734 	const byte *img = getObjectImage(ptr, eo->image);
1735 	if (_game.version == 8) {
1736 		assert(img);
1737 		bomp = img + 8;
1738 	} else {
1739 		if (!img)
1740 			img = getObjectImage(ptr, 1);	// Backward compatibility with samnmax blast objects
1741 		assert(img);
1742 		bomp = findResourceData(MKTAG('B','O','M','P'), img);
1743 	}
1744 
1745 	if (!bomp)
1746 		error("object %d is not a blast object", eo->number);
1747 
1748 	bdd.dst = *vs;
1749 	bdd.dst.setPixels(vs->getPixels(0, 0));
1750 	bdd.x = eo->rect.left;
1751 	bdd.y = eo->rect.top;
1752 
1753 	// Skip the bomp header
1754 	if (_game.version == 8) {
1755 		bdd.src = bomp + 8;
1756 	} else {
1757 		bdd.src = bomp + 10;
1758 	}
1759 	if (_game.version == 8) {
1760 		bdd.srcwidth = READ_LE_UINT32(bomp);
1761 		bdd.srcheight = READ_LE_UINT32(bomp+4);
1762 	} else {
1763 		bdd.srcwidth = READ_LE_UINT16(bomp+2);
1764 		bdd.srcheight = READ_LE_UINT16(bomp+4);
1765 	}
1766 
1767 	bdd.scale_x = (byte)eo->scaleX;
1768 	bdd.scale_y = (byte)eo->scaleY;
1769 
1770 	bdd.maskPtr = NULL;
1771 	bdd.numStrips = _gdi->_numStrips;
1772 
1773 	if ((bdd.scale_x != 255) || (bdd.scale_y != 255)) {
1774 		bdd.shadowMode = 0;
1775 	} else {
1776 		bdd.shadowMode = eo->mode;
1777 	}
1778 	bdd.shadowPalette = _shadowPalette;
1779 
1780 	bdd.actorPalette = 0;
1781 	bdd.mirror = false;
1782 
1783 	drawBomp(bdd);
1784 
1785 	markRectAsDirty(vs->number, bdd.x, bdd.x + bdd.srcwidth, bdd.y, bdd.y + bdd.srcheight);
1786 }
1787 
removeBlastObjects()1788 void ScummEngine_v6::removeBlastObjects() {
1789 	BlastObject *eo;
1790 	int i;
1791 
1792 	eo = _blastObjectQueue;
1793 	for (i = 0; i < _blastObjectQueuePos; i++, eo++) {
1794 		removeBlastObject(eo);
1795 	}
1796 	_blastObjectQueuePos = 0;
1797 }
1798 
removeBlastObject(BlastObject * eo)1799 void ScummEngine_v6::removeBlastObject(BlastObject *eo) {
1800 	VirtScreen *vs = &_virtscr[kMainVirtScreen];
1801 
1802 	Common::Rect r;
1803 	int left_strip, right_strip;
1804 	int i;
1805 
1806 	r = eo->rect;
1807 
1808 	r.clip(Common::Rect(vs->w, vs->h));
1809 
1810 	if (r.width() <= 0 || r.height() <= 0)
1811 		return;
1812 
1813 	left_strip = r.left / 8;
1814 	right_strip = (r.right + (vs->xstart % 8)) / 8;
1815 
1816 	if (left_strip < 0)
1817 		left_strip = 0;
1818 	if (right_strip > _gdi->_numStrips - 1)
1819 		right_strip = _gdi->_numStrips - 1;
1820 	for (i = left_strip; i <= right_strip; i++)
1821 		_gdi->resetBackground(r.top, r.bottom, i);
1822 
1823 	markRectAsDirty(kMainVirtScreen, r, USAGE_BIT_RESTORED);
1824 }
1825 
findLocalObjectSlot()1826 int ScummEngine::findLocalObjectSlot() {
1827 	int i;
1828 
1829 	for (i = 1; i < _numLocalObjects; i++) {
1830 		if (!_objs[i].obj_nr) {
1831 			memset(&_objs[i], 0, sizeof(_objs[i]));
1832 			return i;
1833 		}
1834 	}
1835 
1836 	return -1;
1837 }
1838 
findFlObjectSlot()1839 int ScummEngine::findFlObjectSlot() {
1840 	int i;
1841 	for (i = 1; i < _numFlObject; i++) {
1842 		if (_res->_types[rtFlObject][i]._address == NULL)
1843 			return i;
1844 	}
1845 	error("findFlObjectSlot: Out of FLObject slots");
1846 	return -1;
1847 }
1848 
loadFlObject(uint object,uint room)1849 void ScummEngine_v70he::loadFlObject(uint object, uint room) {
1850 	// Don't load an already stored object
1851 	for (int i = 0; i < _numStoredFlObjects; i++) {
1852 		if (_storedFlObjects[i].obj_nr == object)
1853 			return;
1854 	}
1855 
1856 	ScummEngine_v60he::loadFlObject(object, room);
1857 }
1858 
loadFlObject(uint object,uint room)1859 void ScummEngine::loadFlObject(uint object, uint room) {
1860 	FindObjectInRoom foir;
1861 	int slot, objslot;
1862 	ObjectData *od;
1863 	byte *flob;
1864 	uint32 obcd_size, obim_size, flob_size;
1865 	bool isRoomLocked, isRoomScriptsLocked;
1866 
1867 	// Don't load an already loaded object
1868 	if (getObjectIndex(object) != -1)
1869 		return;
1870 
1871 	// Locate the object in the room resource
1872 	findObjectInRoom(&foir, foImageHeader | foCodeHeader, object, room);
1873 
1874 	// Add an entry for the new floating object in the local object table
1875 	objslot = findLocalObjectSlot();
1876 	if (objslot == -1)
1877 		error("loadFlObject: Local Object Table overflow");
1878 
1879 	od = &_objs[objslot];
1880 
1881 	// Dump object script
1882 	if (_dumpScripts) {
1883 		char buf[32];
1884 		const byte *ptr = foir.obcd;
1885 		sprintf(buf, "roomobj-%u-", room);
1886 		ptr = findResource(MKTAG('V','E','R','B'), ptr);
1887 		dumpResource(buf, object, ptr);
1888 	}
1889 
1890 	// Setup sizes
1891 	obcd_size = READ_BE_UINT32(foir.obcd + 4);
1892 	od->OBCDoffset = 8;
1893 	od->OBIMoffset = obcd_size + 8;
1894 	obim_size = READ_BE_UINT32(foir.obim + 4);
1895 	flob_size = obcd_size + obim_size + 8;
1896 
1897 	// Lock room/roomScripts for the given room. They contains the OBCD/OBIM
1898 	// data, and a call to createResource might expire them, hence we lock them.
1899 	isRoomLocked = _res->isLocked(rtRoom, room);
1900 	isRoomScriptsLocked = _res->isLocked(rtRoomScripts, room);
1901 	if (!isRoomLocked)
1902 		_res->lock(rtRoom, room);
1903 	if (_game.version == 8 && !isRoomScriptsLocked)
1904 		_res->lock(rtRoomScripts, room);
1905 
1906 	// Allocate slot & memory for floating object
1907 	slot = findFlObjectSlot();
1908 	flob = _res->createResource(rtFlObject, slot, flob_size);
1909 	assert(flob);
1910 
1911 	// Copy object code + object image to floating object
1912 	WRITE_UINT32(flob, MKTAG('F','L','O','B'));
1913 	WRITE_BE_UINT32(flob + 4, flob_size);
1914 	memcpy(flob + 8, foir.obcd, obcd_size);
1915 	memcpy(flob + 8 + obcd_size, foir.obim, obim_size);
1916 
1917 	// Unlock room/roomScripts
1918 	if (!isRoomLocked)
1919 		_res->unlock(rtRoom, room);
1920 	if (_game.version == 8 && !isRoomScriptsLocked)
1921 		_res->unlock(rtRoomScripts, room);
1922 
1923 	// Setup local object flags
1924 	resetRoomObject(od, flob, flob);
1925 
1926 	od->fl_object_index = slot;
1927 }
1928 
1929 } // End of namespace Scumm
1930