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  * Based on the original sources
23  *   Faery Tale II -- The Halls of the Dead
24  *   (c) 1993-1996 The Wyrmkeep Entertainment Co.
25  */
26 
27 #include "common/debug.h"
28 
29 #include "saga2/saga2.h"
30 #include "saga2/dispnode.h"
31 #include "saga2/tile.h"
32 #include "saga2/motion.h"
33 #include "saga2/task.h"
34 #include "saga2/assign.h"
35 #include "saga2/setup.h"
36 #include "saga2/stimtype.h"
37 #include "saga2/band.h"
38 #include "saga2/sensor.h"
39 #include "saga2/weapons.h"
40 #include "saga2/localize.h"
41 #include "saga2/intrface.h"
42 #include "saga2/contain.h"
43 #include "saga2/combat.h"
44 
45 //  Include files needed for SAGA script dispatch
46 #include "saga2/script.h"
47 #include "saga2/methods.r"                    // generated by SAGA
48 
49 namespace Saga2 {
50 
51 /* ===================================================================== *
52    Externals
53  * ===================================================================== */
54 
55 extern uint8 identityColors[256];
56 
57 extern hResContext  *listRes;               // object list resource handle
58 
59 extern int16        actorLimboCount;
60 
61 bool unstickObject(GameObject *obj);
62 
63 extern ObjectSoundFXs   *objectSoundFXTable;    // the global object sound effects table
64 
65 #if DEBUG
66 extern bool     massAndBulkCount;
67 #endif
68 
69 /* ===================================================================== *
70    ActorProto member functions
71  * ===================================================================== */
72 
73 //-----------------------------------------------------------------------
74 //	Return a bit mask indicating the properties of this object type
75 
containmentSet(void)76 uint16 ActorProto::containmentSet(void) {
77 	//  All actors may also be weapons (indicating natural attacks)
78 	return ProtoObj::containmentSet() | isWeapon;
79 }
80 
81 //-----------------------------------------------------------------------
82 //	Determine if the specified object can be contained by this object
83 
canContain(ObjectID dObj,ObjectID item)84 bool ActorProto::canContain(ObjectID dObj, ObjectID item) {
85 	assert(isActor(dObj));
86 	assert(isObject(item) || isActor(item));
87 
88 	GameObject      *itemPtr = GameObject::objectAddress(item);
89 
90 	//  Actors can contain any object, except worlds and other actors
91 	return      isObject(item)
92 	            && ((itemPtr->containmentSet() & ProtoObj::isIntangible) == 0
93 	                ||  itemPtr->possessor() == dObj);
94 }
95 
96 //-----------------------------------------------------------------------
97 //	Determine if the specified object can be contained by this object at
98 //	the specified slot
99 
canContainAt(ObjectID dObj,ObjectID item,const TilePoint &)100 bool ActorProto::canContainAt(
101     ObjectID dObj,
102     ObjectID item,
103     const TilePoint &) {
104 	assert(isActor(dObj));
105 	assert(isObject(item) || isActor(item));
106 
107 	GameObject      *itemPtr = GameObject::objectAddress(item);
108 
109 	//  Actors can contain any object, except worlds and other actors
110 	//  REM: must add test to determine if specified slot is valid.
111 	return      isObject(item)
112 	            && ((itemPtr->containmentSet() & ProtoObj::isIntangible) == 0
113 	                ||  itemPtr->possessor() == dObj);
114 }
115 
getWeaponID(void)116 weaponID ActorProto::getWeaponID(void) {
117 	return weaponDamage;
118 }
119 
120 //-----------------------------------------------------------------------
121 //	use this actor
122 
useAction(ObjectID dObj,ObjectID enactor)123 bool ActorProto::useAction(ObjectID dObj, ObjectID enactor) {
124 	assert(isActor(dObj));
125 
126 	Actor       *a = (Actor *)GameObject::objectAddress(dObj);
127 
128 	if (a->isDead())
129 		return ((PhysicalContainerProto *)this)->PhysicalContainerProto::useAction(dObj, enactor);
130 
131 	return false;
132 }
133 
134 //-----------------------------------------------------------------------
135 //	Determine if this actor can be opened
136 
canOpen(ObjectID dObj,ObjectID)137 bool ActorProto::canOpen(ObjectID dObj, ObjectID) {
138 	assert(isActor(dObj));
139 
140 	return ((Actor *)GameObject::objectAddress(dObj))->isDead();
141 }
142 
143 //-----------------------------------------------------------------------
144 //	open this actor
145 
146 //  Kludge!
147 extern int16 openMindType;
148 
openAction(ObjectID dObj,ObjectID)149 bool ActorProto::openAction(ObjectID dObj, ObjectID) {
150 	assert(isActor(dObj));
151 
152 	ContainerNode   *cn;
153 
154 	GameObject *dObjPtr = GameObject::objectAddress(dObj);
155 
156 	assert(!dObjPtr->isOpen() && !dObjPtr->isLocked());
157 
158 	cn = CreateContainerNode(dObj, false, openMindType);
159 	cn->markForShow();                                      //  Deferred open
160 	dObjPtr->_data.objectFlags |= objectOpen;         //  Set open bit;
161 	return true;
162 }
163 
164 //-----------------------------------------------------------------------
165 //	close this actor
166 
closeAction(ObjectID dObj,ObjectID)167 bool ActorProto::closeAction(ObjectID dObj, ObjectID) {
168 	assert(isActor(dObj));
169 
170 	GameObject      *dObjPtr = GameObject::objectAddress(dObj);
171 	ContainerNode   *cn = g_vm->_cnm->find(dObj, ContainerNode::deadType);
172 
173 	assert(dObjPtr->isOpen());
174 	assert(cn);
175 
176 	//  Delete the container (lazy delete)
177 	cn->markForDelete();
178 
179 	//  Clear open bit
180 	dObjPtr->_data.objectFlags &= ~objectOpen;
181 	return true;
182 }
183 
184 //-----------------------------------------------------------------------
185 
strikeAction(ObjectID dObj,ObjectID enactor,ObjectID item)186 bool ActorProto::strikeAction(
187     ObjectID dObj,
188     ObjectID enactor,
189     ObjectID item) {
190 	assert(isActor(dObj));
191 	assert(isActor(enactor));
192 	assert(isObject(item) || isActor(item));
193 
194 	Actor           *a = (Actor *)GameObject::objectAddress(enactor);
195 	ActorAttributes *effStats = a->getStats();
196 	GameObject      *itemPtr = GameObject::objectAddress(item);
197 	ObjectSoundFXs  *soundFXs;
198 	Location        al = Location(a->getLocation(), a->IDParent());
199 
200 	if (itemPtr->acceptStrike(enactor, dObj, effStats->getSkillLevel(skillIDBludgeon)))
201 		return true;
202 
203 	soundFXs = &objectSoundFXTable[soundFXClass];
204 
205 	makeCombatSound(soundFXs->soundFXMissed, al);
206 	return false;
207 }
208 
damageAction(ObjectID dObj,ObjectID enactor,ObjectID target)209 bool ActorProto::damageAction(
210     ObjectID dObj,
211     ObjectID enactor,
212     ObjectID target) {
213 	assert(isActor(dObj));
214 	assert(isActor(enactor));
215 	assert(isObject(target) || isActor(target));
216 
217 	Actor           *a = (Actor *)GameObject::objectAddress(enactor);
218 	ActorAttributes *effStats = a->getStats();
219 	WeaponStuff     *ws = &getWeapon(getWeaponID());
220 	GameObject      *targetPtr = GameObject::objectAddress(target);
221 	uint8           damageSoundID;
222 	Location        al = Location(a->getLocation(), a->IDParent());
223 
224 	damageSoundID = targetPtr->proto()->getDamageSound(
225 	                    objectSoundFXTable[soundFXClass]);
226 
227 
228 	if (damageSoundID != 0)
229 		makeCombatSound(damageSoundID, al);
230 
231 	ws->implement(
232 	    a,
233 	    GameObject::objectAddress(target),
234 	    GameObject::objectAddress(dObj),
235 	    effStats->getSkillLevel(skillIDBrawn));
236 
237 	return true;
238 }
239 
240 //-----------------------------------------------------------------------
241 //	Routine that is called when an object is dragged & dropped
242 //	onto an actor.
243 
acceptDropAction(ObjectID dObj,ObjectID enactor,ObjectID droppedID,int count)244 bool ActorProto::acceptDropAction(
245     ObjectID        dObj,                   // object dropped on
246     ObjectID        enactor,                // person doing dropping
247     ObjectID        droppedID,              // ID of dropped object
248     int             count) {
249 	assert(isActor(dObj));
250 
251 	Actor           *a = (Actor *)GameObject::objectAddress(dObj);
252 	GameObject      *droppedObj = GameObject::objectAddress(droppedID);
253 
254 	if (a->isDead()) {
255 		a->dropInventoryObject(droppedObj, count);
256 		return true;
257 	}
258 
259 	Location        newLoc;
260 	uint16          dropType;
261 	//  For now, we'll just drop the object into the actor's
262 	//  inventory.
263 	//
264 	//  REM: We might want to arrange the inventory item in a
265 	//  semi-sensible way, like putting the new item at the
266 	//  head of the list. (Do this by changing the newLoc coord
267 	//  fields...)
268 	//
269 	//  REM: We might want to ask the object how it feels about
270 	//  being given to an actor...
271 
272 	//  NOTE: Added check so that dropping an object on an actor who
273 	//  already has the object will do nothing.
274 	if (droppedObj->IDParent() == dObj) return true;
275 
276 	dropType = droppedObj->containmentSet();
277 
278 	scriptResult    result;
279 	scriptCallFrame scf;
280 
281 	scf.invokedObject   = dObj;
282 	scf.enactor         = enactor;
283 	scf.directObject    = droppedID;
284 	scf.indirectObject  = dObj;
285 
286 	if (dropType & isIntangible) {
287 		//  Set up the arguments we want to pass to the script
288 
289 		scf.value = droppedObj->proto()->lockType + senseIdeaGreeting;
290 
291 		//  Invoke the script...
292 
293 		if (dropType & isConcept) {
294 			runObjectMethod(dObj, Method_Actor_onTalkTo, scf);
295 		} else if (dropType & isPsych) {
296 			//  What to do???
297 		} else if (dropType & (isSpell | isSkill)) {
298 			//  What to do???
299 			//  Cast the spell on the actor?
300 		}
301 	} else {
302 		scf.value = count;
303 
304 		result = runObjectMethod(dObj, Method_Actor_onReceive, scf);
305 
306 		if (result == scriptResultFinished
307 		        &&  scf.returnVal != actionResultNotDone)
308 			return scf.returnVal == actionResultSuccess;
309 
310 		//  Place the object in the actor's inventory (if possible)
311 		if (!a->placeObject(enactor, droppedID, true, count))
312 			a->dropInventoryObject(droppedObj, count);
313 	}
314 
315 	return true;
316 }
317 
318 //-----------------------------------------------------------------------
319 //	Call the actor's "greet" script.
320 
greetActor(ObjectID dObj,ObjectID enactor)321 bool ActorProto::greetActor(
322     ObjectID        dObj,                   // object dropped on
323     ObjectID        enactor) {              // person doing dropping
324 	assert(isActor(dObj));
325 
326 	scriptCallFrame scf;
327 
328 	scf.invokedObject   = dObj;
329 	scf.enactor         = enactor;
330 	scf.directObject    = Nothing;
331 	scf.indirectObject  = Nothing;
332 	scf.value           = senseIdeaGreeting;
333 
334 	return runObjectMethod(dObj, Method_Actor_onTalkTo, scf);
335 }
336 
337 //-----------------------------------------------------------------------
338 //  cause damage directly
339 
acceptDamageAction(ObjectID dObj,ObjectID enactor,int8 absDamage,effectDamageTypes dType,int8 dice,uint8 sides,int8)340 bool ActorProto::acceptDamageAction(
341     ObjectID            dObj,
342     ObjectID            enactor,
343     int8                absDamage,
344     effectDamageTypes   dType,
345     int8                dice,
346     uint8               sides,
347     int8) {
348 	assert(isActor(dObj));
349 	assert(isObject(enactor) || isActor(enactor));
350 
351 	int8        pdm = 0; //=perDieMod+(resistant ? -2 : 0);
352 	int16       damageScore = 0;
353 	Actor       *a = (Actor *)GameObject::objectAddress(dObj);
354 	Actor       *enactorPtr;
355 	int16       &vitality = a->_effectiveStats.vitality;
356 	bool        resistant = a->resists((effectResistTypes) dType);
357 	PlayerActorID   pID;
358 
359 
360 	if (!a->isImmuneTo((effectImmuneTypes) dType)) {
361 		damageScore = absDamage;
362 
363 		if (dice)
364 			for (int d = 0; d < ABS(dice); d++)
365 				damageScore += (g_vm->_rnd->getRandomNumber(sides - 1) + pdm + 1) * (dice > 0 ? 1 : -1);
366 	}
367 
368 	if (damageScore > 0 && resistant)
369 		damageScore /= 2;
370 
371 	if (damageScore > 0 && isMagicDamage(dType) && makeSavingThrow())
372 		damageScore /= 2;
373 
374 	if (damageScore < 0)
375 		return acceptHealing(dObj, enactor, -damageScore);
376 
377 	//  Apply applicable armor adjustments
378 	if (dType == kDamageImpact
379 	        ||  dType == kDamageSlash
380 	        ||  dType == kDamageProjectile) {
381 		ArmorAttributes     armorAttribs;
382 
383 		a->totalArmorAttributes(armorAttribs);
384 		damageScore /= armorAttribs.damageDivider;
385 		damageScore = MAX(damageScore - armorAttribs.damageAbsorbtion, 0);
386 	}
387 
388 	if (damageScore == 0) return false;
389 
390 	if (isActor(enactor))
391 		enactorPtr = (Actor *)GameObject::objectAddress(enactor);
392 	else {
393 		ObjectID    possessorID;
394 
395 		possessorID = GameObject::objectAddress(enactor)->possessor();
396 		enactorPtr =    possessorID != Nothing
397 		                ? (Actor *)GameObject::objectAddress(possessorID)
398 		                :   NULL;
399 	}
400 
401 	if (vitality > 0) {
402 		Location        al = Location(a->getLocation(), a->IDParent());
403 		if (gruntStyle > 0
404 		        && ((flags & ResourceObjectPrototype::objPropNoSurface)
405 		            || (damageScore > 2 && (int16)g_vm->_rnd->getRandomNumber(vitality - 1) < (damageScore * 2))))
406 			makeGruntSound(gruntStyle, al);
407 
408 		if (enactorPtr != NULL) {
409 			enactorPtr->handleSuccessfulStrike(
410 			    a,
411 			    damageScore < vitality ? damageScore : vitality);
412 		}
413 
414 		//  If we've just lost all vitality, we're dead, else make a
415 		//  morale check
416 		if (damageScore >= vitality) {
417 			MotionTask::die(*a);
418 			AddFactionTally(a->_faction, factionNumKills, 1);
419 			if (enactorPtr != NULL)
420 				enactorPtr->handleSuccessfulKill(a);
421 		} else
422 			a->handleDamageTaken(damageScore);
423 
424 		vitality -= damageScore;
425 
426 		if (actorToPlayerID(a, pID)) {
427 			updateBrotherControls(pID);
428 
429 			if (vitality > 0) {
430 				int16               baseVitality,
431 				                    oldVitality;
432 
433 				baseVitality = a->getBaseStats()->vitality;
434 				oldVitality = vitality + damageScore;
435 
436 				if (baseVitality >= vitality * 3
437 				        &&  baseVitality < oldVitality * 3) {
438 					StatusMsg(WOUNDED_STATUS, a->objName());
439 				} else if (baseVitality * 2 >= vitality * 3
440 				           &&  baseVitality * 2 < oldVitality * 3) {
441 					StatusMsg(HURT_STATUS, a->objName());
442 				}
443 			}
444 		}
445 
446 		WriteStatusF(5, "Damage: %d", damageScore);
447 	}
448 
449 	return true;
450 }
451 
452 //-----------------------------------------------------------------------
453 //  cause healing directly
454 
acceptHealingAction(ObjectID dObj,ObjectID,int8 healing)455 bool ActorProto::acceptHealingAction(
456     ObjectID    dObj,
457     ObjectID,
458     int8        healing) {
459 	assert(isActor(dObj));
460 
461 	Actor       *a = (Actor *)GameObject::objectAddress(dObj);
462 	int16       &vitality = a->_effectiveStats.vitality;
463 	int16       maxVitality = (a->getBaseStats())->vitality;
464 	PlayerActorID   pID;
465 
466 	if (vitality > 0 && !a->hasEffect(actorDiseased)) {
467 
468 		//  If we've just lost all vitality, we're dead, else make a
469 		//  morale check
470 
471 		vitality += healing;
472 		vitality = clamp(0, vitality, maxVitality);
473 
474 		if (actorToPlayerID(a, pID))
475 			updateBrotherControls(pID);
476 
477 		WriteStatusF(5, "Healing: %d", healing);
478 	} else
479 		return false;
480 
481 	return true;
482 }
483 
484 //-----------------------------------------------------------------------
485 //  Accept strike from an object (allows this actor to cause damage to
486 //	the striking object).
487 
acceptStrikeAction(ObjectID dObj,ObjectID enactor,ObjectID strikingObj,uint8 skillIndex)488 bool ActorProto::acceptStrikeAction(
489     ObjectID            dObj,
490     ObjectID            enactor,
491     ObjectID            strikingObj,
492     uint8               skillIndex) {
493 	assert(isActor(dObj));
494 	assert(isActor(enactor));
495 
496 	const int       toHitBase = 100;
497 	const int       avgHitChance = toHitBase / 2;
498 	const int       skillScalingFactor =
499 	    (avgHitChance
500 	     +   ActorAttributes::skillLevels - 1)
501 	    /   ActorAttributes::skillLevels;
502 	const int       dodgingBonus = 10;
503 
504 	Actor           *a = (Actor *)GameObject::objectAddress(dObj);
505 	ActorAttributes *effStats = a->getStats();
506 	GameObject      *weapon = GameObject::objectAddress(strikingObj);
507 
508 	assert(weapon->proto()->containmentSet() & ProtoObj::isWeapon);
509 
510 	Actor           *enactorPtr = (Actor *)GameObject::objectAddress(enactor);
511 	ArmorAttributes armorAttribs;
512 
513 	uint8           hitChance;
514 
515 	if (a->isDead())
516 		return weapon->damage(enactor, dObj);
517 
518 	a->handleOffensiveAct((Actor *)GameObject::objectAddress(enactor));
519 
520 	//  Sum up the armor attributes
521 	a->totalArmorAttributes(armorAttribs);
522 
523 	//  Determine "to hit" percentage
524 	hitChance =     avgHitChance
525 	                + ((int)skillIndex
526 	                   - (int)effStats->getSkillLevel(skillIDAgility))
527 	                *   skillScalingFactor;
528 
529 	//  Factor in armor bonus
530 	hitChance -= armorAttribs.defenseBonus;
531 
532 	//  Factor in dodging bonus if any
533 	if (a->_moveTask != NULL && a->_moveTask->isDodging(enactorPtr))
534 		hitChance -= dodgingBonus;
535 
536 	hitChance = MAX<uint8>(hitChance, 5);
537 
538 	//  Randomly determine hit success
539 	if (g_vm->_rnd->getRandomNumber(toHitBase - 1) < hitChance) {
540 		//  Hit has succeeded
541 
542 		GameObject      *blockingObj = a->blockingObject(enactorPtr);
543 		bool            blocked = false;
544 
545 		//  Test for block success
546 		if (blockingObj != NULL) {
547 			hitChance =     avgHitChance
548 			                + ((int)skillIndex
549 			                   - (int)blockingObj->proto()->getSkillValue(dObj))
550 			                *   skillScalingFactor;
551 
552 			if (g_vm->_rnd->getRandomNumber(toHitBase - 1) >= hitChance) {
553 				//  The shield was hit
554 				blockingObj->acceptStrike(
555 				    enactor,
556 				    strikingObj,
557 				    skillIndex);
558 				blocked = true;
559 
560 				//  Cause skill growth
561 				blockingObj->proto()->applySkillGrowth(dObj, 5);
562 			}
563 		}
564 
565 		if (!blocked) {
566 			//  The strike got through
567 			weapon->damage(enactor, dObj);
568 
569 			//  Notify the attacker of a successful strike
570 			enactorPtr->handleSuccessfulStrike(weapon);
571 
572 			if (!a->isDead()) {
573 				int16 pmass = a->proto()->mass;
574 
575 				if (pmass <= 100 || (int16)g_vm->_rnd->getRandomNumber(155) >= pmass - 100) {
576 					if (g_vm->_rnd->getRandomNumber(7) == 0)
577 						MotionTask::fallDown(*a, *enactorPtr);
578 					else
579 						MotionTask::acceptHit(*a, *enactorPtr);
580 				}
581 			}
582 		}
583 
584 		return true;
585 	} else {
586 		//  This actor has dodged the blow, apply agility growth
587 
588 		PlayerActorID       playerID;
589 
590 		if (actorIDToPlayerID(dObj, playerID)) {
591 			PlayerActor     *player = getPlayerActorAddress(playerID);
592 
593 			player->skillAdvance(skillIDAgility, 1);
594 		}
595 	}
596 
597 	return false;
598 }
599 
600 //-----------------------------------------------------------------------
601 //	Insert another object into this object at the specified slot
602 
acceptInsertionAtAction(ObjectID dObj,ObjectID,ObjectID item,const TilePoint & where,int16 num)603 bool ActorProto::acceptInsertionAtAction(
604     ObjectID        dObj,
605     ObjectID,
606     ObjectID        item,
607     const TilePoint &where,
608     int16           num) {
609 	enum {
610 		notInUse,
611 		heldInLeftHand,
612 		heldInRightHand,
613 		worn
614 	}           inUseType;
615 	int         wornWhere = 0;
616 
617 	assert(isActor(dObj));
618 	assert(isObject(item));
619 
620 	GameObject  *dObjPtr = GameObject::objectAddress(dObj);
621 	Actor       *a = (Actor *)dObjPtr;
622 	GameObject  *itemPtr = GameObject::objectAddress(item);
623 	GameObject  *extractedObj = NULL;
624 	Location    oldLoc(itemPtr->getLocation(), itemPtr->IDParent());
625 
626 	bool        result;
627 
628 	//  Split the merged object if needed.
629 	if (itemPtr->isMergeable()           //  If mergeable
630 	        &&  num < itemPtr->getExtra()) {    //  And not dropping whole pile
631 		if (num == 0) return false;         //  If mergeing zero, then do nothing
632 
633 		extractedObj = itemPtr->extractMerged(itemPtr->getExtra() - num);
634 		if (extractedObj == NULL)
635 			return false;
636 
637 		extractedObj->move(oldLoc);
638 	}
639 
640 	//  Determine if this object is simply being moved within this actor
641 	if (oldLoc.context == dObj) {
642 		//  Determine if and where the object is in use by this actor
643 		if (a->_leftHandObject == item)
644 			inUseType = heldInLeftHand;
645 		else if (a->_rightHandObject == item)
646 			inUseType = heldInRightHand;
647 		else {
648 			int     i;
649 
650 			inUseType = notInUse;
651 
652 			for (i = 0; i < ARMOR_COUNT; i++) {
653 				if (a->_armorObjects[i] == item) {
654 					inUseType = worn;
655 					wornWhere = i;
656 					break;
657 				}
658 			}
659 		}
660 	} else
661 		inUseType = notInUse;
662 
663 	//  Do the deed
664 	itemPtr->move(Location(0, 0, 0, ImportantLimbo));
665 	if (dObjPtr->canFitBulkwise(itemPtr)
666 	        &&  dObjPtr->canFitMasswise(itemPtr)) {
667 		itemPtr->move(Location(where, dObj));
668 		result = true;
669 	} else {
670 		itemPtr->move(oldLoc);
671 		if (extractedObj != NULL)
672 			GameObject::mergeWith(extractedObj, itemPtr, extractedObj->getExtra());
673 		result = false;
674 	}
675 
676 	//  Re-equip the item if necessary
677 	if (inUseType != notInUse) {
678 		switch (inUseType) {
679 		case heldInLeftHand:
680 			a->holdInLeftHand(item);
681 			break;
682 
683 		case heldInRightHand:
684 			a->holdInRightHand(item);
685 			break;
686 
687 		case worn:
688 			a->wear(item, wornWhere);
689 			break;
690 
691 		default:
692 			break;
693 		}
694 	}
695 
696 	return result;
697 }
698 
699 //-----------------------------------------------------------------------
700 //  Initiate a natural attack motion
701 
initiateAttack(ObjectID attacker,ObjectID target)702 void ActorProto::initiateAttack(ObjectID attacker, ObjectID target) {
703 	assert(isActor(attacker));
704 	assert(isObject(target) || isActor(target));
705 
706 	Actor       *attackerPtr = (Actor *)GameObject::objectAddress(attacker);
707 	GameObject  *targetPtr = GameObject::objectAddress(target);
708 
709 	//  Start the attack motion
710 	if (attackerPtr->_appearance != NULL) {
711 		if (attackerPtr->isActionAvailable(actionSwingHigh))
712 			MotionTask::oneHandedSwing(*attackerPtr, *targetPtr);
713 		else if (attackerPtr->isActionAvailable(actionTwoHandSwingHigh))
714 			MotionTask::twoHandedSwing(*attackerPtr, *targetPtr);
715 	} else
716 		MotionTask::oneHandedSwing(*attackerPtr, *targetPtr);
717 }
718 
719 //-----------------------------------------------------------------------
720 //	Given an object sound effect record, which sound should be made
721 //	when this object is damaged
722 
getDamageSound(const ObjectSoundFXs & soundFXs)723 uint8 ActorProto::getDamageSound(const ObjectSoundFXs &soundFXs) {
724 	return  !(flags & ResourceObjectPrototype::objPropNoSurface)
725 	        ?   !(flags & ResourceObjectPrototype::objPropHardSurface)
726 	        ?   soundFXs.soundFXHitFlesh
727 	        :   soundFXs.soundFXHitHard
728 	        :   0;
729 }
730 
731 //-----------------------------------------------------------------------
732 //	Do the background processing, if needed, for this object.
733 
doBackgroundUpdate(GameObject * obj)734 void ActorProto::doBackgroundUpdate(GameObject *obj) {
735 	// get the ID for this object
736 	ObjectID actorID = obj->thisID();
737 
738 	// find out if this object is an actor
739 	if (isActor(actorID)) {
740 		// get a pointer to that actor
741 		GameObject  *actorObj = GameObject::objectAddress(actorID);
742 		Actor       *a = (Actor *)actorObj;
743 
744 		if (!a->isActivated()) {
745 			//  If this is a temporary actor waiting for expiration,
746 			//  then decrement the expiration counter and possibly
747 			//  delete the actor
748 			if ((a->_flags & Actor::temporary) || a->isDead()) {
749 				if (a->_deactivationCounter <= 0) {
750 					a->deleteObjectRecursive();
751 					return;
752 				} else a->_deactivationCounter--;
753 			} else {
754 				//  If the actor has failed morale there is a random
755 				//  chance of him regaining his courage
756 				if ((a->_flags & Actor::afraid) && g_vm->_rnd->getRandomNumber(127) == 0)
757 					a->_flags &= ~Actor::afraid;
758 			}
759 		}
760 
761 
762 		// execute that actor's vitality update function
763 		((Actor *)actorObj)->vitalityUpdate();
764 
765 
766 		// do any updates directly related only to the brothers
767 		if (isPlayerActor(actorID)) {
768 
769 			switch (actorID) {
770 			case ActorBaseID + FTA_JULIAN:
771 				g_vm->_playerList[FTA_JULIAN]->recoveryUpdate();
772 				break;
773 
774 			case ActorBaseID + FTA_PHILIP:
775 				g_vm->_playerList[FTA_PHILIP]->recoveryUpdate();
776 				break;
777 
778 			case ActorBaseID + FTA_KEVIN:
779 				g_vm->_playerList[FTA_KEVIN]->recoveryUpdate();
780 				break;
781 
782 			default:
783 				// no action
784 				break;
785 			}
786 		}
787 	}
788 
789 
790 	// check for other updates
791 	ProtoObj::doBackgroundUpdate(obj);
792 
793 }
794 
795 // ------------------------------------------------------------------------
796 //	Cause the user's associated skill to grow
797 
applySkillGrowth(ObjectID enactor,uint8 points)798 void ActorProto::applySkillGrowth(ObjectID enactor, uint8 points) {
799 	assert(isActor(enactor));
800 
801 	PlayerActorID       playerID;
802 
803 	if (actorIDToPlayerID(enactor, playerID)) {
804 		PlayerActor     *player = getPlayerActorAddress(playerID);
805 
806 		player->skillAdvance(skillIDBludgeon, points);
807 
808 		if (g_vm->_rnd->getRandomNumber(1))
809 			player->skillAdvance(skillIDBrawn, points);
810 	}
811 }
812 
813 // ------------------------------------------------------------------------
814 
canFitBulkwise(GameObject * container,GameObject * obj)815 bool ActorProto::canFitBulkwise(GameObject *container, GameObject *obj) {
816 #if DEBUG
817 	if (massAndBulkCount)
818 #endif
819 	{
820 		uint16                  maxBulk = container->bulkCapacity();
821 		uint16                  totalBulk = container->totalContainedBulk();
822 
823 		return totalBulk + obj->totalBulk() <= maxBulk;
824 	}
825 
826 #if DEBUG
827 	return true;
828 #endif
829 }
830 
831 // ------------------------------------------------------------------------
832 
canFitMasswise(GameObject * container,GameObject * obj)833 bool ActorProto::canFitMasswise(GameObject *container, GameObject *obj) {
834 	assert(isActor(container));
835 
836 #if DEBUG
837 	if (massAndBulkCount)
838 #endif
839 	{
840 		Actor           *a = (Actor *)container;
841 
842 		// get the maxium amount of weight this character should be able to carry
843 		uint16 cmaxCapacity = container->massCapacity();
844 
845 		uint16 totalMass = a->totalContainedMass();
846 
847 		return totalMass + obj->totalMass() <= cmaxCapacity;
848 	}
849 
850 #if DEBUG
851 	return true;
852 #endif
853 }
854 
855 // ------------------------------------------------------------------------
856 //	Return the maximum mass capacity for the specified container
857 
massCapacity(GameObject * container)858 uint16 ActorProto::massCapacity(GameObject *container) {
859 	assert(isActor(container));
860 	Actor           *a = (Actor *)container;
861 	ActorAttributes *effStats = a->getStats();
862 
863 	return      baseCarryingCapacity
864 	            +       effStats->getSkillLevel(skillIDBrawn)
865 	            *   carryingCapacityBonusPerBrawn;
866 }
867 
868 // ------------------------------------------------------------------------
869 //	Return the maximum bulk capacity for the specified container
870 
bulkCapacity(GameObject *)871 uint16 ActorProto::bulkCapacity(GameObject *) {
872 	return bulk * 4;
873 }
874 
875 /* ===================================================================== *
876    ActorArchive struct
877  * ===================================================================== */
878 
879 //	This data structure is used in the creation of an actor archive.  It
880 //	includes all of the fixed size data fields which must be preserved in
881 //	a save file without any of the overhead such as a base class or virtual
882 //	member functions.  Some of the Actor data members, such as moveTask
883 //	currentTask and currentTransaction, are omitted because the links
884 //	to these other objects will archived with their respective 'Task' object.
885 //	Also, the assignment member was not included because it is a complex
886 //	variable sized data structure which will be asked to archive itself.
887 
888 struct ActorArchive {
889 	uint8               faction;
890 	uint8               colorScheme;
891 	int32               appearanceID;
892 	int8                attitude,
893 	                    mood;
894 	uint8               disposition;
895 	Direction           currentFacing;
896 	int16               tetherLocU;
897 	int16               tetherLocV;
898 	int16               tetherDist;
899 	ObjectID            leftHandObject,
900 	                    rightHandObject;
901 	uint16              knowledge[16];
902 	uint16              schedule;
903 	uint8               conversationMemory[4];
904 	uint8               currentAnimation,
905 	                    currentPose,
906 	                    animationFlags;
907 	uint8               flags;
908 	ActorPose           poseInfo;
909 	int16               cycleCount;
910 	int16               kludgeCount;
911 	uint32              enchantmentFlags;
912 	uint8               currentGoal,
913 	                    deactivationCounter;
914 	ActorAttributes     effectiveStats;
915 	uint8               actionCounter;
916 	uint16              effectiveResistance;
917 	uint16              effectiveImmunity;
918 	int16               recPointsPerUpdate;             // fractional vitality recovery
919 	int16               currentRecoveryPoints;
920 	ObjectID            leaderID;
921 	BandID              followersID;
922 	ObjectID            _armorObjects[ARMOR_COUNT];
923 	ObjectID            currentTargetID;
924 	int16               scriptVar[actorScriptVars];
925 };
926 
927 /* ===================================================================== *
928    Actor member functions
929  * ===================================================================== */
930 
931 //-----------------------------------------------------------------------
932 //  Initialize all fields in the actor structure to neutral values.
933 
init(int16 protoIndex,uint16 nameIndex,uint16 scriptIndex,int32 appearanceNum,uint8 colorSchemeIndex,uint8 factionNum,uint8 initFlags)934 void Actor::init(
935     int16   protoIndex,
936     uint16  nameIndex,
937     uint16  scriptIndex,
938     int32   appearanceNum,
939     uint8   colorSchemeIndex,
940     uint8   factionNum,
941     uint8   initFlags) {
942 	debugC(1, kDebugActors, "Actor init flags: %d, permanent: %d", initFlags, initFlags & actorPermanent);
943 
944 	//  Fixup the prototype pointer to point to an actor prototype
945 	prototype           = (ProtoObj *)g_vm->_actorProtos[protoIndex];
946 
947 	//  Initialize object fields
948 //	nameIndex = 0;
949 	setNameIndex(nameIndex);
950 	setScript(scriptIndex);
951 	_data.parentID = _data.siblingID = _data.childID = Nothing;
952 	_data.objectFlags = 0;
953 	_data.massCount = 0;
954 	_data.currentTAG = NoActiveItem;
955 	_data.hitPoints = 0;
956 
957 	//  Initialize actor field
958 	_faction             = factionNum;
959 	_colorScheme         = colorSchemeIndex;
960 	_appearanceID        = appearanceNum;
961 	_attitude            = 0;
962 	_mood                = 0;
963 	_disposition         = 0;
964 	_currentFacing       = dirDown;
965 	_tetherLocU          = 0;
966 	_tetherLocV          = 0;
967 	_tetherDist          = 0;
968 	_leftHandObject      = Nothing;
969 	_rightHandObject     = Nothing;
970 	_schedule            = 0;
971 
972 	for (uint i = 0; i < ARRAYSIZE(_knowledge); ++i)
973 		_knowledge[i] = 0;
974 
975 	//  Initialize the rest of the data members
976 	for (uint i = 0; i < ARRAYSIZE(_conversationMemory); ++i)
977 		_conversationMemory[i] = 0;
978 
979 	_currentAnimation    = actionStand;
980 	_currentPose         = 0;
981 	_animationFlags      = 0;
982 	_flags               = 0;
983 	if (!(initFlags & actorPermanent))
984 		_flags |= temporary;
985 
986 	_poseInfo.flags = 0;
987 	_poseInfo.actorFrameIndex = 0;
988 	_poseInfo.actorFrameBank = 0;
989 	_poseInfo.leftObjectIndex = 0;
990 	_poseInfo.rightObjectIndex = 0;
991 	_poseInfo.leftObjectOffset.x = _poseInfo.leftObjectOffset.y = 0;
992 	_poseInfo.rightObjectOffset.x = _poseInfo.rightObjectOffset.y = 0;
993 
994 	_appearance          = NULL;
995 	_cycleCount          = 0;
996 	_kludgeCount         = 0;
997 	_moveTask            = NULL;
998 	_enchantmentFlags    = 0L;
999 	_curTask             = NULL;
1000 	_currentGoal         = actorGoalFollowAssignment;
1001 	_deactivationCounter = 0;
1002 	_assignment = nullptr;
1003 
1004 	memcpy(
1005 	    &_effectiveStats,
1006 	    &((ActorProto *)prototype)->baseStats,
1007 	    sizeof(_effectiveStats));
1008 	_effectiveStats.vitality = MAX<int16>(_effectiveStats.vitality, 1);
1009 
1010 	_actionCounter       = 0;
1011 	_effectiveResistance = 0;
1012 	_effectiveImmunity   = 0;
1013 	_recPointsPerUpdate      = BASE_REC_RATE;
1014 	_currentRecoveryPoints   = 0;
1015 	_leader              = NULL;
1016 	_followers           = NULL;
1017 	_followersID = NoBand;
1018 	for (int i = 0; i < ARMOR_COUNT; i++)
1019 		_armorObjects[i] = Nothing;
1020 	_currentTarget       = NULL;
1021 	for (int i = 0; i < actorScriptVars; i++)
1022 		_scriptVar[i] = 0;
1023 
1024 	evalActorEnchantments(this);
1025 }
1026 
1027 //-----------------------------------------------------------------------
1028 //  Actor constructor -- copies the resource fields and simply NULL's most
1029 //	of the rest of the data members
Actor(void)1030 Actor::Actor(void) {
1031 	prototype = nullptr;
1032 	_faction             = 0;
1033 	_colorScheme         = 0;
1034 	_appearanceID        = 0;
1035 	_attitude            = 0;
1036 	_mood                = 0;
1037 	_disposition         = 0;
1038 	_currentFacing       = 0;
1039 	_tetherLocU          = 0;
1040 	_tetherLocV          = 0;
1041 	_tetherDist          = 0;
1042 	_leftHandObject      = 0;
1043 	_rightHandObject     = 0;
1044 	_schedule            = 0;
1045 
1046 	for (uint i = 0; i < ARRAYSIZE(_knowledge); ++i)
1047 		_knowledge[i] = 0;
1048 
1049 	//  Initialize the rest of the data members
1050 	for (uint i = 0; i < ARRAYSIZE(_conversationMemory); ++i)
1051 		_conversationMemory[i] = 0;
1052 
1053 	_currentAnimation    = actionStand;
1054 	_currentPose         = 0;
1055 	_animationFlags      = 0;
1056 	_flags               = 0;
1057 
1058 	_poseInfo.flags = 0;
1059 	_poseInfo.actorFrameIndex = 0;
1060 	_poseInfo.actorFrameBank = 0;
1061 	_poseInfo.leftObjectIndex = 0;
1062 	_poseInfo.rightObjectIndex = 0;
1063 	_poseInfo.leftObjectOffset.x = _poseInfo.leftObjectOffset.y = 0;
1064 	_poseInfo.rightObjectOffset.x = _poseInfo.rightObjectOffset.y = 0;
1065 
1066 	_appearance          = nullptr;
1067 	_cycleCount          = 0;
1068 	_kludgeCount         = 0;
1069 	_moveTask            = nullptr;
1070 	_enchantmentFlags    = 0L;
1071 	_curTask             = nullptr;
1072 	_currentGoal         = actorGoalFollowAssignment;
1073 	_deactivationCounter = 0;
1074 	_assignment = nullptr;
1075 
1076 	memset(&_effectiveStats, 0, sizeof(_effectiveStats));
1077 	_effectiveStats.vitality = MAX<uint16>(_effectiveStats.vitality, 1);
1078 
1079 	_actionCounter       = 0;
1080 	_effectiveResistance = 0;
1081 	_effectiveImmunity   = 0;
1082 	_recPointsPerUpdate      = BASE_REC_RATE;
1083 	_currentRecoveryPoints   = 0;
1084 	_leader              = nullptr;
1085 	_leaderID           = Nothing;
1086 	_followers           = nullptr;
1087 	_followersID = NoBand;
1088 	for (int i = 0; i < ARMOR_COUNT; i++)
1089 		_armorObjects[i] = Nothing;
1090 	_currentTarget       = nullptr;
1091 	_currentTargetID    = Nothing;
1092 	for (int i = 0; i < actorScriptVars; i++)
1093 		_scriptVar[i] = 0;
1094 }
1095 
Actor(const ResourceActor & res)1096 Actor::Actor(const ResourceActor &res) : GameObject(res) {
1097 	//  Fixup the prototype pointer to point to an actor prototype
1098 	prototype   =   prototype != NULL
1099 	                ? (ProtoObj *)g_vm->_actorProtos[getProtoNum()]
1100 	                :   NULL;
1101 
1102 	//  Copy the resource fields
1103 	_faction             = res.faction;
1104 	_colorScheme         = res.colorScheme;
1105 	_appearanceID        = res.appearanceID;
1106 	_attitude            = res.attitude;
1107 	_mood                = res.mood;
1108 	_disposition         = res.disposition;
1109 	_currentFacing       = res.currentFacing;
1110 	_tetherLocU          = res.tetherLocU;
1111 	_tetherLocV          = res.tetherLocV;
1112 	_tetherDist          = res.tetherDist;
1113 	_leftHandObject      = res.leftHandObject;
1114 	_rightHandObject     = res.rightHandObject;
1115 	_schedule            = res.schedule;
1116 	memcpy(&_knowledge, &res.knowledge, sizeof(_knowledge));
1117 
1118 	//  Initialize the rest of the data members
1119 	for (uint i = 0; i < ARRAYSIZE(_conversationMemory); ++i)
1120 		_conversationMemory[i] = 0;
1121 
1122 	_currentAnimation    = actionStand;
1123 	_currentPose         = 0;
1124 	_animationFlags      = 0;
1125 	_flags               = 0;
1126 
1127 	_poseInfo.flags = 0;
1128 	_poseInfo.actorFrameIndex = 0;
1129 	_poseInfo.actorFrameBank = 0;
1130 	_poseInfo.leftObjectIndex = 0;
1131 	_poseInfo.rightObjectIndex = 0;
1132 	_poseInfo.leftObjectOffset.x = _poseInfo.leftObjectOffset.y = 0;
1133 	_poseInfo.rightObjectOffset.x = _poseInfo.rightObjectOffset.y = 0;
1134 
1135 	_appearance          = NULL;
1136 	_cycleCount          = 0;
1137 	_kludgeCount         = 0;
1138 	_moveTask            = NULL;
1139 	_enchantmentFlags    = 0L;
1140 	_curTask             = NULL;
1141 	_currentGoal         = actorGoalFollowAssignment;
1142 	_deactivationCounter = 0;
1143 	_assignment = nullptr;
1144 
1145 	if (prototype)
1146 		memcpy(&_effectiveStats, &((ActorProto *)prototype)->baseStats, sizeof(_effectiveStats));
1147 
1148 	_effectiveStats.vitality = MAX<uint16>(_effectiveStats.vitality, 1);
1149 
1150 	_actionCounter       = 0;
1151 	_effectiveResistance = 0;
1152 	_effectiveImmunity   = 0;
1153 	_recPointsPerUpdate      = BASE_REC_RATE;
1154 	_currentRecoveryPoints   = 0;
1155 	_leader              = NULL;
1156 	_leaderID           = Nothing;
1157 	_followers           = NULL;
1158 	_followersID = NoBand;
1159 	for (int i = 0; i < ARMOR_COUNT; i++)
1160 		_armorObjects[i] = Nothing;
1161 	_currentTarget       = NULL;
1162 	_currentTargetID    = Nothing;
1163 	for (int i = 0; i < actorScriptVars; i++)
1164 		_scriptVar[i] = 0;
1165 
1166 	evalActorEnchantments(this);
1167 }
1168 
Actor(Common::InSaveFile * in)1169 Actor::Actor(Common::InSaveFile *in) : GameObject(in) {
1170 	//  Fixup the prototype pointer to point to an actor prototype
1171 	prototype   =   prototype != nullptr
1172 	                ? (ProtoObj *)g_vm->_actorProtos[getProtoNum()]
1173 	                :   nullptr;
1174 
1175 	_faction = in->readByte();
1176 	_colorScheme = in->readByte();
1177 	_appearanceID = in->readSint32BE();
1178 	_attitude = in->readSByte();
1179 	_mood = in->readSByte();
1180 
1181 	_disposition = in->readByte();
1182 	_currentFacing = in->readByte();
1183 	_tetherLocU = in->readSint16LE();
1184 	_tetherLocV = in->readSint16LE();
1185 	_tetherDist = in->readSint16LE();
1186 	_leftHandObject = in->readUint16LE();
1187 	_rightHandObject = in->readUint16LE();
1188 
1189 	for (int i = 0; i < ARRAYSIZE(_knowledge); ++i)
1190 		_knowledge[i] = in->readUint16LE();
1191 
1192 	_schedule = in->readUint16LE();
1193 
1194 	for (int i = 0; i < ARRAYSIZE(_conversationMemory); ++i)
1195 		_conversationMemory[i] = in->readByte();
1196 
1197 	_currentAnimation = in->readByte();
1198 	_currentPose = in->readByte();
1199 	_animationFlags = in->readByte();
1200 
1201 	_flags = in->readByte();
1202 	_poseInfo.load(in);
1203 	_cycleCount = in->readSint16LE();
1204 	_kludgeCount = in->readSint16LE();
1205 	_enchantmentFlags = in->readUint32LE();
1206 	_currentGoal = in->readByte();
1207 	_deactivationCounter = in->readByte();
1208 	_effectiveStats.read(in);
1209 	_actionCounter = in->readByte();
1210 	_effectiveResistance = in->readUint16LE();
1211 	_effectiveImmunity = in->readUint16LE();
1212 	_recPointsPerUpdate = in->readSint16LE();
1213 	_currentRecoveryPoints = in->readUint16LE();
1214 
1215 	_leaderID = in->readUint16LE();
1216 	_leader = nullptr;
1217 
1218 	_followersID = in->readSint16LE();
1219 	_followers = nullptr;
1220 
1221 	for (int i = 0; i < ARRAYSIZE(_armorObjects); ++i)
1222 		_armorObjects[i] = in->readUint16LE();
1223 
1224 	_currentTargetID = in->readUint16LE();
1225 	_currentTarget = nullptr;
1226 
1227 	for (int i = 0; i < ARRAYSIZE(_scriptVar); ++i)
1228 		_scriptVar[i] = in->readSint16LE();
1229 
1230 	if (_flags & hasAssignment) {
1231 		readAssignment(this, in);
1232 	} else {
1233 		_assignment = nullptr;
1234 	}
1235 
1236 	_appearance = nullptr;
1237 	_moveTask = nullptr;
1238 	_curTask = nullptr;
1239 
1240 	debugC(4, kDebugSaveload, "... _faction = %d", _faction);
1241 	debugC(4, kDebugSaveload, "... _colorScheme = %d", _colorScheme);
1242 	debugC(4, kDebugSaveload, "... _appearanceID = %d", _appearanceID);
1243 	debugC(4, kDebugSaveload, "... _attitude = %d", _attitude);
1244 	debugC(4, kDebugSaveload, "... _mood = %d", _mood);
1245 	debugC(4, kDebugSaveload, "... _disposition = %d", _disposition);
1246 	debugC(4, kDebugSaveload, "... _currentFacing = %d", _currentFacing);
1247 	debugC(4, kDebugSaveload, "... _tetherLocU = %d", _tetherLocU);
1248 	debugC(4, kDebugSaveload, "... _tetherLocV = %d", _tetherLocV);
1249 	debugC(4, kDebugSaveload, "... _tetherDist = %d", _tetherDist);
1250 	debugC(4, kDebugSaveload, "... _leftHandObject = %d", _leftHandObject);
1251 	debugC(4, kDebugSaveload, "... _rightHandObject = %d", _rightHandObject);
1252 //	debugC(4, kDebugSaveload, "... knowledge = %d", knowledge);
1253 	debugC(4, kDebugSaveload, "... _schedule = %d", _schedule);
1254 //	debugC(4, kDebugSaveload, "... conversationMemory = %d", conversationMemory);
1255 	debugC(4, kDebugSaveload, "... _currentAnimation = %d", _currentAnimation);
1256 	debugC(4, kDebugSaveload, "... _currentPose = %d", _currentPose);
1257 	debugC(4, kDebugSaveload, "... _animationFlags = %d", _animationFlags);
1258 	debugC(4, kDebugSaveload, "... _flags = %d", _flags);
1259 //	debugC(4, kDebugSaveload, "... out = %d", out);
1260 	debugC(4, kDebugSaveload, "... _cycleCount = %d", _cycleCount);
1261 	debugC(4, kDebugSaveload, "... _kludgeCount = %d", _kludgeCount);
1262 	debugC(4, kDebugSaveload, "... _enchantmentFlags = %d", _enchantmentFlags);
1263 	debugC(4, kDebugSaveload, "... _currentGoal = %d", _currentGoal);
1264 	debugC(4, kDebugSaveload, "... _deactivationCounter = %d", _deactivationCounter);
1265 //	debugC(4, kDebugSaveload, "... out = %d", out);
1266 	debugC(4, kDebugSaveload, "... _actionCounter = %d", _actionCounter);
1267 	debugC(4, kDebugSaveload, "... _effectiveResistance = %d", _effectiveResistance);
1268 	debugC(4, kDebugSaveload, "... _effectiveImmunity = %d", _effectiveImmunity);
1269 	debugC(4, kDebugSaveload, "... _recPointsPerUpdate = %d", _recPointsPerUpdate);
1270 	debugC(4, kDebugSaveload, "... _currentRecoveryPoints = %d", _currentRecoveryPoints);
1271 	debugC(4, kDebugSaveload, "... _leaderID = %d", _leaderID);
1272 	debugC(4, kDebugSaveload, "... _followersID = %d", _followersID);
1273 //	debugC(4, kDebugSaveload, "... armorObjects = %d", armorObjects);
1274 	debugC(4, kDebugSaveload, "... _currentTargetID = %d", _currentTargetID);
1275 //	debugC(4, kDebugSaveload, "... scriptVar = %d", scriptVar);
1276 }
1277 
1278 //-----------------------------------------------------------------------
1279 //	Destructor
1280 
~Actor(void)1281 Actor::~Actor(void) {
1282 	if (_appearance != NULL) ReleaseActorAppearance(_appearance);
1283 
1284 	if (getAssignment())
1285 		delete getAssignment();
1286 }
1287 
1288 //-----------------------------------------------------------------------
1289 //	Return the number of bytes needed to archive this actor
1290 
archiveSize(void)1291 int32 Actor::archiveSize(void) {
1292 	int32   size = GameObject::archiveSize();
1293 
1294 	size += sizeof(ActorArchive);
1295 	if (_flags & hasAssignment)
1296 		size += assignmentArchiveSize(this);
1297 
1298 	return size;
1299 }
1300 
write(Common::MemoryWriteStreamDynamic * out)1301 void Actor::write(Common::MemoryWriteStreamDynamic *out) {
1302 	ProtoObj    *holdProto = prototype;
1303 
1304 	debugC(3, kDebugSaveload, "Saving actor %d", thisID());
1305 
1306 	//  Modify the protoype temporarily so the GameObject::write()
1307 	//  will store the index correctly
1308 	if (prototype != NULL)
1309 		prototype = g_vm->_objectProtos[getProtoNum()];
1310 
1311 	GameObject::write(out, false);
1312 
1313 	//  Restore the prototype pointer
1314 	prototype = holdProto;
1315 
1316 	out->writeByte(_faction);
1317 	out->writeByte(_colorScheme);
1318 	out->writeSint32BE(_appearanceID);
1319 	out->writeSByte(_attitude);
1320 	out->writeSByte(_mood);
1321 
1322 	out->writeByte(_disposition);
1323 	out->writeByte(_currentFacing);
1324 	out->writeSint16LE(_tetherLocU);
1325 	out->writeSint16LE(_tetherLocV);
1326 	out->writeSint16LE(_tetherDist);
1327 	out->writeUint16LE(_leftHandObject);
1328 	out->writeUint16LE(_rightHandObject);
1329 
1330 	out->write(_knowledge, sizeof(_knowledge));
1331 	out->writeUint16LE(_schedule);
1332 	out->write(_conversationMemory, sizeof(_conversationMemory));
1333 
1334 	out->writeByte(_currentAnimation);
1335 	out->writeByte(_currentPose);
1336 	out->writeByte(_animationFlags);
1337 
1338 	out->writeByte(_flags);
1339 	_poseInfo.write(out);
1340 	out->writeSint16LE(_cycleCount);
1341 	out->writeSint16LE(_kludgeCount);
1342 	out->writeUint32LE(_enchantmentFlags);
1343 	out->writeByte(_currentGoal);
1344 	out->writeByte(_deactivationCounter);
1345 	_effectiveStats.write(out);
1346 	out->writeByte(_actionCounter);
1347 	out->writeUint16LE(_effectiveResistance);
1348 	out->writeUint16LE(_effectiveImmunity);
1349 	out->writeSint16LE(_recPointsPerUpdate);
1350 	out->writeUint16LE(_currentRecoveryPoints);
1351 
1352 	_leaderID = (_leader != NULL) ? _leader->thisID() : Nothing;
1353 
1354 	out->writeUint16LE(_leaderID);
1355 
1356 	_followersID = (_followers != NULL) ? getBandID(_followers) : NoBand;
1357 
1358 	out->writeSint16LE(_followersID);
1359 	out->write(_armorObjects, ARMOR_COUNT * 2);
1360 
1361 	_currentTargetID = _currentTarget != NULL ? _currentTarget->thisID() : Nothing;
1362 
1363 	out->writeUint16LE(_currentTargetID);
1364 	out->write(_scriptVar, sizeof(_scriptVar));
1365 
1366 	if (_flags & hasAssignment)
1367 		writeAssignment(this, out);
1368 
1369 	debugC(4, kDebugSaveload, "... _faction = %d", _faction);
1370 	debugC(4, kDebugSaveload, "... _colorScheme = %d", _colorScheme);
1371 	debugC(4, kDebugSaveload, "... _appearanceID = %d", _appearanceID);
1372 	debugC(4, kDebugSaveload, "... _attitude = %d", _attitude);
1373 	debugC(4, kDebugSaveload, "... _mood = %d", _mood);
1374 	debugC(4, kDebugSaveload, "... _disposition = %d", _disposition);
1375 	debugC(4, kDebugSaveload, "... _currentFacing = %d", _currentFacing);
1376 	debugC(4, kDebugSaveload, "... _tetherLocU = %d", _tetherLocU);
1377 	debugC(4, kDebugSaveload, "... _tetherLocV = %d", _tetherLocV);
1378 	debugC(4, kDebugSaveload, "... _tetherDist = %d", _tetherDist);
1379 	debugC(4, kDebugSaveload, "... _leftHandObject = %d", _leftHandObject);
1380 	debugC(4, kDebugSaveload, "... _rightHandObject = %d", _rightHandObject);
1381 //	debugC(4, kDebugSaveload, "... knowledge = %d", knowledge);
1382 	debugC(4, kDebugSaveload, "... _schedule = %d", _schedule);
1383 //	debugC(4, kDebugSaveload, "... conversationMemory = %d", conversationMemory);
1384 	debugC(4, kDebugSaveload, "... _currentAnimation = %d", _currentAnimation);
1385 	debugC(4, kDebugSaveload, "... _currentPose = %d", _currentPose);
1386 	debugC(4, kDebugSaveload, "... _animationFlags = %d", _animationFlags);
1387 	debugC(4, kDebugSaveload, "... _flags = %d", _flags);
1388 //	debugC(4, kDebugSaveload, "... out = %d", out);
1389 	debugC(4, kDebugSaveload, "... _cycleCount = %d", _cycleCount);
1390 	debugC(4, kDebugSaveload, "... _kludgeCount = %d", _kludgeCount);
1391 	debugC(4, kDebugSaveload, "... _enchantmentFlags = %d", _enchantmentFlags);
1392 	debugC(4, kDebugSaveload, "... _currentGoal = %d", _currentGoal);
1393 	debugC(4, kDebugSaveload, "... _deactivationCounter = %d", _deactivationCounter);
1394 //	debugC(4, kDebugSaveload, "... out = %d", out);
1395 	debugC(4, kDebugSaveload, "... _actionCounter = %d", _actionCounter);
1396 	debugC(4, kDebugSaveload, "... _effectiveResistance = %d", _effectiveResistance);
1397 	debugC(4, kDebugSaveload, "... _effectiveImmunity = %d", _effectiveImmunity);
1398 	debugC(4, kDebugSaveload, "... _recPointsPerUpdate = %d", _recPointsPerUpdate);
1399 	debugC(4, kDebugSaveload, "... _currentRecoveryPoints = %d", _currentRecoveryPoints);
1400 	debugC(4, kDebugSaveload, "... _leaderID = %d", _leader != NULL ? _leader->thisID() : Nothing);
1401 	debugC(4, kDebugSaveload, "... _followersID = %d", _followers != NULL ? getBandID(_followers) : NoBand);
1402 //	debugC(4, kDebugSaveload, "... armorObjects = %d", armorObjects);
1403 	debugC(4, kDebugSaveload, "... _currentTargetID = %d", _currentTarget != NULL ? _currentTarget->thisID() : Nothing);
1404 //	debugC(4, kDebugSaveload, "... scriptVar = %d", scriptVar);
1405 }
1406 
1407 //-----------------------------------------------------------------------
1408 //	Return a newly created actor
1409 
newActor(int16 protoNum,uint16 nameIndex,uint16 scriptIndex,int32 appearanceNum,uint8 colorSchemeIndex,uint8 factionNum,uint8 initFlags)1410 Actor *Actor::newActor(
1411     int16   protoNum,
1412     uint16  nameIndex,
1413     uint16  scriptIndex,
1414     int32   appearanceNum,
1415     uint8   colorSchemeIndex,
1416     uint8   factionNum,
1417     uint8   initFlags) {
1418 	GameObject      *limbo = objectAddress(ActorLimbo);
1419 	Actor           *a = nullptr;
1420 
1421 	debugC(2, kDebugActors, "Actor::newActor(protoNum = %d, nameIndex = %d, scriptIndex = %d, appearanceNum = %d, colorSchemeIndex = %d, factionNum = %d, initFlags = %d)",
1422 		protoNum, nameIndex, scriptIndex, appearanceNum, colorSchemeIndex, factionNum, initFlags);
1423 
1424 	if (limbo->IDChild() == Nothing) {
1425 		int16       i;
1426 
1427 		//  Search actor list for first scavangable actor
1428 		for (i = kPlayerActors; i < kActorCount; i++) {
1429 			a = g_vm->_act->_actorList[i];
1430 
1431 			if ((a->_flags & temporary)
1432 			        &&  !a->isActivated()
1433 			        &&  isWorld(a->IDParent()))
1434 				break;
1435 		}
1436 
1437 		//  REM: If things start getting really tight, we can
1438 		//  start recycling common objects...
1439 
1440 		if (i >= kActorCount)
1441 			return nullptr;
1442 	} else {
1443 		actorLimboCount--;
1444 		a = (Actor *)limbo->child();
1445 	}
1446 
1447 	if (!a)
1448 		return nullptr;
1449 
1450 	a->setLocation(Location(0, 0, 0, Nothing));
1451 	a->init(
1452 	    protoNum,
1453 	    nameIndex,
1454 	    scriptIndex,
1455 	    appearanceNum,
1456 	    colorSchemeIndex,
1457 	    factionNum,
1458 	    initFlags);
1459 
1460 	if (a->_flags & temporary) {
1461 		incTempActorCount(protoNum);
1462 		debugC(1, kDebugActors, "Actors: Created temp actor %d (%s) new count:%d", a->thisID() - 32768, a->objName(), getTempActorCount(protoNum));
1463 	}
1464 
1465 	return a;
1466 }
1467 
1468 //-----------------------------------------------------------------------
1469 //	Delete this actor
1470 
deleteActor(void)1471 void Actor::deleteActor(void) {
1472 	if (_flags & temporary) {
1473 		uint16      protoNum = getProtoNum();
1474 
1475 		decTempActorCount(protoNum);
1476 		debugC(1, kDebugActors, "Actors: Deleting temp actor %d (%s) new count:%d", thisID() - 32768, objName(), getTempActorCount(protoNum));
1477 	}
1478 
1479 	//  Kill task
1480 	if (_curTask != NULL) {
1481 		_curTask->abortTask();
1482 		delete _curTask;
1483 		_curTask = NULL;
1484 	}
1485 
1486 	//  Kill motion task
1487 	if (_moveTask != NULL)
1488 		_moveTask->remove();
1489 
1490 	//  If banded, remove from band
1491 	if (_leader != NULL) {
1492 		assert(isActor(_leader));
1493 
1494 		_leader->removeFollower(this);
1495 		_leader = NULL;
1496 	} else if (_followers != NULL) {
1497 		int16       i;
1498 
1499 		for (i = 0; i < _followers->size(); i++) {
1500 			Actor   *follower = (*_followers)[i];
1501 
1502 			follower->_leader = NULL;
1503 			follower->evaluateNeeds();
1504 		}
1505 
1506 		delete _followers;
1507 		_followers = NULL;
1508 	}
1509 
1510 	//  Place in limbo
1511 	if (!(_data.objectFlags & objectNoRecycle)) {
1512 		append(ActorLimbo);
1513 		actorLimboCount++;
1514 	}
1515 }
1516 
1517 
1518 //-----------------------------------------------------------------------
1519 //	Cause the actor to stop his current motion task is he is interruptable
1520 
stopMoving(void)1521 void Actor::stopMoving(void) {
1522 	if (_moveTask != NULL && isInterruptable())
1523 		_moveTask->remove();
1524 }
1525 
1526 //-----------------------------------------------------------------------
1527 //	Cause this actor to die
1528 
die(void)1529 void Actor::die(void) {
1530 	if (!isDead()) return;
1531 
1532 	ObjectID        dObj = thisID();
1533 	scriptCallFrame scf;
1534 	PlayerActorID   playerID;
1535 
1536 	scf.invokedObject   = dObj;
1537 	scf.enactor         = dObj;
1538 	scf.directObject    = dObj;
1539 	scf.indirectObject  = Nothing;
1540 	scf.value           = 0;
1541 
1542 	runObjectMethod(dObj, Method_Actor_onDie, scf);
1543 
1544 	//  Kill task
1545 	if (_curTask != NULL) {
1546 		_curTask->abortTask();
1547 		delete _curTask;
1548 		_curTask = NULL;
1549 	}
1550 
1551 	//  Kill motion task
1552 	if (_moveTask != NULL)
1553 		_moveTask->remove();
1554 
1555 	//  If banded, remove from band
1556 	if (_leader != NULL) {
1557 		assert(isActor(_leader));
1558 
1559 		_leader->removeFollower(this);
1560 		_leader = NULL;
1561 	}
1562 
1563 	if (actorToPlayerID(this, playerID))
1564 		handlePlayerActorDeath(playerID);
1565 }
1566 
1567 //-----------------------------------------------------------------------
1568 //	Cause this actor to come back to life
1569 
imNotQuiteDead(void)1570 void Actor::imNotQuiteDead(void) {
1571 	if (isDead()) {
1572 		PlayerActorID       pID;
1573 
1574 		_effectiveStats.vitality = 1;
1575 		if (actorToPlayerID(this, pID))
1576 			updateBrotherControls(pID);
1577 
1578 		evaluateNeeds();
1579 	}
1580 }
1581 
1582 //-----------------------------------------------------------------------
1583 // Cuase the actor to re-assess his/her vitality
1584 
vitalityUpdate(void)1585 void Actor::vitalityUpdate(void) {
1586 	//  If we're dead, don't heal
1587 	if (isDead()) return;
1588 
1589 	// get the base stats for this actor
1590 	ActorAttributes *baseStats = getBaseStats();
1591 
1592 	// first find out if this actor is wounded
1593 	if (_effectiveStats.vitality < baseStats->vitality) {
1594 		// whole vitality number goes here
1595 		int16 recover;
1596 		int16 fractionRecover;
1597 
1598 		// get the whole number first
1599 		recover = _recPointsPerUpdate / recPointsPerVitality;
1600 
1601 		// get the fraction
1602 		fractionRecover = _recPointsPerUpdate % recPointsPerVitality;
1603 
1604 		// if there is an overrun
1605 		if (_currentRecoveryPoints + fractionRecover > recPointsPerVitality) {
1606 			// add the overrun to the whole number
1607 			recover++;
1608 			_currentRecoveryPoints = (_currentRecoveryPoints + fractionRecover) - recPointsPerVitality;
1609 		} else {
1610 			_currentRecoveryPoints += fractionRecover;
1611 		}
1612 
1613 
1614 		if (_effectiveStats.vitality + recover >=
1615 		        baseStats->vitality) {
1616 			_effectiveStats.vitality = baseStats->vitality;
1617 		} else {
1618 			_effectiveStats.vitality += recover;
1619 
1620 			//WriteStatusF( 5, " Healed: %d, rec: %d, part: %d ", effectiveStats.vitality,
1621 			//            recover, currentRecoveryPoints  );
1622 		}
1623 	}
1624 }
1625 
1626 
1627 //-----------------------------------------------------------------------
1628 //	Perform actor specific activation tasks
1629 
activateActor(void)1630 void Actor::activateActor(void) {
1631 	debugC(1, kDebugActors, "Actors: Activated %d (%s)", thisID() - 32768, objName());
1632 
1633 	evaluateNeeds();
1634 }
1635 
1636 //-----------------------------------------------------------------------
1637 //	Perfrom actor specific deactivation tasks
1638 
deactivateActor(void)1639 void Actor::deactivateActor(void) {
1640 	debugC(1, kDebugActors, "Actors: De-activated %d  (%s)", thisID() - 32768, objName());
1641 
1642 	//  Kill task
1643 	if (_curTask != NULL) {
1644 		_curTask->abortTask();
1645 		delete _curTask;
1646 		_curTask = NULL;
1647 	}
1648 
1649 	//  Kill motion task
1650 	if (_moveTask != NULL)
1651 		_moveTask->remove();
1652 
1653 	//  If banded, remove from band
1654 	if (_leader != NULL) {
1655 		assert(isActor(_leader));
1656 
1657 		_leader->removeFollower(this);
1658 		_leader = NULL;
1659 	}
1660 
1661 	//  Temporary actors get deleted upon deactivation
1662 	if ((_flags & temporary) || isDead()) {
1663 		_deactivationCounter = 10;       // actor lasts for 50 seconds
1664 	}
1665 }
1666 
1667 //-----------------------------------------------------------------------
1668 //	Delobotomize this actor
1669 
delobotomize(void)1670 void Actor::delobotomize(void) {
1671 	if (!(_flags & lobotomized)) return;
1672 
1673 	ObjectID        dObj = thisID();
1674 	scriptCallFrame scf;
1675 
1676 	_flags &= ~lobotomized;
1677 
1678 	scf.invokedObject   = dObj;
1679 	scf.enactor         = dObj;
1680 	scf.directObject    = dObj;
1681 	scf.indirectObject  = Nothing;
1682 	scf.value           = 0;
1683 
1684 	runObjectMethod(dObj, Method_Actor_onDelobotomize, scf);
1685 
1686 	evaluateNeeds();
1687 }
1688 
1689 //-----------------------------------------------------------------------
1690 //	Lobotomize this actor
1691 
lobotomize(void)1692 void Actor::lobotomize(void) {
1693 	if (_flags & lobotomized) return;
1694 
1695 	ObjectID        dObj = thisID();
1696 	scriptCallFrame scf;
1697 
1698 	//  Kill task
1699 	if (_curTask != NULL) {
1700 		_curTask->abortTask();
1701 		delete _curTask;
1702 		_curTask = NULL;
1703 	}
1704 
1705 	//  Kill motion task
1706 	if (_moveTask != NULL)
1707 		_moveTask->remove();
1708 
1709 	_flags |= lobotomized;
1710 
1711 	scf.invokedObject   = dObj;
1712 	scf.enactor         = dObj;
1713 	scf.directObject    = dObj;
1714 	scf.indirectObject  = Nothing;
1715 	scf.value           = 0;
1716 
1717 	runObjectMethod(dObj, Method_Actor_onLobotomize, scf);
1718 }
1719 
1720 //-----------------------------------------------------------------------
1721 //	Return a pointer to the base stats for this actor.  If this actor
1722 //	is a non-player actor, the base stats are in the prototype.  If this
1723 //	actor is a player actor, the base stats are in the PlayerActor
1724 //	structure.
1725 
getBaseStats(void)1726 ActorAttributes *Actor::getBaseStats(void) {
1727 	if (_disposition < dispositionPlayer)
1728 		return &((ActorProto *)prototype)->baseStats;
1729 	else
1730 		return &g_vm->_playerList[_disposition - dispositionPlayer]->baseStats;
1731 }
1732 
1733 //-----------------------------------------------------------------------
1734 //	Return the racial base enchantment flags.  If this actor
1735 //	is a non-player actor, the base stats are in the prototype.
1736 
getBaseEnchantmentEffects(void)1737 uint32 Actor::getBaseEnchantmentEffects(void) {
1738 	//if ( disposition < dispositionPlayer )
1739 	return ((ActorProto *)prototype)->baseEffectFlags;
1740 }
1741 
1742 //-----------------------------------------------------------------------
1743 //	Return the object base resistance flags.  If this actor
1744 //	is a non-player actor, the base stats are in the prototype.
1745 
getBaseResistance(void)1746 uint16 Actor::getBaseResistance(void) {
1747 	//if ( disposition < dispositionPlayer )
1748 	return ((ActorProto *)prototype)->resistance;
1749 }
1750 
1751 //-----------------------------------------------------------------------
1752 //	Return the object base immunity flags.  If this actor
1753 //	is a non-player actor, the base stats are in the prototype.
1754 
getBaseImmunity(void)1755 uint16 Actor::getBaseImmunity(void) {
1756 	//if ( disposition < dispositionPlayer )
1757 	return ((ActorProto *)prototype)->immunity;
1758 }
1759 
1760 //-----------------------------------------------------------------------
1761 //  Return the base recovery rate
1762 
getBaseRecovery(void)1763 uint16 Actor::getBaseRecovery(void) {
1764 	return BASE_REC_RATE;
1765 }
1766 
1767 //-----------------------------------------------------------------------
1768 //  Determine if specified point is within actor's reach
1769 
inReach(const TilePoint & tp)1770 bool Actor::inReach(const TilePoint &tp) {
1771 	return inRange(tp, kDefaultReach);
1772 }
1773 
1774 //-----------------------------------------------------------------------
1775 //  Determine if specified point is within an objects use range
1776 
inUseRange(const TilePoint & tp,GameObject * obj)1777 bool Actor::inUseRange(const TilePoint &tp, GameObject *obj) {
1778 	uint16  range = obj->proto()->maximumRange;
1779 
1780 	return inRange(tp, MAX(range, (uint16)kDefaultReach));
1781 }
1782 
1783 //-----------------------------------------------------------------------
1784 //	Determine if actor is immobile (i.e. can't walk)
1785 
isImmobile(void)1786 bool Actor::isImmobile(void) {
1787 	return      isDead()
1788 	            ||  hasEffect(actorImmobile)
1789 	            ||  hasEffect(actorAsleep)
1790 	            ||  hasEffect(actorParalyzed);
1791 }
1792 
1793 //-----------------------------------------------------------------------
1794 //	Return a pointer to this actor's currently readied offensive object
1795 
offensiveObject(void)1796 GameObject *Actor::offensiveObject(void) {
1797 	if (_rightHandObject != Nothing) {
1798 		assert(isObject(_rightHandObject));
1799 
1800 		GameObject  *obj = GameObject::objectAddress(_rightHandObject);
1801 
1802 		//  Any object in an actor's right hand should be a weapon
1803 		assert(obj->containmentSet() & ProtoObj::isWeapon);
1804 
1805 		return obj;
1806 	}
1807 
1808 	if (_leftHandObject != Nothing) {
1809 		assert(isObject(_leftHandObject));
1810 
1811 		GameObject  *obj = GameObject::objectAddress(_leftHandObject);
1812 
1813 		if (obj->containmentSet() & ProtoObj::isWeapon)
1814 			return obj;
1815 	}
1816 
1817 	//  If not carrying a weapon attack with self
1818 	return this;
1819 }
1820 
1821 //-----------------------------------------------------------------------
1822 //	Returns pointers to this actor's readied primary defensive object
1823 //	and optionally their scondary defensive object
1824 
defensiveObject(GameObject ** priPtr,GameObject ** secPtr)1825 void Actor::defensiveObject(GameObject **priPtr, GameObject **secPtr) {
1826 	assert(priPtr != NULL);
1827 
1828 	GameObject      *leftHandObjPtr,
1829 	                *rightHandObjPtr,
1830 	                *primary = NULL,
1831 	                 *secondary = NULL;
1832 
1833 	//  Get a pointer to the left hand object
1834 	leftHandObjPtr =    _leftHandObject != Nothing
1835 	                    ? (assert(isObject(_leftHandObject))
1836 	                       ,   GameObject::objectAddress(_leftHandObject))
1837 	                    :   NULL;
1838 
1839 	//  Get a pointer to the right hand object
1840 	rightHandObjPtr =   _rightHandObject != Nothing
1841 	                    ? (assert(isObject(_rightHandObject))
1842 	                       ,   GameObject::objectAddress(_rightHandObject))
1843 	                    :   NULL;
1844 
1845 	if (leftHandObjPtr != NULL) {
1846 		GameObject      **rightHandObjDest;
1847 
1848 		if (leftHandObjPtr->proto()->canBlock()) {
1849 			//  Left hand object is primary.  Right hand object may be
1850 			//  secondary
1851 			primary = leftHandObjPtr;
1852 			rightHandObjDest = &secondary;
1853 		} else
1854 			//  Right hand object may be primary
1855 			rightHandObjDest = &primary;
1856 
1857 		if (rightHandObjPtr != NULL && rightHandObjPtr->proto()->canBlock())
1858 			//  Right hand object is defensive
1859 			*rightHandObjDest = rightHandObjPtr;
1860 	} else {
1861 		if (rightHandObjPtr != NULL && rightHandObjPtr->proto()->canBlock())
1862 			//  Right hand object is primary defensive object
1863 			primary = rightHandObjPtr;
1864 	}
1865 
1866 	//  Return the primary pointer
1867 	*priPtr = primary;
1868 	//  Return the secondary pointer
1869 	if (secPtr != NULL) *secPtr = secondary;
1870 }
1871 
1872 //-----------------------------------------------------------------------
1873 //	Returns a pointer to the object with which this actor is currently
1874 //	blocking, if any
1875 
blockingObject(Actor * attacker)1876 GameObject *Actor::blockingObject(Actor *attacker) {
1877 	return  _moveTask != NULL
1878 	        ?   _moveTask->blockingObject(attacker)
1879 	        :   NULL;
1880 }
1881 
1882 //-----------------------------------------------------------------------
1883 //	Return the total used armor attributes
1884 
totalArmorAttributes(ArmorAttributes & armorAttribs)1885 void Actor::totalArmorAttributes(ArmorAttributes &armorAttribs) {
1886 	int         i;
1887 	ProtoObj    *thisProto = proto();
1888 
1889 	//  Plug in actor's natural values
1890 	armorAttribs.damageAbsorbtion = thisProto->damageAbsorbtion;
1891 	armorAttribs.damageDivider = MAX<uint8>(thisProto->damageDivider, 1);
1892 	armorAttribs.defenseBonus = thisProto->defenseBonus;
1893 
1894 	//  Accumulate values for all armor objects
1895 	for (i = 0; i < ARMOR_COUNT; i++) {
1896 		if (_armorObjects[i] != Nothing) {
1897 			ProtoObj    *armorProto = GameObject::protoAddress(_armorObjects[i]);
1898 
1899 			assert(armorProto != NULL);
1900 
1901 			armorAttribs.damageAbsorbtion += armorProto->damageAbsorbtion;
1902 			if (armorProto->damageDivider != 0)
1903 				armorAttribs.damageDivider *= armorProto->damageDivider;
1904 			armorAttribs.defenseBonus += armorProto->defenseBonus;
1905 		}
1906 	}
1907 }
1908 
1909 //-----------------------------------------------------------------------
1910 //  Determine if specified point is within actor's attack range
1911 
inAttackRange(const TilePoint & tp)1912 bool Actor::inAttackRange(const TilePoint &tp) {
1913 	GameObject  *weapon = offensiveObject();
1914 	uint16      range = weapon != NULL ? weapon->proto()->maximumRange : 0;
1915 
1916 	return inRange(tp, MAX(range, (uint16)kDefaultReach));
1917 }
1918 
1919 //-----------------------------------------------------------------------
1920 //	Initiate an attack upon a specified target
1921 
attack(GameObject * target)1922 void Actor::attack(GameObject *target) {
1923 	GameObject  *weapon = offensiveObject();
1924 
1925 	if (weapon != NULL)
1926 		weapon->proto()->initiateAttack(thisID(), target->thisID());
1927 }
1928 
1929 //-----------------------------------------------------------------------
1930 //	Stop all attacks on a specified target
1931 
stopAttack(GameObject * target)1932 void Actor::stopAttack(GameObject *target) {
1933 	if (_moveTask && _moveTask->isAttack() && _moveTask->targetObj == target)
1934 		_moveTask->finishAttack();
1935 }
1936 
1937 //-----------------------------------------------------------------------
1938 //	Determine if this actor can block an attack
1939 
canDefend(void)1940 bool Actor::canDefend(void) {
1941 	if (isDead()) return false;
1942 
1943 	//  Look at left hand object, generally the defensive object
1944 	if (_leftHandObject != Nothing) {
1945 		GameObject  *obj = GameObject::objectAddress(_leftHandObject);
1946 
1947 		if (obj->proto()->canBlock()) return true;
1948 	}
1949 
1950 	//  Look at right hand object, generally the offensive object
1951 	if (_rightHandObject != Nothing) {
1952 		GameObject  *obj = GameObject::objectAddress(_rightHandObject);
1953 
1954 		if (obj->proto()->canBlock()) return true;
1955 	}
1956 
1957 	return false;
1958 }
1959 
1960 //-----------------------------------------------------------------------
1961 //	Return a numeric value which roughly estimates this actor's
1962 //	offensive strength
1963 
offenseScore(void)1964 int16 Actor::offenseScore(void) {
1965 	//  REM: at this time this calculation is somewhat arbitrary
1966 
1967 	int16           score = 0;
1968 	GameObject      *weapon = offensiveObject();
1969 
1970 	if (weapon != NULL) {
1971 		ProtoObj    *proto = weapon->proto();
1972 
1973 		score += proto->weaponDamage + (proto->maximumRange / kTileUVSize);
1974 	}
1975 
1976 	//  Add average mana
1977 	score += (_effectiveStats.redMana
1978 	          +   _effectiveStats.orangeMana
1979 	          +   _effectiveStats.yellowMana
1980 	          +   _effectiveStats.greenMana
1981 	          +   _effectiveStats.blueMana
1982 	          +   _effectiveStats.violetMana)
1983 	         /   6;
1984 
1985 	score += _effectiveStats.spellcraft + _effectiveStats.brawn;
1986 
1987 	return score;
1988 }
1989 
1990 //-----------------------------------------------------------------------
1991 //	Return a numeric value which roughly estimates this actor's
1992 //	defensive strength
1993 
defenseScore(void)1994 int16 Actor::defenseScore(void) {
1995 	//  REM: at this time this calculation is somewhat arbitrary
1996 
1997 	int16           score = 0;
1998 	GameObject      *shield;
1999 	ArmorAttributes armorAttribs;
2000 
2001 	defensiveObject(&shield);
2002 
2003 	if (shield != NULL) {
2004 		ProtoObj    *proto = shield->proto();
2005 
2006 		score += proto->defenseBonus;
2007 	}
2008 
2009 	totalArmorAttributes(armorAttribs);
2010 
2011 	score += (armorAttribs.defenseBonus + armorAttribs.damageAbsorbtion)
2012 	         *   armorAttribs.damageDivider;
2013 
2014 	score += _effectiveStats.agility + _effectiveStats.vitality;
2015 
2016 	return score;
2017 }
2018 
2019 //-----------------------------------------------------------------------
2020 //  Return the sprite color translation table based upon the actor's
2021 //  color scheme
2022 
getColorTranslation(ColorTable map)2023 void Actor::getColorTranslation(ColorTable map) {
2024 	//  If actor has color table loaded, then calculate the
2025 	//  translation table.
2026 	if (_appearance
2027 	        &&  _appearance->schemeList) {
2028 		buildColorTable(map,
2029 		                _appearance->schemeList->_schemes[_colorScheme]->bank,
2030 		                11);
2031 	} else memcpy(map, identityColors, 256);
2032 }
2033 
2034 //-----------------------------------------------------------------------
2035 //  Set the current animation sequence for the actor.
2036 //
2037 //  Each time the nextAnimationFrame() is called, it will increment
2038 //  to the next frame in the sequence.
2039 
setAction(int16 newState,int16 flags)2040 int16 Actor::setAction(int16 newState, int16 flags) {
2041 	ActorAnimation      *anim;
2042 	int16                numPoses = 0;
2043 
2044 	//  Refresh the handles
2045 //  RLockHandle( appearance->animations );
2046 //  RUnlockHandle( appearance->animations );
2047 
2048 	if (_appearance == NULL) return 0;
2049 
2050 	//  If this animation has no frames, then return false
2051 	anim = _appearance->animation(newState);
2052 	if (anim)
2053 		numPoses = anim->count[_currentFacing];
2054 	if (numPoses <= 0) return 0;
2055 
2056 	//  Set up the animation
2057 	_currentAnimation = newState;
2058 	_animationFlags = flags;
2059 
2060 	//  If they haven't set the "no reset" flag, then
2061 	if (!(flags & animateNoRestart)) {
2062 		if (flags & animateReverse) _currentPose = numPoses - 1;
2063 		else _currentPose = 0;
2064 	} else {
2065 		_currentPose = clamp(0, _currentPose, numPoses - 1);
2066 	}
2067 
2068 	return numPoses;
2069 }
2070 
2071 //-----------------------------------------------------------------------
2072 //  returns true if the action is available in the current direction.
2073 //
2074 
isActionAvailable(int16 newState,bool anyDir)2075 bool Actor::isActionAvailable(int16 newState, bool anyDir) {
2076 	ActorAnimation      *anim;
2077 
2078 	//  Refresh the handles
2079 //  RLockHandle( appearance->animations );
2080 //  RUnlockHandle( appearance->animations );
2081 
2082 	if (_appearance == nullptr)
2083 		return false;
2084 
2085 	//  If this animation has no frames, then return false
2086 	anim = _appearance->animation(newState);
2087 	if (anim == nullptr)
2088 		return false;
2089 
2090 	if (anyDir) {
2091 		for (int i = 0; i < numPoseFacings; i++) {
2092 			if (anim->count[i] > 0) return true;
2093 		}
2094 	} else {
2095 		if (anim->count[_currentFacing] > 0) return true;
2096 	}
2097 
2098 	return false;
2099 }
2100 
2101 //-----------------------------------------------------------------------
2102 //  Return the number of animation frames in the specified action for the
2103 //	specified direction
2104 
animationFrames(int16 actionType,Direction dir)2105 int16 Actor::animationFrames(int16 actionType, Direction dir) {
2106 	if (_appearance == nullptr)
2107 		return 0;
2108 
2109 	ActorAnimation  *anim;
2110 
2111 	anim = _appearance->animation(actionType);
2112 
2113 	if (!anim)
2114 		return 0;
2115 
2116 	return anim->count[dir];
2117 }
2118 
2119 //-----------------------------------------------------------------------
2120 //  Update the current animation sequence to the next frame.
2121 //  Returns true if the animation sequence has finished.
2122 
nextAnimationFrame(void)2123 bool Actor::nextAnimationFrame(void) {
2124 	ActorAnimation      *anim;
2125 	int16                numPoses;
2126 
2127 	//  Refresh the handles
2128 //  RLockHandle( appearance->animations );
2129 //  RUnlockHandle( appearance->animations );
2130 
2131 	if (_appearance == NULL) {
2132 		if (_animationFlags & animateOnHold) {
2133 			return false;
2134 		} else if (_animationFlags & animateRepeat) {
2135 			_animationFlags |= animateOnHold;
2136 			return false;
2137 		} else {
2138 			_animationFlags |= animateFinished;
2139 			return true;
2140 		}
2141 	} else _animationFlags &= ~animateOnHold;
2142 
2143 	//  Get the number of frames in the animation
2144 	anim = _appearance->animation(_currentAnimation);
2145 	numPoses = anim->count[_currentFacing];
2146 	if (numPoses <= 0) {
2147 		_animationFlags |= animateFinished;
2148 		return true;                    // no poses, return DONE
2149 	}
2150 
2151 	//  If the sprite could not be displayed because it has not
2152 	//  been loaded, then don't update the animation state --
2153 	//  wait until the sprite gets loaded, and then continue
2154 	//  with the action.
2155 	if (_animationFlags & animateNotLoaded) return false;
2156 
2157 	//  If the animation has reached the last frame, then exit.
2158 	if (_animationFlags & animateFinished) return true;
2159 
2160 	if (_animationFlags & animateRandom) {
2161 		//  Select a random frame from the series.
2162 		_currentPose = g_vm->_rnd->getRandomNumber(numPoses - 1);
2163 	} else if (_animationFlags & animateReverse) {
2164 		//  Note that the logic for forward repeats is slightly
2165 		//  different for reverse repeats. Specifically, the
2166 		//  "alternate" flag is always checked when going forward,
2167 		//  but it's only checked when going backwards if the repeat
2168 		//  flag is also set. This means that an "alternate" with
2169 		//  no "repeat" will ping-pong exactly once.
2170 
2171 		if (_currentPose > 0) {
2172 			_currentPose--;
2173 
2174 			//  Check if this is the last frame
2175 			if (_currentPose <= 0 && !(_animationFlags & animateRepeat)) {
2176 				_animationFlags |= animateFinished;
2177 			}
2178 		} else if (_animationFlags & animateRepeat) {
2179 			//  If we're repeating, check for a back & forth,
2180 			//  or for a wraparound. Also checks for case of
2181 			//  a degenerate series (1 frame only)
2182 
2183 			if (_animationFlags & animateAlternate) {
2184 				_animationFlags &= ~animateReverse;
2185 				_currentPose = MIN(1, numPoses - 1);
2186 			} else {
2187 				_currentPose = numPoses - 1;
2188 			}
2189 		}
2190 	} else {
2191 		if (_currentPose < numPoses - 1) {
2192 			//  Increment the pose number
2193 			_currentPose++;
2194 
2195 			//  Check if this is the last frame
2196 			if (_currentPose >= numPoses - 1 &&
2197 			        !(_animationFlags & (animateAlternate | animateRepeat)))
2198 				_animationFlags |= animateFinished;
2199 		} else if (_animationFlags & animateAlternate) {
2200 			//  At the end of the sequence, reverse direction
2201 			_animationFlags |= animateReverse;
2202 			_currentPose = MAX(_currentPose - 1, 0);
2203 		} else if (_animationFlags & animateRepeat) {
2204 			//  Wrap back to beginning
2205 			_currentPose = 0;
2206 		} else //If Last Frame And Not Animate Repeat or Alternate
2207 			_animationFlags |= animateFinished;
2208 	}
2209 	return false;
2210 }
2211 
2212 //-----------------------------------------------------------------------
2213 //	Drop the all of the actor's inventory
2214 
dropInventory(void)2215 void Actor::dropInventory(void) {
2216 	GameObject          *obj,
2217 	                    *nextObj;
2218 
2219 	for (obj =  _data.childID != Nothing
2220 	            ?   GameObject::objectAddress(_data.childID)
2221 	            :   NULL;
2222 	        obj != NULL;
2223 	        obj = nextObj) {
2224 		nextObj =   obj->IDNext() != Nothing
2225 		            ?   GameObject::objectAddress(obj->IDNext())
2226 		            :   NULL;
2227 
2228 		//  Delete intangible objects and drop tangible objects
2229 		if (obj->containmentSet() & ProtoObj::isIntangible)
2230 			obj->deleteObjectRecursive();
2231 		else
2232 			dropInventoryObject(obj, obj->isMergeable() ? obj->getExtra() : 1);
2233 	}
2234 }
2235 
2236 //-----------------------------------------------------------------------
2237 //	Place an object into this actor's right or left hand
2238 
holdInRightHand(ObjectID objID)2239 void Actor::holdInRightHand(ObjectID objID) {
2240 	assert(isObject(objID));
2241 	_rightHandObject = objID;
2242 
2243 	if (isPlayerActor(this))
2244 		g_vm->_cnm->setUpdate(thisID());
2245 
2246 	evalActorEnchantments(this);
2247 }
2248 
holdInLeftHand(ObjectID objID)2249 void Actor::holdInLeftHand(ObjectID objID) {
2250 	assert(isObject(objID));
2251 	_leftHandObject = objID;
2252 
2253 	if (isPlayerActor(this))
2254 		g_vm->_cnm->setUpdate(thisID());
2255 
2256 	evalActorEnchantments(this);
2257 }
2258 
2259 //-----------------------------------------------------------------------
2260 //	Wear a piece of armor
2261 
wear(ObjectID objID,uint8 where)2262 void Actor::wear(ObjectID objID, uint8 where) {
2263 	assert(where < ARMOR_COUNT);
2264 
2265 	PlayerActorID       playerID;
2266 
2267 #if DEBUG
2268 	if (objID != Nothing) {
2269 		assert(isObject(objID));
2270 
2271 		GameObject      *obj = GameObject::objectAddress(objID);
2272 
2273 		assert(obj->proto()->containmentSet() & ProtoObj::isArmor);
2274 	}
2275 #endif
2276 
2277 	_armorObjects[where] = objID;
2278 
2279 	if (isPlayerActor(this))
2280 		g_vm->_cnm->setUpdate(thisID());
2281 
2282 	evalActorEnchantments(this);
2283 
2284 	if (actorToPlayerID(this, playerID)) {
2285 		updateBrotherArmor(playerID);
2286 	}
2287 }
2288 
2289 //-----------------------------------------------------------------------
2290 //  Called when the actor is on the display list and has no motion task.
2291 
updateAppearance(int32)2292 void Actor::updateAppearance(int32) {
2293 //    static uint16    count;
2294 //    count++;
2295 
2296 	if (isDead() || !isActivated() || (_flags & lobotomized)) return;
2297 
2298 #if DEBUG*0
2299 	WriteStatusF(4, "Wait Count %d Attitude %d", cycleCount, attitude);
2300 #endif
2301 
2302 #if DEBUG*0
2303 	extern void ShowObjectSection(GameObject * obj);
2304 	if (this != getCenterActor())
2305 		if (lineOfSight(getCenterActor(), this, terrainSurface))
2306 			ShowObjectSection(this);
2307 #endif
2308 
2309 	if (_appearance) {
2310 		if (animationFrames(actionStand, _currentFacing) == 1) {
2311 			if (_flags & fightStance) {
2312 				GameObject      *weapon = offensiveObject();
2313 
2314 				if (weapon == this) weapon = NULL;
2315 
2316 				if (weapon != NULL) {
2317 					ProtoObj        *weaponProto = weapon->proto();
2318 
2319 					setAction(weaponProto->fightStanceAction(thisID()), 0);
2320 				} else {
2321 					if (isActionAvailable(actionSwingHigh))
2322 						setAction(actionSwingHigh, 0);
2323 					else
2324 						setAction(actionTwoHandSwingHigh, 0);
2325 				}
2326 
2327 				_cycleCount = 0;
2328 			} else {
2329 				if (_cycleCount > 0) { //If In Wait State Between Wait Animation
2330 					_cycleCount--;
2331 
2332 					setAction(actionStand, 0);   //Just stand still
2333 				} else { // Wait Animation
2334 					if (_cycleCount == 0) { //If Just Starting Wait Animation
2335 						_cycleCount--;
2336 						switch (_attitude) { //Emotion And Character Type
2337 						//Currently Attitude Not Set So Always Hits Zero
2338 						case 0:
2339 							//Returns True If Successful No Checking Yet
2340 							setAvailableAction(actionWaitAgressive,
2341 							                   actionWaitImpatient,
2342 							                   actionWaitFriendly,
2343 							                   actionStand); // This is default
2344 							break;
2345 
2346 						case 1:
2347 							setAvailableAction(actionWaitImpatient,
2348 							                   actionWaitFriendly,
2349 							                   actionWaitAgressive,
2350 							                   actionStand);
2351 							break;
2352 
2353 						case 2:
2354 							setAvailableAction(actionWaitFriendly,
2355 							                   actionWaitImpatient,
2356 							                   actionWaitAgressive,
2357 							                   actionStand);
2358 
2359 						}
2360 					} else //Assume -1
2361 						if (nextAnimationFrame())//If Last Frame In Wait Animation
2362 							_cycleCount = g_vm->_rnd->getRandomNumber(19);
2363 				}
2364 			}
2365 		} else {
2366 			if (_currentAnimation != actionStand
2367 			        || (_animationFlags & animateRepeat) == 0)
2368 				setAction(actionStand, animateRepeat);
2369 			else
2370 				nextAnimationFrame();
2371 		}
2372 	}// End if (appearance)
2373 }
2374 
setAvailableAction(int16 action1,int16 action2,int16 action3,int16 actiondefault)2375 bool Actor::setAvailableAction(int16 action1, int16 action2, int16 action3, int16 actiondefault) {
2376 	if (setAction(action1, 0))
2377 		return true;
2378 
2379 	if (setAction(action2, 0))
2380 		return true;
2381 
2382 	if (setAction(action3, 0))
2383 		return true;
2384 
2385 	if (setAction(actiondefault, 0))
2386 		return true;
2387 
2388 	return false;
2389 }
2390 
2391 //-----------------------------------------------------------------------
2392 //	Set a new goal for this actor
2393 
setGoal(uint8 newGoal)2394 void Actor::setGoal(uint8 newGoal) {
2395 	if (_currentGoal != newGoal) {
2396 		if (_curTask != NULL) {
2397 			_curTask->abortTask();
2398 			delete _curTask;
2399 			_curTask = NULL;
2400 		}
2401 
2402 		_currentGoal = newGoal;
2403 	}
2404 }
2405 
2406 //-----------------------------------------------------------------------
2407 //  Reevaluate actor's built-in needs
2408 
evaluateNeeds(void)2409 void Actor::evaluateNeeds(void) {
2410 	if (!isDead()
2411 	        &&  isActivated()
2412 	        &&  !(_flags & lobotomized)) {
2413 		if (_disposition >= dispositionPlayer) {
2414 			if (g_vm->_act->_combatBehaviorEnabled) {
2415 				SenseInfo       info;
2416 
2417 				if (canSenseActorProperty(
2418 				            info,
2419 				            maxSenseRange,
2420 				            actorPropIDEnemy)
2421 				        ||  canSenseActorPropertyIndirectly(
2422 				            info,
2423 				            maxSenseRange,
2424 				            actorPropIDEnemy)) {
2425 					PlayerActorID   playerID = _disposition - dispositionPlayer;
2426 
2427 					if (isAggressive(playerID))
2428 						setGoal(actorGoalAttackEnemy);
2429 					else {
2430 						if (_leader != NULL && inBandingRange())
2431 							setGoal(actorGoalAvoidEnemies);
2432 						else
2433 							setGoal(actorGoalPreserveSelf);
2434 					}
2435 				} else if (_leader != NULL && inBandingRange()) {
2436 					setGoal(actorGoalFollowLeader);
2437 				} else {
2438 					setGoal(actorGoalFollowAssignment);
2439 				}
2440 			} else if (_leader != NULL && inBandingRange()) {
2441 				setGoal(actorGoalFollowLeader);
2442 			} else {
2443 				setGoal(actorGoalFollowAssignment);
2444 			}
2445 		} else {
2446 			if (_disposition == dispositionEnemy
2447 			        &&  _appearance != NULL
2448 			        &&  !hasEffect(actorNotDefenseless)) {
2449 				GameObject              *obj;
2450 				bool                    foundWeapon = false;
2451 				ContainerIterator       iter(this);
2452 
2453 				while (iter.next(&obj) != Nothing) {
2454 					ProtoObj            *proto = obj->proto();
2455 
2456 					if ((proto->containmentSet() & ProtoObj::isWeapon)
2457 					        &&  isActionAvailable(proto->fightStanceAction(thisID()))) {
2458 						foundWeapon = true;
2459 						break;
2460 					}
2461 				}
2462 
2463 				if (!foundWeapon
2464 				        && (isActionAvailable(actionSwingHigh)
2465 				            ||  isActionAvailable(actionTwoHandSwingHigh)))
2466 					foundWeapon = true;
2467 
2468 				if (!foundWeapon)
2469 					_flags |= afraid;
2470 			}
2471 
2472 			if (_flags & afraid || hasEffect(actorFear) || hasEffect(actorRepelUndead)) {
2473 				setGoal(actorGoalPreserveSelf);
2474 			} else if (_leader != NULL && inBandingRange()) {
2475 				setGoal(_leader->evaluateFollowerNeeds(this));
2476 			} else {
2477 				SenseInfo       info;
2478 
2479 				if (_disposition == dispositionEnemy
2480 				        && (getAssignment() == NULL
2481 				            ||  canSenseProtaganist(
2482 				                info,
2483 				                maxSenseRange)
2484 				            ||  canSenseProtaganistIndirectly(
2485 				                info,
2486 				                maxSenseRange))) {
2487 					setGoal(actorGoalAttackEnemy);
2488 				} else {
2489 					setGoal(actorGoalFollowAssignment);
2490 				}
2491 			}
2492 		}
2493 	}
2494 }
2495 
updateState(void)2496 void Actor::updateState(void) {
2497 	//  The actor should not be set permanently uninterruptable when
2498 	//  the actor does not have a motion task
2499 	assert(isMoving() || _actionCounter != maxuint8);
2500 
2501 	GameObject::updateState();
2502 
2503 	if (_flags & lobotomized)
2504 		return;
2505 
2506 	//  Update the action counter
2507 	if (_actionCounter != 0 && _actionCounter != maxuint8)
2508 		_actionCounter--;
2509 
2510 	if (_appearance != NULL
2511 	        &&  isDead()
2512 	        &&  isInterruptable()
2513 	        && (_moveTask == NULL
2514 	            ||  _moveTask->motionType != MotionTask::motionTypeDie)) {
2515 		int16       deadState = isActionAvailable(actionDead)
2516 		                        ?   actionDead
2517 		                        :   isActionAvailable(actionDie)
2518 		                        ?   actionDie
2519 		                        :   actionStand;
2520 
2521 		if (_currentAnimation != deadState)
2522 			MotionTask::die(*this);
2523 		return;
2524 	}
2525 
2526 	if (!isDead()) {
2527 		if (this == getCenterActor()) return;
2528 
2529 		if (_flags & specialAttack) {
2530 			_flags &= ~specialAttack;
2531 
2532 			if (_currentTarget != NULL) {
2533 				scriptCallFrame scf;
2534 				ObjectID        dObj = thisID();
2535 
2536 				scf.invokedObject   = dObj;
2537 				scf.enactor         = dObj;
2538 				scf.directObject    = dObj;
2539 				scf.indirectObject  = _currentTarget->thisID();
2540 				scf.value           = 0;
2541 
2542 				runObjectMethod(dObj, Method_Actor_onSpecialAttack, scf);
2543 
2544 				//  If this actor is now deactivated or lobotomized
2545 				//  return immediately
2546 				if (isDead() || !isActivated() || (_flags & lobotomized))
2547 					return;
2548 			}
2549 		}
2550 
2551 		switch (_currentGoal) {
2552 		case actorGoalFollowAssignment: {
2553 			ActorAssignment     *assign = getAssignment();
2554 
2555 			//  Iterate until there is no assignment, or the current
2556 			//  assignment is valid
2557 			while (assign != NULL && !assign->isValid()) {
2558 				g_vm->_act->_updatesViaScript++;
2559 				scriptCallFrame scf;
2560 				ObjectID        dObj = thisID();
2561 
2562 				delete assign;
2563 
2564 				//  Notify the scripts that the assignment has ended
2565 				scf.invokedObject   = dObj;
2566 				scf.enactor         = dObj;
2567 				scf.directObject    = dObj;
2568 				scf.indirectObject  = Nothing;
2569 				scf.value           = 0;
2570 
2571 				runObjectMethod(dObj, Method_Actor_onEndAssignment, scf);
2572 
2573 				//  If this actor is now deactivated or lobotomized
2574 				//  return immediately
2575 				if (isDead() || !isActivated() || (_flags & lobotomized))
2576 					return;
2577 
2578 				//  Re-get the assignment
2579 				assign = getAssignment();
2580 			}
2581 
2582 			//  If there is no assignment at this point, call the
2583 			//  schedule to setup a new assignment.
2584 			if (assign == NULL && _schedule != 0) {
2585 				g_vm->_act->_updatesViaScript++;
2586 				assert(_curTask == NULL);
2587 
2588 				scriptCallFrame scf;
2589 
2590 				scf.invokedObject   = Nothing;
2591 				scf.enactor         = Nothing;
2592 				scf.directObject    = thisID();
2593 				scf.indirectObject  = Nothing;
2594 				scf.value           = 0;
2595 
2596 				runScript(_schedule, scf);
2597 
2598 				//  Re-get the assignment
2599 				assign = getAssignment();
2600 			}
2601 
2602 			//  Have the assignment create a new task
2603 			if (assign != NULL && _curTask == NULL)
2604 				_curTask = assign->createTask();
2605 		}
2606 		break;
2607 
2608 		case actorGoalPreserveSelf:
2609 
2610 			if (_leader != NULL || _followers != NULL)
2611 				disband();
2612 
2613 			if (_curTask == NULL) {
2614 				if ((_curTask = newTaskStack(this)) != NULL) {
2615 					Task    *task = new GoAwayFromActorTask(
2616 					                    _curTask,
2617 					                    ActorPropertyTarget(
2618 					                        _disposition == dispositionEnemy
2619 					                        ?   actorPropIDPlayerActor
2620 					                        :   actorPropIDEnemy),
2621 					                    true);
2622 
2623 					if (task != NULL)
2624 						_curTask->setTask(task);
2625 					else {
2626 						delete _curTask;
2627 						_curTask = NULL;
2628 					}
2629 				}
2630 			}
2631 			break;
2632 
2633 		case actorGoalAttackEnemy:
2634 
2635 			if (_curTask == NULL) {
2636 				if ((_curTask = newTaskStack(this)) != NULL) {
2637 					uint8   disp =  _leader != NULL
2638 					                ?   _leader->_disposition
2639 					                :   _disposition;
2640 
2641 					Task    *task = new    HuntToKillTask(
2642 					                    _curTask,
2643 					                    ActorPropertyTarget(
2644 					                        disp == dispositionEnemy
2645 					                        ?   actorPropIDPlayerActor
2646 					                        :   actorPropIDEnemy));
2647 
2648 					if (task != NULL)
2649 						_curTask->setTask(task);
2650 					else {
2651 						delete _curTask;
2652 						_curTask = NULL;
2653 					}
2654 				}
2655 			}
2656 			break;
2657 
2658 		case actorGoalFollowLeader:
2659 
2660 			assert(isActor(_leader));
2661 			assert(_followers == NULL);
2662 
2663 			if (_curTask == NULL)
2664 				_curTask = _leader->createFollowerTask(this);
2665 
2666 			break;
2667 
2668 		case actorGoalAvoidEnemies:
2669 
2670 			assert(isActor(_leader));
2671 			assert(_followers == NULL);
2672 
2673 			if (_curTask == NULL) {
2674 				if ((_curTask = newTaskStack(this)) != NULL) {
2675 					Task    *task = new BandAndAvoidEnemiesTask(_curTask);
2676 
2677 					if (task != NULL)
2678 						_curTask->setTask(task);
2679 					else {
2680 						delete _curTask;
2681 						_curTask = NULL;
2682 					}
2683 				}
2684 			}
2685 		}
2686 	}
2687 }
2688 
2689 //-----------------------------------------------------------------------
2690 //	This routine is used to notify the actor that a task has ended.  The
2691 //	actor should handle the situation appropriately
2692 
handleTaskCompletion(TaskResult result)2693 void Actor::handleTaskCompletion(TaskResult result) {
2694 	//  The task is done, get rid of it
2695 	delete _curTask;
2696 	_curTask = NULL;
2697 
2698 	switch (_currentGoal) {
2699 	case actorGoalFollowAssignment: {
2700 		ActorAssignment     *assign = getAssignment();
2701 
2702 		//  If we've gotten to this point, there had better be an
2703 		//  assignment, or something is amiss
2704 		assert(assign != NULL);
2705 
2706 		//  Notify the assignment
2707 		assign->handleTaskCompletion(result);
2708 	}
2709 	break;
2710 	}
2711 }
2712 
2713 //-----------------------------------------------------------------------
2714 //	This function will cause the actor to react to an offensive act
2715 
handleOffensiveAct(Actor * attacker)2716 void Actor::handleOffensiveAct(Actor *attacker) {
2717 	ObjectID        dObj = thisID();
2718 	scriptCallFrame scf;
2719 
2720 	scf.invokedObject   = dObj;
2721 	scf.enactor         = dObj;
2722 	scf.directObject    = dObj;
2723 	scf.indirectObject  = attacker->thisID();
2724 	scf.value           = 0;
2725 
2726 	runObjectMethod(dObj, Method_Actor_onAttacked, scf);
2727 
2728 	if (_disposition == dispositionFriendly) {
2729 		if (attacker->_disposition >= dispositionPlayer) {
2730 			_disposition = dispositionEnemy;
2731 			evaluateNeeds();
2732 		}
2733 	}
2734 }
2735 
2736 //-----------------------------------------------------------------------
2737 //	This function will cause the actor to react appropriately to taking
2738 //	damage.
2739 
handleDamageTaken(uint8 damage)2740 void Actor::handleDamageTaken(uint8 damage) {
2741 	uint8       combatBehavior = ((ActorProto *)prototype)->combatBehavior;
2742 
2743 	if (combatBehavior == behaviorHungry) return;
2744 
2745 	if (offensiveObject() == this
2746 	        &&  !isActionAvailable(actionSwingHigh)
2747 	        &&  !isActionAvailable(actionTwoHandSwingHigh)
2748 	        &&  !hasEffect(actorNotDefenseless)) {
2749 		_flags |= afraid;
2750 		return;
2751 	}
2752 
2753 	if (combatBehavior != behaviorHungry
2754 	        && (_flags & temporary)
2755 	        &&  !hasEffect(actorFear)
2756 	        &&  !hasEffect(actorRepelUndead)) {
2757 		if (_flags & afraid) {
2758 			//  Let's give monsters a small chance of regaining their courage
2759 			if ((uint16)g_vm->_rnd->getRandomNumber(0xffff) <= 0x3fff)
2760 				_flags &= ~afraid;
2761 		} else {
2762 			int16       i,
2763 			            fellowBandMembers,
2764 			            vitality = _effectiveStats.vitality;
2765 			uint32      moraleBase = ((int32)damage << 16) / vitality,
2766 			            bonus = 0;
2767 
2768 			//  Adjustment added by Talin to globally reduce the amount of cowardice
2769 			//  in the game. I may reduce it further depending on playtesting.
2770 			moraleBase /= 3;
2771 
2772 			//  Adjust morale base according to the combat behavior
2773 			if (combatBehavior == behaviorCowardly)
2774 				moraleBase += moraleBase / 2;
2775 			else if (combatBehavior == behaviorBerserk)
2776 				moraleBase -= moraleBase / 2;
2777 
2778 			//  Determine how many fellow band members this actor has.
2779 			if (_leader != NULL)
2780 				fellowBandMembers = _leader->_followers->size();
2781 			else if (_followers != NULL)
2782 				fellowBandMembers = _followers->size();
2783 			else
2784 				fellowBandMembers = 0;
2785 
2786 			//  REM: this calculation can be done via a lookup table
2787 			for (i = 0; i < fellowBandMembers; i++)
2788 				bonus += ((1 << 16) - bonus) >> 4;
2789 
2790 			//  Adjust the morale base to acount for the number of fellow band
2791 			//  members
2792 			moraleBase -= bonus * moraleBase >> 16;
2793 
2794 			//  Test this actor's morale
2795 			if ((uint16)g_vm->_rnd->getRandomNumber(0xffff) <= moraleBase)
2796 				_flags |= afraid;
2797 		}
2798 	}
2799 }
2800 
2801 //-----------------------------------------------------------------------
2802 //	This function is called when this actor successfully causes damage
2803 //	to another actor.
2804 
handleSuccessfulStrike(Actor * target,int8 damage)2805 void Actor::handleSuccessfulStrike(Actor *target, int8 damage) {
2806 	PlayerActorID       playerID;
2807 
2808 	if (actorToPlayerID(this, playerID)) {
2809 		PlayerActor     *player = getPlayerActorAddress(playerID);
2810 		int16           ratio;
2811 
2812 		//  If it's a weak monster, then reduce amount of vitality advanced.
2813 		//  If we are twice as vital, then get half the exp's. If we are three times
2814 		//  as vital, get 1/3 the exp. etc.
2815 		ratio = clamp(1, getBaseStats()->vitality / target->getBaseStats()->vitality, 4);
2816 
2817 		player->vitalityAdvance(damage / ratio);
2818 	}
2819 }
2820 
2821 //-----------------------------------------------------------------------
2822 //	This function is called when this actor successfully kills another
2823 //	actor.
2824 
handleSuccessfulKill(Actor * target)2825 void Actor::handleSuccessfulKill(Actor *target) {
2826 	PlayerActorID       playerID;
2827 
2828 	if (this != target && actorToPlayerID(this, playerID)) {
2829 		const char vowels[] = "AEIOU";
2830 
2831 		PlayerActor     *player = getPlayerActorAddress(playerID);
2832 		int16           ratio;
2833 		int16           points = target->getBaseStats()->vitality;
2834 		const char      *monsterName = target->objName();
2835 		const char      *aStr;
2836 
2837 		//  If it's a weak monster, then reduce amount of vitality advanced.
2838 		//  If we are twice as vital, then get half the exp's. If we are three times
2839 		//  as vital, get 1/3 the exp. etc.
2840 		ratio = clamp(1, getBaseStats()->vitality / points, 4);
2841 
2842 		player->vitalityAdvance(points / ratio);
2843 
2844 		aStr =  target->getNameIndex() == 0
2845 		        ?   strchr(vowels, toupper(monsterName[0])) == NULL
2846 		        ?   "a "
2847 		        :   "an "
2848 		        :   "";
2849 		StatusMsg("%s has killed %s%s.", objName(), aStr, monsterName);
2850 	}
2851 }
2852 
2853 //-----------------------------------------------------------------------
2854 //	Determine if this actor can block a blow from the specified relative
2855 //	direction with the specified defensive object.
2856 
canBlockWith(GameObject * defenseObj,Direction relativeDir)2857 bool Actor::canBlockWith(GameObject *defenseObj, Direction relativeDir) {
2858 	assert(defenseObj->proto()->canBlock());
2859 	assert(relativeDir < 8);
2860 
2861 	//  Assuming that the actor may increment or decrement their facing
2862 	//  to block, these masks represent the possible relative facings
2863 	//  based upon the current relative facing
2864 	const uint8 dirMaskArray[8] = {
2865 		0x83,       //  10000011
2866 		0x07,       //  00000111
2867 		0x0E,       //  00001110
2868 		0x1C,       //  00011100
2869 		0x38,       //  00111000
2870 		0x70,       //  01110000
2871 		0xE0,       //  11100000
2872 		0xC1        //  11000001
2873 	};
2874 
2875 	return (defenseObj->proto()->defenseDirMask()
2876 	        &   dirMaskArray[relativeDir])
2877 	       !=  0;
2878 }
2879 
2880 //-----------------------------------------------------------------------
2881 //	This function is called to notify this actor of an impending attack
2882 
evaluateMeleeAttack(Actor * attacker)2883 void Actor::evaluateMeleeAttack(Actor *attacker) {
2884 	if (isInterruptable() && !isDead()) {
2885 		Direction       relativeDir;
2886 		GameObject      *defenseObj,
2887 		                *primary,
2888 		                *secondary;
2889 		bool            canBlockWithPrimary;
2890 
2891 		//  Compute the attacker's direction relative to this actor's
2892 		//  facing
2893 		relativeDir = ((attacker->_data.location - _data.location).quickDir()
2894 		               -   _currentFacing) & 0x7;
2895 
2896 		//  Get pointers to this actors primary and secondary defensive
2897 		//  objects
2898 		defensiveObject(&primary, &secondary);
2899 
2900 		canBlockWithPrimary =       primary != NULL
2901 		                            &&  canBlockWith(primary, relativeDir);
2902 
2903 		if (canBlockWithPrimary) {
2904 			bool            canBlockWithSecondary;
2905 
2906 			canBlockWithSecondary =     secondary != NULL
2907 			                            &&  canBlockWith(
2908 			                                secondary,
2909 			                                relativeDir);
2910 
2911 			if (canBlockWithSecondary) {
2912 				//  If we can block with either primary or secondary
2913 				//  there is a 25% chance of using the secondary
2914 				defenseObj = (g_vm->_rnd->getRandomNumber(3) != 0) ? primary : secondary;
2915 			} else {
2916 				//  The primary defensive object will be used
2917 				defenseObj = primary;
2918 			}
2919 		} else
2920 			defenseObj = NULL;
2921 
2922 		if (defenseObj != NULL) {
2923 			//  Start a defensive motion
2924 			defenseObj->proto()->initiateDefense(
2925 			    defenseObj->thisID(),
2926 			    thisID(),
2927 			    attacker->thisID());
2928 		} else {
2929 			if (isActionAvailable(actionJumpUp))
2930 				MotionTask::dodge(*this, *attacker);
2931 		}
2932 	}
2933 }
2934 
2935 //-----------------------------------------------------------------------
2936 //	Cause this actor to accept another actor as his leader.  If the actor
2937 //	has followers, this will band those followers to the new leader as
2938 //	well.
2939 
bandWith(Actor * newLeader)2940 void Actor::bandWith(Actor *newLeader) {
2941 	assert(_leader == NULL);
2942 
2943 	//  If the actor we're banding with is not the leader, then band
2944 	//  with his leader
2945 	if (newLeader->_leader != NULL) {
2946 		newLeader = newLeader->_leader;
2947 		assert(newLeader->_leader == NULL);
2948 	}
2949 
2950 	//  If this actor himself does not have followers then its really
2951 	//  simple, otherwise we need to band all of this actor's followers
2952 	//  with the new leader.
2953 	if (_followers == NULL) {
2954 		if (newLeader->addFollower(this)) _leader = newLeader;
2955 	} else {
2956 		int16       i,
2957 		            oldFollowerCount = _followers->size();
2958 		Actor       **oldFollowers = new Actor * [oldFollowerCount];
2959 
2960 		if (oldFollowers != NULL) {
2961 			//  Copy the list followers
2962 			for (i = 0; i < oldFollowerCount; i++) {
2963 				oldFollowers[i] = (*_followers)[i];
2964 				assert(oldFollowers[i]->_leader == this);
2965 			}
2966 
2967 			//  Disband all of the old followers
2968 			for (i = 0; i < oldFollowerCount; i++)
2969 				oldFollowers[i]->disband();
2970 
2971 			assert(_followers == NULL);
2972 
2973 			//  Add this actor and all of the old followers to the new
2974 			//  leader's followers.
2975 			if (newLeader->addFollower(this)) {
2976 				_leader = newLeader;
2977 
2978 				for (i = 0; i < oldFollowerCount; i++)
2979 					oldFollowers[i]->bandWith(newLeader);
2980 			}
2981 
2982 			delete [] oldFollowers;
2983 		}
2984 	}
2985 
2986 	evaluateNeeds();
2987 }
2988 
2989 //-----------------------------------------------------------------------
2990 //	Simply causes this actor to be removed from his current band.
2991 
disband(void)2992 void Actor::disband(void) {
2993 	if (_leader != NULL) {
2994 		_leader->removeFollower(this);
2995 		_leader = NULL;
2996 
2997 		evaluateNeeds();
2998 	} else if (_followers != NULL) {
2999 		int16       i;
3000 
3001 		for (i = 0; i < _followers->size(); i++) {
3002 			Actor   *follower = (*_followers)[i];
3003 
3004 			follower->_leader = NULL;
3005 			follower->evaluateNeeds();
3006 		}
3007 
3008 		delete _followers;
3009 		_followers = NULL;
3010 	}
3011 }
3012 
3013 //-----------------------------------------------------------------------
3014 //	Add the specified actor to the list of this actor's followers.
3015 
addFollower(Actor * newBandMember)3016 bool Actor::addFollower(Actor *newBandMember) {
3017 	//  The new band member should not be a leader of another band or
3018 	//  a follower of another leader
3019 	assert(newBandMember->_leader == NULL);
3020 	assert(newBandMember->_followers == NULL);
3021 
3022 	//  Allocate a new band, if needed
3023 	if (_followers == NULL && (_followers = new Band(this)) == NULL)
3024 		return false;
3025 
3026 	return _followers->add(newBandMember);
3027 }
3028 
3029 //-----------------------------------------------------------------------
3030 //	Remove the specified actor from this actor's list of followers.
3031 
removeFollower(Actor * bandMember)3032 void Actor::removeFollower(Actor *bandMember) {
3033 	assert(bandMember->_leader == this);
3034 	assert(_followers != NULL);
3035 
3036 	int16       i;
3037 
3038 	_followers->remove(bandMember);
3039 	if (_followers->size() == 0) {
3040 		delete _followers;
3041 		_followers = NULL;
3042 	} else {
3043 		uint16      moraleBonus = 0;
3044 
3045 		for (i = 0; i < _followers->size(); i++)
3046 			moraleBonus += ((1 << 16) - moraleBonus) >> 4;
3047 
3048 		for (i = 0; i < _followers->size(); i++) {
3049 			Actor       *follower = (*_followers)[i];
3050 			ActorProto  *proto = (ActorProto *)follower->prototype;
3051 			uint8       combatBehavior = proto->combatBehavior;
3052 
3053 			if (follower->_currentGoal == actorGoalAttackEnemy
3054 			        &&  combatBehavior != behaviorHungry) {
3055 				uint32      moraleBase;
3056 
3057 				moraleBase =    combatBehavior == behaviorCowardly
3058 				                ? (1 << 16) / 4
3059 				                :   combatBehavior == behaviorSmart
3060 				                ? (1 << 16) / 8
3061 				                : (1 << 16) / 16;
3062 
3063 				moraleBase -= moraleBase * moraleBonus >> 16;
3064 
3065 				if ((uint16)g_vm->_rnd->getRandomNumber(0xffff) <= moraleBase)
3066 					follower->_flags |= afraid;
3067 			}
3068 		}
3069 	}
3070 }
3071 
3072 //-----------------------------------------------------------------------
3073 //	Create a task for a follower of this actor.  This is called when a
3074 //	follower has no task.
3075 
createFollowerTask(Actor * bandMember)3076 TaskStack *Actor::createFollowerTask(Actor *bandMember) {
3077 	assert(bandMember->_leader == this);
3078 
3079 	TaskStack   *ts = NULL;
3080 
3081 	if ((ts = newTaskStack(bandMember)) != NULL) {
3082 		Task    *task = new BandTask(ts);
3083 
3084 		if (task != NULL)
3085 			ts->setTask(task);
3086 		else {
3087 			delete ts;
3088 			ts = NULL;
3089 		}
3090 	}
3091 
3092 	return ts;
3093 }
3094 
3095 //-----------------------------------------------------------------------
3096 //	Evaluate a follower's needs and give him an approriate goal.
3097 
evaluateFollowerNeeds(Actor * follower)3098 uint8 Actor::evaluateFollowerNeeds(Actor *follower) {
3099 	assert(follower->_leader == this);
3100 
3101 	SenseInfo       info;
3102 
3103 	if ((_disposition == dispositionEnemy
3104 	        &&  follower->canSenseProtaganist(info, maxSenseRange))
3105 	        || (_disposition >= dispositionPlayer
3106 	            &&  follower->canSenseActorProperty(
3107 	                info,
3108 	                maxSenseRange,
3109 	                actorPropIDEnemy)))
3110 		return actorGoalAttackEnemy;
3111 
3112 	return actorGoalFollowLeader;
3113 }
3114 
3115 //  Returns 0 if not moving, 1 if path being calculated,
3116 //  2 if path being followed.
pathFindState(void)3117 bool Actor::pathFindState(void) {
3118 	if (_moveTask == NULL)
3119 		return 0;
3120 	if (_moveTask->pathFindTask)
3121 		return 1;
3122 	return 2;
3123 }
3124 
3125 //-----------------------------------------------------------------------
3126 //  Add knowledge package to actor
3127 
addKnowledge(uint16 kID)3128 bool Actor::addKnowledge(uint16 kID) {
3129 	for (int i = 0; i < ARRAYSIZE(_knowledge); i++) {
3130 		if (_knowledge[i] == 0) {
3131 			_knowledge[i] = kID;
3132 			return true;
3133 		}
3134 	}
3135 	return false;
3136 }
3137 
3138 //-----------------------------------------------------------------------
3139 //  Remove knowledge package from actor
3140 
removeKnowledge(uint16 kID)3141 bool Actor::removeKnowledge(uint16 kID) {
3142 	for (int i = 0; i < ARRAYSIZE(_knowledge); i++) {
3143 		if (_knowledge[i] == kID) {
3144 			_knowledge[i] = 0;
3145 			return true;
3146 		}
3147 	}
3148 	return false;
3149 }
3150 
3151 //-----------------------------------------------------------------------
3152 //  Remove all knowledge package from actor
3153 
clearKnowledge(void)3154 void Actor::clearKnowledge(void) {
3155 	for (int i = 0; i < ARRAYSIZE(_knowledge); i++) {
3156 		_knowledge[i] = 0;
3157 	}
3158 }
3159 
3160 //-----------------------------------------------------------------------
3161 //  Called to evaluate actor knowledge
3162 
useKnowledge(scriptCallFrame & scf)3163 void Actor::useKnowledge(scriptCallFrame &scf) {
3164 	uint16          bestResponsePri = 0,
3165 	                bestResponseClass = 0,
3166 	                bestResponseCode = 0;
3167 
3168 	//  First, search for the class with the best response
3169 
3170 	for (int i = 0; i < ARRAYSIZE(_knowledge); i++) {
3171 		if (_knowledge[i]) {
3172 			scriptResult    res;
3173 
3174 			//  Run the script to eval the response of this
3175 			//  knowledge package
3176 
3177 			res = runMethod(_knowledge[i],
3178 			                builtinAbstract,
3179 			                0,
3180 			                Method_KnowledgePackage_evalResponse,
3181 			                scf);
3182 
3183 			//  If script ran OK, then look at result
3184 
3185 			if (res == scriptResultFinished) {
3186 				//  break up return code into priority and
3187 				//  response code
3188 
3189 				int16           pri = scf.returnVal >> 8,
3190 				                response = scf.returnVal & 0xff;
3191 
3192 				if (pri > 0) {
3193 					//  Add a bit of jitter to response
3194 
3195 					pri += g_vm->_rnd->getRandomNumber(3);
3196 
3197 					if (pri > bestResponsePri) {
3198 						bestResponsePri = pri;
3199 						bestResponseClass = _knowledge[i];
3200 						bestResponseCode = response;
3201 					}
3202 				}
3203 			}
3204 		}
3205 	}
3206 
3207 	//  Then, callback whichever one responded best
3208 
3209 	if (bestResponsePri > 0) {
3210 		//  Run the script to eval the response of this
3211 		//  knowledge package
3212 
3213 		scf.responseType = bestResponseCode;
3214 
3215 		runMethod(bestResponseClass,
3216 		          builtinAbstract,
3217 		          0,
3218 		          Method_KnowledgePackage_executeResponse,
3219 		          scf);
3220 	} else {
3221 		scf.returnVal = actionResultNotDone;
3222 	}
3223 }
3224 
3225 //-----------------------------------------------------------------------
3226 //	Polling function to determine if any of this actor's followers can
3227 //	sense a protaganist within a specified range
3228 
canSenseProtaganistIndirectly(SenseInfo & info,int16 range)3229 bool Actor::canSenseProtaganistIndirectly(SenseInfo &info, int16 range) {
3230 	if (_followers != NULL) {
3231 		int         i;
3232 
3233 		for (i = 0; i < _followers->size(); i++) {
3234 			if ((*_followers)[i]->canSenseProtaganist(info, range))
3235 				return true;
3236 		}
3237 	}
3238 
3239 	return false;
3240 }
3241 
3242 //-----------------------------------------------------------------------
3243 //	Polling function to determine if any of this actor's followers can
3244 //	sense a specific actor within a specified range
3245 
canSenseSpecificActorIndirectly(SenseInfo & info,int16 range,Actor * a)3246 bool Actor::canSenseSpecificActorIndirectly(
3247     SenseInfo   &info,
3248     int16       range,
3249     Actor       *a) {
3250 	if (_followers != NULL) {
3251 		int         i;
3252 
3253 		for (i = 0; i < _followers->size(); i++) {
3254 			if ((*_followers)[i]->canSenseSpecificActor(info, range, a))
3255 				return true;
3256 		}
3257 	}
3258 
3259 	return false;
3260 }
3261 
3262 //-----------------------------------------------------------------------
3263 //	Polling function to determine if any of this actor's followers can
3264 //	sense a specific object within a specified range
3265 
canSenseSpecificObjectIndirectly(SenseInfo & info,int16 range,ObjectID obj)3266 bool Actor::canSenseSpecificObjectIndirectly(
3267     SenseInfo   &info,
3268     int16       range,
3269     ObjectID    obj) {
3270 	if (_followers != NULL) {
3271 		int         i;
3272 
3273 		for (i = 0; i < _followers->size(); i++) {
3274 			if ((*_followers)[i]->canSenseSpecificObject(info, range, obj))
3275 				return true;
3276 		}
3277 	}
3278 
3279 	return false;
3280 }
3281 
3282 //-----------------------------------------------------------------------
3283 //	Polling function to determine if any of this actor's followers can
3284 //	sense an actor with a specified property within a specified range
3285 
canSenseActorPropertyIndirectly(SenseInfo & info,int16 range,ActorPropertyID prop)3286 bool Actor::canSenseActorPropertyIndirectly(
3287     SenseInfo           &info,
3288     int16               range,
3289     ActorPropertyID     prop) {
3290 	if (_followers != NULL) {
3291 		int         i;
3292 
3293 		for (i = 0; i < _followers->size(); i++) {
3294 			if ((*_followers)[i]->canSenseActorProperty(info, range, prop))
3295 				return true;
3296 		}
3297 	}
3298 
3299 	return false;
3300 }
3301 
3302 //-----------------------------------------------------------------------
3303 //	Polling function to determine if any of this actor's followers can
3304 //	sense an object with a specified property within a specified range
3305 
canSenseObjectPropertyIndirectly(SenseInfo & info,int16 range,ObjectPropertyID prop)3306 bool Actor::canSenseObjectPropertyIndirectly(
3307     SenseInfo           &info,
3308     int16               range,
3309     ObjectPropertyID    prop) {
3310 	if (_followers != NULL) {
3311 		int         i;
3312 
3313 		for (i = 0; i < _followers->size(); i++) {
3314 			if ((*_followers)[i]->canSenseObjectProperty(info, range, prop))
3315 				return true;
3316 		}
3317 	}
3318 
3319 	return false;
3320 }
3321 
3322 
3323 //-----------------------------------------------------------------------
3324 // Mana check - spell casting uses this to check whether an actor
3325 //   has enough mana to cast a spell & to remove that mana if
3326 //   it's there
3327 
3328 #define NO_MONSTER_MANA 1
3329 
takeMana(ActorManaID i,int8 dMana)3330 bool Actor::takeMana(ActorManaID i, int8 dMana) {
3331 #if NO_MONSTER_MANA
3332 	if (!isPlayerActor(this))
3333 		return true;
3334 #endif
3335 	assert(i >= manaIDRed && i <= manaIDViolet);
3336 	if ((&_effectiveStats.redMana)[i] < dMana)
3337 		return false;
3338 	(&_effectiveStats.redMana)[i] -= dMana;
3339 	updateIndicators();
3340 	return true;
3341 }
3342 
hasMana(ActorManaID i,int8 dMana)3343 bool Actor::hasMana(ActorManaID i, int8 dMana) {
3344 #if NO_MONSTER_MANA
3345 	if (!isPlayerActor(this))
3346 		return true;
3347 #endif
3348 	assert(i >= manaIDRed && i <= manaIDViolet);
3349 	if ((&_effectiveStats.redMana)[i] < dMana)
3350 		return false;
3351 	return true;
3352 }
3353 
3354 //-----------------------------------------------------------------------
3355 // Saving throw funcion
3356 
makeSavingThrow(void)3357 bool Actor::makeSavingThrow(void) {
3358 	return false;
3359 }
3360 
3361 //-------------------------------------------------------------------
3362 //	Determine if the actors are currently initialized
3363 
areActorsInitialized(void)3364 bool areActorsInitialized(void) {
3365 	return g_vm->_act->_actorList.size() > 0;
3366 }
3367 
GetRandomBetween(int start,int end)3368 int16 GetRandomBetween(int start, int end) {
3369 	return g_vm->_rnd->getRandomNumberRng(start, end - 1);
3370 }
3371 
updateActorStates(void)3372 void updateActorStates(void) {
3373 	if (g_vm->_act->_actorStatesPaused) return;
3374 
3375 	int32 actorIndex;
3376 
3377 	actorIndex = g_vm->_act->_baseActorIndex = (g_vm->_act->_baseActorIndex + 1) & ActorManager::kEvalRateMask;
3378 	while (actorIndex < kActorCount) {
3379 		Actor   *a = g_vm->_act->_actorList[actorIndex];
3380 
3381 		if (isWorld(a->IDParent()))
3382 			a->evaluateNeeds();
3383 
3384 		actorIndex += ActorManager::kEvalRate;
3385 	}
3386 
3387 	g_vm->_act->_updatesViaScript = 0;
3388 	for (actorIndex = 0; actorIndex < kActorCount; actorIndex++) {
3389 		Actor   *a = g_vm->_act->_actorList[actorIndex];
3390 
3391 		if (isWorld(a->IDParent()) && a->isActivated())
3392 			a->updateState();
3393 	}
3394 }
3395 
3396 //-------------------------------------------------------------------
3397 
pauseActorStates(void)3398 void pauseActorStates(void) {
3399 	g_vm->_act->_actorStatesPaused = true;
3400 }
3401 
3402 //-------------------------------------------------------------------
3403 
resumeActorStates(void)3404 void resumeActorStates(void) {
3405 	g_vm->_act->_actorStatesPaused = false;
3406 }
3407 
3408 //-------------------------------------------------------------------
3409 
setCombatBehavior(bool enabled)3410 void setCombatBehavior(bool enabled) {
3411 	PlayerActor *player = nullptr;
3412 	LivingPlayerActorIterator iter;
3413 
3414 	g_vm->_act->_combatBehaviorEnabled = enabled;
3415 
3416 	for (player = iter.first(); player != NULL; player = iter.next())
3417 		player->getActor()->evaluateNeeds();
3418 }
3419 
3420 //-------------------------------------------------------------------
3421 //	Initialize the actor list
3422 
ResourceActor(Common::SeekableReadStream * stream)3423 ResourceActor::ResourceActor(Common::SeekableReadStream *stream) : ResourceGameObject(stream) {
3424 	faction = stream->readByte();
3425 	colorScheme = stream->readByte();
3426 	appearanceID = stream->readSint32BE();
3427 	attitude = stream->readSByte();
3428 	mood = stream->readSByte();
3429 	disposition = stream->readByte();
3430 	currentFacing = stream->readByte();
3431 	tetherLocU = stream->readSint16LE();
3432 	tetherLocV = stream->readSint16LE();
3433 	tetherDist = stream->readSint16LE();
3434 	leftHandObject = stream->readUint16LE();
3435 	rightHandObject = stream->readUint16LE();
3436 	for (int i = 0; i < 16; ++i) {
3437 		knowledge[i] = stream->readUint16LE();
3438 	}
3439 	schedule = stream->readUint16LE();
3440 	for (int i = 0; i < 18; ++i) { // padding bytes = not neccessary?
3441 		reserved[i] = stream->readByte();
3442 	}
3443 }
3444 
initActors(void)3445 void initActors(void) {
3446 	//  Load actors
3447 	int i, resourceActorCount;
3448 	Common::Array<ResourceActor> resourceActorList;
3449 	Common::SeekableReadStream *stream;
3450 	const int resourceActorSize = 91; // size of the packed struct
3451 
3452 	resourceActorCount = listRes->size(kActorListID)
3453 	                     / resourceActorSize;
3454 
3455 	if (resourceActorCount < 1)
3456 		error("Unable to load Actors");
3457 
3458 	if ((stream = loadResourceToStream(listRes, kActorListID, "res actor list")) == nullptr)
3459 		error("Unable to load Actors");
3460 
3461 	//  Read the resource actors
3462 	for (int k = 0; k < resourceActorCount; ++k) {
3463 		ResourceActor res(stream);
3464 		resourceActorList.push_back(res);
3465 	}
3466 
3467 	delete stream;
3468 
3469 	for (i = 0; i < resourceActorCount; i++) {
3470 		//  Initialize the actors with the resource data
3471 		Actor *a = new Actor(resourceActorList[i]);
3472 
3473 		a->_index = i + ActorBaseID;
3474 
3475 		g_vm->_act->_actorList.push_back(a);
3476 	}
3477 
3478 	//  Place all of the extra actors in actor limbo
3479 	for (; i < kActorCount; i++) {
3480 		Actor *a = new Actor;
3481 
3482 		a->_index = i + ActorBaseID;
3483 
3484 		g_vm->_act->_actorList.push_back(a);
3485 	}
3486 
3487 	g_vm->_act->_actorList[0]->_disposition = dispositionPlayer + 0;
3488 	g_vm->_act->_actorList[1]->_disposition = dispositionPlayer + 1;
3489 	g_vm->_act->_actorList[2]->_disposition = dispositionPlayer + 2;
3490 }
3491 
saveActors(Common::OutSaveFile * outS)3492 void saveActors(Common::OutSaveFile *outS) {
3493 	debugC(2, kDebugSaveload, "Saving actors");
3494 
3495 	outS->write("ACTR", 4);
3496 	CHUNK_BEGIN;
3497 	out->writeSint16LE(kActorCount);
3498 
3499 	debugC(3, kDebugSaveload, "... kActorCount = %d", kActorCount);
3500 
3501 	for (int i = 0; i < kActorCount; ++i)
3502 		g_vm->_act->_actorList[i]->write(out);
3503 	CHUNK_END;
3504 }
3505 
loadActors(Common::InSaveFile * in)3506 void loadActors(Common::InSaveFile *in) {
3507 	debugC(2, kDebugSaveload, "Loading actors");
3508 
3509 	//  Read in the actor count
3510 	in->readSint16LE();
3511 
3512 	debugC(3, kDebugSaveload, "... kActorCount = %d", kActorCount);
3513 
3514 	for (int i = 0; i < kActorCount; i++) {
3515 		debugC(3, kDebugSaveload, "Loading actor %d", i + ActorBaseID);
3516 
3517 		//  Initilize actors with archive data
3518 		Actor *a = new Actor(in);
3519 
3520 		a->_index = i + ActorBaseID;
3521 
3522 		g_vm->_act->_actorList.push_back(a);
3523 	}
3524 
3525 	for (int i = 0; i < kActorCount; ++i) {
3526 		Actor *a = g_vm->_act->_actorList[i];
3527 
3528 		a->_leader = a->_leaderID != Nothing
3529 					? (Actor *)GameObject::objectAddress(a->_leaderID)
3530 					:   nullptr;
3531 
3532 		a->_followers = a->_followersID != NoBand
3533 					?   getBandAddress(a->_followersID)
3534 					:   nullptr;
3535 
3536 		a->_currentTarget = a->_currentTargetID != Nothing
3537 						?   GameObject::objectAddress(a->_currentTargetID)
3538 						:   nullptr;
3539 	}
3540 }
3541 
3542 //-------------------------------------------------------------------
3543 //	Cleanup the actor list
3544 
cleanupActors(void)3545 void cleanupActors(void) {
3546 	if (g_vm->_act->_actorList.size() > 0) {
3547 		for (int i = 0; i < kActorCount; i++)
3548 			delete g_vm->_act->_actorList[i];
3549 
3550 		g_vm->_act->_actorList.clear();
3551 	}
3552 }
3553 
3554 /* ============================================================================ *
3555    Actor faction tallies
3556  * ============================================================================ */
3557 
AddFactionTally(int faction,enum factionTallyTypes act,int amt)3558 int16 AddFactionTally(int faction, enum factionTallyTypes act, int amt) {
3559 #if DEBUG
3560 	if (faction >= kMaxFactions)
3561 		error("Scripter: Tell Talin to increase kMaxFactions!\n");
3562 	assert(faction >= 0);
3563 	assert(act >= 0);
3564 	assert(act < factionNumColumns);
3565 #endif
3566 	/*
3567 	        //  If faction attitude counts get to big then down-scale all of them
3568 	        //  in proportion.
3569 	    if ( g_vm->_act->_factionTable[faction][act] + amt > maxint16 )
3570 	    {
3571 	        for (int i = 0; i < factionNumColumns; i++)
3572 	            g_vm->_act->_factionTable[faction][i] >>= 1;
3573 	    }
3574 
3575 	        //  Otherwise, if it doesn;t underflow, then add it in.
3576 	    if ( g_vm->_act->_factionTable[faction][act] + amt > minint16 )
3577 	    {
3578 	        g_vm->_act->_factionTable[faction][act] += amt;
3579 	    }
3580 	*/
3581 	g_vm->_act->_factionTable[faction][act] = clamp(minint16,
3582 	                                       g_vm->_act->_factionTable[faction][act] + amt,
3583 	                                       maxint16);
3584 
3585 	return g_vm->_act->_factionTable[faction][act];
3586 }
3587 
3588 //  Get the attitude a particular faction has for a char.
GetFactionTally(int faction,enum factionTallyTypes act)3589 int16 GetFactionTally(int faction, enum factionTallyTypes act) {
3590 #if DEBUG
3591 	if (faction >= kMaxFactions)
3592 		error("Scripter: Tell Talin to increase kMaxFactions!\n");
3593 	assert(faction >= 0);
3594 	assert(act >= 0);
3595 	assert(act < factionNumColumns);
3596 #endif
3597 
3598 	return g_vm->_act->_factionTable[faction][act];
3599 }
3600 
3601 //-------------------------------------------------------------------
3602 //	Initialize the faction tally table
3603 
initFactionTallies(void)3604 void initFactionTallies(void) {
3605 	memset(&g_vm->_act->_factionTable, 0, sizeof(g_vm->_act->_factionTable));
3606 }
3607 
saveFactionTallies(Common::OutSaveFile * outS)3608 void saveFactionTallies(Common::OutSaveFile *outS) {
3609 	debugC(2, kDebugSaveload, "Saving Faction Tallies");
3610 
3611 	outS->write("FACT", 4);
3612 	CHUNK_BEGIN;
3613 	for (int i = 0; i < kMaxFactions; ++i) {
3614 		for (int j = 0; j < factionNumColumns; ++j)
3615 			out->writeSint16LE(g_vm->_act->_factionTable[i][j]);
3616 	}
3617 	CHUNK_END;
3618 }
3619 
loadFactionTallies(Common::InSaveFile * in)3620 void loadFactionTallies(Common::InSaveFile *in) {
3621 	debugC(2, kDebugSaveload, "Loading Faction Tallies");
3622 
3623 	for (int i = 0; i < kMaxFactions; ++i) {
3624 		for (int j = 0; j < factionNumColumns; ++j)
3625 			g_vm->_act->_factionTable[i][j] = in->readSint16LE();
3626 	}
3627 }
3628 
3629 }
3630