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