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/world/actors/main_actor.h"
24 #include "ultima/ultima8/world/teleport_egg.h"
25 #include "ultima/ultima8/world/current_map.h"
26 #include "ultima/ultima8/kernel/kernel.h"
27 #include "ultima/ultima8/world/actors/teleport_to_egg_process.h"
28 #include "ultima/ultima8/world/target_reticle_process.h"
29 #include "ultima/ultima8/world/camera_process.h"
30 #include "ultima/ultima8/graphics/shape_info.h"
31 #include "ultima/ultima8/ultima8.h"
32 #include "ultima/ultima8/world/actors/avatar_death_process.h"
33 #include "ultima/ultima8/kernel/delay_process.h"
34 #include "ultima/ultima8/games/game_data.h"
35 #include "ultima/ultima8/graphics/anim_dat.h"
36 #include "ultima/ultima8/graphics/wpn_ovlay_dat.h"
37 #include "ultima/ultima8/graphics/main_shape_archive.h"
38 #include "ultima/ultima8/gumps/cru_pickup_area_gump.h"
39 #include "ultima/ultima8/audio/audio_process.h"
40 #include "ultima/ultima8/world/world.h"
41 #include "ultima/ultima8/world/get_object.h"
42 #include "ultima/ultima8/usecode/uc_list.h"
43 #include "ultima/ultima8/usecode/uc_machine.h"
44 #include "ultima/ultima8/world/loop_script.h"
45 #include "ultima/ultima8/world/fire_type.h"
46 #include "ultima/ultima8/world/sprite_process.h"
47 #include "ultima/ultima8/world/actors/avatar_gravity_process.h"
48 #include "ultima/ultima8/world/actors/actor_anim_process.h"
49 #include "ultima/ultima8/audio/music_process.h"
50 #include "ultima/ultima8/world/actors/anim_action.h"
51 
52 namespace Ultima {
53 namespace Ultima8 {
54 
55 DEFINE_RUNTIME_CLASSTYPE_CODE(MainActor)
56 
57 ShapeInfo *MainActor::_kneelingShapeInfo = nullptr;
58 
MainActor()59 MainActor::MainActor() : _justTeleported(false), _accumStr(0), _accumDex(0),
60 	_accumInt(0), _cruBatteryType(ChemicalBattery), _keycards(0),
61 	_activeInvItem(0), _shieldType(0), _shieldSpriteProc(0) {
62 }
63 
~MainActor()64 MainActor::~MainActor() {
65 	if (_kneelingShapeInfo) {
66 		delete _kneelingShapeInfo;
67 		_kneelingShapeInfo = nullptr;
68 	}
69 }
70 
ensureGravityProcess()71 GravityProcess *MainActor::ensureGravityProcess() {
72 	AvatarGravityProcess *p;
73 	if (_gravityPid) {
74 		p = dynamic_cast<AvatarGravityProcess *>(
75 		        Kernel::get_instance()->getProcess(_gravityPid));
76 	} else {
77 		p = new AvatarGravityProcess(this, 0);
78 		Kernel::get_instance()->addProcess(p);
79 		p->init();
80 	}
81 	assert(p);
82 	return p;
83 }
84 
CanAddItem(Item * item,bool checkwghtvol)85 bool MainActor::CanAddItem(Item *item, bool checkwghtvol) {
86 
87 	if (!Actor::CanAddItem(item, checkwghtvol)) return false;
88 	if (item->getParent() == _objId) return true; // already in here
89 
90 	// now check 'equipment slots'
91 	// we can have one item of each equipment type, plus one backpack
92 
93 	if (GAME_IS_U8) {
94 		const unsigned int backpack_shape = 529; //!! *cough* constant
95 		uint32 equiptype = item->getShapeInfo()->_equipType;
96 		bool backpack = (item->getShape() == backpack_shape);
97 
98 		// valid item type?
99 		if (equiptype == ShapeInfo::SE_NONE && !backpack) return false;
100 
101 		Std::list<Item *>::iterator iter;
102 		for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
103 			uint32 cet = (*iter)->getShapeInfo()->_equipType;
104 			bool cbackpack = ((*iter)->getShape() == backpack_shape);
105 
106 			// already have an item with the same equiptype
107 			if (cet == equiptype || (cbackpack && backpack)) return false;
108 		}
109 	} else if (GAME_IS_CRUSADER) {
110 		// TODO: Enforce the number of slots by family here (weapon / ammo / other)
111 		// For now just enforce no limit.
112 		return true;
113 	}
114 
115 	return true;
116 }
117 
addItem(Item * item,bool checkwghtvol)118 bool MainActor::addItem(Item *item, bool checkwghtvol) {
119 	if (!Actor::addItem(item, checkwghtvol)) return false;
120 
121 	item->setFlag(FLG_EQUIPPED);
122 
123 	uint32 equiptype = item->getShapeInfo()->_equipType;
124 	item->setZ(equiptype);
125 
126 	return true;
127 }
128 
addItemCru(Item * item,bool showtoast)129 int16 MainActor::addItemCru(Item *item, bool showtoast) {
130 	// This code is a little ugly, it's a somewhat close
131 	// re-implementation of the original and could do
132 	// with some cleanup.
133 
134 	if (!item || !item->getShape())
135 		return 0;
136 
137 	int shapeno = item->getShape();
138 	int32 x, y, z;
139 	getLocation(x, y, z);
140 
141 	CruPickupAreaGump *pickupArea = CruPickupAreaGump::get_instance();
142 	assert(pickupArea);
143 
144 	if (shapeno == 0x4ed) {
145 		Item *credits = getFirstItemWithShape(shapeno, true);
146 		if (credits) {
147 			uint16 q = item->getQuality();
148 			uint32 newq = credits->getQuality() + q;
149 			if (newq > 64000)
150 				newq = 64000;
151 			credits->setQuality(newq);
152 			credits->callUsecodeEvent_combine();
153 			if (showtoast)
154 				pickupArea->addPickup(item, true);
155 			item->destroy();
156 		} else {
157 			item->setFrame(0);
158 			item->moveToContainer(this);
159 			if (!_activeInvItem)
160 				_activeInvItem = item->getObjId();
161 			if (showtoast)
162 				pickupArea->addPickup(item, true);
163 		}
164 		return 1;
165 	}
166 
167 	switch (static_cast<ShapeInfo::SFamily>(item->getShapeInfo()->_family)) {
168 	case ShapeInfo::SF_CRUWEAPON: {	// 0xa
169 		Item *weapon = getFirstItemWithShape(shapeno, true);
170 		if (!weapon) {
171 			// New weapon. Add it.
172 			const WeaponInfo *winfo = item->getShapeInfo()->_weaponInfo;
173 			assert(winfo);
174 			if (winfo->_ammoType == 0) {
175 				item->setQuality(0);
176 				item->callUsecodeEvent_combine();
177 			} else {
178 				item->setQuality(winfo->_clipSize);
179 			}
180 			item->setLocation(x, y, z);
181 			item->moveToContainer(this);
182 			if (!_activeWeapon)
183 				_activeWeapon = item->getObjId();
184 			if (showtoast)
185 				pickupArea->addPickup(item, false);
186 		}
187 		break;
188 	}
189 	case ShapeInfo::SF_CRUAMMO:	{	// 0xb
190 		Item *ammo = getFirstItemWithShape(shapeno, true);
191 		if (!ammo) {
192 			// don't have this ammo yet, add it
193 			item->setQuality(1);
194 			item->callUsecodeEvent_combine();
195 			item->moveToContainer(this);
196 			if (showtoast)
197 				pickupArea->addPickup(item, false);
198 			return 1;
199 		} else {
200 			// already have this, add some ammo.
201 			uint16 q = ammo->getQuality();
202 			if (q < 0x14) {
203 				ammo->setQuality(q + 1);
204 				ammo->callUsecodeEvent_combine();
205 				if (showtoast)
206 					pickupArea->addPickup(item, false);
207 				item->destroy();
208 				return 1;
209 			}
210 		}
211 		break;
212 	}
213 	case ShapeInfo::SF_CRUBOMB:		// 0xc
214 	case ShapeInfo::SF_CRUINVITEM:	// 0xd
215 		if (shapeno == 0x111) {
216 			addKeycard(item->getQuality() & 0xff);
217 			if (showtoast) {
218 				pickupArea->addPickup(item, false);
219 			}
220 			item->destroy();
221 			return 1;
222 		} else if ((shapeno == 0x3a2) || (shapeno == 0x3a3) || (shapeno == 0x3a4)) {
223 			// Batteries
224 			if (showtoast)
225 				pickupArea->addPickup(item, false);
226 			item->destroy();
227 			int plusenergy = 0;
228 			CruBatteryType oldbattery = _cruBatteryType;
229 			if (shapeno == 0x3a2) {
230 				if (oldbattery == NoBattery) {
231 					setBatteryType(ChemicalBattery);
232 				} else {
233 					plusenergy = 0x9c4;
234 				}
235 			} else if (shapeno == 0x3a4) {
236 				if (oldbattery < FissionBattery) {
237 					setBatteryType(FissionBattery);
238 				} else {
239 					plusenergy = 5000;
240 				}
241 			} else if (shapeno == 0x3a3) {
242 				if (oldbattery < FusionBattery) {
243 					setBatteryType(FusionBattery);
244 				} else {
245 					plusenergy = 10000;
246 				}
247 			}
248 			if (plusenergy) {
249 				int newenergy = getMana() + plusenergy;
250 				if (newenergy > getMaxEnergy())
251 					newenergy = getMaxEnergy();
252 				setMana(newenergy);
253 			}
254 			return 1;
255 		} else {
256 			Item *existing = getFirstItemWithShape(shapeno, true);
257 			if (!existing) {
258 				// Shields. Note, these are the same in Remorse and Regret.
259 				if ((shapeno == 0x52e) || (shapeno == 0x52f) || (shapeno == 0x530)) {
260 					uint16 shieldtype;
261 					switch (shapeno) {
262 						default:
263 						case 0x52e:
264 							shieldtype = 1;
265 							break;
266 						case 0x52f:
267 							shieldtype = 2;
268 							break;
269 						case 0x530:
270 							shieldtype = 3;
271 							break;
272 					}
273 					if (_shieldType < shieldtype) {
274 						_shieldType = shieldtype;
275 					}
276 					if (showtoast)
277 						pickupArea->addPickup(item, false);
278 					item->destroy();
279 					return 1;
280 				} else {
281 					item->setFrame(0);
282 					item->setQuality(1);
283 					item->callUsecodeEvent_combine();
284 					item->moveToContainer(this);
285 					if (showtoast)
286 						pickupArea->addPickup(item, true);
287 					if (!_activeInvItem)
288 						_activeInvItem = item->getObjId();
289 					return 1;
290 				}
291 			} else {
292 				// Already have this item..
293 				if ((shapeno == 0x52e) || (shapeno == 0x52f) || (shapeno == 0x530)) {
294 					// shields, already have one, destroy the new one.
295 					item->destroy();
296 					return 1;
297 				} else if (shapeno == 0x560) {
298 					uint16 q = existing->getQuality();
299 					if (q < 0x14) {
300 						existing->setQuality(q + 1);
301 						existing->callUsecodeEvent_combine();
302 						item->setQuality(1); // Count of picked up item is always 1
303 						if (showtoast)
304 							pickupArea->addPickup(item, true);
305 						item->destroy();
306 						return 1;
307 					}
308 				} else {
309 					uint16 q = existing->getQuality();
310 					if (q < 10) {
311 						existing->setQuality(q + 1);
312 						existing->callUsecodeEvent_combine();
313 						item->setQuality(1); // Count of picked up item is always 1
314 						if (showtoast)
315 							pickupArea->addPickup(item, true);
316 						item->destroy();
317 						return 1;
318 					}
319 				}
320 			}
321 		}
322 		break;
323 	default:
324 		break;
325 	}
326 
327 	return 0;
328 }
329 
removeItemCru(Item * item)330 bool MainActor::removeItemCru(Item *item) {
331 	warning("TODO: Implement MainActor::removeItemCru");
332 	return false;
333 }
334 
getShapeInfoFromGameInstance() const335 const ShapeInfo *MainActor::getShapeInfoFromGameInstance() const {
336 	const ShapeInfo *info = Item::getShapeInfoFromGameInstance();
337 
338 	if (!(_actorFlags & ACT_KNEELING) || GAME_IS_U8)
339 		return info;
340 
341 	// When kneeling in Crusader, return a modified shape with a lower height.
342 	if (!_kneelingShapeInfo) {
343 		_kneelingShapeInfo = new ShapeInfo();
344 		// Not great coupling here, we know most fields don't need filling out..
345 		_kneelingShapeInfo->_flags = info->_flags;
346 		_kneelingShapeInfo->_x = info->_x;
347 		_kneelingShapeInfo->_y = info->_y;
348 		_kneelingShapeInfo->_weight = info->_weight;
349 		_kneelingShapeInfo->_volume = info->_volume;
350 		_kneelingShapeInfo->_family = info->_family;
351 		_kneelingShapeInfo->_z = info->_z - 4;
352 	}
353 
354 	return _kneelingShapeInfo;
355 }
356 
move(int32 x,int32 y,int32 z)357 void MainActor::move(int32 x, int32 y, int32 z) {
358 	Actor::move(x, y, z);
359 	if (_shieldSpriteProc != 0) {
360 		SpriteProcess *sprite = dynamic_cast<SpriteProcess *>(
361 			Kernel::get_instance()->getProcess(_shieldSpriteProc));
362 		if (sprite) {
363 			sprite->move(x, y, z);
364 		}
365 	}
366 }
367 
teleport(int mapNum,int32 x,int32 y,int32 z)368 void MainActor::teleport(int mapNum, int32 x, int32 y, int32 z) {
369 	World *world = World::get_instance();
370 
371 	uint16 oldmap = getMapNum();
372 
373 	// (attempt to) load the new map
374 	if (!world->switchMap(mapNum)) {
375 		perr << "MainActor::teleport(): switchMap(" << mapNum << ") failed!" << Std::endl;
376 		return;
377 	}
378 
379 	Actor::teleport(mapNum, x, y, z);
380 
381 	if (GAME_IS_CRUSADER && (x || y) && oldmap == mapNum) {
382 		// Keep the camera on the avatar (the snap process will update on next move)
383 		CameraProcess::GetCameraProcess()->moveToLocation(x, y, z);
384 	}
385 
386 	_justTeleported = true;
387 }
388 
389 // teleport to TeleportEgg
390 // NB: be careful when calling this from a process, as it might kill
391 // all running processes
teleport(int mapNum,int teleport_id)392 void MainActor::teleport(int mapNum, int teleport_id) {
393 	int oldmap = getMapNum();
394 	int32 oldx, oldy, oldz;
395 	getLocation(oldx, oldy, oldz);
396 
397 	World *world = World::get_instance();
398 	CurrentMap *currentmap = world->getCurrentMap();
399 
400 	pout << "MainActor::teleport(): teleporting to map " << mapNum
401 	     << ", egg " << teleport_id << Std::endl;
402 
403 	setMapNum(mapNum);
404 
405 	// (attempt to) load the new map
406 	if (!world->switchMap(mapNum)) {
407 		perr << "MainActor::teleport(): switchMap() failed!" << Std::endl;
408 		setMapNum(oldmap);
409 		return;
410 	}
411 
412 	// find destination
413 	TeleportEgg *egg = currentmap->findDestination(teleport_id);
414 	if (!egg) {
415 		perr << "MainActor::teleport(): destination egg not found!"
416 		     << Std::endl;
417 		teleport(oldmap, oldx, oldy, oldz);
418 		return;
419 	}
420 	int32 xv, yv, zv;
421 	egg->getLocation(xv, yv, zv);
422 
423 	pout << "Found destination: " << xv << "," << yv << "," << zv << Std::endl;
424 	egg->dumpInfo();
425 
426 	if (GAME_IS_CRUSADER) {
427 		// Keep the camera on the avatar (the snap process will update on next move)
428 		// We don't add a new camera process here, as that would update the fast area
429 		// before the cachein calls above have run.
430 		CameraProcess::GetCameraProcess()->moveToLocation(xv, yv, zv);
431 	}
432 
433 	Actor::teleport(mapNum, xv, yv, zv);
434 
435 	_justTeleported = true;
436 }
437 
getDefenseType() const438 uint16 MainActor::getDefenseType() const {
439 	uint16 type = 0;
440 
441 	Std::list<Item *>::const_iterator iter;
442 	for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
443 		uint32 frameNum = (*iter)->getFrame();
444 		const ShapeInfo *si = (*iter)->getShapeInfo();
445 		if (si->_armourInfo) {
446 			type |= si->_armourInfo[frameNum]._defenseType;
447 		}
448 	}
449 
450 	return type;
451 }
452 
getArmourClass() const453 uint32 MainActor::getArmourClass() const {
454 	uint32 armour = 0;
455 
456 	Std::list<Item *>::const_iterator iter;
457 	for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
458 		uint32 frameNum = (*iter)->getFrame();
459 		const ShapeInfo *si = (*iter)->getShapeInfo();
460 		if (si->_armourInfo) {
461 			armour += si->_armourInfo[frameNum]._armourClass;
462 		}
463 		if (si->_weaponInfo) {
464 			armour += si->_weaponInfo->_armourBonus;
465 		}
466 	}
467 
468 	return armour;
469 }
470 
getDefendingDex() const471 int16 MainActor::getDefendingDex() const {
472 	int16 dex = getDex();
473 
474 	Item *weapon = getItem(getEquip(ShapeInfo::SE_WEAPON));
475 	if (weapon) {
476 		const ShapeInfo *si = weapon->getShapeInfo();
477 		assert(si->_weaponInfo);
478 		dex += si->_weaponInfo->_dexDefendBonus;
479 	}
480 
481 	if (dex <= 0) dex = 1;
482 
483 	return dex;
484 }
485 
getAttackingDex() const486 int16 MainActor::getAttackingDex() const {
487 	int16 dex = getDex();
488 
489 	Item *weapon = getItem(getEquip(ShapeInfo::SE_WEAPON));
490 	if (weapon) {
491 		const ShapeInfo *si = weapon->getShapeInfo();
492 		assert(si->_weaponInfo);
493 		dex += si->_weaponInfo->_dexAttackBonus;
494 	}
495 
496 	return dex;
497 }
498 
getDamageType() const499 uint16 MainActor::getDamageType() const {
500 	Item *weapon = getItem(getEquip(ShapeInfo::SE_WEAPON));
501 
502 	if (weapon) {
503 		// weapon equipped?
504 
505 		const ShapeInfo *si = weapon->getShapeInfo();
506 		assert(si->_weaponInfo);
507 
508 		return si->_weaponInfo->_damageType;
509 	}
510 
511 	return Actor::getDamageType();
512 }
513 
getDamageAmount() const514 int MainActor::getDamageAmount() const {
515 	int damage = 0;
516 
517 	if (getLastAnim() == Animation::kick) {
518 		// kick
519 
520 		int kick_bonus = 0;
521 		Item *legs = getItem(getEquip(ShapeInfo::SE_LEGS));
522 		if (legs) {
523 			const ShapeInfo *si = legs->getShapeInfo();
524 			assert(si->_armourInfo);
525 			kick_bonus = si->_armourInfo[legs->getFrame()]._kickAttackBonus;
526 		}
527 
528 		damage = (getRandom() % (getStr() / 2 + 1)) + kick_bonus;
529 
530 		return damage;
531 
532 	}
533 
534 	Item *weapon = getItem(getEquip(ShapeInfo::SE_WEAPON));
535 
536 
537 	if (weapon) {
538 		// weapon equipped?
539 
540 		const ShapeInfo *si = weapon->getShapeInfo();
541 		assert(si->_weaponInfo);
542 
543 		int base = si->_weaponInfo->_baseDamage;
544 		int mod = si->_weaponInfo->_damageModifier;
545 
546 		damage = (getRandom() % (mod + 1)) + base + getStr() / 5;
547 
548 		return damage;
549 	}
550 
551 	// no weapon?
552 
553 	damage = (getRandom() % (getStr() / 2 + 1)) + 1;
554 
555 	return damage;
556 }
557 
setInCombat(int activity)558 void MainActor::setInCombat(int activity) {
559 	setActorFlag(ACT_INCOMBAT);
560 	if (GAME_IS_U8)
561 		MusicProcess::get_instance()->playCombatMusic(98); // CONSTANT!
562 }
563 
clearInCombat()564 void MainActor::clearInCombat() {
565 	clearActorFlag(ACT_INCOMBAT);
566 	if (GAME_IS_U8)
567 		MusicProcess::get_instance()->restoreMusic();
568 }
569 
die(uint16 damageType,uint16 damagePts,Direction srcDir)570 ProcId MainActor::die(uint16 damageType, uint16 damagePts, Direction srcDir) {
571 	ProcId animprocid = Actor::die(damageType, damagePts, srcDir);
572 
573 	Ultima8Engine *app = Ultima8Engine::get_instance();
574 	assert(app);
575 
576 	app->setAvatarInStasis(true);
577 
578 	Process *deathproc = new AvatarDeathProcess();
579 	Kernel::get_instance()->addProcess(deathproc);
580 
581 	Process *delayproc = new DelayProcess(30 * 5); // 5 seconds
582 	Kernel::get_instance()->addProcess(delayproc);
583 
584 	Process *animproc = Kernel::get_instance()->getProcess(animprocid);
585 
586 	if (animproc)
587 		delayproc->waitFor(animproc);
588 
589 	deathproc->waitFor(delayproc);
590 
591 	MusicProcess *music = MusicProcess::get_instance();
592 	if (GAME_IS_U8 && music) {
593 		music->unqueueMusic();
594 		music->playCombatMusic(44); // CONSTANT!!
595 	}
596 
597 	if (GAME_IS_CRUSADER) {
598 		// Force a reticle update
599 		TargetReticleProcess::get_instance()->avatarMoved();
600 	}
601 
602 	return animprocid;
603 }
604 
accumulateStr(int n)605 void MainActor::accumulateStr(int n) {
606 	// already max?
607 	if (_strength == 25) return; //!! constant
608 
609 	_accumStr += n;
610 	if (_accumStr >= 650 || getRandom() % (650 - _accumStr) == 0) { //!! constant
611 		_strength++;
612 		_accumStr = 0;
613 		AudioProcess *audioproc = AudioProcess::get_instance();
614 		if (audioproc) audioproc->playSFX(0x36, 0x60, 1, 0); //constants!!
615 		pout << "Gained _strength!" << Std::endl;
616 	}
617 }
618 
accumulateDex(int n)619 void MainActor::accumulateDex(int n) {
620 	// already max?
621 	if (_dexterity == 25) return; //!! constant
622 
623 	_accumDex += n;
624 	if (_accumDex >= 650 || getRandom() % (650 - _accumDex) == 0) { //!! constant
625 		_dexterity++;
626 		_accumDex = 0;
627 		AudioProcess *audioproc = AudioProcess::get_instance();
628 		if (audioproc) audioproc->playSFX(0x36, 0x60, 1, 0); //constants!!
629 		pout << "Gained _dexterity!" << Std::endl;
630 	}
631 }
632 
accumulateInt(int n)633 void MainActor::accumulateInt(int n) {
634 	// already max?
635 	if (_intelligence == 25) return; //!! constant
636 
637 	_accumInt += n;
638 	if (_accumInt >= 650 || getRandom() % (650 - _accumInt) == 0) { //!! constant
639 		_intelligence++;
640 		_accumInt = 0;
641 		AudioProcess *audioproc = AudioProcess::get_instance();
642 		if (audioproc) audioproc->playSFX(0x36, 0x60, 1, 0); //constants!!
643 		pout << "Gained _intelligence!" << Std::endl;
644 	}
645 }
646 
getWeaponOverlay(const WeaponOverlayFrame * & frame,uint32 & shape)647 void MainActor::getWeaponOverlay(const WeaponOverlayFrame *&frame, uint32 &shape) {
648 	shape = 0;
649 	frame = 0;
650 
651 	if (!isInCombat() && _lastAnim != Animation::unreadyWeapon) return;
652 
653 	uint32 action = AnimDat::getActionNumberForSequence(_lastAnim, this);
654 
655 	ObjId weaponid;
656 	if (GAME_IS_U8)
657 		weaponid = getEquip(ShapeInfo::SE_WEAPON);
658 	else
659 		weaponid = getActiveWeapon();
660 
661 	Item *weapon = getItem(weaponid);
662 	if (!weapon) return;
663 
664 	const ShapeInfo *shapeinfo = weapon->getShapeInfo();
665 	if (!shapeinfo) return;
666 
667 	WeaponInfo *weaponinfo = shapeinfo->_weaponInfo;
668 	if (!weaponinfo) return;
669 
670 	shape = weaponinfo->_overlayShape;
671 
672 	const WpnOvlayDat *wpnovlay = GameData::get_instance()->getWeaponOverlay();
673 	frame = wpnovlay->getOverlayFrame(action, weaponinfo->_overlayType,
674 	                                  _direction, _animFrame);
675 
676 	if (frame == nullptr)
677 		shape = 0;
678 }
679 
getMaxEnergy()680 int16 MainActor::getMaxEnergy() {
681 	switch (_cruBatteryType) {
682 		case ChemicalBattery:
683 			return 0x9c4; // docs say 2500, code says otherwise..
684 		case FissionBattery:
685 			return 5000;
686 		case FusionBattery:
687 			return 10000;
688 		default:
689 			return 0;
690 	}
691 }
692 
hasKeycard(int num) const693 bool MainActor::hasKeycard(int num) const {
694 	if (num > 31)
695 		return 0;
696 
697 	return _keycards & (1 << num);
698 }
699 
addKeycard(int bitno)700 void MainActor::addKeycard(int bitno) {
701 	if (bitno > 31 || bitno < 0)
702 		return;
703 	_keycards |= (1 << bitno);
704 }
705 
getIdOfNextItemInList(const Std::vector<Item * > & items,uint16 current)706 static uint16 getIdOfNextItemInList(const Std::vector<Item *> &items, uint16 current) {
707 	const int n = items.size();
708 	if (n == 0)
709 		return 0;
710 	if (n == 1)
711 		return items[0]->getObjId();
712 
713 	int i;
714 	for (i = 0; i < n; i++) {
715 		if (items[i]->getObjId() == current) {
716 			i++;
717 			break;
718 		}
719 	}
720 	return items[i % n]->getObjId();
721 }
722 
nextWeapon()723 void MainActor::nextWeapon() {
724 	Std::vector<Item *> weapons;
725 	getItemsWithShapeFamily(weapons, ShapeInfo::SF_CRUWEAPON, true);
726 	_activeWeapon = getIdOfNextItemInList(weapons, _activeWeapon);
727 
728 	// Update combat stance in case we switched big/small weapon.
729 	if (_lastAnim == Animation::combatStand) {
730 		if (isBusy()) {
731 			// Corner case - need to stop active "stand"
732 			// animation to correct it.
733 			Kernel::get_instance()->killProcesses(_objId, ActorAnimProcess::ACTOR_ANIM_PROC_TYPE, true);
734 		}
735 		doAnim(Animation::combatStand, dir_current);
736 	}
737 }
738 
nextInvItem()739 void MainActor::nextInvItem() {
740 	Std::vector<Item *> items;
741 	getItemsWithShapeFamily(items, ShapeInfo::SF_CRUINVITEM, true);
742 	getItemsWithShapeFamily(items, ShapeInfo::SF_CRUBOMB, true);
743 	if (GAME_IS_REMORSE) {
744 		Item *credits = getFirstItemWithShape(0x4ed, true);
745 		if (credits)
746 			items.push_back(credits);
747 	}
748 	_activeInvItem = getIdOfNextItemInList(items, _activeInvItem);
749 }
750 
saveData(Common::WriteStream * ws)751 void MainActor::saveData(Common::WriteStream *ws) {
752 	Actor::saveData(ws);
753 	uint8 jt = _justTeleported ? 1 : 0;
754 	ws->writeByte(jt);
755 	ws->writeUint32LE(_accumStr);
756 	ws->writeUint32LE(_accumDex);
757 	ws->writeUint32LE(_accumInt);
758 
759 	if (GAME_IS_CRUSADER) {
760 		ws->writeByte(static_cast<byte>(_cruBatteryType));
761 		ws->writeUint32LE(_keycards);
762 		ws->writeUint16LE(_activeInvItem);
763 		ws->writeUint16LE(_shieldType);
764 		ws->writeUint16LE(_shieldSpriteProc);
765 	}
766 
767 	uint8 namelength = static_cast<uint8>(_name.size());
768 	ws->writeByte(namelength);
769 	for (unsigned int i = 0; i < namelength; ++i)
770 		ws->writeByte(static_cast<uint8>(_name[i]));
771 
772 }
773 
loadData(Common::ReadStream * rs,uint32 version)774 bool MainActor::loadData(Common::ReadStream *rs, uint32 version) {
775 	if (!Actor::loadData(rs, version)) return false;
776 
777 	_justTeleported = (rs->readByte() != 0);
778 	_accumStr = static_cast<int32>(rs->readUint32LE());
779 	_accumDex = static_cast<int32>(rs->readUint32LE());
780 	_accumInt = static_cast<int32>(rs->readUint32LE());
781 
782 	if (GAME_IS_CRUSADER) {
783 		_cruBatteryType = static_cast<CruBatteryType>(rs->readByte());
784 		_keycards = rs->readUint32LE();
785 		_activeInvItem = rs->readUint16LE();
786 		_shieldType = rs->readUint16LE();
787 		_shieldSpriteProc = rs->readUint16LE();
788 	}
789 
790 	uint8 namelength = rs->readByte();
791 	_name.resize(namelength);
792 	for (unsigned int i = 0; i < namelength; ++i)
793 		_name[i] = rs->readByte();
794 
795 	return true;
796 }
797 
I_teleportToEgg(const uint8 * args,unsigned int argsize)798 uint32 MainActor::I_teleportToEgg(const uint8 *args, unsigned int argsize) {
799 	uint16 mapnum;
800 	if (argsize == 6) {
801 		ARG_UINT16(map);
802 		mapnum = map;
803 	} else {
804 		// Crusader teleport intrinsic 096 uses main actor map.
805 		// Intrinsic 079 provides a map argument.
806 		assert(argsize == 4);
807 		MainActor *av = getMainActor();
808 		mapnum = av->getMapNum();
809 	}
810 
811 	ARG_UINT16(teleport_id);
812 	ARG_UINT16(put_in_stasis); // 0/1
813 
814 	return Kernel::get_instance()->addProcess(
815 	           new TeleportToEggProcess(mapnum, teleport_id));
816 }
817 
I_accumulateStrength(const uint8 * args,unsigned int)818 uint32 MainActor::I_accumulateStrength(const uint8 *args,
819 									   unsigned int /*argsize*/) {
820 	ARG_SINT16(n);
821 	MainActor *av = getMainActor();
822 	av->accumulateStr(n);
823 
824 	return 0;
825 }
826 
I_accumulateDexterity(const uint8 * args,unsigned int)827 uint32 MainActor::I_accumulateDexterity(const uint8 *args,
828 										unsigned int /*argsize*/) {
829 	ARG_SINT16(n);
830 	MainActor *av = getMainActor();
831 	av->accumulateDex(n);
832 
833 	return 0;
834 }
835 
I_accumulateIntelligence(const uint8 * args,unsigned int)836 uint32 MainActor::I_accumulateIntelligence(const uint8 *args,
837 		unsigned int /*argsize*/) {
838 	ARG_SINT16(n);
839 	MainActor *av = getMainActor();
840 	av->accumulateInt(n);
841 
842 	return 0;
843 }
844 
I_clrAvatarInCombat(const uint8 *,unsigned int)845 uint32 MainActor::I_clrAvatarInCombat(const uint8 * /*args*/,
846 									  unsigned int /*argsize*/) {
847 	MainActor *av = getMainActor();
848 	av->clearInCombat();
849 
850 	return 0;
851 }
852 
I_setAvatarInCombat(const uint8 *,unsigned int)853 uint32 MainActor::I_setAvatarInCombat(const uint8 * /*args*/,
854 									  unsigned int /*argsize*/) {
855 	MainActor *av = getMainActor();
856 	// Note: only happens in U8, so activity num is not important.
857 	av->setInCombat(0);
858 
859 	return 0;
860 }
861 
I_isAvatarInCombat(const uint8 *,unsigned int)862 uint32 MainActor::I_isAvatarInCombat(const uint8 * /*args*/,
863 									 unsigned int /*argsize*/) {
864 	MainActor *av = getMainActor();
865 	if (av->isInCombat())
866 		return 1;
867 	else
868 		return 0;
869 }
870 
I_getMaxEnergy(const uint8 * args,unsigned int)871 uint32 MainActor::I_getMaxEnergy(const uint8 *args,
872 								 unsigned int /*argsize*/) {
873 	ARG_ACTOR_FROM_PTR(actor);
874 	MainActor *av = getMainActor();
875 	if (!av || actor != av) {
876 		return 0;
877 	}
878 	return av->getMaxEnergy();
879 }
880 
I_hasKeycard(const uint8 * args,unsigned int)881 uint32 MainActor::I_hasKeycard(const uint8 *args,
882 								 unsigned int /*argsize*/) {
883 	ARG_UINT16(num);
884 	MainActor *av = getMainActor();
885 	if (!av)
886 		return 0;
887 	return av->hasKeycard(num);
888 }
889 
I_clrKeycards(const uint8 * args,unsigned int)890 uint32 MainActor::I_clrKeycards(const uint8 *args,
891 								 unsigned int /*argsize*/) {
892 	MainActor *av = getMainActor();
893 	if (!av)
894 		return 0;
895 	av->clrKeycards();
896 	return 0;
897 }
898 
I_addItemCru(const uint8 * args,unsigned int)899 uint32 MainActor::I_addItemCru(const uint8 *args,
900 								 unsigned int /*argsize*/) {
901 	MainActor *av = getMainActor();
902 	ARG_ITEM_FROM_ID(item);
903 	ARG_UINT16(showtoast);
904 
905 	if (!av || !item)
906 		return 0;
907 
908 	if (av->addItemCru(item, showtoast != 0))
909 		return 1;
910 
911 	return 0;
912 }
913 
I_getNumberOfCredits(const uint8 * args,unsigned int)914 uint32 MainActor::I_getNumberOfCredits(const uint8 *args,
915 									   unsigned int /*argsize*/) {
916 	MainActor *av = getMainActor();
917 	if (av) {
918 		Item *item = av->getFirstItemWithShape(0x4ed, true);
919 		if (item)
920 			return item->getQuality();
921 	}
922 	return 0;
923 }
924 
I_switchMap(const uint8 * args,unsigned int)925 uint32 MainActor::I_switchMap(const uint8 *args,
926 									   unsigned int /*argsize*/) {
927 	ARG_UINT16(mapnum);
928 	MainActor *av = getMainActor();
929 	if (av) {
930 		av->teleport(mapnum, 0x1e); // CONSTANT
931 	}
932 	return 0;
933 }
934 
I_removeItemCru(const uint8 * args,unsigned int)935 uint32 MainActor::I_removeItemCru(const uint8 *args,
936 								 unsigned int /*argsize*/) {
937 	MainActor *av = getMainActor();
938 	ARG_ITEM_FROM_ID(item);
939 
940 	if (!av || !item)
941 		return 0;
942 
943 	if (av->removeItemCru(item))
944 		return 1;
945 
946 	return 0;
947 }
948 
949 
useInventoryItem(uint32 shapenum)950 void MainActor::useInventoryItem(uint32 shapenum) {
951 	Item *item = getFirstItemWithShape(shapenum, true);
952 	useInventoryItem(item);
953 }
954 
useInventoryItem(Item * item)955 void MainActor::useInventoryItem(Item *item) {
956 	if (!item)
957 		return;
958 	if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
959 		pout << "Can't use item: avatarInStasis" << Std::endl;
960 		return;
961 	}
962 	const int32 shapenum = item->getShape();
963 	if (shapenum == 0x4ed && GAME_IS_CRUSADER) {
964 		// Do nothing for Credits
965 		return;
966 	}
967 	item->callUsecodeEvent_use();
968 
969 	// 0x4d4 = datalink, 0x52d = scanner, 0x52e = ionic,
970 	// 0x52f = plasma, 0x530 = graviton
971 	// Note: These are the same in Remorse and Regret.
972 	if (GAME_IS_CRUSADER && (shapenum != 0x4d4 && shapenum != 0x52d &&
973 							 shapenum != 0x530 && shapenum != 0x52f &&
974 							 shapenum != 0x52e)) {
975 		uint16 q = item->getQuality();
976 		item->setQuality(q - 1);
977 		item->callUsecodeEvent_combine();
978 		q = item->getQuality();
979 		if (q == 0) {
980 			const ObjId id = item->getObjId();
981 			item->destroy();
982 			if (id == _activeInvItem)
983 				nextInvItem();
984 		}
985 	}
986 }
987 
receiveShieldHit(int damage,uint16 damage_type)988 int MainActor::receiveShieldHit(int damage, uint16 damage_type) {
989 	uint16 shieldtype = getShieldType();
990 	if (shieldtype == 3) {
991 		shieldtype = 4;
992 	}
993 
994 	const FireType *firetype = GameData::get_instance()->getFireType(damage_type);
995 	int energy = getMana();
996 	Kernel *kernel = Kernel::get_instance();
997 
998 	if (shieldtype && firetype && firetype->getShieldCost() && (firetype->getShieldMask() & shieldtype)
999 			&& firetype->getShieldCost() <= energy) {
1000 		setMana(energy - firetype->getShieldCost());
1001 		damage = 0;
1002 		AudioProcess *audio = AudioProcess::get_instance();
1003 		audio->playSFX(0x48, 0x10, _objId, 1, true);
1004 
1005 		// If there's no active shield sprite, create a new one.
1006 		if (!_shieldSpriteProc || kernel->getProcess(_shieldSpriteProc) == nullptr) {
1007 			// Create the shield damage sprite
1008 			uint16 shieldsprite;
1009 			uint16 shieldstartframe;
1010 			uint16 shieldendframe;
1011 			bool remembersprite;
1012 			int32 x, y, z;
1013 
1014 			switch (shieldtype) {
1015 			case 1:
1016 				shieldsprite = 0x5a9;
1017 				shieldstartframe = 7;
1018 				shieldendframe = 0xd;
1019 				remembersprite = false;
1020 				// NOTE: In the game, this is put in the location of the
1021 				// hit.  For now just put in centre.
1022 				getCentre(x, y, z);
1023 				break;
1024 			case 2:
1025 				shieldsprite = 0x5a9;
1026 				shieldstartframe = 0;
1027 				shieldendframe = 6;
1028 				remembersprite = false;
1029 				getCentre(x, y, z);
1030 				break;
1031 			default:
1032 				shieldsprite = 0x52b;
1033 				shieldstartframe = 0;
1034 				shieldendframe = 8;
1035 				getLocation(x, y, z);
1036 				x += 0x10;
1037 				y += 0x18;
1038 				remembersprite = true;
1039 				break;
1040 			}
1041 			Process *p = new SpriteProcess(shieldsprite, shieldstartframe,
1042 										   shieldendframe, 1, 4, x, y, z);
1043 			kernel->addProcess(p);
1044 			if (remembersprite) {
1045 				_shieldSpriteProc = p->getPid();
1046 			} else {
1047 				_shieldSpriteProc = 0;
1048 			}
1049 		}
1050 	}
1051 	return damage;
1052 }
1053 
detonateBomb()1054 void MainActor::detonateBomb() {
1055 	// search area for shape 0x55F (1375) - DETPAC
1056 	UCList uclist(2);
1057 	LOOPSCRIPT(script, LS_SHAPE_EQUAL(0x55F));
1058 	CurrentMap *currentmap = World::get_instance()->getCurrentMap();
1059 	currentmap->areaSearch(&uclist, script, sizeof(script), nullptr,
1060 							0x800, true, _x, _y);
1061 	for (unsigned int i = 0; i < uclist.getSize(); ++i) {
1062 		Item *founditem = getItem(uclist.getuint16(i));
1063 		if (founditem->hasFlags(FLG_CONTAINED))
1064 			continue;
1065 		founditem->callUsecodeEvent_use();
1066 	}
1067 	return;
1068 }
1069 
1070 
1071 } // End of namespace Ultima8
1072 } // End of namespace Ultima
1073