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 "ultima/ultima8/ultima8.h"
24 #include "ultima/ultima8/usecode/usecode.h"
25 #include "ultima/ultima8/games/game_data.h"
26 #include "ultima/ultima8/usecode/uc_machine.h"
27 #include "ultima/ultima8/usecode/uc_list.h"
28 #include "ultima/ultima8/world/world.h"
29 #include "ultima/ultima8/kernel/kernel.h"
30 #include "ultima/ultima8/kernel/delay_process.h"
31 #include "ultima/ultima8/world/get_object.h"
32 #include "ultima/ultima8/graphics/main_shape_archive.h"
33 #include "ultima/ultima8/graphics/gump_shape_archive.h"
34 #include "ultima/ultima8/graphics/shape.h"
35 #include "ultima/ultima8/world/item_factory.h"
36 #include "ultima/ultima8/world/current_map.h"
37 #include "ultima/ultima8/world/fire_type.h"
38 #include "ultima/ultima8/misc/direction_util.h"
39 #include "ultima/ultima8/gumps/bark_gump.h"
40 #include "ultima/ultima8/gumps/ask_gump.h"
41 #include "ultima/ultima8/world/actors/actor_bark_notify_process.h"
42 #include "ultima/ultima8/gumps/paperdoll_gump.h"
43 #include "ultima/ultima8/gumps/game_map_gump.h"
44 #include "ultima/ultima8/world/world_point.h"
45 #include "ultima/ultima8/world/gravity_process.h"
46 #include "ultima/ultima8/world/loop_script.h"
47 #include "ultima/ultima8/world/camera_process.h"
48 #include "ultima/ultima8/world/sprite_process.h"
49 #include "ultima/ultima8/gumps/slider_gump.h"
50 #include "ultima/ultima8/usecode/uc_process.h"
51 #include "ultima/ultima8/world/destroy_item_process.h"
52 #include "ultima/ultima8/world/target_reticle_process.h"
53 #include "ultima/ultima8/world/snap_process.h"
54 #include "ultima/ultima8/world/super_sprite_process.h"
55 #include "ultima/ultima8/audio/audio_process.h"
56 #include "ultima/ultima8/world/actors/main_actor.h"
57 #include "ultima/ultima8/world/missile_tracker.h"
58 #include "ultima/ultima8/world/crosshair_process.h"
59 #include "ultima/ultima8/world/actors/anim_action.h"
60 
61 namespace Ultima {
62 namespace Ultima8 {
63 
64 // Shape for camera snap eggs.  Same in Remorse and Regret.
65 static const uint32 SNAP_EGG_SHAPE = 0x4fe;
66 
67 // Shape for small spark where a bullet hits.  Same in Remorse and Regret.
68 static const uint32 BULLET_SPLASH_SHAPE = 0x1d9;
69 
DEFINE_RUNTIME_CLASSTYPE_CODE(Item)70 DEFINE_RUNTIME_CLASSTYPE_CODE(Item)
71 
72 Item::Item()
73 	: _shape(0), _frame(0), _x(0), _y(0), _z(0),
74 	  _flags(0), _quality(0), _npcNum(0), _mapNum(0),
75 	  _extendedFlags(0), _parent(0),
76 	  _cachedShape(nullptr), _cachedShapeInfo(nullptr),
77 	  _gump(0), _gravityPid(0), _lastSetup(0),
78 	  _ix(0), _iy(0), _iz(0), _damagePoints(1) {
79 }
80 
81 
~Item()82 Item::~Item() {
83 }
84 
dumpInfo() const85 void Item::dumpInfo() const {
86 	pout << "Item " << getObjId() << " (class "
87 	     << GetClassType()._className << ", shape "
88 		 << getShape();
89 
90 	const char *ucname = GameData::get_instance()->getMainUsecode()->get_class_name(_shape);
91 	if (ucname != nullptr) {
92 		pout << " (uc:" << ucname << ")";
93 	}
94 
95 	pout << ", " << getFrame() << ", (";
96 
97 	if (_parent) {
98 		int32 gx, gy;
99 		getGumpLocation(gx, gy);
100 		pout << gx << "," << gy;
101 	} else {
102 		pout << _x << "," << _y << "," << _z;
103 	}
104 
105 	pout << ") q:" << getQuality()
106 	     << ", m:" << getMapNum() << ", n:" << getNpcNum()
107 	     << ", f:0x" << ConsoleStream::hex << getFlags() << ", ef:0x"
108 		 << getExtFlags();
109 
110 	const ShapeInfo *info = getShapeInfo();
111 	if (info) {
112 		pout << " shapeinfo f:" << info->_flags << ", fam:"
113 			 << info->_family << ", et:" << info->_equipType;
114 	}
115 
116 	pout << ")" << ConsoleStream::dec << Std::endl;
117 }
118 
getParentAsContainer() const119 Container *Item::getParentAsContainer() const {
120 	// No parent, no container
121 	if (!_parent || getObject(_parent) == nullptr)
122 		return nullptr;
123 
124 	Container *p = getContainer(_parent);
125 
126 	if (!p) {
127 		perr << "Item " << getObjId() << " _parent (" << _parent << ") is an invalid Container ObjID" << Std::endl;
128 		CANT_HAPPEN();
129 	}
130 
131 	return p;
132 }
133 
getTopItem()134 Item *Item::getTopItem() {
135 	Container *parentItem = getParentAsContainer();
136 
137 	if (!parentItem) return this;
138 
139 	while (parentItem->getParentAsContainer()) {
140 		parentItem = parentItem->getParentAsContainer();
141 	}
142 
143 	return parentItem;
144 }
145 
setLocation(int32 X,int32 Y,int32 Z)146 void Item::setLocation(int32 X, int32 Y, int32 Z) {
147 	_x = X;
148 	_y = Y;
149 	_z = Z;
150 }
151 
move(const Point3 & pt)152 void Item::move(const Point3 &pt) {
153 	move(pt.x, pt.y, pt.z);
154 }
155 
move(int32 X,int32 Y,int32 Z)156 void Item::move(int32 X, int32 Y, int32 Z) {
157 	bool no_lerping = false;
158 	CurrentMap *map = World::get_instance()->getCurrentMap();
159 	int mapChunkSize = map->getChunkSize();
160 
161 	if (getObjId() == 1 && Z < 0) {
162 		perr.Print("Warning: moving avatar below Z=0. (%d,%d,%d)\n", X, Y, Z);
163 	}
164 
165 	// TODO: In Crusader, if we are moving the avatar the game also checks whether
166 	// there is an active "shield zap" sprite that needs moving at the same time.
167 
168 	// It's currently in the ethereal void, remove it from there
169 	if (_flags & FLG_ETHEREAL) {
170 
171 		// Remove us from the ethereal void
172 		World::get_instance()->etherealRemove(_objId);
173 	}
174 
175 	// Remove from container (if contained or equiped)
176 	if (_flags & (FLG_CONTAINED | FLG_EQUIPPED)) {
177 		if (_parent) {
178 			// If we are flagged as Ethereal, we are already removed
179 			if (!(_flags & FLG_ETHEREAL)) {
180 				Container *p = getParentAsContainer();
181 				if (p) p->removeItem(this);
182 			}
183 		} else
184 			perr << "Item " << getObjId() << " FLG_CONTAINED or FLG_EQUIPPED set but item has no _parent" << Std::endl;
185 
186 		// Clear our owner.
187 		_parent = 0;
188 
189 		// No lerping when going from a container to somewhere else
190 		no_lerping = true;
191 	}
192 	// Item needs to be removed if in the map, and it is moving to a
193 	// different chunk
194 	else if ((_extendedFlags & EXT_INCURMAP) &&
195 	         ((_x / mapChunkSize != X / mapChunkSize) ||
196 	          (_y / mapChunkSize != Y / mapChunkSize))) {
197 
198 		// Remove us from the map
199 		map->removeItem(this);
200 	}
201 
202 	// Unset all the various _flags that no longer apply
203 	_flags &= ~(FLG_CONTAINED | FLG_EQUIPPED | FLG_ETHEREAL);
204 
205 	// Set the location
206 	_x = X;
207 	_y = Y;
208 	_z = Z;
209 
210 	// Add it back to the map if needed
211 	if (!(_extendedFlags & EXT_INCURMAP)) {
212 		// Disposable fast only items get put at the end
213 		// While normal items get put at start
214 		if (_flags & (FLG_DISPOSABLE | FLG_FAST_ONLY))
215 			map->addItemToEnd(this);
216 		else
217 			map->addItem(this);
218 	}
219 
220 	// Call just moved
221 	callUsecodeEvent_justMoved();
222 
223 	// Are we moving somewhere fast
224 	bool dest_fast = map->isChunkFast(X / mapChunkSize, Y / mapChunkSize);
225 
226 	// No lerping for this move
227 	if (no_lerping) _extendedFlags |= EXT_LERP_NOPREV;
228 
229 	// If the destination is not in the fast area, and we are in
230 	// the fast area, we need to call leaveFastArea, as long as we aren't
231 	// being followed by the camera. We also disable lerping in such a case
232 	if (!dest_fast && (_flags & Item::FLG_FASTAREA)) {
233 		_extendedFlags |= EXT_LERP_NOPREV;
234 		if (_extendedFlags & EXT_CAMERA)
235 			CameraProcess::GetCameraProcess()->itemMoved();
236 		else
237 			leaveFastArea();
238 
239 		return; //we are done
240 	}
241 	// Or if the dest is fast, and we are not, we need to call enterFastArea
242 	else if (dest_fast && !(_flags & Item::FLG_FASTAREA)) {
243 		_extendedFlags |= EXT_LERP_NOPREV;
244 		enterFastArea();
245 	}
246 
247 	// If we are being followed, notify the camera that we moved
248 	// Note that we don't need to
249 	if (_extendedFlags & EXT_CAMERA)
250 		CameraProcess::GetCameraProcess()->itemMoved();
251 
252 	if (_extendedFlags & EXT_TARGET)
253 		TargetReticleProcess::get_instance()->itemMoved(this);
254 }
255 
moveToContainer(Container * container,bool checkwghtvol)256 bool Item::moveToContainer(Container *container, bool checkwghtvol) {
257 	// Null container, report an error message
258 	if (!container) {
259 		perr << "NULL container passed to Item::moveToContainer" << Std::endl;
260 		return false;
261 	}
262 
263 	// Already there, do nothing, but only if not ethereal
264 	bool ethereal_same = false;
265 	if (container->getObjId() == _parent) {
266 		// If we are ethereal we'd like to know if we are just being moved back
267 		if (_flags & FLG_ETHEREAL) ethereal_same = true;
268 		else return true;
269 	}
270 
271 	// Eh, can't do it
272 	if (!container->CanAddItem(this, checkwghtvol)) return false;
273 
274 	// It's currently in the ethereal void
275 	if (_flags & FLG_ETHEREAL) {
276 
277 		// Remove us from the ethereal void
278 		World::get_instance()->etherealRemove(_objId);
279 	}
280 
281 	// Remove from container (if contained or equiped)
282 	if (_flags & (FLG_CONTAINED | FLG_EQUIPPED)) {
283 		if (_parent) {
284 			// If we are flagged as Ethereal, we are already removed
285 			if (!(_flags & FLG_ETHEREAL)) {
286 				Container *p = getParentAsContainer();
287 				if (p) p->removeItem(this);
288 			}
289 		} else
290 			perr << "Item " << getObjId() << " FLG_CONTAINED or FLG_EQUIPPED set but item has no _parent" << Std::endl;
291 
292 		// Clear our owner.
293 		_parent = 0;
294 	}
295 	// Item needs to be removed if it in the map
296 	else if (_extendedFlags & EXT_INCURMAP) {
297 
298 		// Remove us from the map
299 		World::get_instance()->getCurrentMap()->removeItem(this);
300 	}
301 
302 	// Unset all the various _flags that no longer apply
303 	_flags &= ~(FLG_CONTAINED | FLG_EQUIPPED | FLG_ETHEREAL);
304 
305 	// Set location to 0,0,0 if we aren't an ethereal item moving back
306 	if (!ethereal_same) _x = _y = 0;
307 	_z = 0;
308 
309 	// Add the item, don't bother with checking weight or vol since we already
310 	// know if it will fit or not
311 	container->addItem(this, false);
312 
313 	// Set our owner.
314 	_parent = container->getObjId();
315 
316 	// Set us contained
317 	_flags |= FLG_CONTAINED;
318 
319 	// If moving to avatar, mark as OWNED
320 	Item *p = this;
321 	while (p->getParentAsContainer())
322 		p = p->getParentAsContainer();
323 	// In Avatar's inventory?
324 	if (p->getObjId() == 1)
325 		setFlagRecursively(FLG_OWNED);
326 
327 	// No lerping when moving to a container
328 	_extendedFlags |= EXT_LERP_NOPREV;
329 
330 	// Call just moved
331 	callUsecodeEvent_justMoved();
332 
333 	// For a container, it's fast if it's got a _gump open
334 	bool dest_fast = (container->_flags & FLG_GUMP_OPEN) != 0;
335 
336 	// If the destination is not in the fast area, and we are in
337 	// the fast area, we need to call leaveFastArea
338 	if (!dest_fast && (_flags & Item::FLG_FASTAREA))
339 		leaveFastArea();
340 	// Or if the dest is fast, and we are not, we need to call enterFastArea
341 	else if (dest_fast && !(_flags & Item::FLG_FASTAREA))
342 		enterFastArea();
343 
344 	// Done
345 	return true;
346 }
347 
moveToEtherealVoid()348 void Item::moveToEtherealVoid() {
349 	// It's already Ethereal
350 	if (_flags & FLG_ETHEREAL) return;
351 
352 	// Add it to the ethereal void
353 	World::get_instance()->etherealPush(_objId);
354 
355 	// It's owned by something removed it from the something, but keep _flags
356 	if (_flags & (FLG_CONTAINED | FLG_EQUIPPED)) {
357 
358 		if (_parent) {
359 			Container *p = getParentAsContainer();
360 			if (p) p->removeItem(this);
361 		} else
362 			perr << "Item " << getObjId() << " FLG_CONTAINED or FLG_EQUIPPED set but item has no _parent" << Std::endl;
363 	} else if (_extendedFlags & EXT_INCURMAP) {
364 		World::get_instance()->getCurrentMap()->removeItem(this);
365 	}
366 
367 	// Set the ETHEREAL Flag
368 	_flags |=  FLG_ETHEREAL;
369 }
370 
returnFromEtherealVoid()371 void Item::returnFromEtherealVoid() {
372 	// It's not Ethereal
373 	if (!(_flags & FLG_ETHEREAL)) return;
374 
375 	// Ok, we can do 2 things here
376 	// If an item has the contained or Equipped _flags set, we return it to it's owner
377 	if (_flags & (FLG_CONTAINED | FLG_EQUIPPED)) {
378 		Container *p = getParentAsContainer();
379 		if (!p) {
380 			perr << "Item " << getObjId() << " FLG_CONTAINED or FLG_EQUIPPED set but item has no valid _parent" << Std::endl;
381 			CANT_HAPPEN();
382 		}
383 		moveToContainer(p);
384 	}
385 	// or we return it to the world
386 	else {
387 		move(_x, _y, _z);
388 	}
389 
390 }
391 
movedByPlayer()392 void Item::movedByPlayer() {
393 	// owned-by-avatar items can't be stolen
394 	if (_flags & FLG_OWNED) return;
395 
396 	// Otherwise, player is stealing.
397 	// See if anybody is around to notice.
398 	Item *avatar = getItem(1);
399 	UCList itemlist(2);
400 	LOOPSCRIPT(script, LS_TOKEN_TRUE);
401 	CurrentMap *currentmap = World::get_instance()->getCurrentMap();
402 	currentmap->areaSearch(&itemlist, script, sizeof(script), avatar, 640, 0);
403 
404 	for (unsigned int i = 0; i < itemlist.getSize(); ++i) {
405 		Actor *actor = getActor(itemlist.getuint16(i));
406 		if (actor && !actor->isDead())
407 			actor->callUsecodeEvent_AvatarStoleSomething(getObjId());
408 	}
409 }
410 
getZ() const411 int32 Item::getZ() const {
412 	return _z;
413 }
414 
getLocationAbsolute(int32 & X,int32 & Y,int32 & Z) const415 void Item::getLocationAbsolute(int32 &X, int32 &Y, int32 &Z) const {
416 	if (_parent) {
417 		Item *p = getParentAsContainer();
418 
419 		if (p) {
420 			p->getLocationAbsolute(X, Y, Z);
421 			return;
422 		}
423 	}
424 
425 	X = _x;
426 	Y = _y;
427 	Z = _z;
428 }
429 
getGumpLocation(int32 & X,int32 & Y) const430 void Item::getGumpLocation(int32 &X, int32 &Y) const {
431 	if (!_parent) return;
432 
433 	X = _y & 0xFF;
434 	Y = (_y >> 8) & 0xFF;
435 }
436 
setGumpLocation(int32 X,int32 Y)437 void Item::setGumpLocation(int32 X, int32 Y) {
438 	if (!_parent) return;
439 
440 	_y = (X & 0xFF) + ((Y & 0xFF) << 8);
441 }
442 
randomGumpLocation()443 void Item::randomGumpLocation() {
444 	if (!_parent) return;
445 
446 	// This sets the coordinates to (255,255) and lets the ContainerGump
447 	// randomize the position when it is next opened.
448 	_y = 0xFFFF;
449 }
450 
getCentre(int32 & X,int32 & Y,int32 & Z) const451 void Item::getCentre(int32 &X, int32 &Y, int32 &Z) const {
452 	// constants!
453 	const ShapeInfo *shapeinfo = getShapeInfo();
454 	if (_flags & FLG_FLIPPED) {
455 		X = _x - shapeinfo->_y * 16;
456 		Y = _y - shapeinfo->_x * 16;
457 	} else {
458 		X = _x - shapeinfo->_x * 16;
459 		Y = _y - shapeinfo->_y * 16;
460 	}
461 
462 	Z = _z + shapeinfo->_z * 4;
463 }
464 
getWorldBox() const465 Box Item::getWorldBox() const {
466 	int32 xd, yd, zd;
467 	getFootpadWorld(xd, yd, zd);
468 	return Box(_x, _y, _z, xd, yd, zd);
469 }
470 
setShape(uint32 shape)471 void Item::setShape(uint32 shape) {
472 	_cachedShape = nullptr;
473 
474 	if (GAME_IS_CRUSADER && _shape && shape != _shape) {
475 		// In Crusader, here we need to check if the shape
476 		// changed from targetable to not-targetable, or vice-versa
477 		const ShapeInfo *oldinfo = getShapeInfo();
478 		_shape = shape;
479 		_cachedShapeInfo = nullptr;
480 		const ShapeInfo *newinfo = getShapeInfo();
481 
482 		if (!hasFlags(FLG_BROKEN) && oldinfo && newinfo) {
483 			if (oldinfo->is_targetable() && !newinfo->is_targetable()) {
484 				World::get_instance()->getCurrentMap()->removeTargetItem(this);
485 			}
486 			else if (!oldinfo->is_targetable() && newinfo->is_targetable()) {
487 				World::get_instance()->getCurrentMap()->addTargetItem(this);
488 			}
489 		}
490 	} else {
491 		_shape = shape;
492 		_cachedShapeInfo = nullptr;
493 	}
494 }
495 
overlaps(const Item & item2) const496 bool Item::overlaps(const Item &item2) const {
497 	int32 x1a, y1a, z1a, x1b, y1b, z1b;
498 	int32 x2a, y2a, z2a, x2b, y2b, z2b;
499 	getLocation(x1b, y1b, z1a);
500 	item2.getLocation(x2b, y2b, z2a);
501 
502 	int32 xd, yd, zd;
503 	getFootpadWorld(xd, yd, zd);
504 	x1a = x1b - xd;
505 	y1a = y1b - yd;
506 	z1b = z1a + zd;
507 
508 	item2.getFootpadWorld(xd, yd, zd);
509 	x2a = x2b - xd;
510 	y2a = y2b - yd;
511 	z2b = z2a + zd;
512 
513 	if (x1b <= x2a || x2b <= x1a) return false;
514 	if (y1b <= y2a || y2b <= y1a) return false;
515 	if (z1b <= z2a || z2b <= z1a) return false;
516 	return true;
517 }
518 
overlapsxy(const Item & item2) const519 bool Item::overlapsxy(const Item &item2) const {
520 	int32 x1a, y1a, z1a, x1b, y1b;
521 	int32 x2a, y2a, z2a, x2b, y2b;
522 	getLocation(x1b, y1b, z1a);
523 	item2.getLocation(x2b, y2b, z2a);
524 
525 	int32 xd, yd, zd;
526 	getFootpadWorld(xd, yd, zd);
527 	x1a = x1b - xd;
528 	y1a = y1b - yd;
529 
530 	item2.getFootpadWorld(xd, yd, zd);
531 	x2a = x2b - xd;
532 	y2a = y2b - yd;
533 
534 	if (x1b <= x2a || x2b <= x1a) return false;
535 	if (y1b <= y2a || y2b <= y1a) return false;
536 	return true;
537 }
538 
isOn(const Item & item2) const539 bool Item::isOn(const Item &item2) const {
540 	int32 x1a, y1a, z1a, x1b, y1b;
541 	int32 x2a, y2a, z2a, x2b, y2b, z2b;
542 	getLocation(x1b, y1b, z1a);
543 	item2.getLocation(x2b, y2b, z2a);
544 
545 	int32 xd, yd, zd;
546 	getFootpadWorld(xd, yd, zd);
547 	x1a = x1b - xd;
548 	y1a = y1b - yd;
549 
550 	item2.getFootpadWorld(xd, yd, zd);
551 	x2a = x2b - xd;
552 	y2a = y2b - yd;
553 	z2b = z2a + zd;
554 
555 	if (x1b <= x2a || x2b <= x1a) return false;
556 	if (y1b <= y2a || y2b <= y1a) return false;
557 	if (z2b == z1a) return true;
558 	return false;
559 }
560 
isCompletelyOn(const Item & item2) const561 bool Item::isCompletelyOn(const Item &item2) const {
562 	if (hasFlags(FLG_CONTAINED) || item2.hasFlags(FLG_CONTAINED))
563 		return false;
564 
565 	int32 x1a, y1a, z1a, x1b, y1b;
566 	int32 x2a, y2a, z2a, x2b, y2b, z2b;
567 	getLocation(x1b, y1b, z1a);
568 	item2.getLocation(x2b, y2b, z2a);
569 
570 	int32 xd, yd, zd;
571 	getFootpadWorld(xd, yd, zd);
572 	x1a = x1b - xd;
573 	y1a = y1b - yd;
574 
575 	item2.getFootpadWorld(xd, yd, zd);
576 	x2a = x2b - xd;
577 	y2a = y2b - yd;
578 	z2b = z2a + zd;
579 
580 	return ((x1b <= x2b && x2a <= x1a) &&
581 			(y1b <= y2b && y2a <= y1a) &&
582 			(z2b == z1a));
583 }
584 
isCentreOn(const Item & item2) const585 bool Item::isCentreOn(const Item &item2) const {
586 	int32 x1c, y1c, z1c;
587 	int32 x2a, y2a, z2a, x2b, y2b, z2b;
588 	item2.getLocation(x2b, y2b, z2a);
589 
590 	getCentre(x1c, y1c, z1c);
591 
592 	int32 xd, yd, zd;
593 	item2.getFootpadWorld(xd, yd, zd);
594 	x2a = x2b - xd;
595 	y2a = y2b - yd;
596 	z2b = z2a + zd;
597 
598 	if (x1c <= x2a || x2b <= x1c) return false;
599 	if (y1c <= y2a || y2b <= y1c) return false;
600 	if (z2b == getZ()) return true;
601 	return false;
602 }
603 
isOnScreen() const604 bool Item::isOnScreen() const {
605 	GameMapGump *game_map = Ultima8Engine::get_instance()->getGameMapGump();
606 
607 	if (!game_map)
608 		return false;
609 
610 	Rect game_map_dims;
611 	int32 screenx = -1;
612 	int32 screeny = -1;
613 	game_map->GetLocationOfItem(_objId, screenx, screeny);
614 	game_map->GetDims(game_map_dims);
615 	int32 xd, yd, zd;
616 	getFootpadWorld(xd, yd, zd);
617 
618 	if (game_map_dims.contains(screenx, screeny) &&
619 	    game_map_dims.contains(screenx + xd, screeny + yd)) {
620 		return true;
621 	}
622 
623 	return false;
624 }
625 
isPartlyOnScreen() const626 bool Item::isPartlyOnScreen() const {
627 	GameMapGump *game_map = Ultima8Engine::get_instance()->getGameMapGump();
628 
629 	if (!game_map)
630 		return false;
631 
632 	Rect game_map_dims;
633 	int32 screenx = -1;
634 	int32 screeny = -1;
635 	game_map->GetLocationOfItem(_objId, screenx, screeny);
636 	game_map->GetDims(game_map_dims);
637 	int32 xd, yd, zd;
638 	getFootpadWorld(xd, yd, zd);
639 
640 	if (game_map_dims.contains(screenx, screeny) ||
641 		game_map_dims.contains(screenx + xd, screeny + yd)) {
642 		return true;
643 	}
644 
645 	return false;
646 }
647 
canExistAt(int32 x,int32 y,int32 z,bool needsupport) const648 bool Item::canExistAt(int32 x, int32 y, int32 z, bool needsupport) const {
649 	CurrentMap *cm = World::get_instance()->getCurrentMap();
650 	const Item *support;
651 	bool valid = cm->isValidPosition(x, y, z, getShape(), getObjId(),
652 	                                 &support, 0);
653 	return valid && (!needsupport || support);
654 }
655 
getDirToItemCentre(const Item & item2) const656 Direction Item::getDirToItemCentre(const Item &item2) const {
657 	int32 xv, yv, zv;
658 	getCentre(xv, yv, zv);
659 
660 	int32 i2x, i2y, i2z;
661 	item2.getCentre(i2x, i2y, i2z);
662 
663 	return Direction_GetWorldDir(i2y - yv, i2x - xv, dirmode_8dirs);
664 }
665 
getDirToItemCentre(const Point3 & pt) const666 Direction Item::getDirToItemCentre(const Point3 &pt) const {
667 	int32 xv, yv, zv;
668 	getCentre(xv, yv, zv);
669 
670 	return Direction_GetWorldDir(pt.y - yv, pt.x - xv, dirmode_8dirs);
671 }
672 
673 
getRange(const Item & item2,bool checkz) const674 int Item::getRange(const Item &item2, bool checkz) const {
675 	int32 thisX, thisY, thisZ;
676 	int32 otherX, otherY, otherZ;
677 	int32 thisXd, thisYd, thisZd;
678 	int32 otherXd, otherYd, otherZd;
679 	int32 thisXmin, thisYmin, thisZmax;
680 	int32 otherXmin, otherYmin, otherZmax;
681 
682 	getLocationAbsolute(thisX, thisY, thisZ);
683 	item2.getLocationAbsolute(otherX, otherY, otherZ);
684 	getFootpadWorld(thisXd, thisYd, thisZd);
685 	item2.getFootpadWorld(otherXd, otherYd, otherZd);
686 
687 	thisXmin = thisX - thisXd;
688 	thisYmin = thisY - thisYd;
689 	thisZmax = thisZ + thisZd;
690 
691 	otherXmin = otherX - otherXd;
692 	otherYmin = otherY - otherYd;
693 	otherZmax = otherZ + otherZd;
694 
695 	int32 range = 0;
696 
697 	if (thisXmin - otherX > range)
698 		range = thisYmin - otherY;
699 	if (otherXmin - thisX > range)
700 		range = thisXmin - otherX;
701 	if (thisYmin - otherY > range)
702 		range = otherXmin - thisX;
703 	if (otherYmin - thisY > range)
704 		range = otherYmin - thisY;
705 	if (checkz && thisZ - otherZmax > range)
706 		range = thisZ - otherZmax;
707 	if (checkz && otherZ - thisZmax > range)
708 		range = otherZ - thisZmax;
709 
710 	return range;
711 }
712 
getRangeIfVisible(const Item & item2) const713 int Item::getRangeIfVisible(const Item &item2) const {
714 	World *world = World::get_instance();
715 	CurrentMap *map = world->getCurrentMap();
716 	int32 start[3];
717 	int32 end[3];
718 	int32 dims[3] = {1, 1, 1};
719 	Std::list<CurrentMap::SweepItem> hitItems;
720 	getCentre(start[0], start[1], start[2]);
721 	item2.getCentre(end[0], end[1], end[2]);
722 
723 	int xdiff = abs(start[0] - end[0]);
724 	int ydiff = abs(start[1] - end[1]);
725 	int zdiff = abs(start[2] - end[2]);
726 
727 	map->sweepTest(start, end, dims, getShapeInfo()->_flags, _objId, true, &hitItems);
728 
729 	if (hitItems.size() > 0) {
730 		for (Std::list<CurrentMap::SweepItem>::const_iterator it = hitItems.begin();
731 			 it != hitItems.end();
732 			 it++) {
733 			int objId = it->_item;
734 			if (it->_blocking && objId != _objId && objId != item2.getObjId()) {
735 				//int out[3];
736 				//it->GetInterpolatedCoords(out, start, end);
737 				//warning("found blocking item %d at %d %d %d.", objId, out[0], out[1], out[2]);
738 				return 0;
739 			}
740 		}
741 	}
742 
743 	int distance = MAX(MAX(xdiff, ydiff), zdiff);
744 	return distance;
745 }
746 
getShapeInfoFromGameInstance() const747 const ShapeInfo *Item::getShapeInfoFromGameInstance() const {
748 	return GameData::get_instance()->getMainShapes()->getShapeInfo(_shape);
749 }
750 
getShapeObject() const751 const Shape *Item::getShapeObject() const {
752 	if (!_cachedShape) _cachedShape = GameData::get_instance()->getMainShapes()->getShape(_shape);
753 	return _cachedShape;
754 }
755 
getFamily() const756 uint16 Item::getFamily() const {
757 	const ShapeInfo *info = getShapeInfo();
758 	if (!info)
759 		return 0;
760 	return static_cast<uint16>(info->_family);
761 }
762 
getWeight() const763 uint32 Item::getWeight() const {
764 	uint32 weight = getShapeInfo()->_weight;
765 
766 	switch (getShapeInfo()->_family) {
767 	case ShapeInfo::SF_QUANTITY:
768 		return ((getQuality() * weight) + 9) / 10;
769 	case ShapeInfo::SF_REAGENT:
770 		return getQuality() * weight;
771 	default:
772 		return weight * 10;
773 	}
774 }
775 
getTotalWeight() const776 uint32 Item::getTotalWeight() const {
777 	return getWeight();
778 }
779 
getVolume() const780 uint32 Item::getVolume() const {
781 	// invisible items (trap markers and such) don't take up volume
782 	if (hasFlags(FLG_INVISIBLE))
783 		return 0;
784 
785 
786 	uint32 volume = getShapeInfo()->_volume;
787 
788 	switch (getShapeInfo()->_family) {
789 	case ShapeInfo::SF_QUANTITY:
790 		return ((getQuality() * volume) + 99) / 100;
791 	case ShapeInfo::SF_REAGENT:
792 		return ((getQuality() * volume) + 9) / 10;
793 	case ShapeInfo::SF_CONTAINER:
794 		return (volume == 0) ? 1 : volume;
795 	default:
796 		return volume;
797 	}
798 }
799 
checkLoopScript(const uint8 * script,uint32 scriptsize) const800 bool Item::checkLoopScript(const uint8 *script, uint32 scriptsize) const {
801 	// if really necessary this could be made static to prevent news/deletes
802 	DynamicUCStack stack(0x40); // 64bytes should be plenty of room
803 
804 	unsigned int i = 0;
805 
806 	uint16 ui16a, ui16b;
807 
808 	stack.push2(1); // default to true if script is empty
809 
810 	while (i < scriptsize) {
811 		switch (script[i]) {
812 		case LS_TOKEN_FALSE: // false
813 			stack.push2(0);
814 			break;
815 
816 		case LS_TOKEN_TRUE: // true
817 			stack.push2(1);
818 			break;
819 
820 		case LS_TOKEN_END: // end
821 			ui16a = stack.pop2();
822 			return (ui16a != 0);
823 
824 		case LS_TOKEN_INT: // int
825 			//! check for i out of bounds
826 			ui16a = script[i + 1] + (script[i + 2] << 8);
827 			stack.push2(ui16a);
828 			i += 2;
829 			break;
830 
831 		case LS_TOKEN_AND: // and
832 			ui16a = stack.pop2();
833 			ui16b = stack.pop2();
834 			if (ui16a != 0 && ui16b != 0)
835 				stack.push2(1);
836 			else
837 				stack.push2(0);
838 			break;
839 
840 		case LS_TOKEN_OR: // or
841 			ui16a = stack.pop2();
842 			ui16b = stack.pop2();
843 			if (ui16a != 0 || ui16b != 0)
844 				stack.push2(1);
845 			else
846 				stack.push2(0);
847 			break;
848 
849 		case LS_TOKEN_NOT: // not
850 			ui16a = stack.pop2();
851 			if (ui16a != 0)
852 				stack.push2(0);
853 			else
854 				stack.push2(1);
855 			break;
856 
857 		case LS_TOKEN_STATUS: // item status
858 			stack.push2(getFlags());
859 			break;
860 
861 		case LS_TOKEN_Q: // item _quality
862 			stack.push2(getQuality());
863 			break;
864 
865 		case LS_TOKEN_NPCNUM: // npc num
866 			stack.push2(getNpcNum());
867 			break;
868 
869 		case LS_TOKEN_EQUAL: // equal
870 			ui16a = stack.pop2();
871 			ui16b = stack.pop2();
872 			if (ui16a == ui16b)
873 				stack.push2(1);
874 			else
875 				stack.push2(0);
876 			break;
877 
878 		case LS_TOKEN_GREATER: // greater than
879 			ui16a = stack.pop2();
880 			ui16b = stack.pop2();
881 			if (ui16b > ui16a)
882 				stack.push2(1);
883 			else
884 				stack.push2(0);
885 			break;
886 
887 		case LS_TOKEN_LESS: // less than
888 			ui16a = stack.pop2();
889 			ui16b = stack.pop2();
890 			if (ui16b < ui16a)
891 				stack.push2(1);
892 			else
893 				stack.push2(0);
894 			break;
895 
896 		case LS_TOKEN_GEQUAL: // greater or equal
897 			ui16a = stack.pop2();
898 			ui16b = stack.pop2();
899 			if (ui16b >= ui16a)
900 				stack.push2(1);
901 			else
902 				stack.push2(0);
903 			break;
904 
905 		case LS_TOKEN_LEQUAL: // smaller or equal
906 			ui16a = stack.pop2();
907 			ui16b = stack.pop2();
908 			if (ui16b <= ui16a)
909 				stack.push2(1);
910 			else
911 				stack.push2(0);
912 			break;
913 
914 		case LS_TOKEN_FAMILY: // item family
915 			stack.push2(getFamily());
916 			break;
917 
918 		case LS_TOKEN_SHAPE: // item _shape
919 			stack.push2(static_cast<uint16>(getShape()));
920 			break;
921 
922 		case 'A':
923 		case 'B':
924 		case 'C':
925 		case 'D':
926 		case 'E':
927 		case 'F':
928 		case 'G':
929 		case 'H':
930 		case 'I':
931 		case 'J':
932 		case 'K':
933 		case 'L':
934 		case 'M':
935 		case 'N':
936 		case 'O':
937 		case 'P':
938 		case 'Q':
939 		case 'R':
940 		case 'S':
941 		case 'T':
942 		case 'U':
943 		case 'V':
944 		case 'W':
945 		case 'X':
946 		case 'Y':
947 		case 'Z': {
948 			bool match = false;
949 			int count = script[i] - '@';
950 			for (int j = 0; j < count; j++) {
951 				//! check for i out of bounds
952 				if (getShape() == static_cast<uint32>(script[i + 1] + (script[i + 2] << 8)))
953 					match = true;
954 				i += 2;
955 			}
956 			if (match)
957 				stack.push2(1);
958 			else
959 				stack.push2(0);
960 		}
961 		break;
962 
963 		case LS_TOKEN_FRAME: // item _frame
964 			stack.push2(static_cast<uint16>(getFrame()));
965 			break;
966 
967 		case 'a':
968 		case 'b':
969 		case 'c':
970 		case 'd':
971 		case 'e':
972 		case 'f':
973 		case 'g':
974 		case 'h':
975 		case 'i':
976 		case 'j':
977 		case 'k':
978 		case 'l':
979 		case 'm':
980 		case 'n':
981 		case 'o':
982 		case 'p':
983 		case 'q':
984 		case 'r':
985 		case 's':
986 		case 't':
987 		case 'u':
988 		case 'v':
989 		case 'w':
990 		case 'x':
991 		case 'y':
992 		case 'z': {
993 			bool match = false;
994 			int count = script[i] - '`';
995 			for (int j = 0; j < count; j++) {
996 				//! check for i out of bounds
997 				if (getFrame() == static_cast<uint32>(script[i + 1] + (script[i + 2] << 8)))
998 					match = true;
999 				i += 2;
1000 			}
1001 			if (match)
1002 				stack.push2(1);
1003 			else
1004 				stack.push2(0);
1005 		}
1006 		break;
1007 
1008 		default:
1009 			perr.Print("Unknown loopscript opcode %02X\n", script[i]);
1010 		}
1011 
1012 		i++;
1013 	}
1014 	perr.Print("Didn't encounter $ in loopscript\n");
1015 	return false;
1016 }
1017 
1018 
collideMove(int32 dx,int32 dy,int32 dz,bool teleport,bool force,ObjId * hititem,uint8 * dirs)1019 int32 Item::collideMove(int32 dx, int32 dy, int32 dz, bool teleport, bool force, ObjId *hititem, uint8 *dirs) {
1020 	Ultima8Engine *guiapp = Ultima8Engine::get_instance();
1021 	World *world = World::get_instance();
1022 	CurrentMap *map = world->getCurrentMap();
1023 
1024 	if (hititem) *hititem = 0;
1025 	if (dirs) *dirs = 0;
1026 
1027 	int32 end[3] = { dx, dy, dz };
1028 
1029 	int32 start[3];
1030 	if (_parent) {
1031 		// If we are moving from a container, only check the destination
1032 		start[0] = end[0];
1033 		start[1] = end[1];
1034 		start[2] = end[2];
1035 	} else {
1036 		// Otherwise check from where we are to where we want to go
1037 		getLocation(start[0], start[1], start[2]);
1038 	}
1039 
1040 	int32 dims[3];
1041 	getFootpadWorld(dims[0], dims[1], dims[2]);
1042 
1043 	// Do the sweep test
1044 	Std::list<CurrentMap::SweepItem> collisions;
1045 	Std::list<CurrentMap::SweepItem>::iterator it;
1046 	map->sweepTest(start, end, dims, getShapeInfo()->_flags, _objId, false, &collisions);
1047 
1048 	// Ok, now to work out what to do
1049 
1050 
1051 	// the force of the hit, used for the gotHit/hit usecode calls
1052 	int deltax = ABS(start[0] - end[0]) / 4;
1053 	int deltay = ABS(start[1] - end[1]) / 4;
1054 	int deltaz = ABS(start[2] - end[2]);
1055 	int maxdirdelta = deltay;
1056 	if (deltay > maxdirdelta) maxdirdelta = deltay;
1057 	if (deltaz > maxdirdelta) maxdirdelta = deltaz;
1058 	int hitforce = (deltax + deltay + deltaz + maxdirdelta) / 2;
1059 
1060 	// if we are contained, we always teleport
1061 	if (teleport || _parent) {
1062 		// If teleporting and not force, work out if we can reach the end
1063 		if (!force) {
1064 			for (it = collisions.begin(); it != collisions.end(); it++) {
1065 				// Uh oh, we hit something, can't move
1066 				if (it->_endTime == 0x4000 && !it->_touching && it->_blocking) {
1067 					if (hititem) *hititem = it->_item;
1068 					if (dirs) *dirs = it->_dirs;
1069 					return 0;
1070 				}
1071 			}
1072 		}
1073 
1074 		// Trigger all the required events
1075 		bool we_were_released = false;
1076 		for (it = collisions.begin(); it != collisions.end(); it++) {
1077 			Item *item = getItem(it->_item);
1078 			if (!item)
1079 				continue; // shouldn't happen..
1080 
1081 			// Hitting us at the start and end, don't do anything
1082 			if (!_parent && it->_hitTime == 0x0000 &&
1083 			        it->_endTime == 0x4000) {
1084 				continue;
1085 			}
1086 			// Hitting us at the end (call hit on us, got hit on them)
1087 			else if (it->_endTime == 0x4000) {
1088 				if (_objId == 1 && guiapp->isShowTouchingItems())
1089 					item->setExtFlag(Item::EXT_HIGHLIGHT);
1090 
1091 				item->callUsecodeEvent_gotHit(_objId, hitforce);
1092 				callUsecodeEvent_hit(item->getObjId(), hitforce);
1093 			}
1094 			// Hitting us at the start (call release on us and them)
1095 			else if (!_parent && it->_hitTime == 0x0000) {
1096 				if (_objId == 1) item->clearExtFlag(Item::EXT_HIGHLIGHT);
1097 				we_were_released = true;
1098 				item->callUsecodeEvent_release();
1099 			}
1100 		}
1101 
1102 		// Call release on us
1103 		if (we_were_released) callUsecodeEvent_release();
1104 
1105 		// Move US!
1106 		move(end[0], end[1], end[2]);
1107 
1108 		// We reached the end
1109 		return 0x4000;
1110 	} else {
1111 		int32 hit = 0x4000;
1112 
1113 		// If not force, work out if we can reach the end
1114 		// if not, need to do 'stuff'
1115 		// We don't care about items hitting us at the start
1116 		if (!force) {
1117 			for (it = collisions.begin(); it != collisions.end(); it++) {
1118 				if (it->_blocking && !it->_touching) {
1119 					if (hititem) *hititem = it->_item;
1120 					if (dirs) *dirs = it->_dirs;
1121 					hit = it->_hitTime;
1122 					break;
1123 				}
1124 			}
1125 			if (hit < 0) hit = 0;
1126 
1127 			if (hit != 0x4000) {
1128 #if 0
1129 				pout << " Hit time: " << hit << Std::endl;
1130 				pout << "    Start: " << start[0] << ", " << start[1] << ", " << start[2] << Std::endl;
1131 				pout << "      End: " << end[0] << ", " << end[1] << ", " << end[2] << Std::endl;
1132 #endif
1133 				it->GetInterpolatedCoords(end, start, end);
1134 #if 0
1135 				pout << "Collision: " << end[0] << ", " << end[1] << ", " << end[2] << Std::endl;
1136 #endif
1137 			}
1138 		}
1139 
1140 		// Trigger all the required events
1141 		bool we_were_released = false;
1142 		for (it = collisions.begin(); it != collisions.end(); it++) {
1143 			Item *item = getItem(it->_item);
1144 			if (!item) continue;
1145 
1146 			// Did we go past the endpoint of the move?
1147 			if (it->_hitTime > hit) break;
1148 
1149 			uint16 proc_gothit = 0, proc_rel = 0;
1150 
1151 			// If hitting at start, we should have already
1152 			// called gotHit and hit.  In Crusader we call
1153 			// hit for every hitting frame to make sickbays
1154 			// and teleporters work.
1155 			bool call_hit = it->_hitTime >= 0 || GAME_IS_CRUSADER;
1156 			if ((!it->_touching || it->_touchingFloor) && call_hit) {
1157 				if (_objId == 1 && guiapp->isShowTouchingItems())
1158 					item->setExtFlag(Item::EXT_HIGHLIGHT);
1159 
1160 				proc_gothit = item->callUsecodeEvent_gotHit(_objId, hitforce);
1161 				callUsecodeEvent_hit(item->getObjId(), hitforce);
1162 			}
1163 
1164 			// If not hitting at end, we will need to call release
1165 			if (it->_endTime < hit) {
1166 				if (_objId == 1) item->clearExtFlag(Item::EXT_HIGHLIGHT);
1167 				we_were_released = true;
1168 				proc_rel = item->callUsecodeEvent_release();
1169 			}
1170 
1171 			// We want to make sure that release is called AFTER gotHit.
1172 			if (proc_rel && proc_gothit) {
1173 				Process *p = Kernel::get_instance()->getProcess(proc_rel);
1174 				p->waitFor(proc_gothit);
1175 			}
1176 		}
1177 
1178 		// Call release on us
1179 		if (we_were_released) callUsecodeEvent_release();
1180 
1181 		// Move US!
1182 		move(end[0], end[1], end[2]);
1183 
1184 		return hit;
1185 	}
1186 
1187 	return 0;
1188 }
1189 
fireWeapon(int32 x,int32 y,int32 z,Direction dir,int firetype,bool findtarget)1190 uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype, bool findtarget) {
1191 	int32 ix, iy, iz;
1192 	getLocation(ix, iy, iz);
1193 
1194 	if (!GAME_IS_CRUSADER)
1195 		return 0;
1196 
1197 	ix += x;
1198 	iy += y;
1199 	iz += z;
1200 
1201 	CurrentMap *currentmap = World::get_instance()->getCurrentMap();
1202 	const FireType *firetypedat = GameData::get_instance()->getFireType(firetype);
1203 
1204 	if (!firetypedat)
1205 		return 0;
1206 
1207 	int damage = firetypedat->getRandomDamage();
1208 
1209 	const Item *blocker = nullptr;
1210 	// CHECKME: the original doesn't exclude the source like this,
1211 	// but it seems obvious we have to or NPCs shoot themselves?
1212 	bool isvalid = currentmap->isValidPosition(ix, iy, iz, BULLET_SPLASH_SHAPE, _objId, nullptr, nullptr, &blocker);
1213 
1214 	if (!isvalid && blocker) {
1215 		Item *block = getItem(blocker->getObjId());
1216 		Point3 blockpt;
1217 		block->getLocation(blockpt);
1218 		Direction damagedir = Direction_GetWorldDir(blockpt.y - iy, blockpt.x - ix, dirmode_8dirs);
1219 		block->receiveHit(getObjId(), damagedir, damage, firetype);
1220 		if (firetypedat->getRange() != 0) {
1221 			int splashdamage = firetypedat->getRandomDamage();
1222 			firetypedat->applySplashDamageAround(blockpt, splashdamage, 1, block, this);
1223 		}
1224 		if (firetypedat->getNearSprite())
1225 			firetypedat->makeBulletSplashShapeAndPlaySound(ix, iy, iz);
1226 		return 0;
1227 	}
1228 
1229 	int spriteframe = 0; // fire types 1, 2, 7, 8, 0xb, 0xd
1230 	switch (firetype) {
1231 	case 3:
1232 	case 9:
1233 	case 10:
1234 		spriteframe = dir + 0x11;
1235 		break;
1236 	case 5:
1237 		spriteframe = dir + 1;
1238 		break;
1239 	case 6:
1240 		spriteframe = 0x46;
1241 		break;
1242 	case 0xe:
1243 		spriteframe = 0x47 + getRandom() % 5;
1244 		break;
1245 	case 0xf:
1246 	case 0x12: // No Regret only
1247 	case 0x13: // No Regret only
1248 		spriteframe = 0x4c;
1249 		break;
1250 	case 0x10: // No Regret only
1251 		spriteframe = dir + 0x50;
1252 		break;
1253 	case 0x11: // No Regret only
1254 		spriteframe = dir * 6 + 0x78;
1255 		break;
1256 	case 0x14: // No Regret only
1257 		spriteframe = dir * 3 + 0xdc;
1258 		break;
1259 	case 0x15: // No Regret only
1260 		spriteframe = dir + 100;
1261 		break;
1262 	case 0x16: // No Regret only
1263 		spriteframe = dir + 0x11;
1264 		break;
1265 	default:
1266 		break;
1267 	}
1268 
1269 	// HACK: this should be fixed to use inheritence so the behavior
1270 	// is clean for both Item and Actor.
1271 	DirectionMode dirmode = dirmode_8dirs;
1272 	const Actor *thisactor = dynamic_cast<Actor *>(this);
1273 	if (thisactor) {
1274 		// TODO: Get damage for active inventory item if not a weapon?
1275 		dirmode = thisactor->animDirMode(thisactor->getLastAnim());
1276 	}
1277 
1278 	Item *target = nullptr;
1279 	if (findtarget) {
1280 		if (this != getControlledActor()) {
1281 			target = getControlledActor();
1282 		} else {
1283 			target = currentmap->findBestTargetItem(ix, iy, iz - z, dir, dirmode);
1284 		}
1285 	}
1286 
1287 	int32 tx = -1;
1288 	int32 ty = 0;
1289 	int32 tz = 0;
1290 	if (target) {
1291 		target->getCentre(tx, ty, tz);
1292 		tz = target->getTargetZRelativeToAttackerZ(getZ());
1293 	}
1294 
1295 	// TODO: check if we need the equivalent of FUN_1130_0299 here..
1296 	// maybe not? It seems to reset the target reticle etc which we
1297 	// handle differently.
1298 
1299 	int numshots = firetypedat->getNumShots();
1300 	uint16 spriteprocpid = 0;
1301 	for (int i = 0; i < numshots; i++) {
1302 		SuperSpriteProcess *ssp;
1303 		CrosshairProcess *chp = CrosshairProcess::get_instance();
1304 		assert(chp);
1305 		Item *crosshair = getItem(chp->getItemNum());
1306 		int32 ssx, ssy, ssz;
1307 		if (tx != -1) {
1308 			// Shoot toward the target
1309 			ssx = tx;
1310 			ssy = ty;
1311 			ssz = tz;
1312 			findtarget = true;
1313 		} else if (this == getControlledActor() && crosshair) {
1314 			// Shoot toward the crosshair
1315 			crosshair->getLocation(ssx, ssy, ssz);
1316 			ssz = iz;
1317 		} else {
1318 			// Just send the projectile off into the distance
1319 			ssx = ix + Direction_XFactor(dir) * 0x500;
1320 			ssy = iy + Direction_YFactor(dir) * 0x500;
1321 			ssz = iz;
1322 		}
1323 
1324 		uint16 targetid = (target ? target->getObjId() : 0);
1325 		ssp = new SuperSpriteProcess(BULLET_SPLASH_SHAPE, spriteframe,
1326 									 ix, iy, iz, ssx, ssy, ssz, firetype,
1327 									 damage, _objId, targetid, findtarget);
1328 		Kernel::get_instance()->addProcess(ssp);
1329 		spriteprocpid = ssp->getPid();
1330 	}
1331 	return spriteprocpid;
1332 }
1333 
fireDistance(const Item * other,Direction dir,int16 xoff,int16 yoff,int16 zoff) const1334 uint16 Item::fireDistance(const Item *other, Direction dir, int16 xoff, int16 yoff, int16 zoff) const {
1335 	if (!other)
1336 		return 0;
1337 
1338 	//
1339 	// We pick what animation the actor would do to fire, then
1340 	// pick the frame(s) where they fire in that anim.
1341 	//
1342 	// Then, check if the target can be hit using the attackx/attacky/attackz offsets.
1343 	// The offsets are checked in priority order:
1344 	// * First fire frame in anim
1345 	// * Second fire frame
1346 	// * if there are no fire frames, use the parameter offsets
1347 	//
1348 	int16 xoff2 = 0;
1349 	int16 yoff2 = 0;
1350 	int16 zoff2 = 0;
1351 	bool other_offsets = false;
1352 	const Actor *a = dynamic_cast<const Actor *>(this);
1353 	if (a) {
1354 		Animation::Sequence anim;
1355 		bool kneeling = a->isKneeling();
1356 		bool smallwpn = true;
1357 		const MainActor *ma = dynamic_cast<const MainActor *>(this);
1358 		const Item *wpn = getItem(a->getActiveWeapon());
1359 		if (wpn && wpn->getShapeInfo()->_weaponInfo) {
1360 			smallwpn = wpn->getShapeInfo()->_weaponInfo->_small;
1361 		}
1362 
1363 		if (kneeling) {
1364 			if (smallwpn)
1365 				anim = Animation::kneelAndFireSmallWeapon;
1366 			else
1367 				anim = Animation::kneelAndFireLargeWeapon;
1368 		} else {
1369 			if (smallwpn || !ma)
1370 				anim = Animation::fireSmallWeapon;
1371 			else
1372 				anim = Animation::fireLargeWeapon;
1373 		}
1374 
1375 		bool first_offsets = false;
1376 		const AnimAction *action = GameData::get_instance()->getMainShapes()->getAnim(_shape, static_cast<int32>(anim));
1377 		unsigned int nframes = action ? action->getSize() : 0;
1378 		for (unsigned int i = 0; i < nframes; i++) {
1379 			const AnimFrame &frame = action->getFrame(dir, i);
1380 			if (frame.is_cruattack()) {
1381 				if (!first_offsets) {
1382 					xoff = frame.cru_attackx();
1383 					yoff = frame.cru_attacky();
1384 					zoff = frame.cru_attackz();
1385 					first_offsets = true;
1386 				} else {
1387 					xoff2 = frame.cru_attackx();
1388 					yoff2 = frame.cru_attacky();
1389 					zoff2 = frame.cru_attackz();
1390 					other_offsets = true;
1391 					break;
1392 				}
1393 			}
1394 		}
1395 	}
1396 
1397 	int32 x, y, z;
1398 	getLocation(x, y, z);
1399 
1400 	int32 ox, oy, oz;
1401 	other->getLocation(ox, oy, oz);
1402 
1403 	int32 dist = 0;
1404 
1405 	CurrentMap *cm = World::get_instance()->getCurrentMap();
1406 	if (!cm)
1407 		return 0;
1408 
1409 	for (int i = 0; i < (other_offsets ? 2 : 1) && dist == 0; i++) {
1410 		int32 cx = x + (i == 0 ? xoff : xoff2);
1411 		int32 cy = y + (i == 0 ? yoff : yoff2);
1412 		int32 cz = z + (i == 0 ? zoff : zoff2);
1413 		const Item *blocker = nullptr;
1414 		bool valid = cm->isValidPosition(cx, cy, cz, BULLET_SPLASH_SHAPE,
1415 										 getObjId(), nullptr, nullptr, &blocker);
1416 		if (!valid) {
1417 			if (blocker->getObjId() == other->getObjId())
1418 				dist = MAX(abs(_x - ox), abs(_y - oy));
1419 		} else {
1420 			int32 ocx, ocy, ocz;
1421 			other->getCentre(ocx, ocy, ocz);
1422 			ocz = other->getTargetZRelativeToAttackerZ(getZ());
1423 			const int32 start[3] = {cx, cy, cz};
1424 			const int32 end[3] = {ocx, ocy, ocz};
1425 			const int32 dims[3] = {2, 2, 2};
1426 
1427 			Std::list<CurrentMap::SweepItem> collisions;
1428 			Std::list<CurrentMap::SweepItem>::iterator it;
1429 			cm->sweepTest(start, end, dims, ShapeInfo::SI_SOLID,
1430 						   _objId, true, &collisions);
1431 			for (it = collisions.begin(); it != collisions.end(); it++) {
1432 				if (it->_item == getObjId())
1433 					continue;
1434 				if (it->_touching)
1435 					continue;
1436 				if (it->_item != other->getObjId())
1437 					break;
1438 				int32 out[3];
1439 				it->GetInterpolatedCoords(out, start, end);
1440 				dist = MAX(abs(_x - out[0]), abs(_y - out[1]));
1441 				break;
1442 			}
1443 		}
1444 	}
1445 
1446 	if (!dist)
1447 		return 0;
1448 	return dist < 32 ? 1 : dist / 32;
1449 }
1450 
getTargetZRelativeToAttackerZ(int32 otherz) const1451 int32 Item::getTargetZRelativeToAttackerZ(int32 otherz) const {
1452 	int32 tsx, tsy, tsz;
1453 	getFootpadData(tsx, tsy, tsz);
1454 
1455 	int32 tz = getZ() + tsz * 8;
1456 
1457 	if (tsz < 3) {
1458 		if (tsz)
1459 			tz -= 8;
1460 	} else {
1461 		int32 targetz = tz;
1462 		tz -= 16;
1463 		if (otherz - targetz < -0x2f) {
1464 			tz += 8;
1465 		} else if (otherz - targetz > 0x2f) {
1466 			if (tsz == 6) {
1467 				tz -= 16;
1468 			} else if (tsz >= 7) {
1469 				tz -= 24;
1470 			}
1471 		}
1472 	}
1473 	return tz;
1474 }
1475 
1476 
countNearby(uint32 shape,uint16 range)1477 unsigned int Item::countNearby(uint32 shape, uint16 range) {
1478 	CurrentMap *currentmap = World::get_instance()->getCurrentMap();
1479 	UCList itemlist(2);
1480 	LOOPSCRIPT(script, LS_SHAPE_EQUAL(shape));
1481 	currentmap->areaSearch(&itemlist, script, sizeof(script),
1482 	                       this, range, false);
1483 	return itemlist.getSize();
1484 }
1485 
1486 
callUsecodeEvent(uint32 event,const uint8 * args,int argsize)1487 uint32 Item::callUsecodeEvent(uint32 event, const uint8 *args, int argsize) {
1488 	uint32  class_id = _shape;
1489 
1490 	// Non-monster NPCs use _objId/_npcNum + 1024 (2048 in crusader)
1491 	// Note: in the original, a non-monster NPC is specified with
1492 	// the FAST_ONLY flag. However, this causes some summoned monster which
1493 	// do not receive the FAST_ONLY flag to behave strangely. (Confirmed that
1494 	// happens in the original as well.) -wjp 20050128
1495 	if (_objId < 256 && (_extendedFlags & EXT_PERMANENT_NPC)) {
1496 		if (GAME_IS_U8)
1497 			class_id = _objId + 1024;
1498 		else
1499 			class_id = _objId + 2048;
1500 	}
1501 
1502 	// CHECKME: to make Pentagram behave as much like the original as possible,
1503 	// don't call any usecode if the original would call the wrong class
1504 	if (GAME_IS_U8 && _objId < 256 && !(_extendedFlags & EXT_PERMANENT_NPC) &&
1505 	        !(_flags & FLG_FAST_ONLY))
1506 		return 0;
1507 
1508 	// UnkEggs have class quality + 0x47F in U8, +0x900 in Crusader
1509 	if (getFamily() == ShapeInfo::SF_UNKEGG)
1510 		class_id = _quality + (GAME_IS_U8 ? 0x47F : 0x900);
1511 
1512 	Usecode *u = GameData::get_instance()->getMainUsecode();
1513 	uint32 offset = u->get_class_event(class_id, event);
1514 	if (!offset) return 0; // event not found
1515 
1516 	debug(10, "Item: %d (shape %d) calling usecode event %d @ %04X:%04X",
1517 			_objId, _shape, event, class_id, offset);
1518 
1519 	return callUsecode(static_cast<uint16>(class_id),
1520 	                   static_cast<uint16>(offset),
1521 	                   args, argsize);
1522 }
1523 
callUsecodeEvent_look()1524 uint32 Item::callUsecodeEvent_look() {                          // event 0
1525 	return callUsecodeEvent(0); // CONSTANT
1526 }
1527 
callUsecodeEvent_use()1528 uint32 Item::callUsecodeEvent_use() {                           // event 1
1529 	return callUsecodeEvent(1); // CONSTANT
1530 }
1531 
callUsecodeEvent_anim()1532 uint32 Item::callUsecodeEvent_anim() {                          // event 2
1533 	return callUsecodeEvent(2); // CONSTANT
1534 }
1535 
callUsecodeEvent_cachein()1536 uint32 Item::callUsecodeEvent_cachein() {                       // event 4
1537 	return callUsecodeEvent(4); // CONSTANT
1538 }
1539 
callUsecodeEvent_hit(uint16 hitter,int16 hitforce)1540 uint32 Item::callUsecodeEvent_hit(uint16 hitter, int16 hitforce) { // event 5
1541 	DynamicUCStack  arg_stack(4);
1542 	arg_stack.push2(hitforce);
1543 	arg_stack.push2(hitter);
1544 	return callUsecodeEvent(5, arg_stack.access(), 4);  // CONSTANT 5
1545 }
1546 
callUsecodeEvent_gotHit(uint16 hitter,int16 hitforce)1547 uint32 Item::callUsecodeEvent_gotHit(uint16 hitter, int16 hitforce) { // event 6
1548 	DynamicUCStack  arg_stack(4);
1549 	arg_stack.push2(hitforce);
1550 	arg_stack.push2(hitter);
1551 	return callUsecodeEvent(6, arg_stack.access(), 4);  // CONSTANT 6
1552 }
1553 
callUsecodeEvent_hatch()1554 uint32 Item::callUsecodeEvent_hatch() {                         // event 7
1555 	return callUsecodeEvent(7);     // CONSTANT
1556 }
1557 
callUsecodeEvent_schedule(uint32 time)1558 uint32 Item::callUsecodeEvent_schedule(uint32 time) {           // event 8
1559 	DynamicUCStack  arg_stack(4);
1560 	arg_stack.push4(time);
1561 	return callUsecodeEvent(8, arg_stack.access(), 4);  // CONSTANT 8
1562 }
1563 
callUsecodeEvent_release()1564 uint32 Item::callUsecodeEvent_release() {                       // event 9
1565 	return callUsecodeEvent(9);     // CONSTANT
1566 }
1567 
callUsecodeEvent_equip()1568 uint32 Item::callUsecodeEvent_equip() {                         // event A
1569 	return callUsecodeEvent(0xA); // CONSTANT
1570 }
1571 
callUsecodeEvent_equipWithParam(ObjId param)1572 uint32 Item::callUsecodeEvent_equipWithParam(ObjId param) {     // event A
1573 	DynamicUCStack  arg_stack(2);
1574 	arg_stack.push2(param);
1575 	return callUsecodeEvent(0xA, arg_stack.access(), 2);
1576 }
1577 
callUsecodeEvent_unequip()1578 uint32 Item::callUsecodeEvent_unequip() {                       // event B
1579 	return callUsecodeEvent(0xB); // CONSTANT
1580 }
1581 
callUsecodeEvent_unequipWithParam(ObjId param)1582 uint32 Item::callUsecodeEvent_unequipWithParam(ObjId param) {   // event B
1583 	DynamicUCStack  arg_stack(2);
1584 	arg_stack.push2(param);
1585 	return callUsecodeEvent(0xB, arg_stack.access(), 2);
1586 }
1587 
callUsecodeEvent_combine()1588 uint32 Item::callUsecodeEvent_combine() {                       // event C
1589 	return callUsecodeEvent(0xC);   // CONSTANT
1590 }
1591 
callUsecodeEvent_calledFromAnim()1592 uint32 Item::callUsecodeEvent_calledFromAnim() {                // event E
1593 	return callUsecodeEvent(0xE);   // CONSTANT
1594 }
1595 
callUsecodeEvent_enterFastArea()1596 uint32 Item::callUsecodeEvent_enterFastArea() {                 // event F
1597 	return callUsecodeEvent(0xF);   // CONSTANT
1598 }
1599 
callUsecodeEvent_leaveFastArea()1600 uint32 Item::callUsecodeEvent_leaveFastArea() {                 // event 10
1601 	return callUsecodeEvent(0x10);  // CONSTANT
1602 }
1603 
callUsecodeEvent_cast(uint16 unk)1604 uint32 Item::callUsecodeEvent_cast(uint16 unk) {                // event 11
1605 	DynamicUCStack  arg_stack(2);
1606 	arg_stack.push2(unk);
1607 	return callUsecodeEvent(0x11, arg_stack.access(), 2); // CONSTANT 0x11
1608 }
1609 
callUsecodeEvent_justMoved()1610 uint32 Item::callUsecodeEvent_justMoved() {                     // event 12
1611 	return callUsecodeEvent(0x12);  // CONSTANT
1612 }
1613 
callUsecodeEvent_AvatarStoleSomething(uint16 unk)1614 uint32 Item::callUsecodeEvent_AvatarStoleSomething(uint16 unk) { // event 13
1615 	DynamicUCStack  arg_stack(2);
1616 	arg_stack.push2(unk);
1617 	return callUsecodeEvent(0x13, arg_stack.access(), 2); // CONSTANT 0x13
1618 }
1619 
callUsecodeEvent_guardianBark(int16 unk)1620 uint32 Item::callUsecodeEvent_guardianBark(int16 unk) {         // event 15
1621 	DynamicUCStack  arg_stack(2);
1622 	arg_stack.push2(unk);
1623 	return callUsecodeEvent(0x15, arg_stack.access(), 2); // CONSTANT 0x15
1624 }
1625 
callUsecodeEvent_unhatch()1626 uint32 Item::callUsecodeEvent_unhatch() {                     // event 15
1627 	return callUsecodeEvent(0x15);  // CONSTANT
1628 }
1629 
use()1630 uint32 Item::use() {
1631 	Actor *actor = dynamic_cast<Actor *>(this);
1632 	if (actor) {
1633 		if (actor->isDead()) {
1634 			if (GAME_IS_U8) {
1635 				// dead actor, so open/close the dead-body-gump
1636 				if (hasFlags(FLG_GUMP_OPEN)) {
1637 					closeGump();
1638 				} else {
1639 					openGump(12); // CONSTANT!!
1640 				}
1641 			}
1642 			return 0;
1643 		}
1644 	}
1645 
1646 	return callUsecodeEvent_use();
1647 }
1648 
destroy(bool delnow)1649 void Item::destroy(bool delnow) {
1650 	if (_flags & FLG_ETHEREAL) {
1651 		// Remove us from the ether
1652 		World::get_instance()->etherealRemove(_objId);
1653 	} else if (_parent) {
1654 		// we're in a container, so remove self from _parent
1655 		//!! need to make sure this works for equipped items too...
1656 		Container *p = getParentAsContainer();
1657 		if (p) p->removeItem(this);
1658 	} else if (_extendedFlags & EXT_INCURMAP) {
1659 		// remove self from CurrentMap
1660 		World::get_instance()->getCurrentMap()->removeItemFromList(this, _x, _y);
1661 	}
1662 
1663 	if (GAME_IS_CRUSADER) {
1664 		// Ensure sounds for this object are stopped
1665 		AudioProcess *audio = AudioProcess::get_instance();
1666 		if (audio)
1667 			audio->stopSFX(-1, _objId);
1668 		if (_shape == SNAP_EGG_SHAPE) {
1669 			SnapProcess *snap = SnapProcess::get_instance();
1670 			if (snap)
1671 				snap->removeEgg(this);
1672 		}
1673 	}
1674 
1675 	if (_extendedFlags & Item::EXT_CAMERA)
1676 		CameraProcess::SetCameraProcess(0);
1677 
1678 	// Do we want to delete now or not?
1679 	if (!delnow) {
1680 		Process *dap = new DestroyItemProcess(this);
1681 		Kernel::get_instance()->addProcess(dap);
1682 		return;
1683 	}
1684 
1685 	clearObjId();
1686 	delete this; // delete self.
1687 }
1688 
1689 //
1690 // Item::setupLerp()
1691 //
1692 // Desc: Setup the lerped info for this frame
1693 //
setupLerp(int32 gametick)1694 void Item::setupLerp(int32 gametick) {
1695 	// Don't need to set us up
1696 	if (_lastSetup && gametick == _lastSetup)
1697 		return;
1698 
1699 	// Are we lerping or are we not? Default we lerp.
1700 	bool no_lerp = false;
1701 
1702 	// No lerping this _frame if Shape Changed, or No lerp is set,
1703 	// or no last setup, or last setup more than 1 tick ago
1704 	if ((_lastSetup == 0) || (_lNext._shape != _shape) ||
1705 	        (_extendedFlags & EXT_LERP_NOPREV) ||
1706 	        (gametick - _lastSetup) > 1 || _flags & FLG_CONTAINED)
1707 		no_lerp = true;
1708 
1709 	// Update the time we were just setup
1710 	_lastSetup = gametick;
1711 
1712 	// Clear the flag
1713 	_extendedFlags &= ~EXT_LERP_NOPREV;
1714 
1715 	// Animate it, if needed.
1716 	//
1717 	// We use (tick % speed == 0) here. To be completely faithful to the original
1718 	// game it should be (tick % speed == tick % _objId).  That is how the game
1719 	// does it, but it also causes animation frame mismatches on multi-shape
1720 	// objects.  This is easily noticable on the waterfall West of Tenebrae,
1721 	// which appears to tear slightly even on the original.
1722 	//
1723 	// In the original it was likely done to spread CPU time over different frames,
1724 	// but it's a little bit nicer to do it correctly now that we can afford to..
1725 	//
1726 	const ShapeInfo *info = getShapeInfo();
1727 	if (info->_animType &&
1728 			((gametick % info->_animSpeed) == 0))
1729 		animateItem();
1730 
1731 	// Setup the prev values for lerping
1732 	if (!no_lerp) _lPrev = _lNext;
1733 
1734 	// Setup next
1735 	if (_flags & FLG_CONTAINED) {
1736 		_lNext._x = _ix = _y & 0xFF;
1737 		_lNext._y = _iy = (_y >> 8) & 0xFF;
1738 		_lNext._z = _iz = 0;
1739 	} else {
1740 		_lNext._x = _ix = _x;
1741 		_lNext._y = _iy = _y;
1742 		_lNext._z = _iz = _z;
1743 	}
1744 	_lNext._shape = _shape;
1745 	_lNext._frame = _frame;
1746 
1747 	// Setup prev values for not lerping
1748 	if (no_lerp) _lPrev = _lNext;
1749 }
1750 
1751 // Animate the item
animateItem()1752 void Item::animateItem() {
1753 	const ShapeInfo *info = getShapeInfo();
1754 
1755 	if (!info->_animType)
1756 		return;
1757 
1758 	uint32 anim_data = info->_animData;
1759 	const Shape *shp = getShapeObject();
1760 
1761 	switch (info->_animType) {
1762 	case 2:
1763 		// Randomly change frame
1764 		if ((getRandom() & 1) && shp)
1765 			_frame = getRandom() % shp->frameCount();
1766 		break;
1767 
1768 	case 1:
1769 	case 3:
1770 		// animdata 0 = always increment
1771 		// animdata 1 = 50 % chance of changing
1772 		// animdata 2+ = loop in frame blocks of size animdata
1773 		if (anim_data == 0 || (anim_data == 1 && (getRandom() & 1))) {
1774 			_frame++;
1775 			if (shp && _frame >= shp->frameCount())
1776 				_frame = 0;
1777 		} else if (anim_data > 1) {
1778 			_frame++;
1779 			uint32 num = (_frame - 1) / anim_data;
1780 			if (_frame == ((num + 1) * anim_data))
1781 				_frame = num * anim_data;
1782 		}
1783 		break;
1784 
1785 	case 4:
1786 		// Randomly start animating, with chance of 1/(animdata + 2)
1787 		// once animating, go through all frames.
1788 		if (_frame || getRandom() % (anim_data + 2)) {
1789 			_frame++;
1790 			if (shp && _frame >= shp->frameCount())
1791 				_frame = 0;
1792 		}
1793 		break;
1794 
1795 	case 5:
1796 		// Just call the usecode
1797 		callUsecodeEvent_anim();
1798 		break;
1799 
1800 	case 6:
1801 		// animdata 0 = stick on frame 0, else loop from 1 to count
1802 		// animdata 1 = same as 0, but with 50% chance of change
1803 		// animdata 2+ = same, but loop in frame blocks of size animdata
1804 		if (anim_data == 0 || (anim_data == 1 && (getRandom() & 1))) {
1805 			if (!_frame)
1806 				break;
1807 			_frame++;
1808 			if (shp && _frame >= shp->frameCount())
1809 				_frame = 1;
1810 		} else if (anim_data > 1) {
1811 			if (!(_frame % anim_data))
1812 				break;
1813 			_frame++;
1814 			uint32 num = (_frame - 1) / anim_data;
1815 			if (_frame == ((num + 1) * anim_data))
1816 				_frame = num * anim_data + 1;
1817 		}
1818 		break;
1819 
1820 	default:
1821 		pout << "type " << info->_animType << " data " << anim_data << Std::endl;
1822 		break;
1823 	}
1824 }
1825 
1826 
1827 // Called when an item has entered the fast area
enterFastArea()1828 uint32 Item::enterFastArea() {
1829 	uint16 retval = 0;
1830 	//!! HACK to get rid of endless SFX loops
1831 	if (_shape == 0x2c8 && GAME_IS_U8)
1832 		return 0;
1833 
1834 	const ShapeInfo *si = getShapeInfo();
1835 
1836 	// Call usecode
1837 	if (!(_flags & FLG_FASTAREA)) {
1838 		Actor *actor = dynamic_cast<Actor *>(this);
1839 		// Crusader special-cases a few shapes even when they are dead.
1840 		bool call_even_if_dead = (_shape == 0x576 || _shape == 0x596 ||
1841 								  _shape == 0x59c || _shape == 0x58f) && GAME_IS_CRUSADER;
1842 		if (actor && actor->isDead() && !call_even_if_dead) {
1843 			// dead actor, don't call the usecode
1844 		} else {
1845 			if (actor && _objId != 1 && GAME_IS_CRUSADER) {
1846 				uint16 lastactivity = actor->getLastActivityNo();
1847 				actor->clearLastActivityNo();
1848 				actor->clearInCombat();
1849 				actor->setToStartOfAnim(Animation::stand);
1850 				actor->clearActorFlag(Actor::ACT_WEAPONREADY);
1851 				actor->setActivity(lastactivity);
1852 			}
1853 
1854 			// TODO: For eggs, Crusader also resets the NPC info if a
1855 			// certain global is set.  For now just skip that.
1856 
1857 			//
1858 			// NOTE: Original games only call usecode for actors or NOISY
1859 			// types.  We call for all types to fix an usecode bug in
1860 			// Crusader: No Remorse.  See note about it below.
1861 			//
1862 			// if (actor || si->_flags & ShapeInfo::SI_NOISY)
1863 			retval = callUsecodeEvent_enterFastArea();
1864 		}
1865 	}
1866 
1867 	if (!hasFlags(FLG_BROKEN) && GAME_IS_CRUSADER) {
1868 		if (si->is_targetable()) {
1869 			World::get_instance()->getCurrentMap()->addTargetItem(this);
1870 		}
1871 		if (_shape == SNAP_EGG_SHAPE) {
1872 			SnapProcess *snap = SnapProcess::get_instance();
1873 			if (snap)
1874 				snap->addEgg(this);
1875 		}
1876 	}
1877 
1878 	// We're fast!
1879 	_flags |= FLG_FASTAREA;
1880 
1881 	//
1882 	// WORKAROUND: Crusader: No Remorse usecode has one place (REB_EGG, after
1883 	// randomly creating 0x34D REB_COUP (rebel couple) at the bar) where this
1884 	// is called with an "implies" but the enterFastArea event code never
1885 	// returns.  Every other instance this is "spawn"ed.
1886 	//
1887 	// In the original this bug is never triggered as enterFastArea intrinsic
1888 	// does not call usecode event unless the shape is SI_NOISY (REB_COUP is
1889 	// not). The result is the rebel couple do not start moving until you move
1890 	// near them and trigger their event, then enterFastArea function is
1891 	// spawned directly.
1892 	//
1893 	// Work around both problems by always calling event above and return 0.
1894 	//
1895 	if (_shape == 0x34D && GAME_IS_REMORSE)
1896 		return 0;
1897 
1898 	return retval;
1899 }
1900 
1901 // Called when an item is leaving the fast area
leaveFastArea()1902 void Item::leaveFastArea() {
1903 	if (_objId == 1) {
1904 		debug(6, "avatar leaving fast area");
1905 	}
1906 
1907 	// Call usecode
1908 	if ((!(_flags & FLG_FAST_ONLY) || getShapeInfo()->is_noisy()) &&
1909 	        (_flags & FLG_FASTAREA))
1910 		callUsecodeEvent_leaveFastArea();
1911 
1912 	// If we have a gump open, close it (unless we're in a container)
1913 	if (!_parent && (_flags & FLG_GUMP_OPEN)) {
1914 		Gump *g = Ultima8Engine::get_instance()->getGump(_gump);
1915 		if (g) g->Close();
1916 	}
1917 
1918 	// Unset the flag
1919 	_flags &= ~FLG_FASTAREA;
1920 
1921 	if (!hasFlags(FLG_BROKEN) && GAME_IS_CRUSADER) {
1922 		World::get_instance()->getCurrentMap()->removeTargetItem(this);
1923 		if (_shape == SNAP_EGG_SHAPE) {
1924 			SnapProcess *snap = SnapProcess::get_instance();
1925 			if (snap)
1926 				snap->removeEgg(this);
1927 		}
1928 	}
1929 
1930 	// CHECKME: what do we need to do exactly?
1931 	// currently,  destroy object
1932 
1933 	// Kill us if we are fast only, unless we're in a container
1934 	if ((_flags & FLG_FAST_ONLY) && !getParent()) {
1935 		// destroy contents if container
1936 		Container *c = dynamic_cast<Container *>(this);
1937 		if (c) c->destroyContents();
1938 
1939 		destroy();
1940 		// NB: destroy() creates an DestroyItemProcess to actually
1941 		// delete the item in this case.
1942 	}
1943 	// If we have a gravity process, move us to the ground
1944 	else if (_gravityPid) {
1945 		Process *p = Kernel::get_instance()->getProcess(_gravityPid);
1946 		if (p) {
1947 			p->terminateDeferred();
1948 			_gravityPid = 0;
1949 			collideMove(_x, _y, 0, true, false);
1950 		}
1951 	}
1952 }
1953 
openGump(uint32 gumpshape)1954 uint16 Item::openGump(uint32 gumpshape) {
1955 	if (_flags & FLG_GUMP_OPEN) return 0;
1956 	assert(_gump == 0);
1957 	const Shape *shapeP = GameData::get_instance()->getGumps()->getShape(gumpshape);
1958 
1959 	ContainerGump *cgump;
1960 
1961 	if (getObjId() != 1) { //!! constant
1962 		cgump = new ContainerGump(shapeP, 0, _objId, Gump::FLAG_ITEM_DEPENDENT |
1963 		                          Gump::FLAG_DRAGGABLE);
1964 	} else {
1965 		cgump = new PaperdollGump(shapeP, 0, _objId, Gump::FLAG_ITEM_DEPENDENT |
1966 		                          Gump::FLAG_DRAGGABLE);
1967 	}
1968 	//!!TODO: clean up the way this is set
1969 	//!! having the itemarea associated with the shapeP through the
1970 	//!! GumpShapeFlex maybe
1971 	cgump->setItemArea(GameData::get_instance()->
1972 	                   getGumps()->getGumpItemArea(gumpshape));
1973 	cgump->InitGump(0);
1974 	_flags |= FLG_GUMP_OPEN;
1975 	_gump = cgump->getObjId();
1976 
1977 	return _gump;
1978 }
1979 
closeGump()1980 void Item::closeGump() {
1981 	if (!(_flags & FLG_GUMP_OPEN)) return;
1982 
1983 	Gump *g = Ultima8Engine::get_instance()->getGump(_gump);
1984 	if (g)
1985 		g->Close();
1986 
1987 	// can we already clear gump here, or do we need to wait for the gump
1988 	// to really close??
1989 	clearGump();
1990 }
1991 
1992 
clearGump()1993 void Item::clearGump() {
1994 	_gump = 0;
1995 	_flags &= ~FLG_GUMP_OPEN;
1996 }
1997 
ascend(int delta)1998 int32 Item::ascend(int delta) {
1999 //	pout << "Ascend: _objId=" << getObjId() << ", delta=" << delta << Std::endl;
2000 
2001 	if (delta == 0) return 0x4000;
2002 
2003 	// * gather all items on top of this item (recursively?)
2004 	// * etherealize all those items to get them out of the way
2005 	// * move self up/down
2006 	// * attempt to rematerialize the items up/down
2007 	// (going through etherealness to avoid having to sort the supported items)
2008 
2009 	UCList uclist(2);
2010 	LOOPSCRIPT(script, LS_TOKEN_TRUE); // we want all items
2011 	World *world = World::get_instance();
2012 	world->getCurrentMap()->surfaceSearch(&uclist, script, sizeof(script),
2013 	                                      this, true, false, false);
2014 	for (uint32 i = 0; i < uclist.getSize(); i++) {
2015 		Item *item = getItem(uclist.getuint16(i));
2016 		if (!item) continue;
2017 		if (item->getShapeInfo()->is_fixed()) continue;
2018 
2019 		item->moveToEtherealVoid();
2020 	}
2021 
2022 	// move self
2023 	int32 xv, yv, zv;
2024 	getLocation(xv, yv, zv);
2025 	int dist = collideMove(xv, yv, zv + delta, false, false);
2026 	delta = (delta * dist) / 0x4000;
2027 
2028 //	pout << "Ascend: dist=" << dist << Std::endl;
2029 
2030 	// move other items
2031 	for (uint32 i = 0; i < uclist.getSize(); i++) {
2032 		Item *item = getItem(uclist.getuint16(i));
2033 		if (!item) continue;
2034 		if (item->getShapeInfo()->is_fixed()) continue;
2035 
2036 		item->getLocation(_ix, _iy, _iz);
2037 
2038 		if (item->canExistAt(_ix, _iy, _iz + delta)) {
2039 			item->move(_ix, _iy, _iz + delta); // automatically un-etherealizes item
2040 		} else {
2041 			// uh oh...
2042 			// CHECKME: what do we do here?
2043 			item->move(_ix, _iy, _iz);
2044 			if (delta < 0) item->fall();
2045 		}
2046 	}
2047 
2048 	return dist;
2049 }
2050 
ensureGravityProcess()2051 GravityProcess *Item::ensureGravityProcess() {
2052 	GravityProcess *p;
2053 	if (_gravityPid) {
2054 		p = dynamic_cast<GravityProcess *>(
2055 		        Kernel::get_instance()->getProcess(_gravityPid));
2056 	} else {
2057 		p = new GravityProcess(this, 0);
2058 		Kernel::get_instance()->addProcess(p);
2059 		p->init();
2060 	}
2061 	assert(p);
2062 	return p;
2063 }
2064 
fall()2065 void Item::fall() {
2066 	const ShapeInfo *info = getShapeInfo();
2067 	bool hanging = GAME_IS_U8 && (_flags & FLG_HANGING);
2068 
2069 	if (hanging || info->is_fixed() || info->_weight == 0) {
2070 		// can't fall
2071 		return;
2072 	}
2073 
2074 	int gravity = GAME_IS_CRUSADER ? 2 : 4; //!! constants
2075 
2076 	hurl(0, 0, 0, gravity);
2077 }
2078 
grab()2079 void Item::grab() {
2080 	// CHECKME: is the fall/release timing correct?
2081 
2082 	UCList uclist(2);
2083 	LOOPSCRIPT(script, LS_TOKEN_TRUE); // we want all items
2084 	World *world = World::get_instance();
2085 	world->getCurrentMap()->surfaceSearch(&uclist, script, sizeof(script),
2086 	                                      this, true, false, true);
2087 
2088 	for (uint32 i = 0; i < uclist.getSize(); i++) {
2089 		Item *item = getItem(uclist.getuint16(i));
2090 		if (!item) continue;
2091 		item->fall();
2092 	}
2093 
2094 	uclist.free();
2095 
2096 	world->getCurrentMap()->surfaceSearch(&uclist, script, sizeof(script),
2097 	                                      this, false, true, false);
2098 
2099 	for (uint32 i = 0; i < uclist.getSize(); i++) {
2100 		Item *item = getItem(uclist.getuint16(i));
2101 		if (!item) continue;
2102 		item->callUsecodeEvent_release();
2103 	}
2104 
2105 }
2106 
2107 
hurl(int xs,int ys,int zs,int grav)2108 void Item::hurl(int xs, int ys, int zs, int grav) {
2109 	if (_parent) {
2110 		// Should be removed from the container first??
2111 		// This will break otherwise as location is 0,0,0
2112 		warning("Ignoring hurl for contained item %d.", _objId);
2113 		return;
2114 	}
2115 	// crusader sleeps existing gravity at first
2116 	bool do_sleep = GAME_IS_CRUSADER && (_gravityPid == 0);
2117 	GravityProcess *p = ensureGravityProcess();
2118 	p->setGravity(grav);
2119 	p->move(xs, ys, zs);
2120 	if (do_sleep) {
2121 		Process *delayProc = new DelayProcess(0x14);
2122 		ProcId pid = Kernel::get_instance()->addProcess(delayProc);
2123 		p->waitFor(pid);
2124 	}
2125 }
2126 
2127 
explode(int explosion_type,bool destroy_item,bool cause_damage)2128 void Item::explode(int explosion_type, bool destroy_item, bool cause_damage) {
2129 	Process *p;
2130 	int damage_divisor = 1;
2131 
2132 	if (GAME_IS_CRUSADER) {
2133 
2134 		damage_divisor = explosion_type + 1;
2135 		if (damage_divisor == 1)
2136 			damage_divisor = 3;
2137 		else if (damage_divisor == 3)
2138 			damage_divisor = 1;
2139 
2140 		setFlag(FLG_BROKEN);
2141 		// TODO: original game puts them at cx/cy/cz, but that looks wrong..
2142 		int32 cx, cy, cz;
2143 		getCentre(cx, cy, cz);
2144 		static const int expshapes[] = {0x31C, 0x31F, 0x326, 0x320, 0x321, 0x324, 0x323, 0x325};
2145 		int rnd = getRandom();
2146 		int spriteno;
2147 		// NOTE: The game does some weird 32-bit stuff to decide what
2148 		// shapenum to use.  Just simplified to a random.
2149 		switch (explosion_type) {
2150 		case 0:
2151 			spriteno = expshapes[rnd % 2];
2152 			break;
2153 		case 1:
2154 			spriteno = expshapes[2 + rnd % 3];
2155 			break;
2156 		case 2:
2157 		default:
2158 			spriteno = expshapes[5 + rnd % 3];
2159 			break;
2160 		}
2161 		p = new SpriteProcess(spriteno, 0, 39, 1, 1, //!! constants
2162 	                               _x, _y, cz);
2163 	} else {
2164 		p = new SpriteProcess(578, 20, 34, 1, 1, //!! constants
2165 	                               _x, _y, _z);
2166 	}
2167 	Kernel::get_instance()->addProcess(p);
2168 
2169 	AudioProcess *audioproc = AudioProcess::get_instance();
2170 	if (audioproc) {
2171 		int sfx;
2172 		if (GAME_IS_CRUSADER) {
2173 			sfx = (getRandom() % 2) ? 28 : 108;
2174 			audioproc->stopSFX(-1, _objId);
2175 		} else {
2176 			sfx = (getRandom() % 2) ? 31 : 158;
2177 		}
2178 		audioproc->playSFX(sfx, 0x60, 0, 0);
2179 	}
2180 
2181 	int32 xv, yv, zv;
2182 	getLocation(xv, yv, zv);
2183 
2184 	if (destroy_item) {
2185 		destroy(); // delete self
2186 		// WARNING: we are deleted at this point
2187 	}
2188 
2189 	if (!cause_damage)
2190 		return;
2191 
2192 	if (GAME_IS_U8) {
2193 		UCList itemlist(2);
2194 		LOOPSCRIPT(script, LS_TOKEN_TRUE); // we want all items
2195 		CurrentMap *currentmap = World::get_instance()->getCurrentMap();
2196 		currentmap->areaSearch(&itemlist, script, sizeof(script), 0,
2197 							   160, false, xv, yv); //! CHECKME: 128?
2198 
2199 		for (unsigned int i = 0; i < itemlist.getSize(); ++i) {
2200 			Item *item = getItem(itemlist.getuint16(i));
2201 			if (!item) continue;
2202 			if (getRange(*item, true) > 160) continue; // check vertical distance
2203 
2204 			item->getLocation(xv, yv, zv);
2205 			Direction dir = Direction_GetWorldDir(xv - xv, yv - yv, dirmode_8dirs); //!! CHECKME
2206 			item->receiveHit(0, dir, 6 + (getRandom() % 6),
2207 							 WeaponInfo::DMG_BLUNT | WeaponInfo::DMG_FIRE);
2208 		}
2209 	} else {
2210 		Point3 pt;
2211 		getLocation(pt);
2212 		// Note: same FireType number used in both Remorse and Regret
2213 		const FireType *firetypedat = GameData::get_instance()->getFireType(4);
2214 		if (firetypedat) {
2215 			int damage = firetypedat->getRandomDamage() / damage_divisor;
2216 			firetypedat->applySplashDamageAround(pt, damage, damage_divisor, this, this);
2217 		} else {
2218 			warning("couldn't explode properly - no firetype 4 data");
2219 		}
2220 	}
2221 }
2222 
getDamageType() const2223 uint16 Item::getDamageType() const {
2224 	const ShapeInfo *si = getShapeInfo();
2225 	if (si->_weaponInfo) {
2226 		return si->_weaponInfo->_damageType;
2227 	}
2228 
2229 	return 0;
2230 }
2231 
receiveHit(uint16 other,Direction dir,int damage,uint16 type)2232 void Item::receiveHit(uint16 other, Direction dir, int damage, uint16 type) {
2233 	if (GAME_IS_U8)
2234 		receiveHitU8(other, dir, damage, type);
2235 	else
2236 		receiveHitCru(other, dir, damage, type);
2237 }
2238 
receiveHitU8(uint16 other,Direction dir,int damage,uint16 type)2239 void Item::receiveHitU8(uint16 other, Direction dir, int damage, uint16 type) {
2240 	// first, check if the item has a 'gotHit' usecode event
2241 	if (callUsecodeEvent_gotHit(other, 0)) //!! TODO: what should the 0 be??
2242 		return;
2243 
2244 	// explosive?
2245 	if (getShapeInfo()->is_u8_explode()) {
2246 		explode(0, true); // warning: deletes this
2247 		return;
2248 	}
2249 
2250 	// breakable?
2251 	if (getFamily() == ShapeInfo::SF_BREAKABLE) {
2252 		// CHECKME: anything else?
2253 		destroy();
2254 		return;
2255 	}
2256 
2257 	if (getShapeInfo()->is_fixed() || getShapeInfo()->_weight == 0) {
2258 		// can't move
2259 		return;
2260 	}
2261 
2262 	// nothing special, so just hurl the item
2263 	// TODO: hurl item in direction, with speed depending on damage
2264 	hurl(-16 * Direction_XFactor(dir), -16 * Direction_YFactor(dir), 16, 4); //!! constants
2265 }
2266 
2267 
receiveHitCru(uint16 other,Direction dir,int damage,uint16 type)2268 void Item::receiveHitCru(uint16 other, Direction dir, int damage, uint16 type) {
2269 	damage = scaleReceivedDamageCru(damage, type);
2270 	const ShapeInfo *shapeInfo = getShapeInfo();
2271 	if (!shapeInfo)
2272 		return;
2273 	const DamageInfo *damageInfo = shapeInfo->_damageInfo;
2274 
2275 	// TODO: work out how this flag is decided.
2276 	uint8 shouldCallUsecode = 1;
2277 
2278 	if (shouldCallUsecode)
2279 		callUsecodeEvent_gotHit(0x4000, (type << 8) | (damage & 0xff));
2280 
2281 	if (damageInfo) {
2282 		bool wasbroken = damageInfo->applyToItem(this, damage);
2283 		if (wasbroken) {
2284 			Kernel::get_instance()->killProcesses(_objId, Kernel::PROC_TYPE_ALL, true);
2285 		}
2286 	}
2287 
2288 	// Fixed items or 0 weight etems can't move.
2289 	// Only damage types 3 and 4 move items in Crusader.
2290 	if (shapeInfo->is_fixed() || shapeInfo->_weight == 0 || (type != 3 && type != 4)) {
2291 		return;
2292 	}
2293 
2294 	assert((int)dir >= 0 && (int)dir < 16);
2295 
2296 	static const int hurl_x_factor[] = {  0, +1, +2, +2, +2, +2, +2, +1, 0, -1, -2, -2, -2, -2, -2, -1 };
2297 	static const int hurl_y_factor[] = { -2, -2, -2, -1,  0, +1, +2, +2, +2, +2, +2, +1, 0, -1, -2, -2 };
2298 
2299 	int xhurl = 10 + getRandom() % 15;
2300 	int yhurl = 10 + getRandom() % 15;
2301 	hurl(-xhurl * hurl_x_factor[(int)dir], -yhurl * hurl_y_factor[(int)dir], 0, 2); //!! constants
2302 }
2303 
2304 
canDrag()2305 bool Item::canDrag() {
2306 	const ShapeInfo *si = getShapeInfo();
2307 	if (si->is_fixed()) return false;
2308 	if (si->_weight == 0) return false;
2309 
2310 	Actor *actor = dynamic_cast<Actor *>(this);
2311 	if (actor) {
2312 		// living actors can't be moved
2313 		if (!actor->isDead()) return false;
2314 	}
2315 
2316 	// CHECKME: might need more checks here (weight?)
2317 
2318 	return true;
2319 }
2320 
getThrowRange()2321 int Item::getThrowRange() {
2322 	if (!canDrag()) return 0;
2323 
2324 	Actor *avatar = getMainActor();
2325 
2326 	int range = 64 - getTotalWeight() + avatar->getStr();
2327 	if (range < 1) range = 1;
2328 	range = (range * range) / 2;
2329 
2330 	return range;
2331 }
2332 
checkLineOfSightCollisions(const Std::list<CurrentMap::SweepItem> & collisions,bool usingAlternatePos,ObjId item,ObjId other)2333 static bool checkLineOfSightCollisions(
2334 	const Std::list<CurrentMap::SweepItem> &collisions,
2335 	bool usingAlternatePos, ObjId item, ObjId other) {
2336 	Std::list<CurrentMap::SweepItem>::const_iterator it;
2337 	int32 other_hit_time = 0x4000;
2338 	int32 blocked_time = 0x4000;
2339 	for (it = collisions.begin(); it != collisions.end(); it++) {
2340 		// ignore self and other
2341 		if (it->_item == item) continue;
2342 		if (it->_item == other && !usingAlternatePos) {
2343 			other_hit_time = it->_hitTime;
2344 			continue;
2345 		}
2346 
2347 		// only touching?
2348 		if (it->_touching) continue;
2349 
2350 		// hit something
2351 		if (it->_blocking && it->_hitTime < blocked_time) {
2352 			blocked_time = it->_hitTime;
2353 		}
2354 	}
2355 
2356 	// 'other' must be the first item that is hit.
2357 	return (blocked_time >= other_hit_time);
2358 }
2359 
canReach(Item * other,int range,int32 otherX,int32 otherY,int32 otherZ)2360 bool Item::canReach(Item *other, int range,
2361 					int32 otherX, int32 otherY, int32 otherZ) {
2362 	// get location and dimensions of self and other (or their root containers)
2363 	int32 thisX, thisY, thisZ;
2364 	int32 thisXd, thisYd, thisZd;
2365 	int32 otherXd, otherYd, otherZd;
2366 	int32 thisXmin, thisYmin;
2367 	int32 otherXmin, otherYmin;
2368 
2369 	bool usingAlternatePos = (otherX != 0);
2370 
2371 	getLocationAbsolute(thisX, thisY, thisZ);
2372 	other = other->getTopItem();
2373 	if (otherX == 0)
2374 		other->getLocationAbsolute(otherX, otherY, otherZ);
2375 
2376 	getFootpadWorld(thisXd, thisYd, thisZd);
2377 	other->getFootpadWorld(otherXd, otherYd, otherZd);
2378 
2379 	thisXmin = thisX - thisXd;
2380 	thisYmin = thisY - thisYd;
2381 
2382 	otherXmin = otherX - otherXd;
2383 	otherYmin = otherY - otherYd;
2384 
2385 	// if items are further away than range in any direction, return false
2386 	if (thisXmin - otherX > range) return false;
2387 	if (otherXmin - thisX > range) return false;
2388 	if (thisYmin - otherY > range) return false;
2389 	if (otherYmin - thisY > range) return false;
2390 
2391 
2392 	// if not, do line of sight between origins of items
2393 	int32 start[3];
2394 	int32 end[3];
2395 	int32 dims[3] = { 2, 2, 2 };
2396 
2397 	start[0] = thisX;
2398 	start[1] = thisY;
2399 	start[2] = thisZ;
2400 	end[0] = otherX;
2401 	end[1] = otherY;
2402 	end[2] = otherZ;
2403 	if (otherZ > thisZ && otherZ < thisZ + thisZd)
2404 		start[2] = end[2]; // bottom of other between bottom and top of this
2405 
2406 	Std::list<CurrentMap::SweepItem> collisions;
2407 	Std::list<CurrentMap::SweepItem>::iterator it;
2408 	World *world = World::get_instance();
2409 	CurrentMap *map = world->getCurrentMap();
2410 	map->sweepTest(start, end, dims, ShapeInfo::SI_SOLID,
2411 	               _objId, false, &collisions);
2412 	if (checkLineOfSightCollisions(collisions, usingAlternatePos,
2413 	                               getObjId(), other->getObjId()))
2414 		return true;
2415 
2416 	// if that fails, try line of sight between centers
2417 	start[0] = thisX - thisXd / 2; // xy center of this
2418 	start[1] = thisY - thisYd / 2;
2419 	start[2] = thisZ;
2420 	if (thisZd > 16)
2421 		start[2] += thisZd - 8; // eye height
2422 
2423 	end[0] = otherX - otherXd / 2; // xyz center of other
2424 	end[1] = otherY - otherYd / 2;
2425 	end[2] = otherZ + otherZd / 2;
2426 
2427 	collisions.clear();
2428 	map->sweepTest(start, end, dims, ShapeInfo::SI_SOLID,
2429 	               _objId, false, &collisions);
2430 	if (checkLineOfSightCollisions(collisions, usingAlternatePos,
2431 	                               getObjId(), other->getObjId()))
2432 		return true;
2433 
2434 	// if that fails, try line of sight between eye level and top of 2nd
2435 	end[2] = otherZ + otherZd;
2436 
2437 	collisions.clear();
2438 	map->sweepTest(start, end, dims, ShapeInfo::SI_SOLID,
2439 	               _objId, false, &collisions);
2440 	return checkLineOfSightCollisions(collisions, usingAlternatePos,
2441 	                                  getObjId(), other->getObjId());
2442 }
2443 
2444 
2445 /**
2446  * A helper function to check if both frames are in the
2447  * same range (inclusive) for the merge check below */
bothInRange(uint32 x,uint32 y,uint32 lo,uint32 hi)2448 static inline bool bothInRange(uint32 x, uint32 y, uint32 lo, uint32 hi) {
2449 	return (x >= lo && x <= hi && y >= lo && y <= hi);
2450 }
2451 
canMergeWith(Item * other)2452 bool Item::canMergeWith(Item *other) {
2453 	// can't merge with self
2454 	if (other->getObjId() == getObjId()) return false;
2455 
2456 	if (other->getShape() != getShape()) return false;
2457 
2458 	int family = getFamily();
2459 	if (family == ShapeInfo::SF_QUANTITY) return true;
2460 
2461 	if (family != ShapeInfo::SF_REAGENT) return false;
2462 
2463 	uint32 frame1 = getFrame();
2464 	uint32 frame2 = other->getFrame();
2465 	if (frame1 == frame2) return true;
2466 
2467 	// special cases:
2468 	// necromancy reagents (shape 395)
2469 	// 		blood: frame 0-5
2470 	// 		bone: frame 6-7
2471 	// 		wood: frame 8
2472 	// 		dirt: frame 9
2473 	// 		ex.hood: frame 10-12
2474 	// 		blackmoor: frame 14-15
2475 	// 		dead man's elbow: frame 16-20
2476 	// sorcery reagents (shape 398)
2477 	//		volcanic ash: frame 0-1
2478 	//		pumice: frame 2-5
2479 	//		obsidian: 6-9
2480 	//		iron: 10-13
2481 	//		brimstone: 14-17
2482 	// 		daemon bones: 18-20
2483 	// ether reagents (shape 399)
2484 	//      only one frame per type, no special case needed
2485 	// Note: necromancy reagents are special-cased to be plural in their look()
2486 	// function, but the sorcery ones aren't, but the original game is the same.
2487 	//
2488 	if (GAME_IS_U8) {
2489 		if (getShape() == 395) {
2490 			if (bothInRange(frame1, frame2, 0, 5))
2491 				return true;
2492 			if (bothInRange(frame1, frame2, 6, 7))
2493 				return true;
2494 			if (bothInRange(frame1, frame2, 10, 12))
2495 				return true;
2496 			if (bothInRange(frame1, frame2, 14, 15))
2497 				return true;
2498 			if (bothInRange(frame1, frame2, 16, 20))
2499 				return true;
2500 		}
2501 		if (getShape() == 398) {
2502 			if (bothInRange(frame1, frame2, 0, 1))
2503 				return true;
2504 			if (bothInRange(frame1, frame2, 2, 5))
2505 				return true;
2506 			if (bothInRange(frame1, frame2, 6, 9))
2507 				return true;
2508 			if (bothInRange(frame1, frame2, 10, 13))
2509 				return true;
2510 			if (bothInRange(frame1, frame2, 14, 17))
2511 				return true;
2512 			if (bothInRange(frame1, frame2, 18, 20))
2513 				return true;
2514 		}
2515 	}
2516 	return false;
2517 }
2518 
isRobotCru() const2519 bool Item::isRobotCru() const {
2520 	uint32 shape = getShape();
2521 	return (shape == 0x4c8 || shape == 0x338 || shape == 0x45d ||
2522 			shape == 0x2cb || shape == 0x4e6 || shape == 899 ||
2523 			shape == 0x385);
2524 }
2525 
scaleReceivedDamageCru(int damage,uint16 type) const2526 int Item::scaleReceivedDamageCru(int damage, uint16 type) const {
2527 	uint8 difficulty = World::get_instance()->getGameDifficulty();
2528 	const Actor *actor = dynamic_cast<const Actor *>(this);
2529 	//
2530 	// For difficulty 1 and 2, we scale damage to others *up* and damage
2531 	// to avatar *down*.
2532 	//
2533 	if (!actor || (this != getMainActor() && this != getControlledActor())) {
2534 		if (difficulty == 1) {
2535 			damage *= 5;
2536 		} else if (difficulty == 2) {
2537 			damage *= 3;
2538 		}
2539 	} else {
2540 		if (difficulty == 1) {
2541 			damage /= 5;
2542 		} else if (difficulty == 2) {
2543 			damage /= 3;
2544 		}
2545 	}
2546 
2547 	if (isRobotCru() && (type == 1 || type == 2 || type == 0xb || type == 0xd)) {
2548 		damage /= 3;
2549 	}
2550 
2551 	damage = CLIP(damage, 1, 0xfa);
2552 	return damage;
2553 }
2554 
2555 
saveData(Common::WriteStream * ws)2556 void Item::saveData(Common::WriteStream *ws) {
2557 	Object::saveData(ws);
2558 	ws->writeUint16LE(static_cast<uint16>(_extendedFlags));
2559 	ws->writeUint16LE(_flags);
2560 	ws->writeUint16LE(static_cast<uint16>(_shape));
2561 	ws->writeUint16LE(static_cast<uint16>(_frame));
2562 	ws->writeUint16LE(static_cast<uint16>(_x));
2563 	ws->writeUint16LE(static_cast<uint16>(_y));
2564 	ws->writeUint16LE(static_cast<uint16>(_z));
2565 	ws->writeUint16LE(_quality);
2566 	ws->writeUint16LE(_npcNum);
2567 	ws->writeUint16LE(_mapNum);
2568 	if (getObjId() != 0xFFFF) {
2569 		// these only make sense in currently loaded items
2570 		ws->writeUint16LE(_gump);
2571 		ws->writeUint16LE(_gravityPid);
2572 	}
2573 	if ((_flags & FLG_ETHEREAL) && (_flags & (FLG_CONTAINED | FLG_EQUIPPED)))
2574 		ws->writeUint16LE(_parent);
2575 }
2576 
loadData(Common::ReadStream * rs,uint32 version)2577 bool Item::loadData(Common::ReadStream *rs, uint32 version) {
2578 	if (!Object::loadData(rs, version)) return false;
2579 
2580 	_extendedFlags = rs->readUint16LE();
2581 	_flags = rs->readUint16LE();
2582 	_shape = rs->readUint16LE();
2583 	_frame = rs->readUint16LE();
2584 	_x = rs->readUint16LE();
2585 	_y = rs->readUint16LE();
2586 	_z = rs->readUint16LE();
2587 
2588 	_quality = rs->readUint16LE();
2589 	_npcNum = rs->readUint16LE();
2590 	_mapNum = rs->readUint16LE();
2591 	if (getObjId() != 0xFFFF) {
2592 		_gump = rs->readUint16LE();
2593 		_gravityPid = rs->readUint16LE();
2594 	} else {
2595 		_gump = _gravityPid = 0;
2596 	}
2597 
2598 	if ((_flags & FLG_ETHEREAL) && (_flags & (FLG_CONTAINED | FLG_EQUIPPED)))
2599 		_parent = rs->readUint16LE();
2600 	else
2601 		_parent = 0;
2602 
2603 	//!! hackish...
2604 	if (_extendedFlags & EXT_INCURMAP) {
2605 		World::get_instance()->getCurrentMap()->addItem(this);
2606 	}
2607 
2608 	return true;
2609 }
2610 
2611 
I_touch(const uint8 * args,unsigned int)2612 uint32 Item::I_touch(const uint8 *args, unsigned int /*argsize*/) {
2613 	ARG_NULL32(); // ARG_ITEM_FROM_PTR(item);
2614 
2615 	// Guess: this is used to make sure an item is painted in the original.
2616 	// Our renderer is different, making this intrinsic unnecessary.
2617 
2618 	return 0;
2619 }
2620 
I_getX(const uint8 * args,unsigned int)2621 uint32 Item::I_getX(const uint8 *args, unsigned int /*argsize*/) {
2622 	ARG_ITEM_FROM_PTR(item);
2623 	if (!item) return 0;
2624 
2625 	int32 x, y, z;
2626 	item->getLocationAbsolute(x, y, z);
2627 	if (GAME_IS_CRUSADER)
2628 		return x / 2;
2629 	else
2630 		return x;
2631 }
2632 
I_getY(const uint8 * args,unsigned int)2633 uint32 Item::I_getY(const uint8 *args, unsigned int /*argsize*/) {
2634 	ARG_ITEM_FROM_PTR(item);
2635 	if (!item) return 0;
2636 
2637 	int32 x, y, z;
2638 	item->getLocationAbsolute(x, y, z);
2639 	if (GAME_IS_CRUSADER)
2640 		return y / 2;
2641 	else
2642 		return y;
2643 }
2644 
I_getZ(const uint8 * args,unsigned int)2645 uint32 Item::I_getZ(const uint8 *args, unsigned int /*argsize*/) {
2646 	ARG_ITEM_FROM_PTR(item);
2647 	if (!item) return 0;
2648 
2649 	int32 x, y, z;
2650 	item->getLocationAbsolute(x, y, z);
2651 	return z;
2652 }
2653 
I_getCX(const uint8 * args,unsigned int)2654 uint32 Item::I_getCX(const uint8 *args, unsigned int /*argsize*/) {
2655 	ARG_ITEM_FROM_PTR(item);
2656 	if (!item) return 0;
2657 
2658 	int32 x, y, z;
2659 	item->getLocationAbsolute(x, y, z);
2660 
2661 	int mul = 16;
2662 	if (GAME_IS_CRUSADER) {
2663 		x /= 2;
2664 		mul /= 2;
2665 	}
2666 
2667 	if (item->_flags & FLG_FLIPPED)
2668 		return x - item->getShapeInfo()->_y * mul;
2669 	else
2670 		return x - item->getShapeInfo()->_x * mul;
2671 }
2672 
I_getCY(const uint8 * args,unsigned int)2673 uint32 Item::I_getCY(const uint8 *args, unsigned int /*argsize*/) {
2674 	ARG_ITEM_FROM_PTR(item);
2675 	if (!item) return 0;
2676 
2677 	int32 x, y, z;
2678 	item->getLocationAbsolute(x, y, z);
2679 
2680 	int mul = 16;
2681 	if (GAME_IS_CRUSADER) {
2682 		y /= 2;
2683 		mul /= 2;
2684 	}
2685 
2686 	if (item->_flags & FLG_FLIPPED)
2687 		return y - item->getShapeInfo()->_x * mul;
2688 	else
2689 		return y - item->getShapeInfo()->_y * mul;
2690 }
2691 
I_getCZ(const uint8 * args,unsigned int)2692 uint32 Item::I_getCZ(const uint8 *args, unsigned int /*argsize*/) {
2693 	ARG_ITEM_FROM_PTR(item);
2694 	if (!item) return 0;
2695 
2696 	int32 x, y, z;
2697 	item->getLocationAbsolute(x, y, z);
2698 
2699 	return z + item->getShapeInfo()->_z * 4;
2700 }
2701 
I_getPoint(const uint8 * args,unsigned int)2702 uint32 Item::I_getPoint(const uint8 *args, unsigned int /*argsize*/) {
2703 	ARG_ITEM_FROM_PTR(item);
2704 	ARG_UC_PTR(ptr);
2705 	if (!item) return 0;
2706 
2707 	int32 x, y, z;
2708 	item->getLocationAbsolute(x, y, z);
2709 
2710 	if (GAME_IS_CRUSADER) {
2711 		x /= 2;
2712 		y /= 2;
2713 	}
2714 
2715 	WorldPoint point;
2716 	point.setX(x);
2717 	point.setY(y);
2718 	point.setZ(z);
2719 
2720 	UCMachine::get_instance()->assignPointer(ptr, point._buf, 5);
2721 
2722 	return 0;
2723 }
2724 
I_getShape(const uint8 * args,unsigned int)2725 uint32 Item::I_getShape(const uint8 *args, unsigned int /*argsize*/) {
2726 	ARG_ITEM_FROM_PTR(item);
2727 	if (!item) return 0;
2728 
2729 	return item->getShape();
2730 }
2731 
I_setShape(const uint8 * args,unsigned int)2732 uint32 Item::I_setShape(const uint8 *args, unsigned int /*argsize*/) {
2733 	ARG_ITEM_FROM_PTR(item);
2734 	ARG_UINT16(shape);
2735 	if (!item) return 0;
2736 
2737 #if 0
2738 	debug(6, "Item::setShape: objid %04X shape (%d -> %d)",
2739 		  item->getObjId(), item->getShape(), shape);
2740 #endif
2741 
2742 	item->setShape(shape);
2743 	return 0;
2744 }
2745 
I_getFrame(const uint8 * args,unsigned int)2746 uint32 Item::I_getFrame(const uint8 *args, unsigned int /*argsize*/) {
2747 	ARG_ITEM_FROM_PTR(item);
2748 	if (!item) return 0;
2749 
2750 	return item->getFrame();
2751 }
2752 
I_setFrame(const uint8 * args,unsigned int)2753 uint32 Item::I_setFrame(const uint8 *args, unsigned int /*argsize*/) {
2754 	ARG_ITEM_FROM_PTR(item);
2755 	ARG_UINT16(frame);
2756 	if (!item) return 0;
2757 
2758 	item->setFrame(frame);
2759 	return 0;
2760 }
2761 
I_getQuality(const uint8 * args,unsigned int)2762 uint32 Item::I_getQuality(const uint8 *args, unsigned int /*argsize*/) {
2763 	ARG_ITEM_FROM_PTR(item);
2764 	if (!item) return 0;
2765 
2766 	if (item->getFamily() == ShapeInfo::SF_QUALITY)
2767 		return item->getQuality();
2768 	else
2769 		return 0;
2770 }
2771 
I_getUnkEggType(const uint8 * args,unsigned int)2772 uint32 Item::I_getUnkEggType(const uint8 *args, unsigned int /*argsize*/) {
2773 	ARG_ITEM_FROM_PTR(item);
2774 	if (!item) return 0;
2775 
2776 	if (item->getFamily() == ShapeInfo::SF_UNKEGG) {
2777 		if (GAME_IS_U8) {
2778 			return item->getQuality();
2779 		} else {
2780 			return item->getQuality() & 0xFF;
2781 		}
2782 	} else {
2783 		return 0;
2784 	}
2785 }
2786 
I_setUnkEggType(const uint8 * args,unsigned int)2787 uint32 Item::I_setUnkEggType(const uint8 *args, unsigned int /*argsize*/) {
2788 	ARG_ITEM_FROM_PTR(item);
2789 	ARG_UINT16(val);
2790 	if (!item) return 0;
2791 
2792 	if (item->getFamily() == ShapeInfo::SF_UNKEGG) {
2793 		item->setQuality(val);
2794 	}
2795 	return 0;
2796 }
2797 
I_getQuantity(const uint8 * args,unsigned int)2798 uint32 Item::I_getQuantity(const uint8 *args, unsigned int /*argsize*/) {
2799 	ARG_ITEM_FROM_PTR(item);
2800 	if (!item) return 0;
2801 
2802 	if (item->getFamily() == ShapeInfo::SF_QUANTITY ||
2803 	        item->getFamily() == ShapeInfo::SF_REAGENT)
2804 		return item->getQuality();
2805 	else
2806 		return 0;
2807 }
2808 
I_getContainer(const uint8 * args,unsigned int)2809 uint32 Item::I_getContainer(const uint8 *args, unsigned int /*argsize*/) {
2810 	ARG_ITEM_FROM_PTR(item);
2811 	if (!item) return 0;
2812 
2813 	//! What do we do if item has no _parent?
2814 	//! What do we do with equipped items?
2815 
2816 	if (item->getParent())
2817 		return item->getParent();
2818 	else
2819 		return 0;
2820 }
2821 
I_getRootContainer(const uint8 * args,unsigned int)2822 uint32 Item::I_getRootContainer(const uint8 *args, unsigned int /*argsize*/) {
2823 	ARG_ITEM_FROM_PTR(item);
2824 	if (!item) return 0;
2825 
2826 	Container *_parent = item->getParentAsContainer();
2827 
2828 	//! What do we do if item has no _parent?
2829 	//! What do we do with equipped items?
2830 
2831 	if (!_parent) return 0;
2832 
2833 	while (_parent->getParentAsContainer()) {
2834 		_parent = _parent->getParentAsContainer();
2835 	}
2836 
2837 	return _parent->getObjId();
2838 }
2839 
I_getQ(const uint8 * args,unsigned int)2840 uint32 Item::I_getQ(const uint8 *args, unsigned int /*argsize*/) {
2841 	ARG_ITEM_FROM_PTR(item);
2842 	if (!item) return 0;
2843 
2844 	return item->getQuality();
2845 }
2846 
I_getQLo(const uint8 * args,unsigned int)2847 uint32 Item::I_getQLo(const uint8 *args, unsigned int /*argsize*/) {
2848 	ARG_ITEM_FROM_PTR(item);
2849 	if (!item) return 0;
2850 
2851 	return item->getQuality() & 0xFF;
2852 }
2853 
I_getQHi(const uint8 * args,unsigned int)2854 uint32 Item::I_getQHi(const uint8 *args, unsigned int /*argsize*/) {
2855 	ARG_ITEM_FROM_PTR(item);
2856 	if (!item) return 0;
2857 
2858 	return (item->getQuality() >> 8) & 0xFF;
2859 }
2860 
I_setQ(const uint8 * args,unsigned int)2861 uint32 Item::I_setQ(const uint8 *args, unsigned int /*argsize*/) {
2862 	ARG_ITEM_FROM_PTR(item);
2863 	ARG_UINT16(q);
2864 	if (!item) return 0;
2865 
2866 	item->setQuality(q);
2867 	return 0;
2868 }
2869 
I_setQLo(const uint8 * args,unsigned int)2870 uint32 Item::I_setQLo(const uint8 *args, unsigned int /*argsize*/) {
2871 	ARG_ITEM_FROM_PTR(item);
2872 	ARG_UINT16(q);
2873 	if (!item) return 0;
2874 
2875 	uint16 iq = item->getQuality() & 0xFF00;
2876 
2877 	item->setQuality(iq | (q & 0xFF));
2878 	return 0;
2879 }
2880 
I_setQHi(const uint8 * args,unsigned int)2881 uint32 Item::I_setQHi(const uint8 *args, unsigned int /*argsize*/) {
2882 	ARG_ITEM_FROM_PTR(item);
2883 	ARG_UINT16(q);
2884 	if (!item) return 0;
2885 
2886 	uint16 iq = item->getQuality() & 0x00FF;
2887 
2888 	item->setQuality(iq | ((q << 8) & 0xFF00));
2889 	return 0;
2890 }
2891 
I_setQuality(const uint8 * args,unsigned int)2892 uint32 Item::I_setQuality(const uint8 *args, unsigned int /*argsize*/) {
2893 	ARG_ITEM_FROM_PTR(item);
2894 	ARG_UINT16(q);
2895 	if (!item) return 0;
2896 
2897 	if (item->getFamily() != ShapeInfo::SF_GENERIC)
2898 		item->setQuality(q);
2899 
2900 	return 0;
2901 }
2902 
I_setQuantity(const uint8 * args,unsigned int)2903 uint32 Item::I_setQuantity(const uint8 *args, unsigned int /*argsize*/) {
2904 	ARG_ITEM_FROM_PTR(item);
2905 	ARG_UINT16(q);
2906 	if (!item) return 0;
2907 
2908 	if (item->getFamily() == ShapeInfo::SF_QUANTITY ||
2909 	        item->getFamily() == ShapeInfo::SF_REAGENT)
2910 		item->setQuality(q);
2911 
2912 	return 0;
2913 }
2914 
I_setQAndCombine(const uint8 * args,unsigned int)2915 uint32 Item::I_setQAndCombine(const uint8 *args, unsigned int /*argsize*/) {
2916 	ARG_ITEM_FROM_PTR(item);
2917 	ARG_UINT16(q);
2918 	if (!item) return 0;
2919 
2920 	item->setQuality(q);
2921 	item->callUsecodeEvent_combine();
2922 
2923 	return 0;
2924 }
2925 
I_getFamily(const uint8 * args,unsigned int)2926 uint32 Item::I_getFamily(const uint8 *args, unsigned int /*argsize*/) {
2927 	ARG_ITEM_FROM_PTR(item);
2928 	if (!item) return 0;
2929 
2930 	return item->getFamily();
2931 }
2932 
I_getTypeFlag(const uint8 * args,unsigned int)2933 uint32 Item::I_getTypeFlag(const uint8 *args, unsigned int /*argsize*/) {
2934 	ARG_ITEM_FROM_PTR(item);
2935 	ARG_UINT16(typeflag);
2936 	if (!item) return 0;
2937 
2938 	const ShapeInfo *info = item->getShapeInfo();
2939 
2940 	if (GAME_IS_U8 && typeflag >= 64)
2941 		perr << "Invalid TypeFlag greater than 63 requested (" << typeflag << ") by Usecode" << Std::endl;
2942 	if (GAME_IS_CRUSADER && typeflag >= 72)
2943 		perr << "Invalid TypeFlag greater than 72 requested (" << typeflag << ") by Usecode" << Std::endl;
2944 
2945 	if (info->getTypeFlag(typeflag))
2946 		return 1;
2947 	else
2948 		return 0;
2949 }
2950 
I_getStatus(const uint8 * args,unsigned int)2951 uint32 Item::I_getStatus(const uint8 *args, unsigned int /*argsize*/) {
2952 	ARG_ITEM_FROM_PTR(item);
2953 	if (!item) return 0;
2954 
2955 	return item->getFlags();
2956 }
2957 
I_orStatus(const uint8 * args,unsigned int argsize)2958 uint32 Item::I_orStatus(const uint8 *args, unsigned int argsize) {
2959 	ARG_ITEM_FROM_PTR(item);
2960 	ARG_UINT16(mask);
2961 	if (!item) return 0;
2962 
2963 	item->setFlag(mask);
2964 	return 0;
2965 }
2966 
I_andStatus(const uint8 * args,unsigned int argsize)2967 uint32 Item::I_andStatus(const uint8 *args, unsigned int argsize) {
2968 	ARG_ITEM_FROM_PTR(item);
2969 	ARG_UINT16(mask);
2970 	if (!item) return 0;
2971 
2972 	item->_flags &= mask;
2973 	return 0;
2974 }
2975 
I_ascend(const uint8 * args,unsigned int)2976 uint32 Item::I_ascend(const uint8 *args, unsigned int /*argsize*/) {
2977 	ARG_ITEM_FROM_PTR(item);
2978 	ARG_SINT16(delta);
2979 	if (!item) return 0;
2980 
2981 	int dist = item->ascend(delta);
2982 
2983 	if (dist == 0x4000)
2984 		return 1;
2985 	else
2986 		return 0;
2987 }
2988 
I_getWeight(const uint8 * args,unsigned int)2989 uint32 Item::I_getWeight(const uint8 *args, unsigned int /*argsize*/) {
2990 	ARG_ITEM_FROM_PTR(item);
2991 	if (!item) return 0;
2992 
2993 	return item->getWeight();
2994 }
2995 
I_getWeightIncludingContents(const uint8 * args,unsigned int)2996 uint32 Item::I_getWeightIncludingContents(const uint8 *args,
2997 		unsigned int /*argsize*/) {
2998 	ARG_ITEM_FROM_PTR(item);
2999 	if (!item) return 0;
3000 
3001 	return item->getTotalWeight();
3002 }
3003 
I_bark(const uint8 * args,unsigned int)3004 uint32 Item::I_bark(const uint8 *args, unsigned int /*argsize*/) {
3005 	ARG_ITEM_FROM_PTR(item);
3006 	ARG_STRING(str);
3007 	if (id_item == 666)
3008 		item = getItem(1);
3009 
3010 	if (!item) {
3011 		// Hack! Items should always be valid?
3012 		warning("skipping bark of '%s' because item invalid.", str.c_str());
3013 		return 0;
3014 	}
3015 
3016 	uint32 shapenum = item->getShape();
3017 	if (id_item == 666)
3018 		shapenum = 666; // Hack for guardian barks
3019 	Gump *gump = new BarkGump(item->getObjId(), str, shapenum);
3020 
3021 	if (item->getObjId() < 256) { // CONSTANT!
3022 		GumpNotifyProcess *notifyproc;
3023 		notifyproc = new ActorBarkNotifyProcess(item->getObjId());
3024 		Kernel::get_instance()->addProcess(notifyproc);
3025 		gump->SetNotifyProcess(notifyproc);
3026 	}
3027 
3028 	gump->InitGump(0);
3029 
3030 	return gump->GetNotifyProcess()->getPid();
3031 }
3032 
I_look(const uint8 * args,unsigned int)3033 uint32 Item::I_look(const uint8 *args, unsigned int /*argsize*/) {
3034 	ARG_ITEM_FROM_PTR(item);
3035 	if (!item) return 0;
3036 
3037 	return item->callUsecodeEvent_look();
3038 }
3039 
I_use(const uint8 * args,unsigned int)3040 uint32 Item::I_use(const uint8 *args, unsigned int /*argsize*/) {
3041 	ARG_ITEM_FROM_PTR(item);
3042 	if (!item) return 0;
3043 
3044 	return item->callUsecodeEvent_use();
3045 }
3046 
I_gotHit(const uint8 * args,unsigned int)3047 uint32 Item::I_gotHit(const uint8 *args, unsigned int /*argsize*/) {
3048 	ARG_ITEM_FROM_PTR(item);
3049 	ARG_UINT16(hitter);
3050 	ARG_SINT16(force);
3051 	if (!item) return 0;
3052 
3053 	return item->callUsecodeEvent_gotHit(hitter, force);
3054 }
3055 
I_equip(const uint8 * args,unsigned int argsize)3056 uint32 Item::I_equip(const uint8 *args, unsigned int argsize) {
3057 	ARG_ITEM_FROM_PTR(item);
3058 	if (!item) return 0;
3059 
3060 	// Note: The U8 version (no param) is never actually called in the usecode.
3061 	assert(argsize > 4);
3062 
3063 	ARG_UINT16(val);
3064 	return item->callUsecodeEvent_equipWithParam(val);
3065 }
3066 
I_unequip(const uint8 * args,unsigned int argsize)3067 uint32 Item::I_unequip(const uint8 *args, unsigned int argsize) {
3068 	ARG_ITEM_FROM_PTR(item);
3069 	if (!item) return 0;
3070 
3071 	// Note: The U8 version (no param) is never actually called in the usecode.
3072 	assert(argsize > 4);
3073 
3074 	ARG_UINT16(val)
3075 	return item->callUsecodeEvent_unequipWithParam(val);
3076 }
3077 
I_enterFastArea(const uint8 * args,unsigned int)3078 uint32 Item::I_enterFastArea(const uint8 *args, unsigned int /*argsize*/) {
3079 	ARG_ITEM_FROM_PTR(item);
3080 	if (!item) return 0;
3081 
3082 	return item->enterFastArea();
3083 }
3084 
I_cast(const uint8 * args,unsigned int)3085 uint32 Item::I_cast(const uint8 *args, unsigned int /*argsize*/) {
3086 	ARG_ITEM_FROM_PTR(item);
3087 	if (!item) return 0;
3088 	ARG_UINT16(arg);
3089 
3090 	return item->callUsecodeEvent_cast(arg);
3091 }
3092 
I_avatarStoleSomething(const uint8 * args,unsigned int)3093 uint32 Item::I_avatarStoleSomething(const uint8 *args, unsigned int /*argsize*/) {
3094 	ARG_ITEM_FROM_PTR(item);
3095 	if (!item) return 0;
3096 
3097 	// Abort if npc && dead to match original game behavior
3098 	Actor *actor = dynamic_cast<Actor *>(item);
3099 	if (actor && actor->isDead())
3100 		return 0;
3101 
3102 	ARG_UINT16(arg);
3103 
3104 	return item->callUsecodeEvent_AvatarStoleSomething(arg);
3105 }
3106 
I_ask(const uint8 * args,unsigned int)3107 uint32 Item::I_ask(const uint8 *args, unsigned int /*argsize*/) {
3108 	ARG_NULL32(); // ARG_ITEM_FROM_PTR(item); // currently unused.
3109 	ARG_LIST(answers);
3110 
3111 	if (!answers) return 0;
3112 
3113 	// Use AskGump
3114 	Gump *_gump = new AskGump(1, answers);
3115 	_gump->InitGump(0);
3116 	return _gump->GetNotifyProcess()->getPid();
3117 }
3118 
I_legalCreateAtPoint(const uint8 * args,unsigned int)3119 uint32 Item::I_legalCreateAtPoint(const uint8 *args, unsigned int /*argsize*/) {
3120 	ARG_UC_PTR(itemptr); // need to store the item id at *itemptr
3121 	ARG_UINT16(shape);
3122 	ARG_UINT16(frame);
3123 	ARG_WORLDPOINT(point);
3124 
3125 	int32 x = point.getX();
3126 	int32 y = point.getY();
3127 	int32 z = point.getZ();
3128 
3129 	if (GAME_IS_CRUSADER) {
3130 		x *= 2;
3131 		y *= 2;
3132 	}
3133 
3134 	// check if item can exist
3135 	CurrentMap *cm = World::get_instance()->getCurrentMap();
3136 	bool valid = cm->isValidPosition(x, y, z, shape, 0, 0, 0);
3137 	if (!valid)
3138 		return 0;
3139 
3140 	Item *newitem = ItemFactory::createItem(shape, frame, 0, 0, 0, 0, 0, true);
3141 	if (!newitem) {
3142 		perr << "I_legalCreateAtPoint failed to create item (" << shape
3143 		     << "," << frame << ")." << Std::endl;
3144 		return 0;
3145 	}
3146 	uint16 objID = newitem->getObjId();
3147 	newitem->move(x, y, z);
3148 
3149 	uint8 buf[2];
3150 	buf[0] = static_cast<uint8>(objID);
3151 	buf[1] = static_cast<uint8>(objID >> 8);
3152 	UCMachine::get_instance()->assignPointer(itemptr, buf, 2);
3153 
3154 	return 1;
3155 }
3156 
I_legalCreateAtCoords(const uint8 * args,unsigned int)3157 uint32 Item::I_legalCreateAtCoords(const uint8 *args, unsigned int /*argsize*/) {
3158 	ARG_UC_PTR(itemptr); // need to store the item id at *itemptr
3159 	ARG_UINT16(shape);
3160 	ARG_UINT16(frame);
3161 	ARG_UINT16(x);
3162 	ARG_UINT16(y);
3163 	ARG_UINT8(z);
3164 
3165 	if (GAME_IS_CRUSADER) {
3166 		x *= 2;
3167 		y *= 2;
3168 	}
3169 
3170 	// check if item can exist
3171 	CurrentMap *cm = World::get_instance()->getCurrentMap();
3172 	bool valid = cm->isValidPosition(x, y, z, shape, 0, 0, 0);
3173 	if (!valid)
3174 		return 0;
3175 
3176 	// if yes, create it
3177 	Item *newitem = ItemFactory::createItem(shape, frame, 0, 0, 0, 0, 0, true);
3178 	if (!newitem) {
3179 		perr << "I_legalCreateAtCoords failed to create item (" << shape
3180 		     << "," << frame << ")." << Std::endl;
3181 		return 0;
3182 	}
3183 	uint16 objID = newitem->getObjId();
3184 	newitem->move(x, y, z);
3185 
3186 	uint8 buf[2];
3187 	buf[0] = static_cast<uint8>(objID);
3188 	buf[1] = static_cast<uint8>(objID >> 8);
3189 	UCMachine::get_instance()->assignPointer(itemptr, buf, 2);
3190 
3191 	return 1;
3192 }
3193 
I_legalCreateInCont(const uint8 * args,unsigned int)3194 uint32 Item::I_legalCreateInCont(const uint8 *args, unsigned int /*argsize*/) {
3195 	ARG_UC_PTR(itemptr); // need to store the item id at *itemptr
3196 	ARG_UINT16(shape);
3197 	ARG_UINT16(frame);
3198 	ARG_CONTAINER_FROM_ID(container);
3199 	ARG_UINT16(unknown); // ?
3200 
3201 	uint8 buf[2];
3202 	buf[0] = 0;
3203 	buf[1] = 0;
3204 	UCMachine::get_instance()->assignPointer(itemptr, buf, 2);
3205 
3206 	// Create an item and try to add it to the given container.
3207 	// If it fits, return id; otherwise return 0.
3208 
3209 	Item *newitem = ItemFactory::createItem(shape, frame, 0, 0, 0, 0, 0, true);
3210 	if (!newitem) {
3211 		perr << "I_legalCreateInCont failed to create item (" << shape
3212 		     << "," << frame << ")." << Std::endl;
3213 		return 0;
3214 	}
3215 
3216 	// also need to check weight, volume maybe??
3217 	if (newitem->moveToContainer(container)) {
3218 		uint16 objID = newitem->getObjId();
3219 
3220 		buf[0] = static_cast<uint8>(objID);
3221 		buf[1] = static_cast<uint8>(objID >> 8);
3222 		UCMachine::get_instance()->assignPointer(itemptr, buf, 2);
3223 
3224 		return 1;
3225 	} else {
3226 		perr << "I_legalCreateInCont failed to add item to container ("
3227 		     << container->getObjId() << ")" << Std::endl;
3228 		// failed to add; clean up
3229 		newitem->destroy();
3230 
3231 		return 0;
3232 	}
3233 }
3234 
I_destroy(const uint8 * args,unsigned int)3235 uint32 Item::I_destroy(const uint8 *args, unsigned int /*argsize*/) {
3236 	ARG_ITEM_FROM_PTR(item);
3237 	if (!item || item->getObjId() == 1) return 0;
3238 
3239 	item->destroy();
3240 
3241 	return 0;
3242 }
3243 
I_getFootpadData(const uint8 * args,unsigned int)3244 uint32 Item::I_getFootpadData(const uint8 *args, unsigned int /*argsize*/) {
3245 	ARG_ITEM_FROM_PTR(item);
3246 	ARG_UC_PTR(xptr);
3247 	ARG_UC_PTR(yptr);
3248 	ARG_UC_PTR(zptr);
3249 	if (!item) return 0;
3250 
3251 	// TODO: Data is packed differently in Crusader - check that this still works.
3252 
3253 	uint8 buf[2];
3254 	int32 x, y, z;
3255 	item->getFootpadData(x, y, z);
3256 
3257 	buf[0] = static_cast<uint8>(x);
3258 	buf[1] = static_cast<uint8>(x >> 8);
3259 	UCMachine::get_instance()->assignPointer(xptr, buf, 2);
3260 
3261 	buf[0] = static_cast<uint8>(y);
3262 	buf[1] = static_cast<uint8>(y >> 8);
3263 	UCMachine::get_instance()->assignPointer(yptr, buf, 2);
3264 
3265 	buf[0] = static_cast<uint8>(z);
3266 	buf[1] = static_cast<uint8>(z >> 8);
3267 	UCMachine::get_instance()->assignPointer(zptr, buf, 2);
3268 
3269 	return 0;
3270 }
3271 
I_overlaps(const uint8 * args,unsigned int)3272 uint32 Item::I_overlaps(const uint8 *args, unsigned int /*argsize*/) {
3273 	ARG_ITEM_FROM_PTR(item);
3274 	ARG_ITEM_FROM_ID(item2);
3275 	if (!item) return 0;
3276 	if (!item2) return 0;
3277 
3278 	if (item->overlaps(*item2))
3279 		return 1;
3280 	else
3281 		return 0;
3282 }
3283 
I_overlapsXY(const uint8 * args,unsigned int)3284 uint32 Item::I_overlapsXY(const uint8 *args, unsigned int /*argsize*/) {
3285 	ARG_ITEM_FROM_PTR(item);
3286 	ARG_ITEM_FROM_ID(item2);
3287 	if (!item) return 0;
3288 	if (!item2) return 0;
3289 
3290 	if (item->overlapsxy(*item2))
3291 		return 1;
3292 	else
3293 		return 0;
3294 }
3295 
I_isOn(const uint8 * args,unsigned int)3296 uint32 Item::I_isOn(const uint8 *args, unsigned int /*argsize*/) {
3297 	ARG_ITEM_FROM_PTR(item);
3298 	ARG_ITEM_FROM_ID(item2);
3299 	if (!item) return 0;
3300 	if (!item2) return 0;
3301 
3302 	if (item->isOn(*item2))
3303 		return 1;
3304 	else
3305 		return 0;
3306 }
3307 
I_isCompletelyOn(const uint8 * args,unsigned int)3308 uint32 Item::I_isCompletelyOn(const uint8 *args, unsigned int /*argsize*/) {
3309 	ARG_ITEM_FROM_PTR(item);
3310 	ARG_ITEM_FROM_ID(item2);
3311 	if (!item) return 0;
3312 	if (!item2) return 0;
3313 
3314 	if (item->isCompletelyOn(*item2))
3315 		return 1;
3316 	else
3317 		return 0;
3318 }
3319 
I_isCentreOn(const uint8 * args,unsigned int)3320 uint32 Item::I_isCentreOn(const uint8 *args, unsigned int /*argsize*/) {
3321 	ARG_ITEM_FROM_PTR(item);
3322 	ARG_ITEM_FROM_ID(item2);
3323 	if (!item) return 0;
3324 	if (!item2) return 0;
3325 
3326 	if (item->isCentreOn(*item2))
3327 		return 1;
3328 	else
3329 		return 0;
3330 }
3331 
I_isInNpc(const uint8 * args,unsigned int)3332 uint32 Item::I_isInNpc(const uint8 *args, unsigned int /*argsize*/) {
3333 	ARG_ITEM_FROM_PTR(item);
3334 	if (!item)
3335 		return 0;
3336 
3337 	const Container *container = item->getParentAsContainer();
3338 	while (container) {
3339 		const Actor *actor = dynamic_cast<const Actor *>(container);
3340 		if (actor)
3341 			return 1;
3342 		container = container->getParentAsContainer();
3343 	}
3344 	return 0;
3345 }
3346 
I_getFamilyOfType(const uint8 * args,unsigned int)3347 uint32 Item::I_getFamilyOfType(const uint8 *args, unsigned int /*argsize*/) {
3348 	ARG_UINT16(shape);
3349 
3350 	return GameData::get_instance()->getMainShapes()->
3351 	       getShapeInfo(shape)->_family;
3352 }
3353 
I_push(const uint8 * args,unsigned int)3354 uint32 Item::I_push(const uint8 *args, unsigned int /*argsize*/) {
3355 	ARG_ITEM_FROM_PTR(item);
3356 	if (!item)
3357 		return 0;
3358 
3359 	#if 0
3360 		perr << "Pushing item to ethereal void: id: " << item->getObjId() << " shp: " << item->getShape() << "," << item->getFrame() << Std::endl;
3361 	#endif
3362 
3363 	item->moveToEtherealVoid();
3364 
3365 	return 0;
3366 }
3367 
I_create(const uint8 * args,unsigned int)3368 uint32 Item::I_create(const uint8 *args, unsigned int /*argsize*/) {
3369 	ARG_UC_PTR(itemptr); // need to store the item id at *itemptr (????)
3370 	ARG_UINT16(shape);
3371 	ARG_UINT16(frame);
3372 
3373 	Item *newitem = ItemFactory::createItem(shape, frame, 0, 0, 0, 0, 0, true);
3374 	if (!newitem) {
3375 		perr << "I_create failed to create item (" << shape
3376 		     << "," << frame << ")." << Std::endl;
3377 		return 0;
3378 	}
3379 	uint16 objID = newitem->getObjId();
3380 
3381 #if 0
3382 	debug(6, "Item::create: objid %04X shape (%d, %d)",
3383 		  objID, shape, frame);
3384 #endif
3385 
3386 	newitem->moveToEtherealVoid();
3387 
3388 	uint8 buf[2];
3389 	buf[0] = static_cast<uint8>(objID);
3390 	buf[1] = static_cast<uint8>(objID >> 8);
3391 	UCMachine::get_instance()->assignPointer(itemptr, buf, 2);
3392 
3393 	return 1;
3394 }
3395 
I_pop(const uint8 * args,unsigned int)3396 uint32 Item::I_pop(const uint8 *args, unsigned int /*argsize*/) {
3397 	ARG_NULL32(); // ARG_ITEM_FROM_PTR(item); // unused
3398 
3399 	World *w = World::get_instance();
3400 
3401 	if (w->etherealEmpty())
3402 		return 0; // no items left on stack
3403 
3404 	uint16 _objId = w->etherealPeek();
3405 	Item *item = getItem(_objId);
3406 	if (!item) {
3407 		w->etherealRemove(_objId);
3408 		return 0; // top item was invalid
3409 	}
3410 
3411 	item->returnFromEtherealVoid();
3412 
3413 #if 0
3414 	perr << "Popping item to original location: " << item->getShape() << "," << item->getFrame() << Std::endl;
3415 #endif
3416 
3417 	//! Anything else?
3418 
3419 	return _objId;
3420 }
3421 
I_popToCoords(const uint8 * args,unsigned int)3422 uint32 Item::I_popToCoords(const uint8 *args, unsigned int /*argsize*/) {
3423 	ARG_NULL32(); // ARG_ITEM_FROM_PTR(item); // unused
3424 	ARG_UINT16(x);
3425 	ARG_UINT16(y);
3426 	ARG_UINT8(z);
3427 
3428 	World *w = World::get_instance();
3429 
3430 	if (w->etherealEmpty())
3431 		return 0; // no items left on stack
3432 
3433 	uint16 objId = w->etherealPeek();
3434 	Item *item = getItem(objId);
3435 	if (!item) {
3436 		w->etherealRemove(objId);
3437 		return 0; // top item was invalid
3438 	}
3439 
3440 	if (GAME_IS_CRUSADER) {
3441 		x *= 2;
3442 		y *= 2;
3443 		// HACK!!  DEATHFL::ordinal20 has a hack to add 1 to z for the death
3444 		// animation (falling into acid), but then our animation tracker
3445 		// detects a fall and stops animating.  Fight hacks with hacks..
3446 		if (item->_shape == 1408 && z > 0)
3447 			z -= 1;
3448 	}
3449 
3450 	item->move(x, y, z);
3451 
3452 #if 0
3453 	perr << "Popping item into map: " << item->getShape() << "," << item->getFrame() << " at (" << x << "," << y << "," << z << ")" << Std::endl;
3454 #endif
3455 
3456 	//! Anything else?
3457 
3458 	return objId;
3459 }
3460 
I_popToContainer(const uint8 * args,unsigned int)3461 uint32 Item::I_popToContainer(const uint8 *args, unsigned int /*argsize*/) {
3462 	ARG_NULL32(); // ARG_ITEM_FROM_PTR(item); // unused
3463 	ARG_CONTAINER_FROM_ID(container);
3464 
3465 	if (!container) {
3466 		perr << "Trying to pop item to invalid container (" << id_container << ")." << Std::endl;
3467 		return 0;
3468 	}
3469 
3470 	World *w = World::get_instance();
3471 
3472 	if (w->etherealEmpty())
3473 		return 0; // no items left on stack
3474 
3475 	uint16 _objId = w->etherealPeek();
3476 	Item *item = getItem(_objId);
3477 	if (!item) {
3478 		w->etherealRemove(_objId);
3479 		return 0; // top item was invalid
3480 	}
3481 
3482 	item->moveToContainer(container);
3483 
3484 	//! Anything else?
3485 
3486 	return _objId;
3487 }
3488 
I_popToEnd(const uint8 * args,unsigned int)3489 uint32 Item::I_popToEnd(const uint8 *args, unsigned int /*argsize*/) {
3490 	ARG_NULL32(); // ARG_ITEM_FROM_PTR(item); // unused
3491 	ARG_CONTAINER_FROM_ID(container);
3492 
3493 	if (!container) {
3494 		perr << "Trying to pop item to invalid container (" << id_container << ")." << Std::endl;
3495 		return 0;
3496 	}
3497 
3498 	World *w = World::get_instance();
3499 
3500 	if (w->etherealEmpty())
3501 		return 0; // no items left on stack
3502 
3503 	uint16 _objId = w->etherealPeek();
3504 	Item *item = getItem(_objId);
3505 	if (!item) {
3506 		w->etherealRemove(_objId);
3507 		return 0; // top item was invalid
3508 	}
3509 
3510 	item->moveToContainer(container);
3511 
3512 	//! Anything else?
3513 
3514 	//! This should probably be different from I_popToContainer, but
3515 	//! how exactly?
3516 
3517 	return _objId;
3518 }
3519 
I_move(const uint8 * args,unsigned int)3520 uint32 Item::I_move(const uint8 *args, unsigned int /*argsize*/) {
3521 	ARG_ITEM_FROM_PTR(item);
3522 	ARG_UINT16(x);
3523 	ARG_UINT16(y);
3524 	ARG_UINT8(z);
3525 	if (!item)
3526 		return 0;
3527 
3528 	//! What should this do to ethereal items?
3529 	if (GAME_IS_CRUSADER) {
3530 		x *= 2;
3531 		y *= 2;
3532 	}
3533 
3534 	#if 0
3535 		perr << "Moving item: " << item->getShape() << "," << item->getFrame() << " to (" << x << "," << y << "," << z << ")" << Std::endl;
3536 	#endif
3537 
3538 	item->move(x, y, z);
3539 	return 0;
3540 }
3541 
I_legalMoveToPoint(const uint8 * args,unsigned int argsize)3542 uint32 Item::I_legalMoveToPoint(const uint8 *args, unsigned int argsize) {
3543 	ARG_ITEM_FROM_PTR(item);
3544 	ARG_WORLDPOINT(point);
3545 	ARG_UINT16(move_if_blocked); // 0/1
3546 	ARG_UINT16(unknown2); // always 0
3547 
3548 	int32 x = point.getX();
3549 	int32 y = point.getY();
3550 	int32 z = point.getZ();
3551 
3552 	if (GAME_IS_CRUSADER) {
3553 		x *= 2;
3554 		y *= 2;
3555 	}
3556 
3557 	if (!item)
3558 		return 0;
3559 
3560 	//
3561 	// Return true when there are no blockers.
3562 	// If there are blockers, only move if move_if_blocked is set.
3563 	//
3564 	int retval = 1;
3565 	Std::list<CurrentMap::SweepItem> collisions;
3566 	int32 start[3], end[3], dims[3];
3567 	end[0] = x;
3568 	end[1] = y;
3569 	end[2] = z;
3570 	item->getLocation(start[0], start[1], start[2]);
3571 	item->getFootpadWorld(dims[0], dims[1], dims[2]);
3572 	CurrentMap *map = World::get_instance()->getCurrentMap();
3573 	map->sweepTest(start, end, dims, item->getShapeInfo()->_flags,
3574 				   item->getObjId(), true, &collisions);
3575 	for (Std::list<CurrentMap::SweepItem>::iterator it = collisions.begin();
3576 		 it != collisions.end(); it++) {
3577 		if (it->_blocking && !it->_touching && it->_endTime > 0) {
3578 			if (!move_if_blocked)
3579 				return 0;
3580 			retval = 0;
3581 			break;
3582 		}
3583 	}
3584 
3585 	item->collideMove(x, y, z, false, false);
3586 	return retval;
3587 }
3588 
I_legalMoveToContainer(const uint8 * args,unsigned int)3589 uint32 Item::I_legalMoveToContainer(const uint8 *args, unsigned int /*argsize*/) {
3590 	ARG_ITEM_FROM_PTR(item);
3591 	ARG_CONTAINER_FROM_PTR(container);
3592 	ARG_UINT16(unknown); // always 0
3593 	if (!item || !container) return 0; // shouldn't happen?
3594 
3595 	// try to move item to container checking weight and volume
3596 	return item->moveToContainer(container, true);
3597 }
3598 
I_getEtherealTop(const uint8 *,unsigned int)3599 uint32 Item::I_getEtherealTop(const uint8 * /*args*/, unsigned int /*argsize*/) {
3600 	World *w = World::get_instance();
3601 	if (w->etherealEmpty()) return 0; // no items left on stack
3602 	return w->etherealPeek();
3603 }
3604 
3605 
3606 //!!! is this correct?
I_getMapArray(const uint8 * args,unsigned int)3607 uint32 Item::I_getMapArray(const uint8 *args, unsigned int /*argsize*/) {
3608 	ARG_ITEM_FROM_PTR(item);
3609 	if (!item) return 0;
3610 
3611 	return item->getMapNum();
3612 }
3613 
3614 //!!! is this correct?
I_setMapArray(const uint8 * args,unsigned int)3615 uint32 Item::I_setMapArray(const uint8 *args, unsigned int /*argsize*/) {
3616 	ARG_ITEM_FROM_PTR(item);
3617 	ARG_UINT16(mapNum);
3618 	if (!item) return 0;
3619 
3620 	item->setMapNum(mapNum);
3621 	return 0;
3622 }
3623 
I_getNpcNum(const uint8 * args,unsigned int)3624 uint32 Item::I_getNpcNum(const uint8 *args, unsigned int /*argsize*/) {
3625 	ARG_ITEM_FROM_PTR(item);
3626 	if (!item) return 0;
3627 
3628 	return item->getNpcNum();
3629 }
3630 
I_setNpcNum(const uint8 * args,unsigned int)3631 uint32 Item::I_setNpcNum(const uint8 *args, unsigned int /*argsize*/) {
3632 	ARG_ITEM_FROM_PTR(item);
3633 	ARG_UINT16(npcNum);
3634 	if (!item) return 0;
3635 
3636 	item->setNpcNum(npcNum);
3637 	return 0;
3638 }
3639 
I_getDirToCoords(const uint8 * args,unsigned int)3640 uint32 Item::I_getDirToCoords(const uint8 *args, unsigned int /*argsize*/) {
3641 	ARG_ITEM_FROM_PTR(item);
3642 	ARG_UINT16(x);
3643 	ARG_UINT16(y);
3644 	if (!item) return 0;
3645 
3646 	if (GAME_IS_CRUSADER) {
3647 		x *= 2;
3648 		y *= 2;
3649 	}
3650 
3651 	int32 ix, iy, iz;
3652 	item->getLocationAbsolute(ix, iy, iz);
3653 
3654 	return Direction_ToUsecodeDir(Direction_GetWorldDir(y - iy, x - ix, dirmode_8dirs));
3655 }
3656 
I_getDirFromCoords(const uint8 * args,unsigned int)3657 uint32 Item::I_getDirFromCoords(const uint8 *args, unsigned int /*argsize*/) {
3658 	ARG_ITEM_FROM_PTR(item);
3659 	ARG_UINT16(x);
3660 	ARG_UINT16(y);
3661 	if (!item) return 0;
3662 
3663 	if (GAME_IS_CRUSADER) {
3664 		x *= 2;
3665 		y *= 2;
3666 	}
3667 
3668 	int32 ix, iy, iz;
3669 	item->getLocationAbsolute(ix, iy, iz);
3670 
3671 	return Direction_ToUsecodeDir(Direction_GetWorldDir(iy - y, ix - x, dirmode_8dirs));
3672 }
3673 
I_getDirToItem(const uint8 * args,unsigned int)3674 uint32 Item::I_getDirToItem(const uint8 *args, unsigned int /*argsize*/) {
3675 	ARG_ITEM_FROM_PTR(item);
3676 	ARG_ITEM_FROM_ID(item2);
3677 	if (!item) return 0;
3678 	if (!item2) return 0;
3679 
3680 	int32 ix, iy, iz;
3681 	item->getLocationAbsolute(ix, iy, iz);
3682 
3683 	int32 i2x, i2y, i2z;
3684 	item2->getLocationAbsolute(i2x, i2y, i2z);
3685 
3686 	return Direction_ToUsecodeDir(Direction_GetWorldDir(i2y - iy, i2x - ix, dirmode_8dirs));
3687 }
3688 
I_getDirFromItem(const uint8 * args,unsigned int)3689 uint32 Item::I_getDirFromItem(const uint8 *args, unsigned int /*argsize*/) {
3690 	ARG_ITEM_FROM_PTR(item);
3691 	ARG_ITEM_FROM_ID(item2);
3692 	if (!item) return 0;
3693 	if (!item2) return 0;
3694 
3695 	int32 ix, iy, iz;
3696 	item->getLocationAbsolute(ix, iy, iz);
3697 
3698 	int32 i2x, i2y, i2z;
3699 	item2->getLocationAbsolute(i2x, i2y, i2z);
3700 
3701 	return Direction_ToUsecodeDir(Direction_Invert(Direction_GetWorldDir(i2y - iy, i2x - ix, dirmode_8dirs)));
3702 }
3703 
I_getDirFromTo16(const uint8 * args,unsigned int)3704 uint32 Item::I_getDirFromTo16(const uint8 *args, unsigned int /*argsize*/) {
3705 	ARG_UINT16(x1);
3706 	ARG_UINT16(y1);
3707 	ARG_UINT16(x2);
3708 	ARG_UINT16(y2);
3709 
3710 	if (x1 == x2 && y1 == y2)
3711 		return 16;
3712 
3713 	return Direction_ToUsecodeDir(Direction_GetWorldDir(y2 - y1, x2 - x1, dirmode_16dirs));
3714 }
3715 
I_getClosestDirectionInRange(const uint8 * args,unsigned int)3716 uint32 Item::I_getClosestDirectionInRange(const uint8 *args, unsigned int /*argsize*/) {
3717 	ARG_UINT16(x1);
3718 	ARG_UINT16(y1);
3719 	ARG_UINT16(x2);
3720 	ARG_UINT16(y2);
3721 	ARG_UINT16(ndirs);
3722 	ARG_UINT16(mind);
3723 	ARG_UINT16(maxd);
3724 
3725 	Direction mindir = Direction_FromUsecodeDir(mind);
3726 	Direction maxdir = Direction_FromUsecodeDir(maxd);
3727 	DirectionMode mode = (ndirs == 16 ? dirmode_16dirs : dirmode_8dirs);
3728 	Direction result = Direction_GetWorldDirInRange(y2 - y1, x2 - x1, mode, mindir, maxdir);
3729 	return Direction_ToUsecodeDir(result);
3730 }
3731 
I_hurl(const uint8 * args,unsigned int)3732 uint32 Item::I_hurl(const uint8 *args, unsigned int /*argsize*/) {
3733 	ARG_ITEM_FROM_PTR(item);
3734 	ARG_SINT16(xs);
3735 	ARG_SINT16(ys);
3736 	ARG_SINT16(zs);
3737 	ARG_SINT16(grav);
3738 	if (!item) return 0;
3739 
3740 	item->hurl(xs, ys, zs, grav);
3741 
3742 	return item->_gravityPid;
3743 }
3744 
I_shoot(const uint8 * args,unsigned int)3745 uint32 Item::I_shoot(const uint8 *args, unsigned int /*argsize*/) {
3746 	ARG_ITEM_FROM_PTR(item);
3747 	ARG_WORLDPOINT(point);
3748 	ARG_UINT16(speed); // either 0x20 (fish) or 0x40 (death disk, dart)
3749 	ARG_UINT16(gravity); // either 2 (fish) or 1 (death disk, dart)
3750 	if (!item) return 0;
3751 
3752 	MissileTracker tracker(item, point.getX(), point.getY(), point.getZ(),
3753 	                       speed, gravity);
3754 	tracker.launchItem();
3755 
3756 	return 0;
3757 }
3758 
I_fall(const uint8 * args,unsigned int)3759 uint32 Item::I_fall(const uint8 *args, unsigned int /*argsize*/) {
3760 	ARG_ITEM_FROM_PTR(item);
3761 	if (!item) return 0;
3762 
3763 	item->fall();
3764 
3765 	return 0;
3766 }
3767 
I_grab(const uint8 * args,unsigned int)3768 uint32 Item::I_grab(const uint8 *args, unsigned int /*argsize*/) {
3769 	ARG_ITEM_FROM_PTR(item);
3770 	if (!item) return 0;
3771 
3772 	item->grab();
3773 
3774 	return 0;
3775 }
3776 
I_getSliderInput(const uint8 * args,unsigned int)3777 uint32 Item::I_getSliderInput(const uint8 *args, unsigned int /*argsize*/) {
3778 	ARG_NULL32(); //    ARG_ITEM_FROM_PTR(item);
3779 	ARG_SINT16(minval);
3780 	ARG_SINT16(maxval);
3781 	ARG_SINT16(step);
3782 
3783 	UCProcess *current = dynamic_cast<UCProcess *>(Kernel::get_instance()->getRunningProcess());
3784 	assert(current);
3785 
3786 //	pout << "SliderGump: min=" << minval << ", max=" << maxval << ", step=" << step << Std::endl;
3787 
3788 	SliderGump *_gump = new SliderGump(100, 100, minval, maxval, minval, step);
3789 	_gump->InitGump(0); // modal _gump
3790 	_gump->setUsecodeNotify(current);
3791 
3792 	current->suspend();
3793 
3794 	return 0;
3795 }
3796 
I_openGump(const uint8 * args,unsigned int)3797 uint32 Item::I_openGump(const uint8 *args, unsigned int /*argsize*/) {
3798 	ARG_ITEM_FROM_PTR(item);
3799 	ARG_UINT16(gumpshape);
3800 	if (!item) return 0;
3801 
3802 	item->openGump(gumpshape);
3803 	return 0;
3804 }
3805 
I_closeGump(const uint8 * args,unsigned int)3806 uint32 Item::I_closeGump(const uint8 *args, unsigned int /*argsize*/) {
3807 	ARG_ITEM_FROM_PTR(item);
3808 	if (!item) return 0;
3809 
3810 	item->closeGump();
3811 	return 0;
3812 }
3813 
I_guardianBark(const uint8 * args,unsigned int)3814 uint32 Item::I_guardianBark(const uint8 *args, unsigned int /*argsize*/) {
3815 	ARG_ITEM_FROM_PTR(item);
3816 	ARG_UINT16(num);
3817 	if (!item) return 0;
3818 
3819 	assert(GAME_IS_U8);
3820 
3821 	return item->callUsecodeEvent_guardianBark(num);
3822 }
3823 
I_getSurfaceWeight(const uint8 * args,unsigned int)3824 uint32 Item::I_getSurfaceWeight(const uint8 *args, unsigned int /*argsize*/) {
3825 	ARG_ITEM_FROM_PTR(item);
3826 	if (!item) return 0;
3827 
3828 	UCList uclist(2);
3829 	LOOPSCRIPT(script, LS_TOKEN_TRUE);
3830 	World *world = World::get_instance();
3831 	world->getCurrentMap()->surfaceSearch(&uclist, script, sizeof(script),
3832 	                                      item, true, false, true);
3833 
3834 	uint32 weight = 0;
3835 	for (uint32 i = 0; i < uclist.getSize(); i++) {
3836 		Item *other = getItem(uclist.getuint16(i));
3837 		if (!other) continue;
3838 		weight += other->getTotalWeight();
3839 	}
3840 
3841 	return weight;
3842 }
3843 
I_isExplosive(const uint8 * args,unsigned int)3844 uint32 Item::I_isExplosive(const uint8 *args, unsigned int /*argsize*/) {
3845 	ARG_ITEM_FROM_PTR(item);
3846 	assert(GAME_IS_U8); // explode bit has different meaning in Cru.
3847 	if (!item) return 0;
3848 	return item->getShapeInfo()->is_u8_explode() ? 1 : 0;
3849 }
3850 
I_receiveHit(const uint8 * args,unsigned int)3851 uint32 Item::I_receiveHit(const uint8 *args, unsigned int /*argsize*/) {
3852 	ARG_ITEM_FROM_PTR(item);
3853 	ARG_UINT16(other);
3854 	ARG_SINT16(dir);
3855 	ARG_SINT16(damage); // force of the hit
3856 	ARG_UINT16(type); // hit type
3857 	if (!item) return 0;
3858 
3859 	item->receiveHit(other, Direction_FromUsecodeDir(dir), damage, type);
3860 
3861 	return 0;
3862 }
3863 
I_explode(const uint8 * args,unsigned int argsize)3864 uint32 Item::I_explode(const uint8 *args, unsigned int argsize) {
3865 	ARG_ITEM_FROM_PTR(item);
3866 	if (!item) return 0;
3867 
3868 	int exptype = 0;
3869 	bool destroy_item = true;
3870 	if (argsize > 4) {
3871 		ARG_UINT16(etype)
3872 		ARG_UINT16(destroy)
3873 		exptype = etype;
3874 		destroy_item = (destroy != 0);
3875 	}
3876 
3877 	item->explode(exptype, destroy_item);
3878 	return 0;
3879 }
3880 
I_igniteChaos(const uint8 * args,unsigned int)3881 uint32 Item::I_igniteChaos(const uint8 *args, unsigned int /*argsize*/) {
3882 	ARG_UINT16(x);
3883 	ARG_UINT16(y);
3884 	ARG_NULL8(); // z, unused
3885 
3886 	assert(GAME_IS_U8);
3887 
3888 	UCList itemlist(2);
3889 	LOOPSCRIPT(script, LS_SHAPE_EQUAL(592)); // all oilflasks (CONSTANT!)
3890 	CurrentMap *currentmap = World::get_instance()->getCurrentMap();
3891 	currentmap->areaSearch(&itemlist, script, sizeof(script), 0,
3892 	                       160, false, x, y); //! CHECKME: 160?
3893 
3894 	for (unsigned int i = 0; i < itemlist.getSize(); ++i) {
3895 		Item *item = getItem(itemlist.getuint16(i));
3896 		if (!item) continue;
3897 		item->use();
3898 	}
3899 
3900 	return 0;
3901 }
3902 
I_canReach(const uint8 * args,unsigned int)3903 uint32 Item::I_canReach(const uint8 *args, unsigned int /*argsize*/) {
3904 	ARG_ITEM_FROM_PTR(item);
3905 	ARG_ITEM_FROM_ID(other);
3906 	ARG_SINT16(range);
3907 
3908 	// TODO: add cheat to make this always return 1
3909 
3910 	if (item && other && item->canReach(other, range))
3911 		return 1;
3912 	else
3913 		return 0;
3914 }
3915 
3916 
I_getRange(const uint8 * args,unsigned int)3917 uint32 Item::I_getRange(const uint8 *args, unsigned int /*argsize*/) {
3918 	ARG_ITEM_FROM_PTR(item);
3919 	ARG_ITEM_FROM_ID(other);
3920 	if (!item) return 0;
3921 	if (!other) return 0;
3922 
3923 	assert(GAME_IS_U8);
3924 
3925 	return item->getRange(*other);
3926 }
3927 
I_getRangeIfVisible(const uint8 * args,unsigned int)3928 uint32 Item::I_getRangeIfVisible(const uint8 *args, unsigned int /*argsize*/) {
3929 	// TODO: This is not exactly the same as the implementation in Cruasder,
3930 	// but it should work?
3931 	ARG_ITEM_FROM_PTR(item);
3932 	ARG_ITEM_FROM_ID(other);
3933 
3934 	if (!item || !other)
3935 		return 0;
3936 
3937 	// Somewhat arbitrary maths in here to replicate Crusader behavior.
3938 	int range = item->getRangeIfVisible(*other) / 32;
3939 	if ((range & 0xf) != 0)
3940 		range++;
3941 
3942 	if (range <= 48) {
3943 		return range;
3944 	}
3945 	return 0;
3946 }
3947 
I_isCrusTypeNPC(const uint8 * args,unsigned int)3948 uint32 Item::I_isCrusTypeNPC(const uint8 *args, unsigned int /*argsize*/) {
3949 	ARG_UINT16(sh);
3950 
3951 	if (sh == 0x7FE) return 1;
3952 
3953 	const ShapeInfo *info;
3954 	info = GameData::get_instance()->getMainShapes()->getShapeInfo(sh);
3955 	if (!info) return 0;
3956 
3957 	if (info->_flags & ShapeInfo::SI_CRU_NPC)
3958 		return 1;
3959 	else
3960 		return 0;
3961 }
3962 
I_setBroken(const uint8 * args,unsigned int)3963 uint32 Item::I_setBroken(const uint8 *args, unsigned int /*argsize*/) {
3964 	ARG_ITEM_FROM_PTR(item);
3965 	if (!item)
3966 		return 0;
3967 
3968 	World::get_instance()->getCurrentMap()->removeTargetItem(item);
3969 	item->setFlag(FLG_BROKEN);
3970 	return 0;
3971 }
3972 
I_inFastArea(const uint8 * args,unsigned int)3973 uint32 Item::I_inFastArea(const uint8 *args, unsigned int /*argsize*/) {
3974 	ARG_ITEM_FROM_PTR(item);
3975 
3976 	if (!item)
3977 		return 0;
3978 
3979 	return item->hasFlags(FLG_FASTAREA);
3980 }
3981 
I_isPartlyOnScreen(const uint8 * args,unsigned int)3982 uint32 Item::I_isPartlyOnScreen(const uint8 *args, unsigned int /*argsize*/) {
3983 	ARG_ITEM_FROM_PTR(item);
3984 	if (!item) return 0;
3985 
3986 	return item->isPartlyOnScreen();
3987 }
3988 
I_fireWeapon(const uint8 * args,unsigned int)3989 uint32 Item::I_fireWeapon(const uint8 *args, unsigned int /*argsize*/) {
3990 	ARG_ITEM_FROM_PTR(item);
3991 	ARG_SINT16(x);
3992 	ARG_SINT16(y);
3993 	ARG_SINT16(z);
3994 	ARG_UINT16(dir);
3995 	ARG_UINT16(firetype);
3996 	ARG_UINT16(findtarget);
3997 
3998 	if (!item) return 0;
3999 
4000 	return item->fireWeapon(x * 2, y * 2, z, Direction_FromUsecodeDir(dir), firetype, findtarget != 0);
4001 }
4002 
I_fireDistance(const uint8 * args,unsigned int)4003 uint32 Item::I_fireDistance(const uint8 *args, unsigned int /*argsize*/) {
4004 	ARG_ITEM_FROM_PTR(item);
4005 	ARG_UINT16(other);
4006 	ARG_SINT16(dir);
4007 	ARG_SINT16(xoff);
4008 	ARG_SINT16(yoff);
4009 	ARG_SINT16(zoff);
4010 
4011 	Item *otheritem = getItem(other);
4012 
4013 	if (!item || !otheritem) return 0;
4014 
4015 	return item->fireDistance(otheritem, Direction_FromUsecodeDir(dir), xoff * 2, yoff * 2, zoff);
4016 }
4017 
4018 } // End of namespace Ultima8
4019 } // End of namespace Ultima
4020