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 * aint32 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 "saga2/saga2.h"
28 #include "saga2/intrface.h"
29 #include "saga2/contain.h"
30 #include "saga2/task.h"
31 #include "saga2/motion.h"
32 #include "saga2/transit.h"
33 #include "saga2/localize.h"
34
35 namespace Saga2 {
36
37 extern bool allPlayerActorsDead;
38
39 extern ObjectID viewCenterObject; // ID of object which the
40 // camera tracks
41
42 extern ReadyContainerView *TrioCviews[kNumViews];
43 extern ReadyContainerView *indivCviewTop, *indivCviewBot;
44 extern ContainerNode *indivReadyNode;
45
46 void updateMainDisplay(void);
47
48 TilePoint selectNearbySite(
49 ObjectID worldID,
50 const TilePoint &startingCoords,
51 int32 minDist,
52 int32 maxDist,
53 bool offScreenOnly);
54
55 bool checkPath(
56 ObjectID worldID,
57 uint8 height,
58 const TilePoint &startingPt,
59 const TilePoint &destPt);
60
61 /* ===================================================================== *
62 Globals
63 * ===================================================================== */
64
65 static PlayerActorID centerActor; // Index of the current center
66 // actor
67
68 bool brotherBandingEnabled;
69
70 /* ===================================================================== *
71 Methods
72 * ===================================================================== */
73
74 //-----------------------------------------------------------------------
75 // Resolve the banding state of this actor
76
resolveBanding(void)77 void PlayerActor::resolveBanding(void) {
78 Actor *follower = getActor();
79 Actor *centerActor_ = getCenterActor();
80
81 // if already following, tell the actor to cease and desist
82 if (follower->_leader) {
83 follower->disband();
84 }
85
86 // do not allow actor to follow it's self
87 if (brotherBandingEnabled
88 && isBanded()
89 && follower != centerActor_) {
90 // create a new follow assignment
91
92 follower->bandWith(centerActor_);
93 }
94 }
95
96 //-----------------------------------------------------------------------
97 // Re-evaluate the portrait type for this player actor
98
recalcPortraitType(void)99 void PlayerActor::recalcPortraitType(void) {
100 PortraitType pType;
101 Actor *a = getActor();
102 ActorAttributes &stats = getBaseStats();
103
104 if (a->isDead())
105 pType = kPortraitDead;
106 else if (a->_enchantmentFlags & (1 << actorAsleep))
107 pType = kPortraitAsleep;
108 else if (stats.vitality >= a->_effectiveStats.vitality * 3)
109 pType = kPortraitWounded;
110 else if (a->_enchantmentFlags & ((1 << actorDiseased) | (1 << actorPoisoned)))
111 pType = kPortraitSick;
112 else if (stats.vitality * 2 > a->_effectiveStats.vitality * 3)
113 pType = kPortraitOuch;
114 else if (a->_enchantmentFlags & ((1 << actorParalyzed) | (1 << actorFear) | (1 << actorBlind)))
115 pType = kPortraitConfused;
116 else if (isAggressive())
117 pType = kPortraitAngry;
118 else
119 pType = kPortraitNormal;
120
121 if (pType != portraitType)
122 updateBrotherPortrait(getPlayerActorID(this), portraitType = pType);
123 }
124
125
recoveryUpdate(void)126 void PlayerActor::recoveryUpdate(void) { // change name to recovery update
127 manaUpdate();
128 AttribUpdate();
129 }
130
131
132
AttribUpdate(void)133 void PlayerActor::AttribUpdate(void) {
134 // get the actor pointer for this character
135 Actor *actor = getActor();
136
137 // get the effective stats for this player actor
138 ActorAttributes *effStats = &actor->_effectiveStats;
139
140 for (int16 i = 0; i < numSkills; i++) {
141 // go through each skill and update as needed
142 stdAttribUpdate(effStats->skill(i),
143 baseStats.skill(i),
144 i);
145 }
146 }
147
148
stdAttribUpdate(uint8 & stat,uint8 baseStat,int16 index)149 void PlayerActor::stdAttribUpdate(uint8 &stat, uint8 baseStat, int16 index) {
150 // first find out if this actor is wounded
151 if (stat < baseStat) {
152 // whole vitality number goes here
153 int16 recover;
154 int16 fractionRecover;
155
156 // get the whole number first
157 recover = attribPointsPerUpdate / attribPointsPerValue;
158
159 // get the fraction
160 fractionRecover = attribPointsPerUpdate % attribPointsPerValue;
161
162 // if there is an overrun
163 if (attribRecPools[index] + fractionRecover > attribPointsPerValue) {
164 // add the overrun to the whole number
165 recover++;
166 attribRecPools[index] = (attribRecPools[index] + fractionRecover) - attribPointsPerValue;
167 } else {
168 attribRecPools[index] += fractionRecover;
169 }
170
171
172 if (stat + recover >= baseStat) {
173 stat = baseStat;
174 } else {
175 stat += recover;
176 }
177 }
178 }
179
manaUpdate(void)180 void PlayerActor::manaUpdate(void) {
181 const int numManas = 6;
182 const int minMana = 0;
183
184 // get the actor pointer for this character
185 Actor *actor = getActor();
186
187 // get indirections for each of the effective mana types
188 int16 *effectiveMana[numManas] = { &actor->_effectiveStats.redMana,
189 &actor->_effectiveStats.orangeMana,
190 &actor->_effectiveStats.yellowMana,
191 &actor->_effectiveStats.greenMana,
192 &actor->_effectiveStats.blueMana,
193 &actor->_effectiveStats.violetMana
194 };
195
196 // get indirections for each of the base mana types
197 int16 *baseMana[numManas] = { &baseStats.redMana,
198 &baseStats.orangeMana,
199 &baseStats.yellowMana,
200 &baseStats.greenMana,
201 &baseStats.blueMana,
202 &baseStats.violetMana
203 };
204
205 uint16 diff;
206
207 // do each mana type
208 for (int16 i = 0; i < numManas; i++) {
209 int levelBump;
210 int recRate;
211
212 // if baseMana has gone to zero, force it to 1
213 if (*baseMana[i] <= 0) *baseMana[i] = 1;
214
215 // Make mana harder to increase as it goes up.
216 if (*baseMana[i] >= 100) levelBump = 40;
217 else if (*baseMana[i] >= 40) levelBump = 20;
218 else levelBump = 10;
219
220 // is their current mana less then their maximum
221 if (*effectiveMana[i] < *baseMana[i]) {
222 diff = *effectiveMana[i];
223
224 recRate = 1;
225 if (*baseMana[i] >= 120) recRate = 3;
226 else if (*baseMana[i] >= 80) recRate = 2;
227 else if (*baseMana[i] >= 40) {
228 // This effectively causes recRate to be 1.5, i.e.
229 // hald of the time its 1 and the other half its 2.
230 if (*effectiveMana[i] % 3 == 0) recRate = 2;
231 }
232
233 // recover mana at specified rate
234 *effectiveMana[i] = clamp(minMana,
235 *effectiveMana[i] += recRate,
236 *baseMana[i]);
237
238 // get the difference between the manas
239 diff = *effectiveMana[i] - diff;
240
241
242 // find out if we're recovering from below one third
243 if (*effectiveMana[i] < *baseMana[i] / 3) {
244 // add the diff
245 // Deleted at request of Client.
246 // manaMemory[i] -= diff;
247 } else {
248 manaMemory[i] += diff;
249 }
250
251
252 // if we bumped passed the ( +/- ) levelBump mark
253 // decrement the base mana
254 *baseMana[i] += (manaMemory[i] / levelBump);
255
256 // get the fraction back to memory
257 manaMemory[i] = manaMemory[i] % levelBump;
258
259 //WriteStatusF( 4, " mana: %d", *effectiveMana[i] );
260 }
261 }
262 }
263
264 // get the skill advancement number ( could be zero )
skillAdvance(SkillProto * proto,uint8 points,uint8 useMult)265 void PlayerActor::skillAdvance(SkillProto *proto,
266 uint8 points,
267 uint8 useMult) { // useMult defaulted to 1
268 // get the skill level for the skill passed ( i.e. 1-100 )
269 uint8 skillLevel = getSkillLevel(proto, true); // true, use base stats
270
271 // get the stat index in question
272 uint8 stat = getStatIndex(proto);
273
274 // get the percentile chance of advancing
275 uint8 advanceChance = ActorAttributes::skillBasePercent - skillLevel;
276
277 // call the main body of code
278 skillAdvance(stat, advanceChance, points, useMult);
279 }
280
281 // get the skill advancement number ( could be zero )
skillAdvance(ActorSkillID stat,uint8 points,uint8 useMult)282 void PlayerActor::skillAdvance(ActorSkillID stat,
283 uint8 points,
284 uint8 useMult) { // useMult defaulted to 1
285 // get the skill level for the skill passed ( i.e. 1-100 )
286 uint8 skillLevel = clamp(0, baseStats.skill(stat), ActorAttributes::skillMaxLevel);
287
288 // get the percentile chance of advancing
289 uint8 advanceChance = ActorAttributes::skillBasePercent - skillLevel;
290
291 // call the main body of code
292 skillAdvance(stat, advanceChance, points, useMult);
293
294 //WriteStatusF( 1, "adchan: %d, skillev: %d ", advanceChance, skillLevel );
295 }
296
297
skillAdvance(uint8 stat,uint8 advanceChance,uint8 points,uint8 useMult)298 void PlayerActor::skillAdvance(uint8 stat,
299 uint8 advanceChance,
300 uint8 points,
301 uint8 useMult) {
302 // roll percentile dice
303 if (g_vm->_rnd->getRandomNumber(99) < advanceChance) {
304 uint8 increase;
305 int16 oldValue = baseStats.skill(stat) / ActorAttributes::skillFracPointsPerLevel;
306
307 // success, now apply the multiplyer
308 attribMemPools[stat] += points * useMult;
309
310 // get the amout of whole increase points
311 increase = attribMemPools[stat] / ActorAttributes::skillFracPointsPerLevel;
312
313 // now set the pool with the fraction
314 attribMemPools[stat] =
315 attribMemPools[stat]
316 - increase
317 * ActorAttributes::skillFracPointsPerLevel;
318
319 // now apply changes to the baseStats
320 baseStats.skill(stat) = clamp(
321 0,
322 baseStats.skill(stat) += increase,
323 ActorAttributes::skillMaxLevel);
324
325 if (baseStats.skill(stat) / ActorAttributes::skillFracPointsPerLevel != oldValue) {
326 static const char *skillNames[] = {
327 ARCHERY_SKILL,
328 SWORD_SKILL,
329 SHIELD_SKILL,
330 BLUDGEON_SKILL,
331 DEAD_SKILL,
332 SPELL_SKILL,
333 DEAD2_SKILL,
334 AGILITY_SKILL,
335 BRAWN_SKILL
336 };
337
338 StatusMsg(SKILL_STATUS, getActor()->objName(), skillNames[stat]);
339 }
340 //WriteStatusF( 6, "frac: %d inc: %d, base: %d", attribMemPools[stat], increase, baseStats.allSkills[stat] );
341 }
342 }
343
vitalityAdvance(uint8 points)344 void PlayerActor::vitalityAdvance(uint8 points) {
345 while (points-- > 0) {
346 if ((int16)g_vm->_rnd->getRandomNumber(ActorAttributes::vitalityLimit - 1) > baseStats.vitality) {
347 if (++vitalityMemory >= vitalityLevelBump) {
348 vitalityMemory -= vitalityLevelBump;
349 baseStats.vitality++;
350 StatusMsg(VITALITY_STATUS, getActor()->objName());
351 }
352 }
353 }
354
355 assert(baseStats.vitality < ActorAttributes::vitalityLimit);
356 }
357
358 // this function will return a value of 0 - 4 to indicate
359 // relative proficiency in requested skill
getSkillLevel(SkillProto * skill,bool base)360 int8 PlayerActor::getSkillLevel(SkillProto *skill, bool base) { // basestats defaulted to false
361 // get the id for this skill
362 SpellID skillID = skill->getSpellID();
363
364 // index
365 uint16 stat;
366
367 // get the current stats for this player actor
368 ActorAttributes *effStats = getEffStats();
369
370 // check to see if this is a special case
371 if (skillID == skillVitality) {
372 return effStats->vitality / ActorAttributes::skillFracPointsPerLevel;
373 } else if (skillID == skillCartography) {
374 // cartography has no levels
375 return 0;
376 }
377
378 // get the index
379 stat = getStatIndex(skill);
380
381
382 // stat stored as skillLevel *
383 // skillFracPointsPerLevel +
384 // skillCurrentFracPoints
385 if (base) {
386 return clamp(0,
387 baseStats.skill(stat) / ActorAttributes::skillFracPointsPerLevel,
388 ActorAttributes::skillLevels - 1);
389 } else {
390 return clamp(0,
391 effStats->skill(stat) / ActorAttributes::skillFracPointsPerLevel,
392 ActorAttributes::skillLevels - 1);
393 }
394 }
395
getStatIndex(SkillProto * proto)396 uint8 PlayerActor::getStatIndex(SkillProto *proto) {
397 // get the id for this skill
398 SpellID skillID = proto->getSpellID();
399 uint16 stat;
400
401 // now map the id gotten from spellid to the
402 // attributeskilll enum for the allSkills array
403 switch (skillID) {
404 case skillPickpocket:
405 stat = skillIDPilfer;
406 break;
407
408 case skillSeeHidden:
409 stat = skillIDSpotHidden;
410 break;
411
412 case skillLockPick:
413 stat = skillIDLockpick;
414 break;
415
416 case skillFirstAid:
417 stat = skillIDFirstAid;
418 break;
419
420 case skillArchery:
421 stat = skillIDArchery;
422 break;
423
424 case skillSwordcraft:
425 stat = skillIDSwordcraft;
426 break;
427
428 case skillShieldcraft:
429 stat = skillIDShieldcraft;
430 break;
431
432 case skillBludgeon:
433 stat = skillIDBludgeon;
434 break;
435
436 case skillThrowing:
437 stat = skillIDThrowing;
438 break;
439
440 case skillSpellcraft:
441 stat = skillIDSpellcraft;
442 break;
443
444 case skillStealth:
445 stat = skillIDStealth;
446 break;
447
448 case skillAgility:
449 stat = skillIDAgility;
450 break;
451
452 case skillBrawn:
453 stat = skillIDBrawn;
454 break;
455
456 default:
457
458 error("Invalid case detected: Player.cpp-getSkillLevel() = %d", skillID);
459 }
460
461 // make sure we have a good index
462 if (stat >= numSkills) {
463 error("Invalid array index detected: Player.cpp-getSkillLevel()");
464 }
465
466 // return the index
467 return stat;
468 }
469
getEffStats(void)470 ActorAttributes *PlayerActor::getEffStats(void) {
471 // get the actor pointer for this character
472 Actor *actor = getActor();
473
474 // get the effective stats for this player actor
475 ActorAttributes *effStats = &actor->_effectiveStats;
476
477 // valid?
478 assert(effStats);
479
480 // return current stats for this player actor
481 return effStats;
482 }
483
484 //-----------------------------------------------------------------------
485 // Notify the user of attack if necessary
486
handleAttacked(void)487 void PlayerActor::handleAttacked(void) {
488 if (!notifiedOfAttack) {
489 StatusMsg(ATTACK_STATUS, getActor()->objName());
490 notifiedOfAttack = true;
491 }
492 }
493
494 /* ===================================================================== *
495 Functions
496 * ===================================================================== */
497
498 //-----------------------------------------------------------------------
499 // Return a pointer to a PlayerActor given it's ID
500
getPlayerActorAddress(PlayerActorID id)501 PlayerActor *getPlayerActorAddress(PlayerActorID id) {
502 assert(id >= 0 && id < (int)g_vm->_playerList.size());
503
504 return g_vm->_playerList[id];
505 }
506
507 //-----------------------------------------------------------------------
508 // Return a PlayerActor ID given it's address
509
getPlayerActorID(PlayerActor * p)510 PlayerActorID getPlayerActorID(PlayerActor *p) {
511 for (int i = 0; i < (int)g_vm->_playerList.size(); ++i) {
512 if (g_vm->_playerList[i] == p)
513 return i;
514 }
515
516 return -1;
517 }
518
519 //-----------------------------------------------------------------------
520 // Return a pointer the center actor's Actor structure
521
getCenterActor(void)522 Actor *getCenterActor(void) {
523 return g_vm->_playerList[centerActor]->getActor();
524 }
525
526 //-----------------------------------------------------------------------
527 // Return the center actor's object ID
528
getCenterActorID(void)529 ObjectID getCenterActorID(void) {
530 return ActorBaseID + centerActor;
531 }
532
533 //-----------------------------------------------------------------------
534 // Return the center actor's player actor ID
535
getCenterActorPlayerID(void)536 PlayerActorID getCenterActorPlayerID(void) {
537 return centerActor;
538 }
539
540 //-----------------------------------------------------------------------
541 // Set a new center actor based upon a PlayerActor ID
542
setCenterActor(PlayerActorID newCenter)543 void setCenterActor(PlayerActorID newCenter) {
544 extern void setEnchantmentDisplay(void);
545
546 assert(newCenter < kPlayerActors);
547
548 Actor *a = g_vm->_playerList[newCenter]->getActor();
549 PlayerActorIterator iter;
550 PlayerActor *player;
551
552 // If this actor is dead return immediately
553 if (a->isDead()) return;
554
555 // Take previous center actor out of fight stance
556 getCenterActor()->setFightStance(false);
557
558 // get rid of any following assignments the center actor might have
559 if (a->_leader) {
560 a->disband();
561 }
562
563 centerActor = newCenter;
564 viewCenterObject = g_vm->_playerList[centerActor]->getActorID();
565
566 indivReadyNode->changeOwner(newCenter);
567 g_vm->_cnm->setPlayerNum(newCenter);
568 setEnchantmentDisplay();
569
570 if (a->_curTask != NULL) {
571 a->_curTask->abortTask();
572 delete a->_curTask;
573 a->_curTask = NULL;
574 }
575
576 // Set the new centers fight stance based upon his aggression state
577 a->setFightStance(g_vm->_playerList[newCenter]->isAggressive());
578
579 // band actors to new center if banding button set
580 for (player = iter.first(); player != NULL; player = iter.next()) {
581 player->resolveBanding();
582 }
583
584 // clear the last center actor's button state
585 updateBrotherRadioButtons(newCenter);
586 }
587
588 //-----------------------------------------------------------------------
589 // Set a new center actor based upon an Actor address
590
setCenterActor(Actor * newCenter)591 void setCenterActor(Actor *newCenter) {
592 assert(newCenter->_disposition >= dispositionPlayer);
593 setCenterActor(newCenter->_disposition - dispositionPlayer);
594 }
595
596 //-----------------------------------------------------------------------
597 // Set a new center actor based upon a PlayerActor address
598
setCenterActor(PlayerActor * newCenter)599 void setCenterActor(PlayerActor *newCenter) {
600 assert(getPlayerActorID(newCenter) >= 0);
601 setCenterActor(getPlayerActorID(newCenter));
602 }
603
604 //-----------------------------------------------------------------------
605 // Return the coordinates of the current center actor
606
centerActorCoords(void)607 TilePoint centerActorCoords(void) {
608 Actor *a;
609
610 a = g_vm->_playerList[centerActor]->getActor();
611 return a->getLocation();
612 }
613
614
615 //-----------------------------------------------------------------------
616 // Set or clear a player's aggressive state
617
setAggression(PlayerActorID player,bool aggression)618 void setAggression(PlayerActorID player, bool aggression) {
619 assert(player >= 0 && player < kPlayerActors);
620
621 Actor *a = g_vm->_playerList[player]->getActor();
622
623 if (a->isDead()) return;
624
625 if (aggression)
626 g_vm->_playerList[player]->setAggression();
627 else
628 g_vm->_playerList[player]->clearAggression();
629
630 if (player == centerActor)
631 a->setFightStance(aggression);
632
633 a->evaluateNeeds();
634
635 updateBrotherAggressionButton(player, aggression);
636 }
637
638 //-----------------------------------------------------------------------
639 // Determine if player actor is in an aggressive state
640
isAggressive(PlayerActorID player)641 bool isAggressive(PlayerActorID player) {
642 assert(player >= 0 && player < kPlayerActors);
643 return g_vm->_playerList[player]->isAggressive();
644 }
645
646 //-----------------------------------------------------------------------
647 // Adjust the player actors aggression setting based upon their
648 // proximity to enemies
649
autoAdjustAggression(void)650 void autoAdjustAggression(void) {
651 PlayerActorID i;
652
653 // Iterate through all player actors
654 for (i = 0; i < kPlayerActors; i++) {
655 if (i == centerActor || isBanded(i)) {
656 bool enemiesPresent = false;
657 Actor *actor = g_vm->_playerList[i]->getActor();
658
659 if (actor->getStats()->vitality >= kMinAutoAggressionVitality) {
660 GameObject *obj = nullptr;
661 ActiveRegion *activeReg = getActiveRegion(i);
662 TileRegion region = activeReg->getRegion();
663 GameWorld *world = activeReg->getWorld();
664
665 RegionalObjectIterator iter(world, region.min, region.max);
666
667 // Iterate through the objects in this player actor's
668 // active region to determine if their are enemy actor's
669 // in the vicinity.
670 for (iter.first(&obj); obj != NULL; iter.next(&obj)) {
671 Actor *a;
672
673 if (!isActor(obj)) continue;
674
675 a = (Actor *)obj;
676
677 if (a->_disposition == dispositionEnemy) {
678 enemiesPresent = true;
679 break;
680 }
681 }
682 }
683
684 // Set this player actor's aggression
685 setAggression(i, enemiesPresent);
686 }
687 }
688 }
689
690 //-----------------------------------------------------------------------
691 // Set a player actor's banding
692
setBanded(PlayerActorID player,bool banded)693 void setBanded(PlayerActorID player, bool banded) {
694 assert(player >= 0 && player < kPlayerActors);
695
696 if (g_vm->_playerList[player]->getActor()->isDead()) return;
697
698 if (banded)
699 g_vm->_playerList[player]->setBanded();
700 else
701 g_vm->_playerList[player]->clearBanded();
702
703 g_vm->_playerList[player]->resolveBanding();
704
705 updateBrotherBandingButton(player, banded);
706 }
707
708 //-----------------------------------------------------------------------
709 // Determine if a player actor is banded
710
isBanded(PlayerActorID player)711 bool isBanded(PlayerActorID player) {
712 assert(player >= 0 && player < kPlayerActors);
713 return g_vm->_playerList[player]->isBanded();
714 }
715
716 //-----------------------------------------------------------------------
717 // Globally enable or disable brother banding
718
setBrotherBanding(bool enabled)719 void setBrotherBanding(bool enabled) {
720 if (brotherBandingEnabled != enabled) {
721 brotherBandingEnabled = enabled;
722
723 if (areActorsInitialized()) {
724 PlayerActorIterator iter;
725 PlayerActor *player;
726
727 // Update the state of the banding
728 for (player = iter.first(); player != NULL; player = iter.next()) {
729 player->resolveBanding();
730 }
731 }
732 }
733 }
734
735
736
737 //-----------------------------------------------------------------------
738 // Set the portrait type for this actor based on the current state.
739
recalcPortraitType(PlayerActorID brotherID)740 void recalcPortraitType(PlayerActorID brotherID) {
741 PlayerActor *pa = getPlayerActorAddress(brotherID);
742
743 pa->recalcPortraitType();
744 }
745
746 //-----------------------------------------------------------------------
747 // Returns an integer value representing this player actor's portrait
748 // state
749
getPortraitType(PlayerActorID id)750 int16 getPortraitType(PlayerActorID id) {
751 PlayerActor *pa = getPlayerActorAddress(id);
752
753 return pa->getPortraitType();
754 }
755
756 //-----------------------------------------------------------------------
757 // Given an actor, returns the corresponding player Actor ID
758
actorToPlayerID(Actor * a,PlayerActorID & result)759 bool actorToPlayerID(Actor *a, PlayerActorID &result) {
760 if (a->_disposition >= dispositionPlayer) {
761 result = a->_disposition - dispositionPlayer;
762 return true;
763 }
764
765 return false;
766 }
767
actorIDToPlayerID(ObjectID id,PlayerActorID & result)768 bool actorIDToPlayerID(ObjectID id, PlayerActorID &result) {
769 if (!isActor(id)) return false;
770
771 Actor *a = (Actor *)GameObject::objectAddress(id);
772
773 if (a->_disposition >= dispositionPlayer) {
774 result = a->_disposition - dispositionPlayer;
775 return true;
776 }
777
778 return false;
779 }
780
handlePlayerActorDeath(PlayerActorID id)781 void handlePlayerActorDeath(PlayerActorID id) {
782 assert(id >= 0 && id < kPlayerActors);
783
784 if (getCenterActor()->isDead()) {
785 PlayerActor *newCenter;
786 LivingPlayerActorIterator iter;
787
788 if ((newCenter = iter.first()) != NULL)
789 setCenterActor(getPlayerActorID(newCenter));
790 else
791 allPlayerActorsDead = true;
792 }
793
794 PlayerActor *player = g_vm->_playerList[id];
795
796 player->clearAggression();
797 player->clearBanded();
798 updateBrotherAggressionButton(id, false);
799 updateBrotherBandingButton(id, false);
800
801 StatusMsg(DEATH_STATUS, player->getActor()->objName());
802 }
803
804 //-----------------------------------------------------------------------
805 // Transport the center actor and the banded brothers who have a path
806 // to the center actor
807
transportCenterBand(const Location & loc)808 void transportCenterBand(const Location &loc) {
809 assert(isWorld(loc.context));
810
811 fadeDown();
812
813 Actor *center = getCenterActor();
814 TilePoint centerLoc = center->getLocation();
815 ObjectID centerWorldID = center->world()->thisID();
816 PlayerActor *player;
817 LivingPlayerActorIterator iter;
818
819 center->move(loc);
820 if (center->_moveTask != NULL)
821 center->_moveTask->finishWalk();
822
823 for (player = iter.first();
824 player != NULL;
825 player = iter.next()) {
826 Actor *a = player->getActor();
827
828 if (a != center
829 && player->isBanded()
830 && a->world()->thisID() == centerWorldID
831 && checkPath(
832 centerWorldID,
833 a->proto()->height,
834 a->getLocation(),
835 centerLoc)) {
836 TilePoint dest;
837
838 dest = selectNearbySite(
839 loc.context,
840 loc,
841 1,
842 3,
843 false);
844
845 if (dest != Nowhere) {
846 a->move(Location(dest, loc.context));
847 if (a->_moveTask != NULL)
848 a->_moveTask->finishWalk();
849 player->resolveBanding();
850 }
851 }
852 }
853
854 updateMainDisplay();
855
856 fadeUp();
857 }
858
859 //-----------------------------------------------------------------------
860
handlePlayerActorAttacked(PlayerActorID id)861 void handlePlayerActorAttacked(PlayerActorID id) {
862 PlayerActor *pa = getPlayerActorAddress(id);
863
864 pa->handleAttacked();
865 }
866
867 //-----------------------------------------------------------------------
868
handleEndOfCombat(void)869 void handleEndOfCombat(void) {
870 PlayerActorID i;
871
872 // Iterate through all player actors
873 for (i = 0; i < kPlayerActors; i++)
874 g_vm->_playerList[i]->resetAttackNotification();
875 }
876
877 /* ======================================================================= *
878 PlayerActor list management functions
879 * ======================================================================= */
880
881
882
883 // This structure is used in archiving the player actor list
884 struct PlayerActorArchive {
885 int16 portraitType;
886 uint16 flags;
887 ActorAttributes baseStats;
888 int16 manaMemory[numManas];
889 uint8 attribRecPools[numSkills];
890 uint8 attribMemPools[numSkills];
891 uint8 vitalityMemory;
892 bool notifiedOfAttack;
893 };
894
895
896 //-----------------------------------------------------------------------
897 // Initialize the player list
898
initPlayerActors(void)899 void initPlayerActors(void) {
900 g_vm->_playerList.push_back(new PlayerActor(ActorBaseID + 0)); // Julian
901 g_vm->_playerList.push_back(new PlayerActor(ActorBaseID + 1)); // Philip
902 g_vm->_playerList.push_back(new PlayerActor(ActorBaseID + 2)); // Kevin
903
904 for (int i = 0; i < kPlayerActors; i++) {
905 PlayerActor *p = g_vm->_playerList[i];
906 Actor *a = p->getActor();
907 ActorProto *proto = (ActorProto *)a->proto();
908
909 // Set the portrait type
910 p->portraitType = kPortraitNormal;
911
912 // Clear all flags
913 p->flags = 0;
914 // Copy the base stats from the actor's prototype
915 memcpy(&p->baseStats, &proto->baseStats, sizeof(p->baseStats));
916
917 // Clear out the accumulation arrays
918 memset(&p->manaMemory, 0, sizeof(p->manaMemory));
919 memset(&p->attribRecPools, 0, sizeof(p->attribRecPools));
920 memset(&p->attribMemPools, 0, sizeof(p->attribMemPools));
921
922 // Clear the vitalityMemory
923 p->vitalityMemory = 0;
924
925 // Clear the attack notification flag
926 p->notifiedOfAttack = false;
927
928 // Set the actor's disposition field to reflect that that
929 // actor is a player actor
930 a->_disposition = dispositionPlayer + i;
931
932 // Turn on banding for player actors
933 setBanded(i, true);
934 }
935
936 readyContainerSetup();
937 }
938
savePlayerActors(Common::OutSaveFile * outS)939 void savePlayerActors(Common::OutSaveFile *outS) {
940 debugC(2, kDebugSaveload, "Saving PlayerActors");
941
942 outS->write("PLYR", 4);
943 CHUNK_BEGIN;
944 for (int i = 0; i < kPlayerActors; i++) {
945 debugC(3, kDebugSaveload, "Saving PlayerActor %d", i);
946
947 PlayerActor *p = g_vm->_playerList[i];
948
949 // Store the portrait type
950 out->writeSint16LE(p->portraitType);
951
952 // Store the flags
953 out->writeUint16LE(p->flags);
954
955 // Store the base stats
956 p->baseStats.write(out);
957
958 // Store accumulation arrays
959 for (int j = 0; j < numManas; ++j)
960 out->writeSint16LE(p->manaMemory[j]);
961
962 for (int j = 0; j < numSkills; ++j)
963 out->writeByte(p->attribRecPools[j]);
964
965 for (int j = 0; j < numSkills; ++j)
966 out->writeByte(p->attribMemPools[j]);
967
968 // Store the vitality memory
969 out->writeByte(p->vitalityMemory);
970
971 // Store the attack notification flag
972 out->writeUint16LE(p->notifiedOfAttack);
973
974 debugC(4, kDebugSaveload, "... playerList[%d].portraitType = %d", i, p->portraitType);
975 debugC(4, kDebugSaveload, "... playerList[%d].flags = %d", i, p->flags);
976 debugC(4, kDebugSaveload, "... playerList[%d].vitalityMemory = %d", i, p->vitalityMemory);
977 debugC(4, kDebugSaveload, "... playerList[%d].notifiedOfAttack = %d", i, p->notifiedOfAttack);
978 }
979 CHUNK_END;
980 }
981
loadPlayerActors(Common::InSaveFile * in)982 void loadPlayerActors(Common::InSaveFile *in) {
983 debugC(2, kDebugSaveload, "Loading PlayerActors");
984
985 for (int i = 0; i < kPlayerActors; i++) {
986 debugC(3, kDebugSaveload, "Loading PlayerActor %d", i);
987
988 PlayerActor *p = g_vm->_playerList[i];
989
990 // Restore the portrait type
991 p->portraitType = in->readSint16LE();
992
993 // Restore the flags
994 p->flags = in->readUint16LE();
995
996 // Restore the base stats
997 p->baseStats.read(in);
998
999 // Restore the accumulation arrays
1000 for (int j = 0; j < numManas; ++j)
1001 p->manaMemory[j] = in->readSint16LE();
1002
1003 for (int j = 0; j < numSkills; ++j)
1004 p->attribRecPools[j] = in->readByte();
1005
1006 for (int j = 0; j < numSkills; ++j)
1007 p->attribMemPools[j] = in->readByte();
1008
1009 // Restore the vitality memory
1010 p->vitalityMemory = in->readByte();
1011
1012 // Restore the attack notification flag
1013 p->notifiedOfAttack = in->readUint16LE();
1014
1015 debugC(4, kDebugSaveload, "... playerList[%d].portraitType = %d", i, p->portraitType);
1016 debugC(4, kDebugSaveload, "... playerList[%d].flags = %d", i, p->flags);
1017 debugC(4, kDebugSaveload, "... playerList[%d].vitalityMemory = %d", i, p->vitalityMemory);
1018 debugC(4, kDebugSaveload, "... playerList[%d].notifiedOfAttack = %d", i, p->notifiedOfAttack);
1019 }
1020
1021 readyContainerSetup();
1022 }
1023
1024 //-----------------------------------------------------------------------
1025 // Cleanup the player actor list
1026
cleanupPlayerActors(void)1027 void cleanupPlayerActors(void) {
1028 cleanupReadyContainers();
1029 }
1030
1031 /* ======================================================================= *
1032 CenterActor management function prototypes
1033 * ======================================================================= */
1034
1035 // This structure is used in archiving the center actor ID and the view
1036 // object ID
1037 struct CenterActorArchive {
1038 PlayerActorID centerActor;
1039 ObjectID viewCenterObject;
1040 };
1041
1042 //-----------------------------------------------------------------------
1043 // Initialize the center actor ID and view object ID
1044
initCenterActor(void)1045 void initCenterActor(void) {
1046 centerActor = FTA_JULIAN;
1047 viewCenterObject = g_vm->_playerList[centerActor]->getActorID();
1048
1049 // clear the last center actor's button state
1050 updateBrotherRadioButtons(FTA_JULIAN);
1051 }
1052
saveCenterActor(Common::OutSaveFile * outS)1053 void saveCenterActor(Common::OutSaveFile *outS) {
1054 debugC(2, kDebugSaveload, "Saving CenterActor");
1055
1056 outS->write("CNTR", 4);
1057 CHUNK_BEGIN;
1058 // Store the center actor and view object
1059 out->writeSint16LE(centerActor);
1060 out->writeUint16LE(viewCenterObject);
1061 CHUNK_END;
1062
1063 debugC(3, kDebugSaveload, "... centerActor = %d", centerActor);
1064 debugC(3, kDebugSaveload, "... viewCenterObject = %d", viewCenterObject);
1065 }
1066
loadCenterActor(Common::InSaveFile * in)1067 void loadCenterActor(Common::InSaveFile *in) {
1068 debugC(2, kDebugSaveload, "Loading CenterActor");
1069
1070 // Restore the center actor and view object
1071 centerActor = in->readSint16LE();
1072 viewCenterObject = in->readUint16LE();
1073
1074 debugC(3, kDebugSaveload, "... centerActor = %d", centerActor);
1075 debugC(3, kDebugSaveload, "... viewCenterObject = %d", viewCenterObject);
1076 }
1077
1078 //-----------------------------------------------------------------------
1079 // Iterates through all player actors
1080
first(void)1081 PlayerActor *PlayerActorIterator::first(void) {
1082 index = 0;
1083 return g_vm->_playerList[index++];
1084 }
1085
next(void)1086 PlayerActor *PlayerActorIterator::next(void) {
1087 return (index < kPlayerActors) ? g_vm->_playerList[index++] : NULL;
1088 }
1089
1090 //-----------------------------------------------------------------------
1091 // Iterates through all player actors that are not dead.
1092
first(void)1093 PlayerActor *LivingPlayerActorIterator::first(void) {
1094 index = 0;
1095 return LivingPlayerActorIterator::next();
1096 }
1097
next(void)1098 PlayerActor *LivingPlayerActorIterator::next(void) {
1099 if (index >= kPlayerActors)
1100 return nullptr;
1101
1102 Actor *a = g_vm->_playerList[index]->getActor();
1103
1104 while (a == nullptr || a->isDead()) {
1105 if (++index >= kPlayerActors)
1106 break;
1107 a = g_vm->_playerList[index]->getActor();
1108 }
1109
1110 return (index < kPlayerActors) ? g_vm->_playerList[index++] : nullptr;
1111 }
1112
1113 } // end of namespace Saga2
1114