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