1 ////////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2010 by The Allacrost Project
3 // All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ////////////////////////////////////////////////////////////////////////////////
9
10 /** ****************************************************************************
11 *** \file global_actors.cpp
12 *** \author Tyler Olsen, roots@allacrost.org
13 *** \brief Source file for global game actors
14 *** ***************************************************************************/
15
16 #include "video.h"
17 #include "global_actors.h"
18 #include "global_objects.h"
19 #include "global_effects.h"
20 #include "global_skills.h"
21
22 using namespace std;
23
24 using namespace hoa_utils;
25 using namespace hoa_video;
26 using namespace hoa_script;
27
28 namespace hoa_global {
29
30 ////////////////////////////////////////////////////////////////////////////////
31 // GlobalAttackPoint class
32 ////////////////////////////////////////////////////////////////////////////////
33
GlobalAttackPoint(GlobalActor * owner)34 GlobalAttackPoint::GlobalAttackPoint(GlobalActor* owner) :
35 _actor_owner(owner),
36 _x_position(0),
37 _y_position(0),
38 _fortitude_modifier(0.0f),
39 _protection_modifier(0.0f),
40 _evade_modifier(0.0f),
41 _total_physical_defense(0),
42 _total_metaphysical_defense(0),
43 _total_evade_rating(0)
44 {}
45
46
47
LoadData(ReadScriptDescriptor & script)48 bool GlobalAttackPoint::LoadData(ReadScriptDescriptor& script) {
49 if (script.IsFileOpen() == false) {
50 return false;
51 }
52
53 _name = MakeUnicodeString(script.ReadString("name"));
54 _x_position = script.ReadInt("x_position");
55 _y_position = script.ReadInt("y_position");
56 _fortitude_modifier = script.ReadFloat("fortitude_modifier");
57 _protection_modifier = script.ReadFloat("protection_modifier");
58 _evade_modifier = script.ReadFloat("evade_modifier");
59
60 if (script.IsErrorDetected()) {
61 if (GLOBAL_DEBUG) {
62 PRINT_WARNING << "one or more errors occurred while reading the save game file - they are listed below" << endl;
63 cerr << script.GetErrorMessages() << endl;
64 }
65 return false;
66 }
67
68 return true;
69 }
70
71
72
CalculateTotalDefense(const GlobalArmor * equipped_armor)73 void GlobalAttackPoint::CalculateTotalDefense(const GlobalArmor* equipped_armor) {
74 if (_actor_owner == NULL) {
75 IF_PRINT_WARNING(GLOBAL_DEBUG) << "attack point has no owning actor" << endl;
76 return;
77 }
78
79 // Calculate defense ratings from owning actor's base stat properties and the attack point modifiers
80 if (_fortitude_modifier <= -1.0f) // If the modifier is less than or equal to -100%, set the total defense to zero
81 _total_physical_defense = 0;
82 else
83 _total_physical_defense = _actor_owner->GetFortitude() + static_cast<int32>(_actor_owner->GetFortitude() * _fortitude_modifier);
84
85 if (_protection_modifier <= -1.0f) // If the modifier is less than or equal to -100%, set the total defense to zero
86 _total_metaphysical_defense = 0;
87 else
88 _total_metaphysical_defense = _actor_owner->GetProtection() + static_cast<int32>(_actor_owner->GetProtection() * _protection_modifier);
89
90 // If present, add defense ratings from the armor equipped
91 if (equipped_armor != NULL) {
92 _total_physical_defense += equipped_armor->GetPhysicalDefense();
93 _total_metaphysical_defense += equipped_armor->GetMetaphysicalDefense();
94 }
95 }
96
97
98
CalculateTotalEvade()99 void GlobalAttackPoint::CalculateTotalEvade() {
100 if (_actor_owner == NULL) {
101 IF_PRINT_WARNING(GLOBAL_DEBUG) << "attack point has no owning actor" << endl;
102 return;
103 }
104
105 // Calculate evade ratings from owning actor's base evade stat and the evade modifier
106 if (_fortitude_modifier <= -1.0f) // If the modifier is less than or equal to -100%, set the total evade to zero
107 _total_evade_rating = 0.0f;
108 else
109 _total_evade_rating = _actor_owner->GetEvade() + (_actor_owner->GetEvade() * _evade_modifier);
110 }
111
112 ////////////////////////////////////////////////////////////////////////////////
113 // GlobalActor class
114 ////////////////////////////////////////////////////////////////////////////////
115
GlobalActor()116 GlobalActor::GlobalActor() :
117 _id(0),
118 _experience_level(0),
119 _experience_points(0),
120 _hit_points(0),
121 _max_hit_points(0),
122 _skill_points(0),
123 _max_skill_points(0),
124 _strength(0),
125 _vigor(0),
126 _fortitude(0),
127 _protection(0),
128 _agility(0),
129 _evade(0.0f),
130 _total_physical_attack(0),
131 _total_metaphysical_attack(0),
132 _weapon_equipped(NULL)
133 {}
134
135
136
~GlobalActor()137 GlobalActor::~GlobalActor() {
138 // Delete all attack points
139 for (uint32 i = 0; i < _attack_points.size(); i++) {
140 delete _attack_points[i];
141 }
142 _attack_points.clear();
143
144 // Delete all equipment
145 if (_weapon_equipped != NULL)
146 delete _weapon_equipped;
147 for (uint32 i = 0; i < _armor_equipped.size(); i++) {
148 if (_armor_equipped[i] != NULL)
149 delete _armor_equipped[i];
150 }
151 _armor_equipped.clear();
152
153 // Delete all skills
154 for (map<uint32, GlobalSkill*>::iterator i = _skills.begin(); i != _skills.end(); i++) {
155 delete i->second;
156 }
157 _skills.clear();
158 }
159
160
161
GlobalActor(const GlobalActor & copy)162 GlobalActor::GlobalActor(const GlobalActor& copy) {
163 _id = copy._id;
164 _name = copy._name;
165 _filename = copy._filename;
166 _experience_level = copy._experience_level;
167 _experience_points = copy._experience_points;
168 _hit_points = copy._hit_points;
169 _max_hit_points = copy._max_hit_points;
170 _skill_points = copy._skill_points;
171 _max_skill_points = copy._max_skill_points;
172 _strength = copy._strength;
173 _vigor = copy._vigor;
174 _fortitude = copy._fortitude;
175 _protection = copy._protection;
176 _agility = copy._agility;
177 _evade = copy._evade;
178 _total_physical_attack = copy._total_physical_attack;
179 _total_metaphysical_attack = copy._total_metaphysical_attack;
180
181 // Copy all attack points
182 for (uint32 i = 0; i < copy._attack_points.size(); i++) {
183 _attack_points.push_back(new GlobalAttackPoint(*copy._attack_points[i]));
184 _attack_points[i]->SetActorOwner(this);
185 }
186
187 // Copy all equipment
188 if (copy._weapon_equipped == NULL)
189 _weapon_equipped = NULL;
190 else
191 _weapon_equipped = new GlobalWeapon(*copy._weapon_equipped);
192
193 for (uint32 i = 0; i < _armor_equipped.size(); i++) {
194 if (_armor_equipped[i] == NULL)
195 _armor_equipped.push_back(NULL);
196 else
197 _armor_equipped.push_back(new GlobalArmor(*copy._armor_equipped[i]));
198 }
199
200 // Copy all skills
201 for (map<uint32, GlobalSkill*>::const_iterator i = copy._skills.begin(); i != copy._skills.end(); i++) {
202 _skills.insert(make_pair(i->first, new GlobalSkill(*(i->second))));
203 }
204 }
205
206
207
operator =(const GlobalActor & copy)208 GlobalActor& GlobalActor::operator=(const GlobalActor& copy) {
209 if (this == ©) // Handle self-assignment case
210 return *this;
211
212 _id = copy._id;
213 _name = copy._name;
214 _filename = copy._filename;
215 _experience_level = copy._experience_level;
216 _experience_points = copy._experience_points;
217 _hit_points = copy._hit_points;
218 _max_hit_points = copy._max_hit_points;
219 _skill_points = copy._skill_points;
220 _max_skill_points = copy._max_skill_points;
221 _strength = copy._strength;
222 _vigor = copy._vigor;
223 _fortitude = copy._fortitude;
224 _protection = copy._protection;
225 _agility = copy._agility;
226 _evade = copy._evade;
227 _total_physical_attack = copy._total_physical_attack;
228 _total_metaphysical_attack = copy._total_metaphysical_attack;
229
230 // Copy all attack points
231 for (uint32 i = 0; i < copy._attack_points.size(); i++) {
232 _attack_points.push_back(new GlobalAttackPoint(*_attack_points[i]));
233 _attack_points[i]->SetActorOwner(this);
234 }
235
236 // Copy all equipment
237 if (copy._weapon_equipped == NULL)
238 _weapon_equipped = NULL;
239 else
240 _weapon_equipped = new GlobalWeapon(*copy._weapon_equipped);
241
242 for (uint32 i = 0; i < _armor_equipped.size(); i++) {
243 if (_armor_equipped[i] == NULL)
244 _armor_equipped.push_back(NULL);
245 else
246 _armor_equipped.push_back(new GlobalArmor(*copy._armor_equipped[i]));
247 }
248
249 // Copy all skills
250 for (map<uint32, GlobalSkill*>::const_iterator i = copy._skills.begin(); i != copy._skills.end(); i++) {
251 _skills.insert(make_pair(i->first, new GlobalSkill(*(i->second))));
252 }
253 return *this;
254 }
255
256
257
EquipWeapon(GlobalWeapon * weapon)258 GlobalWeapon* GlobalActor::EquipWeapon(GlobalWeapon* weapon) {
259 GlobalWeapon* old_weapon = _weapon_equipped;
260 _weapon_equipped = weapon;
261 _CalculateAttackRatings();
262 return old_weapon;
263 }
264
265
266
EquipArmor(GlobalArmor * armor,uint32 index)267 GlobalArmor* GlobalActor::EquipArmor(GlobalArmor* armor, uint32 index) {
268 if (index >= _armor_equipped.size()) {
269 IF_PRINT_WARNING(GLOBAL_DEBUG) << "index argument exceeded number of pieces of armor equipped: " << index << endl;
270 return armor;
271 }
272
273 GlobalArmor* old_armor = _armor_equipped[index];
274 _armor_equipped[index] = armor;
275
276 if (old_armor != NULL && armor != NULL) {
277 if (old_armor->GetObjectType() != armor->GetObjectType()) {
278 IF_PRINT_WARNING(GLOBAL_DEBUG) << "old armor was replaced with a different type of armor" << endl;
279 }
280 }
281
282 _attack_points[index]->CalculateTotalDefense(_armor_equipped[index]);
283 return old_armor;
284 }
285
286
287
GetTotalPhysicalDefense(uint32 index) const288 uint32 GlobalActor::GetTotalPhysicalDefense(uint32 index) const {
289 if (index >= _attack_points.size()) {
290 IF_PRINT_WARNING(GLOBAL_DEBUG) << "index argument exceeded number of attack points: " << index << endl;
291 return 0;
292 }
293
294 return _attack_points[index]->GetTotalPhysicalDefense();
295 }
296
297
298
GetTotalMetaphysicalDefense(uint32 index) const299 uint32 GlobalActor::GetTotalMetaphysicalDefense(uint32 index) const {
300 if (index >= _attack_points.size()) {
301 IF_PRINT_WARNING(GLOBAL_DEBUG) << "index argument exceeded number of attack points: " << index << endl;
302 return 0;
303 }
304
305 return _attack_points[index]->GetTotalMetaphysicalDefense();
306 }
307
308
309
GetTotalEvadeRating(uint32 index) const310 float GlobalActor::GetTotalEvadeRating(uint32 index) const {
311 if (index >= _attack_points.size()) {
312 IF_PRINT_WARNING(GLOBAL_DEBUG) << "index argument exceeded number of attack points: " << index << endl;
313 return 0.0f;
314 }
315
316 return _attack_points[index]->GetTotalEvadeRating();
317 }
318
319
320
GetArmorEquipped(uint32 index) const321 GlobalArmor* GlobalActor::GetArmorEquipped(uint32 index) const {
322 if (index >= _armor_equipped.size()) {
323 IF_PRINT_WARNING(GLOBAL_DEBUG) << "index argument exceeded number of pieces of armor equipped: " << index << endl;
324 return NULL;
325 }
326
327 return _armor_equipped[index];
328 }
329
330
331
GetAttackPoint(uint32 index) const332 GlobalAttackPoint* GlobalActor::GetAttackPoint(uint32 index) const {
333 if (index >= _attack_points.size()) {
334 IF_PRINT_WARNING(GLOBAL_DEBUG) << "index argument exceeded number of attack points: " << index << endl;
335 return NULL;
336 }
337
338 return _attack_points[index];
339 }
340
341
342
GetSkill(uint32 skill_id) const343 GlobalSkill* GlobalActor::GetSkill(uint32 skill_id) const {
344 map<uint32, GlobalSkill*>::const_iterator skill_location = _skills.find(skill_id);
345 if (skill_location == _skills.end()) {
346 IF_PRINT_WARNING(GLOBAL_DEBUG) << "actor did not have a skill with the requested skill_id: " << skill_id << endl;
347 return NULL;
348 }
349
350 return skill_location->second;
351 }
352
353
354
GetSkill(const GlobalSkill * skill) const355 GlobalSkill* GlobalActor::GetSkill(const GlobalSkill* skill) const {
356 if (skill == NULL) {
357 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function received a NULL pointer argument" << endl;
358 return NULL;
359 }
360
361 return GetSkill(skill->GetID());
362 }
363
364
365
AddHitPoints(uint32 amount)366 void GlobalActor::AddHitPoints(uint32 amount) {
367 if ((0xFFFFFFFF - amount) < _hit_points) {
368 IF_PRINT_WARNING(GLOBAL_DEBUG) << "integer overflow condition detected: " << amount << endl;
369 _hit_points = 0xFFFFFFFF;
370 }
371 else {
372 _hit_points += amount;
373 }
374
375 if (_hit_points > _max_hit_points)
376 _hit_points = _max_hit_points;
377 }
378
379
380
SubtractHitPoints(uint32 amount)381 void GlobalActor::SubtractHitPoints(uint32 amount) {
382 if (amount >= _hit_points)
383 _hit_points = 0;
384 else
385 _hit_points -= amount;
386 }
387
388
389
AddMaxHitPoints(uint32 amount)390 void GlobalActor::AddMaxHitPoints(uint32 amount) {
391 if ((0xFFFFFFFF - amount) < _max_hit_points) {
392 IF_PRINT_WARNING(GLOBAL_DEBUG) << "integer overflow condition detected: " << amount << endl;
393 _max_hit_points = 0xFFFFFFFF;
394 }
395 else {
396 _max_hit_points += amount;
397 }
398 }
399
400
401
SubtractMaxHitPoints(uint32 amount)402 void GlobalActor::SubtractMaxHitPoints(uint32 amount) {
403 if (amount > _max_hit_points) {
404 IF_PRINT_WARNING(GLOBAL_DEBUG) << "argument value will cause max hit points to decrease to zero: " << amount << endl;
405 _max_hit_points = 0;
406 _hit_points = 0;
407 }
408 else {
409 _max_hit_points -= amount;
410 if (_hit_points > _max_hit_points)
411 _hit_points = _max_hit_points;
412 }
413 }
414
415
416
AddSkillPoints(uint32 amount)417 void GlobalActor::AddSkillPoints(uint32 amount) {
418 if ((0xFFFFFFFF - amount) < _skill_points) {
419 IF_PRINT_WARNING(GLOBAL_DEBUG) << "integer overflow condition detected: " << amount << endl;
420 _skill_points = 0xFFFFFFFF;
421 }
422 else {
423 _skill_points += amount;
424 }
425
426 if (_skill_points > _max_skill_points)
427 _skill_points = _max_skill_points;
428 }
429
430
431
SubtractSkillPoints(uint32 amount)432 void GlobalActor::SubtractSkillPoints(uint32 amount) {
433 if (amount >= _skill_points)
434 _skill_points = 0;
435 else
436 _skill_points -= amount;
437 }
438
439
440
AddMaxSkillPoints(uint32 amount)441 void GlobalActor::AddMaxSkillPoints(uint32 amount) {
442 if ((0xFFFFFFFF - amount) < _max_skill_points) {
443 IF_PRINT_WARNING(GLOBAL_DEBUG) << "integer overflow condition detected: " << amount << endl;
444 _max_skill_points = 0xFFFFFFFF;
445 }
446 else {
447 _max_skill_points += amount;
448 }
449 }
450
451
452
SubtractMaxSkillPoints(uint32 amount)453 void GlobalActor::SubtractMaxSkillPoints(uint32 amount) {
454 if (amount > _max_skill_points) {
455 IF_PRINT_WARNING(GLOBAL_DEBUG) << "argument value will cause max skill points to decrease to zero: " << amount << endl;
456 _max_skill_points = 0;
457 _skill_points = 0;
458 }
459 else {
460 _max_skill_points -= amount;
461 if (_skill_points > _max_skill_points)
462 _skill_points = _max_skill_points;
463 }
464 }
465
466
467
AddStrength(uint32 amount)468 void GlobalActor::AddStrength(uint32 amount) {
469 if ((0xFFFFFFFF - amount) < _strength) {
470 IF_PRINT_WARNING(GLOBAL_DEBUG) << "integer overflow condition detected: " << amount << endl;
471 _strength = 0xFFFFFFFF;
472 }
473 else {
474 _strength += amount;
475 }
476
477 _CalculateAttackRatings();
478 }
479
480
481
SubtractStrength(uint32 amount)482 void GlobalActor::SubtractStrength(uint32 amount) {
483 if (amount >= _strength)
484 _strength = 0;
485 else
486 _strength -= amount;
487
488 _CalculateAttackRatings();
489 }
490
491
492
AddVigor(uint32 amount)493 void GlobalActor::AddVigor(uint32 amount) {
494 if ((0xFFFFFFFF - amount) < _vigor) {
495 IF_PRINT_WARNING(GLOBAL_DEBUG) << "integer overflow condition detected: " << amount << endl;
496 _vigor = 0xFFFFFFFF;
497 }
498 else {
499 _vigor += amount;
500 }
501
502 _CalculateAttackRatings();
503 }
504
505
506
SubtractVigor(uint32 amount)507 void GlobalActor::SubtractVigor(uint32 amount) {
508 if (amount >= _vigor)
509 _vigor = 0;
510 else
511 _vigor -= amount;
512
513 _CalculateAttackRatings();
514 }
515
516
517
AddFortitude(uint32 amount)518 void GlobalActor::AddFortitude(uint32 amount) {
519 if ((0xFFFFFFFF - amount) < _fortitude) {
520 IF_PRINT_WARNING(GLOBAL_DEBUG) << "integer overflow condition detected: " << amount << endl;
521 _fortitude = 0xFFFFFFFF;
522 }
523 else {
524 _fortitude += amount;
525 }
526
527 _CalculateDefenseRatings();
528 }
529
530
531
SubtractFortitude(uint32 amount)532 void GlobalActor::SubtractFortitude(uint32 amount) {
533 if (amount >= _fortitude)
534 _fortitude = 0;
535 else
536 _fortitude -= amount;
537
538 _CalculateDefenseRatings();
539 }
540
541
542
AddProtection(uint32 amount)543 void GlobalActor::AddProtection(uint32 amount) {
544 if ((0xFFFFFFFF - amount) < _protection) {
545 IF_PRINT_WARNING(GLOBAL_DEBUG) << "integer overflow condition detected: " << amount << endl;
546 _protection = 0xFFFFFFFF;
547 }
548 else {
549 _protection += amount;
550 }
551
552 _CalculateDefenseRatings();
553 }
554
555
556
SubtractProtection(uint32 amount)557 void GlobalActor::SubtractProtection(uint32 amount) {
558 if (amount >= _protection)
559 _protection = 0;
560 else
561 _protection -= amount;
562
563 _CalculateDefenseRatings();
564 }
565
566
567
AddAgility(uint32 amount)568 void GlobalActor::AddAgility(uint32 amount) {
569 if ((0xFFFFFFFF - amount) < _agility) {
570 IF_PRINT_WARNING(GLOBAL_DEBUG) << "integer overflow condition detected: " << amount << endl;
571 _agility = 0xFFFFFFFF;
572 }
573 else {
574 _agility += amount;
575 }
576 }
577
578
579
SubtractAgility(uint32 amount)580 void GlobalActor::SubtractAgility(uint32 amount) {
581 if (amount >= _agility)
582 _agility = 0;
583 else
584 _agility -= amount;
585 }
586
587
588
AddEvade(float amount)589 void GlobalActor::AddEvade(float amount) {
590 if (amount < 0.0f) {
591 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function received negative argument value: " << amount << endl;
592 return;
593 }
594
595 float new_evade = _evade + amount;
596 if (new_evade < _evade) {
597 IF_PRINT_WARNING(GLOBAL_DEBUG) << "floating point overflow condition detected: " << amount << endl;
598 _evade = 1.0f;
599 }
600 else if (new_evade > 1.0f) {
601 IF_PRINT_WARNING(GLOBAL_DEBUG) << "evade rating increased above 1.0f: " << amount << endl;
602 _evade = 1.0f;
603 }
604 else {
605 _evade = new_evade;
606 }
607
608 _CalculateEvadeRatings();
609 }
610
611
612
SubtractEvade(float amount)613 void GlobalActor::SubtractEvade(float amount) {
614 if (amount > 0.0f) {
615 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function received positive argument value: " << amount << endl;
616 return;
617 }
618
619 float new_evade = _evade + amount;
620 if (new_evade > _evade) {
621 IF_PRINT_WARNING(GLOBAL_DEBUG) << "floating point overflow condition detected: " << amount << endl;
622 _evade = 0.0f;
623 }
624 else if (new_evade < 0.0f) {
625 IF_PRINT_WARNING(GLOBAL_DEBUG) << "evade rating decreased below 0.0f: " << amount << endl;
626 _evade = 0.0f;
627 }
628 else {
629 _evade = new_evade;
630 }
631
632 _CalculateEvadeRatings();
633 }
634
635
636
_CalculateAttackRatings()637 void GlobalActor::_CalculateAttackRatings() {
638 _total_physical_attack = _strength;
639 _total_metaphysical_attack = _vigor;
640
641 if (_weapon_equipped != NULL) {
642 _total_physical_attack += _weapon_equipped->GetPhysicalAttack();
643 _total_metaphysical_attack += _weapon_equipped->GetMetaphysicalAttack();
644 }
645 }
646
647
648
_CalculateDefenseRatings()649 void GlobalActor::_CalculateDefenseRatings() {
650 // Re-calculate the defense ratings for all attack points
651 for (uint32 i = 0; i < _attack_points.size(); i++) {
652 if ((i < _armor_equipped.size()) && (_armor_equipped[i] != NULL))
653 _attack_points[i]->CalculateTotalDefense(_armor_equipped[i]);
654 else
655 _attack_points[i]->CalculateTotalDefense(NULL);
656 }
657 }
658
659
660
_CalculateEvadeRatings()661 void GlobalActor::_CalculateEvadeRatings() {
662 // Re-calculate the evade ratings for all attack points
663 for (uint32 i = 0; i < _attack_points.size(); i++) {
664 _attack_points[i]->CalculateTotalEvade();
665 }
666 }
667
668 ////////////////////////////////////////////////////////////////////////////////
669 // GlobalCharacterGrowth class
670 ////////////////////////////////////////////////////////////////////////////////
671
GlobalCharacterGrowth(GlobalCharacter * owner)672 GlobalCharacterGrowth::GlobalCharacterGrowth(GlobalCharacter* owner) :
673 _character_owner(owner),
674 _experience_level_gained(false),
675 _growth_detected(false),
676 _experience_for_next_level(0),
677 _experience_for_last_level(0),
678 _hit_points_growth(0),
679 _skill_points_growth(0),
680 _strength_growth(0),
681 _vigor_growth(0),
682 _fortitude_growth(0),
683 _protection_growth(0),
684 _agility_growth(0),
685 _evade_growth(0.0f)
686 {}
687
688
689
~GlobalCharacterGrowth()690 GlobalCharacterGrowth::~GlobalCharacterGrowth() {
691 for (uint32 i = 0; i < _skills_learned.size(); i++)
692 delete _skills_learned[i];
693 _skills_learned.clear();
694 }
695
696
697
AcknowledgeGrowth()698 void GlobalCharacterGrowth::AcknowledgeGrowth() {
699 if (_growth_detected == false) {
700 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function was invoked when there was no character growth detected" << endl;
701 return;
702 }
703
704 _growth_detected = false;
705
706 // Add all growth stats to the character actor
707 if (_hit_points_growth != 0) {
708 _character_owner->AddMaxHitPoints(_hit_points_growth);
709 _character_owner->AddHitPoints(_hit_points_growth);
710 }
711 if (_skill_points_growth != 0) {
712 _character_owner->AddMaxSkillPoints(_skill_points_growth);
713 _character_owner->AddSkillPoints(_skill_points_growth);
714 }
715 if (_strength_growth != 0)
716 _character_owner->AddStrength(_strength_growth);
717 if (_vigor_growth != 0)
718 _character_owner->AddVigor(_vigor_growth);
719 if (_fortitude_growth != 0)
720 _character_owner->AddFortitude(_fortitude_growth);
721 if (_protection_growth != 0)
722 _character_owner->AddProtection(_protection_growth);
723 if (_agility_growth != 0)
724 _character_owner->AddAgility(_agility_growth);
725 if (IsFloatEqual(_evade_growth, 0.0f) == false)
726 _character_owner->AddEvade(_evade_growth);
727
728 _hit_points_growth = 0;
729 _skill_points_growth = 0;
730 _strength_growth = 0;
731 _vigor_growth = 0;
732 _fortitude_growth = 0;
733 _protection_growth = 0;
734 _agility_growth = 0;
735 _evade_growth = 0.0f;
736
737 // If a new experience level has been gained, we must retrieve the growth data for the new experience level
738 if (_experience_level_gained) {
739 _character_owner->_experience_level += 1;
740 _experience_level_gained = false;
741 _DetermineNextLevelExperience();
742
743 string filename = "dat/actors/characters.lua";
744 ReadScriptDescriptor character_script;
745 if (character_script.OpenFile(filename) == false) {
746 IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to open character data file: " << filename << endl;
747 return;
748 }
749
750 try {
751 ScriptCallFunction<void>(character_script.GetLuaState(), "DetermineGrowth", _character_owner);
752 _ConstructPeriodicGrowth();
753 _CheckForGrowth();
754 }
755 catch (luabind::error e) {
756 ScriptManager->HandleLuaError(e);
757 }
758 catch (luabind::cast_failed e) {
759 ScriptManager->HandleCastError(e);
760 }
761
762 character_script.CloseFile();
763
764 // Add any newly learned skills
765 for (uint32 i = 0; i < _skills_learned.size(); i++) {
766 GlobalSkill* skill = _skills_learned[i];
767 if (_character_owner->_skills.find(skill->GetID()) != _character_owner->_skills.end()) {
768 IF_PRINT_WARNING(GLOBAL_DEBUG) << "character had already learned the skill with the id: " << skill->GetID() << endl;
769 delete _skills_learned[i];
770 continue;
771 }
772
773 // Insert the pointer to the new skill inside of the global skills map and the skill type vector
774 _character_owner->_skills.insert(make_pair(skill->GetID(), skill));
775 switch (skill->GetType()) {
776 case GLOBAL_SKILL_ATTACK:
777 _character_owner->_attack_skills.push_back(skill);
778 break;
779 case GLOBAL_SKILL_DEFEND:
780 _character_owner->_defense_skills.push_back(skill);
781 break;
782 case GLOBAL_SKILL_SUPPORT:
783 _character_owner->_support_skills.push_back(skill);
784 break;
785 default:
786 IF_PRINT_WARNING(GLOBAL_DEBUG) << "newly learned skill had an unknown skill type: " << skill->GetType() << endl;
787 break;
788 }
789 }
790 // skills have been given out, clean out the vector
791 //CD: Negatory! Leeave the vector as is. I need it in order to display the
792 // correct stuff in the skills learned window in FinishWindow. I will clear it
793 // myself from there. Not only that, but if they level up multiple times, I need
794 // to save the whole list so I can show all the skills they learned across
795 // multiple levels. If this were called, they would never know they learned a skill
796 // at level 3 because they jumped to level 4 in the same battle
797 //_skills_learned.clear();
798 } // if (_experience_level_gained)
799 } // void GlobalCharacterGrowth::AcknowledgeGrowth()
800
801
802
_AddSkill(uint32 skill_id)803 void GlobalCharacterGrowth::_AddSkill(uint32 skill_id) {
804 if (skill_id == 0) {
805 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function received an invalid skill_id argument: " << skill_id << endl;
806 return;
807 }
808 // Make sure we don't add a skill to learn more than once
809 for (vector<GlobalSkill*>::iterator i = _skills_learned.begin(); i != _skills_learned.end(); i++) {
810 if (skill_id == (*i)->GetID()) {
811 IF_PRINT_WARNING(GLOBAL_DEBUG) << "the skill to add was already present in the list of skills to learn: " << skill_id << endl;
812 return;
813 }
814 }
815
816 GlobalSkill* skill = new GlobalSkill(skill_id);
817 if (skill->IsValid() == false) {
818 IF_PRINT_WARNING(GLOBAL_DEBUG) << "the skill to add failed to load: " << skill_id << endl;
819 delete skill;
820 }
821 else {
822 _skills_learned.push_back(skill);
823 }
824 }
825
826
827
_CheckForGrowth()828 void GlobalCharacterGrowth::_CheckForGrowth() {
829 // ----- (1): If a new experience level is gained, empty the periodic growth containers into the growth members
830 if (_character_owner->GetExperiencePoints() >= _experience_for_next_level) {
831 _experience_level_gained = true;
832 _growth_detected = true;
833
834 for (uint32 i = 0; i < _hit_points_periodic_growth.size(); i++)
835 _hit_points_growth += _hit_points_periodic_growth[i].second;
836 _hit_points_periodic_growth.clear();
837
838 for (uint32 i = 0; i < _skill_points_periodic_growth.size(); i++)
839 _skill_points_growth += _skill_points_periodic_growth[i].second;
840 _skill_points_periodic_growth.clear();
841
842 for (uint32 i = 0; i < _strength_periodic_growth.size(); i++)
843 _strength_growth += _strength_periodic_growth[i].second;
844 _strength_periodic_growth.clear();
845
846 for (uint32 i = 0; i < _vigor_periodic_growth.size(); i++)
847 _vigor_growth += _vigor_periodic_growth[i].second;
848 _vigor_periodic_growth.clear();
849
850 for (uint32 i = 0; i < _fortitude_periodic_growth.size(); i++)
851 _fortitude_growth += _fortitude_periodic_growth[i].second;
852 _fortitude_periodic_growth.clear();
853
854 for (uint32 i = 0; i < _protection_periodic_growth.size(); i++)
855 _protection_growth += _protection_periodic_growth[i].second;
856 _protection_periodic_growth.clear();
857
858 for (uint32 i = 0; i < _agility_periodic_growth.size(); i++)
859 _agility_growth += _agility_periodic_growth[i].second;
860 _agility_periodic_growth.clear();
861
862 for (uint32 i = 0; i < _evade_periodic_growth.size(); i++)
863 _evade_growth += _evade_periodic_growth[i].second;
864 _evade_periodic_growth.clear();
865
866
867 return;
868 } // if (_actor_ower->GetExperiencePoints() >= _experience_for_next_level)
869
870 // ----- (2): If there is no growth detected, check all periodic growth containers
871 switch (_growth_detected) { // switch statement used instead of if statement here so we can break out of it early
872 case true:
873 break;
874 case false:
875 if (_hit_points_periodic_growth.empty() == false) {
876 if (_character_owner->GetExperiencePoints() >= _hit_points_periodic_growth.front().first) {
877 _growth_detected = true;
878 break;
879 }
880 }
881
882 if (_skill_points_periodic_growth.empty() == false) {
883 if (_character_owner->GetExperiencePoints() >= _skill_points_periodic_growth.front().first) {
884 _growth_detected = true;
885 break;
886 }
887 }
888
889 if (_strength_periodic_growth.empty() == false) {
890 if (_character_owner->GetExperiencePoints() >= _strength_periodic_growth.front().first) {
891 _growth_detected = true;
892 break;
893 }
894 }
895
896 if (_vigor_periodic_growth.empty() == false) {
897 if (_character_owner->GetExperiencePoints() >= _vigor_periodic_growth.front().first) {
898 _growth_detected = true;
899 break;
900 }
901 }
902
903 if (_fortitude_periodic_growth.empty() == false) {
904 if (_character_owner->GetExperiencePoints() >= _fortitude_periodic_growth.front().first) {
905 _growth_detected = true;
906 break;
907 }
908 }
909
910 if (_protection_periodic_growth.empty() == false) {
911 if (_character_owner->GetExperiencePoints() >= _protection_periodic_growth.front().first) {
912 _growth_detected = true;
913 break;
914 }
915 }
916
917 if (_agility_periodic_growth.empty() == false) {
918 if (_character_owner->GetExperiencePoints() >= _agility_periodic_growth.front().first) {
919 _growth_detected = true;
920 break;
921 }
922 }
923
924 if (_evade_periodic_growth.empty() == false) {
925 if (_character_owner->GetExperiencePoints() >= _evade_periodic_growth.front().first) {
926 _growth_detected = true;
927 break;
928 }
929 }
930 break;
931 } // switch (_growth_detected)
932
933 // ----- (3): If there is growth detected update all periodic growth containers
934 if (_growth_detected == true) {
935 while (_hit_points_periodic_growth.begin() != _hit_points_periodic_growth.end()) {
936 if (_character_owner->GetExperiencePoints() >= _hit_points_periodic_growth.begin()->first) {
937 _hit_points_growth += _hit_points_periodic_growth.begin()->second;
938 _hit_points_periodic_growth.pop_front();
939 }
940 else {
941 break;
942 }
943 }
944
945 while (_skill_points_periodic_growth.begin() != _skill_points_periodic_growth.end()) {
946 if (_character_owner->GetExperiencePoints() >= _skill_points_periodic_growth.begin()->first) {
947 _skill_points_growth += _skill_points_periodic_growth.begin()->second;
948 _skill_points_periodic_growth.pop_front();
949 }
950 else {
951 break;
952 }
953 }
954
955 while (_strength_periodic_growth.begin() != _strength_periodic_growth.end()) {
956 if (_character_owner->GetExperiencePoints() >= _strength_periodic_growth.begin()->first) {
957 _strength_growth += _strength_periodic_growth.begin()->second;
958 _strength_periodic_growth.pop_front();
959 }
960 else {
961 break;
962 }
963 }
964
965 while (_vigor_periodic_growth.begin() != _vigor_periodic_growth.end()) {
966 if (_character_owner->GetExperiencePoints() >= _vigor_periodic_growth.begin()->first) {
967 _vigor_growth += _vigor_periodic_growth.begin()->second;
968 _vigor_periodic_growth.pop_front();
969 }
970 else {
971 break;
972 }
973 }
974
975 while (_fortitude_periodic_growth.begin() != _fortitude_periodic_growth.end()) {
976 if (_character_owner->GetExperiencePoints() >= _fortitude_periodic_growth.begin()->first) {
977 _fortitude_growth += _fortitude_periodic_growth.begin()->second;
978 _fortitude_periodic_growth.pop_front();
979 }
980 else {
981 break;
982 }
983 }
984
985 while (_protection_periodic_growth.begin() != _protection_periodic_growth.end()) {
986 if (_character_owner->GetExperiencePoints() >= _protection_periodic_growth.begin()->first) {
987 _protection_growth += _protection_periodic_growth.begin()->second;
988 _protection_periodic_growth.pop_front();
989 }
990 else {
991 break;
992 }
993 }
994
995 while (_agility_periodic_growth.begin() != _agility_periodic_growth.end()) {
996 if (_character_owner->GetExperiencePoints() >= _agility_periodic_growth.begin()->first) {
997 _agility_growth += _agility_periodic_growth.begin()->second;
998 _agility_periodic_growth.pop_front();
999 }
1000 else {
1001 break;
1002 }
1003 }
1004
1005 while (_evade_periodic_growth.begin() != _evade_periodic_growth.end()) {
1006 if (_character_owner->GetExperiencePoints() >= _evade_periodic_growth.begin()->first) {
1007 _evade_growth += _evade_periodic_growth.begin()->second;
1008 _evade_periodic_growth.pop_front();
1009 }
1010 else {
1011 break;
1012 }
1013 }
1014 } // if (_growth_detected == true)
1015 } // void GlobalCharacterGrowth::_CheckForGrowth()
1016
1017
1018
_ConstructPeriodicGrowth()1019 void GlobalCharacterGrowth::_ConstructPeriodicGrowth() {
1020 // TODO: Implement a gradual growth algorithm
1021
1022 // TEMP: all growth is done when the experience level is gained
1023 _hit_points_periodic_growth.push_back(make_pair(_experience_for_next_level, _hit_points_growth));
1024 _skill_points_periodic_growth.push_back(make_pair(_experience_for_next_level, _skill_points_growth));
1025 _strength_periodic_growth.push_back(make_pair(_experience_for_next_level, _strength_growth));
1026 _vigor_periodic_growth.push_back(make_pair(_experience_for_next_level, _vigor_growth));
1027 _fortitude_periodic_growth.push_back(make_pair(_experience_for_next_level, _fortitude_growth));
1028 _protection_periodic_growth.push_back(make_pair(_experience_for_next_level, _protection_growth));
1029 _agility_periodic_growth.push_back(make_pair(_experience_for_next_level, _agility_growth));
1030 _evade_periodic_growth.push_back(make_pair(_experience_for_next_level, _evade_growth));
1031
1032 _hit_points_growth = 0;
1033 _skill_points_growth = 0;
1034 _strength_growth = 0;
1035 _vigor_growth = 0;
1036 _fortitude_growth = 0;
1037 _protection_growth = 0;
1038 _agility_growth = 0;
1039 _evade_growth = 0.0f;
1040 }
1041
1042
1043
_DetermineNextLevelExperience()1044 void GlobalCharacterGrowth::_DetermineNextLevelExperience() {
1045 uint32 base_xp = 0;
1046 uint32 new_xp = 0;
1047
1048 // TODO: implement a real algorithm for determining the next experience goal
1049 base_xp = _character_owner->GetExperienceLevel() * 40;
1050 new_xp = GaussianRandomValue(base_xp, base_xp / 10.0f);
1051
1052 _experience_for_last_level = _experience_for_next_level;
1053 _experience_for_next_level = new_xp;
1054 }
1055
1056 ////////////////////////////////////////////////////////////////////////////////
1057 // GlobalCharacter class
1058 ////////////////////////////////////////////////////////////////////////////////
1059
GlobalCharacter(uint32 id,bool initial)1060 GlobalCharacter::GlobalCharacter(uint32 id, bool initial) :
1061 _growth(this)
1062 {
1063 _id = id;
1064
1065 // ----- (1): Open the characters script file
1066 string filename = "dat/actors/characters.lua";
1067 ReadScriptDescriptor char_script;
1068 if (char_script.OpenFile(filename) == false) {
1069 PRINT_ERROR << "failed to open character data file: " << filename << endl;
1070 return;
1071 }
1072
1073 // ----- (2): Retrieve their basic character property data
1074 char_script.OpenTable("characters");
1075 char_script.OpenTable(_id);
1076 _name = MakeUnicodeString(char_script.ReadString("name"));
1077 _filename = char_script.ReadString("filename");
1078
1079 // ----- (3): Construct the character from the initial stats if necessary
1080 if (initial == true) {
1081 char_script.OpenTable("initial_stats");
1082 _experience_level = char_script.ReadUInt("experience_level");
1083 _experience_points = char_script.ReadUInt("experience_points");
1084 _max_hit_points = char_script.ReadUInt("max_hit_points");
1085 _hit_points = _max_hit_points;
1086 _max_skill_points = char_script.ReadUInt("max_skill_points");
1087 _skill_points = _max_skill_points;
1088 _strength = char_script.ReadUInt("strength");
1089 _vigor = char_script.ReadUInt("vigor");
1090 _fortitude = char_script.ReadUInt("fortitude");
1091 _protection = char_script.ReadUInt("protection");
1092 _agility = char_script.ReadUInt("agility");
1093 _evade = char_script.ReadFloat("evade");
1094
1095 // Add the character's initial equipment. If any equipment ids are zero, that indicates nothing is to be equipped.
1096 uint32 equipment_id = 0;
1097 equipment_id = char_script.ReadUInt("weapon");
1098 if (equipment_id != 0)
1099 _weapon_equipped = new GlobalWeapon(equipment_id);
1100 else
1101 _weapon_equipped = NULL;
1102
1103 equipment_id = char_script.ReadUInt("head_armor");
1104 if (equipment_id != 0)
1105 _armor_equipped.push_back(new GlobalArmor(equipment_id));
1106 else
1107 _armor_equipped.push_back(NULL);
1108
1109 equipment_id = char_script.ReadUInt("torso_armor");
1110 if (equipment_id != 0)
1111 _armor_equipped.push_back(new GlobalArmor(equipment_id));
1112 else
1113 _armor_equipped.push_back(NULL);
1114
1115 equipment_id = char_script.ReadUInt("arm_armor");
1116 if (equipment_id != 0)
1117 _armor_equipped.push_back(new GlobalArmor(equipment_id));
1118 else
1119 _armor_equipped.push_back(NULL);
1120
1121 equipment_id = char_script.ReadUInt("leg_armor");
1122 if (equipment_id != 0)
1123 _armor_equipped.push_back(new GlobalArmor(equipment_id));
1124 else
1125 _armor_equipped.push_back(NULL);
1126
1127 char_script.CloseTable();
1128 if (char_script.IsErrorDetected()) {
1129 if (GLOBAL_DEBUG) {
1130 PRINT_WARNING << "one or more errors occurred while reading initial data - they are listed below" << endl;
1131 cerr << char_script.GetErrorMessages() << endl;
1132 }
1133 }
1134 } // if (initial == true)
1135 else {
1136 // Make sure the _armor_equipped vector is sized appropriately
1137 _armor_equipped.resize(4, NULL);
1138 }
1139
1140 // ----- (4): Setup the character's attack points
1141 char_script.OpenTable("attack_points");
1142 for (uint32 i = GLOBAL_POSITION_HEAD; i <= GLOBAL_POSITION_LEGS; i++) {
1143 _attack_points.push_back(new GlobalAttackPoint(this));
1144 char_script.OpenTable(i);
1145 if (_attack_points[i]->LoadData(char_script) == false) {
1146 IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to succesfully load data for attack point: " << i << endl;
1147 }
1148 char_script.CloseTable();
1149 }
1150 char_script.CloseTable();
1151
1152 if (char_script.IsErrorDetected()) {
1153 if (GLOBAL_DEBUG) {
1154 PRINT_WARNING << "one or more errors occurred while reading attack point data - they are listed below" << endl;
1155 cerr << char_script.GetErrorMessages() << endl;
1156 }
1157 }
1158
1159 // ----- (5): Construct the character's initial skill set if necessary
1160 if (initial) {
1161 // The skills table contains key/value pairs. The key indicate the level required to learn the skill and the value is the skill's id
1162 uint32 skill_id;
1163 vector<uint32> skill_levels;
1164 char_script.OpenTable("skills");
1165 char_script.ReadTableKeys(skill_levels);
1166
1167 // Only add the skills for which the experience level requirements are met
1168 for (uint32 i = 0; i < skill_levels.size(); i++) {
1169 skill_id = char_script.ReadUInt(skill_levels[i]);
1170 if (skill_levels[i] <= _experience_level) {
1171 AddSkill(skill_id);
1172 }
1173 }
1174
1175 char_script.CloseTable();
1176 if (char_script.IsErrorDetected()) {
1177 if (GLOBAL_DEBUG) {
1178 PRINT_WARNING << "one or more errors occurred while reading skill data - they are listed below" << endl;
1179 cerr << char_script.GetErrorMessages() << endl;
1180 }
1181 }
1182 } // if (initial)
1183
1184 char_script.CloseTable(); // "characters[id]"
1185 char_script.CloseTable(); // "characters"
1186
1187 // ----- (6): Determine the character's initial growth if necessary
1188 if (initial) {
1189 // Initialize the experience level milestones
1190 _growth._experience_for_last_level = _experience_points;
1191 _growth._experience_for_next_level = _experience_points;
1192 _growth._DetermineNextLevelExperience();
1193 try {
1194 ScriptCallFunction<void>(char_script.GetLuaState(), "DetermineGrowth", this);
1195 _growth._ConstructPeriodicGrowth();
1196 }
1197 catch (luabind::error e) {
1198 ScriptManager->HandleLuaError(e);
1199 }
1200 catch (luabind::cast_failed e) {
1201 ScriptManager->HandleCastError(e);
1202 }
1203 }
1204
1205 // ----- (7): Close the script file and calculate all rating totals
1206 if (char_script.IsErrorDetected()) {
1207 if (GLOBAL_DEBUG) {
1208 PRINT_WARNING << "one or more errors occurred while reading final data - they are listed below" << endl;
1209 cerr << char_script.GetErrorMessages() << endl;
1210 }
1211 }
1212 char_script.CloseFile();
1213
1214 _CalculateAttackRatings();
1215 _CalculateDefenseRatings();
1216 _CalculateEvadeRatings();
1217
1218 // ----- (8) Load character sprite and portrait images
1219 // NOTE: The code below is all TEMP and is subject to great change or removal in the future
1220 // TEMP: load standard map sprite walking frames
1221 if (ImageDescriptor::LoadMultiImageFromElementGrid(_map_frames_standard, "img/sprites/map/" + _filename + "_walk.png", 4, 6) == false) {
1222 exit(1);
1223 }
1224
1225 // TEMP: Load the character's idle animation
1226 AnimatedImage idle;
1227 idle.SetDimensions(96, 96);
1228 vector<uint32> idle_timings(4, 15);
1229
1230 if (idle.LoadFromFrameGrid("img/sprites/map/" + _filename + "_idle.png", idle_timings, 1, 4) == false) {
1231 exit(1);
1232 }
1233 _battle_animation["idle"] = idle;
1234
1235 // TEMP: Load the character's attack animation
1236 AnimatedImage attack;
1237 attack.SetDimensions(96, 96);
1238 vector<uint32> attack_timings(5, 5);
1239
1240 if (attack.LoadFromFrameGrid("img/sprites/map/" + _filename + "_attack.png", attack_timings, 1, 5) == false) {
1241 exit(1);
1242 }
1243 _battle_animation["attack"] = attack;
1244
1245 // TEMP: Load the character's battle portraits from a multi image
1246 _battle_portraits.assign(5, StillImage());
1247 for (uint32 i = 0; i < _battle_portraits.size(); i++) {
1248 _battle_portraits[i].SetDimensions(100, 100);
1249 }
1250 if (ImageDescriptor::LoadMultiImageFromElementGrid(_battle_portraits, "img/portraits/battle/" + _filename + "_damage.png", 1, 5) == false)
1251 exit(1);
1252 } // GlobalCharacter::GlobalCharacter(uint32 id, bool initial)
1253
1254
1255
AddExperiencePoints(uint32 xp)1256 bool GlobalCharacter::AddExperiencePoints(uint32 xp) {
1257 _experience_points += xp;
1258 _growth._CheckForGrowth();
1259 return _growth.IsGrowthDetected();
1260 }
1261
1262
1263
AddSkill(uint32 skill_id)1264 void GlobalCharacter::AddSkill(uint32 skill_id) {
1265 if (skill_id == 0) {
1266 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function received an invalid skill_id argument: " << skill_id << endl;
1267 return;
1268 }
1269 if (_skills.find(skill_id) != _skills.end()) {
1270 IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to add skill because the character already knew this skill: " << skill_id << endl;
1271 return;
1272 }
1273
1274 GlobalSkill* skill = new GlobalSkill(skill_id);
1275 if (skill->IsValid() == false) {
1276 IF_PRINT_WARNING(GLOBAL_DEBUG) << "the skill to add failed to load: " << skill_id << endl;
1277 delete skill;
1278 return;
1279 }
1280
1281 // Insert the pointer to the new skill inside of the global skills map and the skill type vector
1282 _skills.insert(make_pair(skill_id, skill));
1283 switch (skill->GetType()) {
1284 case GLOBAL_SKILL_ATTACK:
1285 _attack_skills.push_back(skill);
1286 break;
1287 case GLOBAL_SKILL_DEFEND:
1288 _defense_skills.push_back(skill);
1289 break;
1290 case GLOBAL_SKILL_SUPPORT:
1291 _support_skills.push_back(skill);
1292 break;
1293 default:
1294 IF_PRINT_WARNING(GLOBAL_DEBUG) << "loaded a new skill with an unknown skill type: " << skill->GetType() << endl;
1295 break;
1296 }
1297 }
1298
1299 ////////////////////////////////////////////////////////////////////////////////
1300 // GlobalEnemy class
1301 ////////////////////////////////////////////////////////////////////////////////
1302
GlobalEnemy(uint32 id)1303 GlobalEnemy::GlobalEnemy(uint32 id) :
1304 GlobalActor(),
1305 _growth_hit_points(0.0f),
1306 _growth_skill_points(0.0f),
1307 _growth_experience_points(0.0f),
1308 _growth_strength(0.0f),
1309 _growth_vigor(0.0f),
1310 _growth_fortitude(0.0f),
1311 _growth_protection(0.0f),
1312 _growth_agility(0.0f),
1313 _growth_evade(0.0f),
1314 _growth_drunes(0.0f),
1315 _drunes_dropped(0),
1316 _sprite_width(0),
1317 _sprite_height(0)
1318 {
1319 _id = id;
1320
1321 // ----- (1): Use the id member to determine the name of the data file that the enemy is defined in
1322 string file_ext;
1323 string filename;
1324
1325 if (_id == 0)
1326 PRINT_ERROR << "invalid id for loading enemy data: " << _id << endl;
1327 else if ((_id > 0) && (_id <= 100))
1328 file_ext = "01";
1329 else if ((_id > 100) && (_id <= 200))
1330 file_ext = "02";
1331
1332 filename = "dat/actors/enemies_set_" + file_ext + ".lua";
1333
1334 // ----- (2): Open the script file and table that store the enemy data
1335 ReadScriptDescriptor enemy_data;
1336 if (enemy_data.OpenFile(filename) == false) {
1337 PRINT_ERROR << "failed to open enemy data file: " << filename << endl;
1338 return;
1339 }
1340
1341 enemy_data.OpenTable("enemies");
1342 enemy_data.OpenTable(_id);
1343
1344 // ----- (3): Load the enemy's name and sprite data
1345 _name = MakeUnicodeString(enemy_data.ReadString("name"));
1346 _filename = enemy_data.ReadString("filename");
1347 _sprite_width = enemy_data.ReadInt("sprite_width");
1348 _sprite_height = enemy_data.ReadInt("sprite_height");
1349
1350 // ----- (4): Attempt to load the MultiImage for the sprite's frames, which should contain one row and four columns of images
1351 _battle_sprite_frames.assign(4, StillImage());
1352 string sprite_filename = "img/sprites/battle/enemies/" + _filename + ".png";
1353 if (ImageDescriptor::LoadMultiImageFromElementGrid(_battle_sprite_frames, sprite_filename, 1, 4) == false) {
1354 IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to load sprite frames for enemy: " << sprite_filename << endl;
1355 }
1356
1357 // ----- (5): Load the enemy's initial stats
1358 enemy_data.OpenTable("initial_stats");
1359 _max_hit_points = enemy_data.ReadUInt("hit_points");
1360 _hit_points = _max_hit_points;
1361 _max_skill_points = enemy_data.ReadUInt("skill_points");
1362 _skill_points = _max_skill_points;
1363 _experience_points = enemy_data.ReadUInt("experience_points");
1364 _strength = enemy_data.ReadUInt("strength");
1365 _vigor = enemy_data.ReadUInt("vigor");
1366 _fortitude = enemy_data.ReadUInt("fortitude");
1367 _protection = enemy_data.ReadUInt("protection");
1368 _agility = enemy_data.ReadUInt("agility");
1369 _evade = enemy_data.ReadFloat("evade");
1370 _drunes_dropped = enemy_data.ReadUInt("drunes");
1371 enemy_data.CloseTable();
1372
1373 // ----- (6): Load the enemy's growth statistics
1374 enemy_data.OpenTable("growth_stats");
1375 _growth_hit_points = enemy_data.ReadFloat("hit_points");
1376 _growth_skill_points = enemy_data.ReadFloat("skill_points");
1377 _growth_experience_points = enemy_data.ReadFloat("experience_points");
1378 _growth_strength = enemy_data.ReadFloat("strength");
1379 _growth_vigor = enemy_data.ReadFloat("vigor");
1380 _growth_fortitude = enemy_data.ReadFloat("fortitude");
1381 _growth_protection = enemy_data.ReadFloat("protection");
1382 _growth_agility = enemy_data.ReadFloat("agility");
1383 _growth_evade = enemy_data.ReadFloat("evade");
1384 _growth_drunes = enemy_data.ReadFloat("drunes");
1385 enemy_data.CloseTable();
1386
1387 // ----- (7): Create the attack points for the enemy
1388 enemy_data.OpenTable("attack_points");
1389 uint32 ap_size = enemy_data.GetTableSize();
1390 for (uint32 i = 1; i <= ap_size; i++) {
1391 _attack_points.push_back(new GlobalAttackPoint(this));
1392 enemy_data.OpenTable(i);
1393 if (_attack_points.back()->LoadData(enemy_data) == false) {
1394 IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to load data for an attack point: " << i << endl;
1395 }
1396 enemy_data.CloseTable();
1397 }
1398 enemy_data.CloseTable();
1399
1400 // ----- (8): Add the set of skills to the enemy
1401 vector<int32> skill_levels;
1402 enemy_data.OpenTable("skills");
1403 enemy_data.ReadTableKeys(skill_levels);
1404 for (uint32 i = 0; i < skill_levels.size(); i++) {
1405 uint32 skill_id = enemy_data.ReadUInt(skill_levels[i]);
1406 _skill_set[skill_id] = skill_levels[i];
1407 }
1408 enemy_data.CloseTable();
1409
1410 // ----- (9): Load the possible items that the enemy may drop
1411 enemy_data.OpenTable("drop_objects");
1412 for (uint32 i = 1; i <= enemy_data.GetTableSize(); i++) {
1413 enemy_data.OpenTable(i);
1414 _dropped_objects.push_back(enemy_data.ReadUInt(1));
1415 _dropped_chance.push_back(enemy_data.ReadFloat(2));
1416 _dropped_level_required.push_back(enemy_data.ReadUInt(3));
1417 enemy_data.CloseTable();
1418 }
1419 enemy_data.CloseTable();
1420
1421 enemy_data.CloseTable(); // enemies[_id]
1422 enemy_data.CloseTable(); // enemies
1423
1424 if (enemy_data.IsErrorDetected()) {
1425 if (GLOBAL_DEBUG) {
1426 PRINT_WARNING << "one or more errors occurred while reading the enemy data - they are listed below" << endl;
1427 cerr << enemy_data.GetErrorMessages() << endl;
1428 }
1429 }
1430
1431 enemy_data.CloseFile();
1432
1433 _CalculateAttackRatings();
1434 _CalculateDefenseRatings();
1435 _CalculateEvadeRatings();
1436 } // GlobalEnemy::GlobalEnemy(uint32 id)
1437
1438
1439
AddSkill(uint32 skill_id)1440 void GlobalEnemy::AddSkill(uint32 skill_id) {
1441 if (skill_id == 0) {
1442 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function received an invalid skill_id argument: " << skill_id << endl;
1443 return;
1444 }
1445 if (_skills.find(skill_id) != _skills.end()) {
1446 IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to add skill because the enemy already knew this skill: " << skill_id << endl;
1447 return;
1448 }
1449
1450 GlobalSkill* skill = new GlobalSkill(skill_id);
1451 if (skill->IsValid() == false) {
1452 IF_PRINT_WARNING(GLOBAL_DEBUG) << "the skill to add failed to load: " << skill_id << endl;
1453 delete skill;
1454 return;
1455 }
1456
1457 // Insert the pointer to the new skill inside of the global skills map and the skill type vector
1458 _skills.insert(make_pair(skill_id, skill));
1459 }
1460
1461
1462
Initialize(uint32 xp_level)1463 void GlobalEnemy::Initialize(uint32 xp_level) {
1464 if (xp_level == 0) {
1465 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function received an invalid xp_level argument of 0: " << _id << endl;
1466 return;
1467 }
1468 if (_skills.empty() == false) { // Indicates that the enemy has already been initialized
1469 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function was invoked for an already initialized enemy: " << _id << endl;
1470 return;
1471 }
1472
1473 _experience_level = xp_level;
1474
1475 // ----- (1): Add all new skills that should be available at the current experience level
1476 for (map<uint32, uint32>::iterator i = _skill_set.begin(); i != _skill_set.end(); i++) {
1477 if (_experience_level >= i->second)
1478 AddSkill(i->first);
1479 }
1480
1481 if (_skills.empty()) {
1482 IF_PRINT_WARNING(GLOBAL_DEBUG) << "no skills were added for the enemy: " << _id << endl;
1483 }
1484
1485 // ----- (2): Add the new stats to the growth rate multiplied by the experience level
1486 _max_hit_points += static_cast<uint32>(_growth_hit_points * _experience_level);
1487 _max_skill_points += static_cast<uint32>(_growth_skill_points * _experience_level);
1488 _experience_points += static_cast<uint32>(_growth_experience_points * _experience_level);
1489 _strength += static_cast<uint32>(_growth_strength * _experience_level);
1490 _vigor += static_cast<uint32>(_growth_vigor * _experience_level);
1491 _fortitude += static_cast<uint32>(_growth_fortitude * _experience_level);
1492 _protection += static_cast<uint32>(_growth_protection * _experience_level);
1493 _agility += static_cast<uint32>(_growth_agility * _experience_level);
1494 _evade += static_cast<float>(_growth_evade * _experience_level);
1495 _drunes_dropped += static_cast<uint32>(_growth_drunes * _experience_level);
1496
1497 // ----- (3): Randomize the new stats by using a guassian random variable
1498 // Use the inital stats as the means and a standard deviation of 10% of the mean
1499 _max_hit_points = GaussianRandomValue(_max_hit_points, _max_hit_points / 10.0f);
1500 _max_skill_points = GaussianRandomValue(_max_skill_points, _max_skill_points / 10.0f);
1501 _experience_points = GaussianRandomValue(_experience_points, _experience_points / 10.0f);
1502 _strength = GaussianRandomValue(_strength, _strength / 10.0f);
1503 _vigor = GaussianRandomValue(_strength, _strength / 10.0f);
1504 _fortitude = GaussianRandomValue(_fortitude, _fortitude / 10.0f);
1505 _protection = GaussianRandomValue(_protection, _protection / 10.0f);
1506 _agility = GaussianRandomValue(_agility, _agility / 10.0f);
1507 // TODO: need a gaussian random var function that takes a float arg
1508 //_evade = static_cast<float>(GaussianRandomValue(_evade, _evade / 10.0f));
1509 _drunes_dropped = GaussianRandomValue(_drunes_dropped, _drunes_dropped / 10.0f);
1510
1511 // ----- (4): Set the current hit points and skill points to their new maximum values
1512 _hit_points = _max_hit_points;
1513 _skill_points = _max_skill_points;
1514 } // void GlobalEnemy::Initialize(uint32 xp_level)
1515
1516
1517
DetermineDroppedObjects(vector<GlobalObject * > & objects)1518 void GlobalEnemy::DetermineDroppedObjects(vector<GlobalObject*>& objects) {
1519 objects.clear();
1520
1521 for (uint32 i = 0; i < _dropped_objects.size(); i++) {
1522 if (_experience_level >= _dropped_level_required[i]) {
1523 if (RandomFloat() < _dropped_chance[i]) {
1524 objects.push_back(GlobalCreateNewObject(_dropped_objects[i]));
1525 }
1526 }
1527 }
1528 }
1529
1530 ////////////////////////////////////////////////////////////////////////////////
1531 // GlobalParty class
1532 ////////////////////////////////////////////////////////////////////////////////
1533
AddActor(GlobalActor * actor,int32 index)1534 void GlobalParty::AddActor(GlobalActor* actor, int32 index) {
1535 if (actor == NULL) {
1536 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function received a NULL actor argument" << endl;
1537 return;
1538 }
1539
1540 if (_allow_duplicates == false) {
1541 // Check that this actor is not already in the party
1542 for (uint32 i = 0; i < _actors.size(); i++) {
1543 if (actor->GetID() == _actors[i]->GetID()) {
1544 IF_PRINT_WARNING(GLOBAL_DEBUG) << "attempted to add an actor that was already in the party "
1545 << "when duplicates were not allowed: " << actor->GetID() << endl;
1546 return;
1547 }
1548 }
1549 }
1550
1551 // Add actor to the end of the party if index is negative
1552 if (index < 0) {
1553 _actors.push_back(actor);
1554 return;
1555 }
1556
1557 // Check that the requested index does not exceed the size of the container
1558 if (static_cast<uint32>(index) >= _actors.size()) {
1559 IF_PRINT_WARNING(GLOBAL_DEBUG) << "index argument exceeded the current party size: " << index << endl;
1560 _actors.push_back(actor); // Add the actor to the end of the party instead
1561 return;
1562 }
1563 else {
1564 vector<GlobalActor*>::iterator position = _actors.begin();
1565 for (int32 i = 0; i < index; i++, position++);
1566 _actors.insert(position, actor);
1567 }
1568 }
1569
1570
1571
RemoveActorAtIndex(uint32 index)1572 GlobalActor* GlobalParty::RemoveActorAtIndex(uint32 index) {
1573 if (index >= _actors.size()) {
1574 IF_PRINT_WARNING(GLOBAL_DEBUG) << "index argument exceeded current party size: " << index << endl;
1575 return NULL;
1576 }
1577
1578 GlobalActor* removed_actor = _actors[index];
1579 vector<GlobalActor*>::iterator position = _actors.begin();
1580 for (uint32 i = 0; i < index; i++, position++);
1581 _actors.erase(position);
1582
1583 return removed_actor;
1584 }
1585
1586
1587
RemoveActorByID(uint32 id)1588 GlobalActor* GlobalParty::RemoveActorByID(uint32 id) {
1589 if (_allow_duplicates) {
1590 IF_PRINT_WARNING(GLOBAL_DEBUG) << "tried to remove actor when duplicates were allowed in the party: " << id << endl;
1591 return NULL;
1592 }
1593
1594 GlobalActor* removed_actor = NULL;
1595 for (vector<GlobalActor*>::iterator position = _actors.begin(); position != _actors.end(); position++) {
1596 if (id == (*position)->GetID()) {
1597 removed_actor = *position;
1598 _actors.erase(position);
1599 break;
1600 }
1601 }
1602
1603 if (removed_actor == NULL) {
1604 IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to find an actor in the party with the requested id: " << id << endl;
1605 }
1606
1607 return removed_actor;
1608 }
1609
1610
1611
GetActorAtIndex(uint32 index) const1612 GlobalActor* GlobalParty::GetActorAtIndex(uint32 index) const {
1613 if (index >= _actors.size()) {
1614 IF_PRINT_WARNING(GLOBAL_DEBUG) << "index argument exceeded current party size: " << index << endl;
1615 return NULL;
1616 }
1617
1618 return _actors[index];
1619 }
1620
1621
1622
GetActorByID(uint32 id) const1623 GlobalActor* GlobalParty::GetActorByID(uint32 id) const {
1624 if (_allow_duplicates) {
1625 IF_PRINT_WARNING(GLOBAL_DEBUG) << "tried to retrieve actor when duplicates were allowed in the party: " << id << endl;
1626 return NULL;
1627 }
1628
1629 for (uint32 i = 0; i < _actors.size(); i++) {
1630 if (_actors[i]->GetID() == id) {
1631 return _actors[i];
1632 }
1633 }
1634
1635 IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to find an actor in the party with the requested id: " << id << endl;
1636 return NULL;
1637 }
1638
1639
1640
SwapActorsByIndex(uint32 first_index,uint32 second_index)1641 void GlobalParty::SwapActorsByIndex(uint32 first_index, uint32 second_index) {
1642 if (first_index == second_index) {
1643 IF_PRINT_WARNING(GLOBAL_DEBUG) << "first_index and second_index arguments had the same value: " << first_index << endl;
1644 return;
1645 }
1646 if (first_index >= _actors.size()) {
1647 IF_PRINT_WARNING(GLOBAL_DEBUG) << "first_index argument exceeded current party size: " << first_index << endl;
1648 return;
1649 }
1650 if (second_index >= _actors.size()) {
1651 IF_PRINT_WARNING(GLOBAL_DEBUG) << "second_index argument exceeded current party size: " << second_index << endl;
1652 return;
1653 }
1654
1655 GlobalActor* tmp = _actors[first_index];
1656 _actors[first_index] = _actors[second_index];
1657 _actors[second_index] = tmp;
1658 }
1659
1660
1661
SwapActorsByID(uint32 first_id,uint32 second_id)1662 void GlobalParty::SwapActorsByID(uint32 first_id, uint32 second_id) {
1663 if (first_id == second_id) {
1664 IF_PRINT_WARNING(GLOBAL_DEBUG) << "first_id and second_id arguments had the same value: " << first_id << endl;
1665 return;
1666 }
1667 if (_allow_duplicates) {
1668 IF_PRINT_WARNING(GLOBAL_DEBUG) << "tried to swap actors when duplicates were allowed in the party: " << first_id << endl;
1669 return;
1670 }
1671
1672 vector<GlobalActor*>::iterator first_position;
1673 vector<GlobalActor*>::iterator second_position;
1674 for (first_position = _actors.begin(); first_position != _actors.end(); first_position++) {
1675 if ((*first_position)->GetID() == first_id)
1676 break;
1677 }
1678 for (second_position = _actors.begin(); second_position != _actors.end(); second_position++) {
1679 if ((*second_position)->GetID() == second_id)
1680 break;
1681 }
1682
1683 if (first_position == _actors.end()) {
1684 IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to find an actor in the party with the requested first_id: " << first_id << endl;
1685 return;
1686 }
1687 if (second_position == _actors.end()) {
1688 IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to find an actor in the party with the requested second_id: " << second_id << endl;
1689 return;
1690 }
1691
1692 GlobalActor* tmp = *first_position;
1693 *first_position = *second_position;
1694 *second_position = tmp;
1695 }
1696
1697
1698
ReplaceActorByIndex(uint32 index,GlobalActor * new_actor)1699 GlobalActor* GlobalParty::ReplaceActorByIndex(uint32 index, GlobalActor* new_actor) {
1700 if (new_actor == NULL) {
1701 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function received a NULL new_actor argument" << endl;
1702 return NULL;
1703 }
1704 if (index >= _actors.size()) {
1705 IF_PRINT_WARNING(GLOBAL_DEBUG) << "index argument exceeded current party size: " << index << endl;
1706 return NULL;
1707 }
1708
1709 GlobalActor* tmp = _actors[index];
1710 _actors[index] = new_actor;
1711 return tmp;
1712 }
1713
1714
1715
ReplaceActorByID(uint32 id,GlobalActor * new_actor)1716 GlobalActor* GlobalParty::ReplaceActorByID(uint32 id, GlobalActor* new_actor) {
1717 if (_allow_duplicates) {
1718 IF_PRINT_WARNING(GLOBAL_DEBUG) << "tried to replace actor when duplicates were allowed in the party: " << id << endl;
1719 return NULL;
1720 }
1721 if (new_actor == NULL) {
1722 IF_PRINT_WARNING(GLOBAL_DEBUG) << "function received a NULL new_actor argument" << endl;
1723 return NULL;
1724 }
1725
1726 GlobalActor* removed_actor = NULL;
1727 for (vector<GlobalActor*>::iterator position = _actors.begin(); position != _actors.end(); position++) {
1728 if ((*position)->GetID() == id) {
1729 removed_actor = *position;
1730 *position = new_actor;
1731 break;
1732 }
1733 }
1734
1735 if (removed_actor == NULL) {
1736 IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to find an actor in the party with the requested id: " << id << endl;
1737 }
1738
1739 return removed_actor;
1740 }
1741
1742
1743
AverageExperienceLevel() const1744 float GlobalParty::AverageExperienceLevel() const {
1745 if (_actors.empty())
1746 return 0.0f;
1747
1748 float xp_level_sum = 0.0f;
1749 for (uint32 i = 0; i < _actors.size(); i++)
1750 xp_level_sum += static_cast<float>(_actors[i]->GetExperienceLevel());
1751 return (xp_level_sum / static_cast<float>(_actors.size()));
1752 }
1753
1754
1755
AddHitPoints(uint32 hp)1756 void GlobalParty::AddHitPoints(uint32 hp) {
1757 for (vector<GlobalActor*>::iterator i = _actors.begin(); i != _actors.end(); i++) {
1758 (*i)->AddHitPoints(hp);
1759 }
1760 }
1761
1762 } // namespace hoa_global
1763