1 /***************************************************************************
2  *   Copyright (C) 2009 by Andrey Afletdinov <fheroes2@gmail.com>          *
3  *                                                                         *
4  *   Part of the Free Heroes2 Engine:                                      *
5  *   http://sourceforge.net/projects/fheroes2                              *
6  *                                                                         *
7  *   This program is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU General Public License as published by  *
9  *   the Free Software Foundation; either version 2 of the License, or     *
10  *   (at your option) any later version.                                   *
11  *                                                                         *
12  *   This program is distributed in the hope that it will be useful,       *
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15  *   GNU General Public License for more details.                          *
16  *                                                                         *
17  *   You should have received a copy of the GNU General Public License     *
18  *   along with this program; if not, write to the                         *
19  *   Free Software Foundation, Inc.,                                       *
20  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
21  ***************************************************************************/
22 
23 #include <algorithm>
24 #include <iterator>
25 
26 #include "game_static.h"
27 #include "heroes.h"
28 #include "race.h"
29 #include "rand.h"
30 #include "serialize.h"
31 #include "skill.h"
32 #include "skill_static.h"
33 #include "tools.h"
34 #include "translations.h"
35 #include "world.h"
36 
37 namespace Skill
38 {
39     int SecondaryGetWeightSkillFromRace( int race, int skill );
40     int SecondaryPriorityFromRace( int, const std::vector<int> &, uint32_t seed );
41 
42     const int secskills[]
43         = {Secondary::PATHFINDING, Secondary::ARCHERY,   Secondary::LOGISTICS, Secondary::SCOUTING,   Secondary::DIPLOMACY, Secondary::NAVIGATION, Secondary::LEADERSHIP,
44            Secondary::WISDOM,      Secondary::MYSTICISM, Secondary::LUCK,      Secondary::BALLISTICS, Secondary::EAGLEEYE,  Secondary::NECROMANCY, Secondary::ESTATES};
45 }
46 
GetValues(void) const47 u32 Skill::Secondary::GetValues( void ) const
48 {
49     const values_t * val = GameStatic::GetSkillValues( Skill() );
50 
51     if ( val )
52         switch ( Level() ) {
53         case Level::BASIC:
54             return val->values.basic;
55         case Level::ADVANCED:
56             return val->values.advanced;
57         case Level::EXPERT:
58             return val->values.expert;
59         default:
60             break;
61         }
62 
63     return 0;
64 }
65 
Primary()66 Skill::Primary::Primary()
67     : attack( 0 )
68     , defense( 0 )
69     , power( 0 )
70     , knowledge( 0 )
71 {}
72 
LoadDefaults(int type,int race)73 void Skill::Primary::LoadDefaults( int type, int race )
74 {
75     const stats_t * ptr = GameStatic::GetSkillStats( race );
76 
77     if ( ptr )
78         switch ( type ) {
79         case HeroBase::CAPTAIN:
80             attack = ptr->captain_primary.attack;
81             defense = ptr->captain_primary.defense;
82             power = ptr->captain_primary.power;
83             knowledge = ptr->captain_primary.knowledge;
84             break;
85 
86         case HeroBase::HEROES:
87             attack = ptr->initial_primary.attack;
88             defense = ptr->initial_primary.defense;
89             power = ptr->initial_primary.power;
90             knowledge = ptr->initial_primary.knowledge;
91             break;
92 
93         default:
94             break;
95         }
96 }
97 
GetInitialSpell(int race)98 int Skill::Primary::GetInitialSpell( int race )
99 {
100     const stats_t * ptr = GameStatic::GetSkillStats( race );
101     return ptr ? ptr->initial_spell : 0;
102 }
103 
LevelUp(int race,int level,uint32_t seed)104 int Skill::Primary::LevelUp( int race, int level, uint32_t seed )
105 {
106     Rand::Queue percents( MAXPRIMARYSKILL );
107 
108     const stats_t * ptr = GameStatic::GetSkillStats( race );
109     if ( ptr ) {
110         if ( ptr->over_level > level ) {
111             percents.Push( ATTACK, ptr->mature_primary_under.attack );
112             percents.Push( DEFENSE, ptr->mature_primary_under.defense );
113             percents.Push( POWER, ptr->mature_primary_under.power );
114             percents.Push( KNOWLEDGE, ptr->mature_primary_under.knowledge );
115         }
116         else {
117             percents.Push( ATTACK, ptr->mature_primary_over.attack );
118             percents.Push( DEFENSE, ptr->mature_primary_over.defense );
119             percents.Push( POWER, ptr->mature_primary_over.power );
120             percents.Push( KNOWLEDGE, ptr->mature_primary_over.knowledge );
121         }
122     }
123 
124     int result = percents.Size() ? percents.GetWithSeed( seed ) : UNKNOWN;
125 
126     switch ( result ) {
127     case ATTACK:
128         ++attack;
129         break;
130     case DEFENSE:
131         ++defense;
132         break;
133     case POWER:
134         ++power;
135         break;
136     case KNOWLEDGE:
137         ++knowledge;
138         break;
139     default:
140         break;
141     }
142 
143     return result;
144 }
145 
String(int skill)146 const char * Skill::Primary::String( int skill )
147 {
148     switch ( skill ) {
149     case ATTACK:
150         return _( "Attack Skill" );
151     case DEFENSE:
152         return _( "Defense Skill" );
153     case POWER:
154         return _( "Spell Power" );
155     case KNOWLEDGE:
156         return _( "Knowledge" );
157     default:
158         break;
159     }
160 
161     return "Unknown";
162 }
163 
StringDescription(int skill,const Heroes * hero)164 std::string Skill::Primary::StringDescription( int skill, const Heroes * hero )
165 {
166     std::string res;
167     std::string ext;
168 
169     switch ( skill ) {
170     case ATTACK:
171         res = _( "Your attack skill is a bonus added to each creature's attack skill." );
172         if ( hero )
173             hero->GetAttack( &ext );
174         break;
175 
176     case DEFENSE:
177         res = _( "Your defense skill is a bonus added to each creature's defense skill." );
178         if ( hero )
179             hero->GetDefense( &ext );
180         break;
181 
182     case POWER:
183         res = _( "Your spell power determines the length or power of a spell." );
184         if ( hero )
185             hero->GetPower( &ext );
186         break;
187 
188     case KNOWLEDGE:
189         res = _(
190             "Your knowledge determines how many spell points your hero may have. Under normal circumstances, a hero is limited to 10 spell points per level of knowledge." );
191         if ( hero )
192             hero->GetKnowledge( &ext );
193         break;
194 
195     default:
196         break;
197     }
198 
199     if ( !ext.empty() ) {
200         res.append( "\n \n" );
201         res.append( _( "Current Modifiers:" ) );
202         res.append( "\n \n" );
203         res.append( ext );
204     }
205 
206     return res;
207 }
208 
String(int level)209 const char * Skill::Level::String( int level )
210 {
211     switch ( level ) {
212     case BASIC:
213         return _( "skill|Basic" );
214     case ADVANCED:
215         return _( "skill|Advanced" );
216     case EXPERT:
217         return _( "skill|Expert" );
218     default:
219         break;
220     }
221 
222     return "None";
223 }
224 
StringWithBonus(const Heroes & hero,int skill,int level)225 std::string Skill::Level::StringWithBonus( const Heroes & hero, int skill, int level )
226 {
227     const std::string levelStr = String( level );
228     if ( skill == Skill::Secondary::NECROMANCY && Skill::GetNecromancyBonus( hero ) > 0 ) {
229         return levelStr + "+" + std::to_string( Skill::GetNecromancyBonus( hero ) );
230     }
231     return levelStr;
232 }
233 
Secondary()234 Skill::Secondary::Secondary()
235     : std::pair<int, int>( UNKNOWN, Level::NONE )
236 {}
237 
Secondary(int skill,int level)238 Skill::Secondary::Secondary( int skill, int level )
239     : std::pair<int, int>( UNKNOWN, Level::NONE )
240 {
241     SetSkill( skill );
242     SetLevel( level );
243 }
244 
Reset(void)245 void Skill::Secondary::Reset( void )
246 {
247     first = UNKNOWN;
248     second = Level::NONE;
249 }
250 
Set(const Secondary & skill)251 void Skill::Secondary::Set( const Secondary & skill )
252 {
253     first = skill.first;
254     second = skill.second;
255 }
256 
SetSkill(int skill)257 void Skill::Secondary::SetSkill( int skill )
258 {
259     first = skill <= ESTATES ? skill : UNKNOWN;
260 }
261 
SetLevel(int level)262 void Skill::Secondary::SetLevel( int level )
263 {
264     second = level <= Level::EXPERT ? level : Level::NONE;
265 }
266 
NextLevel(void)267 void Skill::Secondary::NextLevel( void )
268 {
269     switch ( second ) {
270     case Level::NONE:
271         second = Level::BASIC;
272         break;
273     case Level::BASIC:
274         second = Level::ADVANCED;
275         break;
276     case Level::ADVANCED:
277         second = Level::EXPERT;
278         break;
279     default:
280         break;
281     }
282 }
283 
Skill(void) const284 int Skill::Secondary::Skill( void ) const
285 {
286     return first;
287 }
288 
Level(void) const289 int Skill::Secondary::Level( void ) const
290 {
291     return second;
292 }
293 
isSkill(int skill) const294 bool Skill::Secondary::isSkill( int skill ) const
295 {
296     return skill == first;
297 }
298 
isValid(void) const299 bool Skill::Secondary::isValid( void ) const
300 {
301     return Skill() != UNKNOWN && Level() != Level::NONE;
302 }
303 
RandForWitchsHut(void)304 int Skill::Secondary::RandForWitchsHut( void )
305 {
306     const Skill::secondary_t * sec = GameStatic::GetSkillForWitchsHut();
307     std::vector<int> v;
308 
309     if ( sec ) {
310         v.reserve( 14 );
311 
312         if ( sec->archery )
313             v.push_back( ARCHERY );
314         if ( sec->ballistics )
315             v.push_back( BALLISTICS );
316         if ( sec->diplomacy )
317             v.push_back( DIPLOMACY );
318         if ( sec->eagleeye )
319             v.push_back( EAGLEEYE );
320         if ( sec->estates )
321             v.push_back( ESTATES );
322         if ( sec->leadership )
323             v.push_back( LEADERSHIP );
324         if ( sec->logistics )
325             v.push_back( LOGISTICS );
326         if ( sec->luck )
327             v.push_back( LUCK );
328         if ( sec->mysticism )
329             v.push_back( MYSTICISM );
330         if ( sec->navigation )
331             v.push_back( NAVIGATION );
332         if ( sec->necromancy )
333             v.push_back( NECROMANCY );
334         if ( sec->pathfinding )
335             v.push_back( PATHFINDING );
336         if ( sec->scouting )
337             v.push_back( SCOUTING );
338         if ( sec->wisdom )
339             v.push_back( WISDOM );
340     }
341 
342     return v.empty() ? UNKNOWN : Rand::Get( v );
343 }
344 
345 /* index sprite from SECSKILL */
GetIndexSprite1(void) const346 int Skill::Secondary::GetIndexSprite1( void ) const
347 {
348     return Skill() <= ESTATES ? Skill() : 0;
349 }
350 
351 /* index sprite from MINISS */
GetIndexSprite2(void) const352 int Skill::Secondary::GetIndexSprite2( void ) const
353 {
354     return Skill() <= ESTATES ? Skill() - 1 : 0xFF;
355 }
356 
String(int skill)357 const char * Skill::Secondary::String( int skill )
358 {
359     const char * str_skill[]
360         = {_( "Pathfinding" ), _( "Archery" ), _( "Logistics" ),  _( "Scouting" ),  _( "Diplomacy" ),  _( "Navigation" ), _( "Leadership" ), _( "Wisdom" ),
361            _( "Mysticism" ),   _( "Luck" ),    _( "Ballistics" ), _( "Eagle Eye" ), _( "Necromancy" ), _( "Estates" ),    "Unknown"};
362 
363     switch ( skill ) {
364     case PATHFINDING:
365         return str_skill[0];
366     case ARCHERY:
367         return str_skill[1];
368     case LOGISTICS:
369         return str_skill[2];
370     case SCOUTING:
371         return str_skill[3];
372     case DIPLOMACY:
373         return str_skill[4];
374     case NAVIGATION:
375         return str_skill[5];
376     case LEADERSHIP:
377         return str_skill[6];
378     case WISDOM:
379         return str_skill[7];
380     case MYSTICISM:
381         return str_skill[8];
382     case LUCK:
383         return str_skill[9];
384     case BALLISTICS:
385         return str_skill[10];
386     case EAGLEEYE:
387         return str_skill[11];
388     case NECROMANCY:
389         return str_skill[12];
390     case ESTATES:
391         return str_skill[13];
392 
393     default:
394         break;
395     }
396 
397     return str_skill[14];
398 }
399 
GetName(void) const400 std::string Skill::Secondary::GetName( void ) const
401 {
402     const char * name_skill[]
403         = {_( "Basic Pathfinding" ),  _( "Advanced Pathfinding" ), _( "Expert Pathfinding" ),  _( "Basic Archery" ),      _( "Advanced Archery" ),
404            _( "Expert Archery" ),     _( "Basic Logistics" ),      _( "Advanced Logistics" ),  _( "Expert Logistics" ),   _( "Basic Scouting" ),
405            _( "Advanced Scouting" ),  _( "Expert Scouting" ),      _( "Basic Diplomacy" ),     _( "Advanced Diplomacy" ), _( "Expert Diplomacy" ),
406            _( "Basic Navigation" ),   _( "Advanced Navigation" ),  _( "Expert Navigation" ),   _( "Basic Leadership" ),   _( "Advanced Leadership" ),
407            _( "Expert Leadership" ),  _( "Basic Wisdom" ),         _( "Advanced Wisdom" ),     _( "Expert Wisdom" ),      _( "Basic Mysticism" ),
408            _( "Advanced Mysticism" ), _( "Expert Mysticism" ),     _( "Basic Luck" ),          _( "Advanced Luck" ),      _( "Expert Luck" ),
409            _( "Basic Ballistics" ),   _( "Advanced Ballistics" ),  _( "Expert Ballistics" ),   _( "Basic Eagle Eye" ),    _( "Advanced Eagle Eye" ),
410            _( "Expert Eagle Eye" ),   _( "Basic Necromancy" ),     _( "Advanced Necromancy" ), _( "Expert Necromancy" ),  _( "Basic Estates" ),
411            _( "Advanced Estates" ),   _( "Expert Estates" )};
412 
413     return isValid() ? name_skill[( Level() - 1 ) + ( Skill() - 1 ) * 3] : "unknown";
414 }
415 
GetNameWithBonus(const Heroes & hero) const416 std::string Skill::Secondary::GetNameWithBonus( const Heroes & hero ) const
417 {
418     if ( Skill() == NECROMANCY && Skill::GetNecromancyBonus( hero ) > 0 ) {
419         return GetName() + " (+" + std::to_string( Skill::GetNecromancyBonus( hero ) ) + ")";
420     }
421     return GetName();
422 }
423 
GetDescription(const Heroes & hero) const424 std::string Skill::Secondary::GetDescription( const Heroes & hero ) const
425 {
426     u32 count = GetValues();
427     std::string name = GetName();
428     std::string str = "unknown";
429 
430     switch ( Skill() ) {
431     case PATHFINDING:
432         switch ( Level() ) {
433         case Level::BASIC:
434         case Level::ADVANCED:
435             str = _( "%{skill} reduces the movement penalty for rough terrain by %{count} percent." );
436             break;
437         case Level::EXPERT:
438             str = _( "%{skill} eliminates the movement penalty for rough terrain." );
439             break;
440         default:
441             break;
442         }
443         break;
444     case ARCHERY: {
445         str = _( "%{skill} increases the damage done by range attacking creatures by %{count} percent." );
446         break;
447     }
448     case LOGISTICS: {
449         str = _( "%{skill} increases your hero's movement points by %{count} percent." );
450         break;
451     }
452     case SCOUTING: {
453         str = _n( "%{skill} increases your hero's viewable area by one square.", "%{skill} increases your hero's viewable area by %{count} squares.", count );
454         break;
455     }
456     case DIPLOMACY:
457         str = _( "%{skill} allows you to negotiate with monsters who are weaker than your group. " );
458         switch ( Level() ) {
459         case Level::BASIC:
460         case Level::ADVANCED:
461             str.append( _( "Approximately %{count} percent of the creatures may offer to join you." ) );
462             break;
463         case Level::EXPERT:
464             str.append( _( "All of the creatures may offer to join you." ) );
465             break;
466         default:
467             break;
468         }
469         break;
470     case NAVIGATION: {
471         str = _( "%{skill} increases your hero's movement points over water by %{count} percent." );
472         break;
473     }
474     case LEADERSHIP: {
475         str = _( "%{skill} increases your hero's troops morale by %{count}." );
476         break;
477     }
478     case WISDOM: {
479         switch ( Level() ) {
480         case Level::BASIC:
481             str = _( "%{skill} allows your hero to learn third level spells." );
482             break;
483         case Level::ADVANCED:
484             str = _( "%{skill} allows your hero to learn fourth level spells." );
485             break;
486         case Level::EXPERT:
487             str = _( "%{skill} allows your hero to learn fifth level spells." );
488             break;
489         default:
490             break;
491         }
492         break;
493     }
494     case MYSTICISM: {
495         str = _n( "%{skill} regenerates one of your hero's spell points per day.", "%{skill} regenerates %{count} of your hero's spell points per day.", count );
496         break;
497     }
498     case LUCK: {
499         str = _( "%{skill} increases your hero's luck by %{count}." );
500         break;
501     }
502     case BALLISTICS:
503         switch ( Level() ) {
504         case Level::BASIC:
505             str = _( "%{skill} gives your hero's catapult shots a greater chance to hit and do damage to castle walls." );
506             break;
507         case Level::ADVANCED:
508             str = _( "%{skill} gives your hero's catapult an extra shot, and each shot has a greater chance to hit and do damage to castle walls." );
509             break;
510         case Level::EXPERT:
511             str = _( "%{skill} gives your hero's catapult an extra shot, and each shot automatically destroys any wall, except a fortified wall in a Knight castle." );
512             break;
513         default:
514             break;
515         }
516         break;
517     case EAGLEEYE:
518         switch ( Level() ) {
519         case Level::BASIC:
520             str = _( "%{skill} gives your hero a %{count} percent chance to learn any given 1st or 2nd level enemy spell used against him in a combat." );
521             break;
522         case Level::ADVANCED:
523             str = _( "%{skill} gives your hero a %{count} percent chance to learn any given 3rd level spell (or below) used against him in combat." );
524             break;
525         case Level::EXPERT:
526             str = _( "%{skill} gives your hero a %{count} percent chance to learn any given 4th level spell (or below) used against him in combat." );
527             break;
528         default:
529             break;
530         }
531         break;
532     case NECROMANCY: {
533         count += Skill::GetNecromancyPercent( hero ) - hero.GetSecondaryValues( Skill::Secondary::NECROMANCY );
534         name = GetNameWithBonus( hero );
535         str = _( "%{skill} allows %{count} percent of the creatures killed in combat to be brought back from the dead as Skeletons." );
536         break;
537     }
538     case ESTATES:
539         str = _( "Your hero produces %{count} gold pieces per turn as tax revenue from estates." );
540         break;
541     default:
542         break;
543     }
544 
545     StringReplace( str, "%{skill}", name );
546     StringReplace( str, "%{count}", count );
547 
548     return str;
549 }
550 
SecSkills()551 Skill::SecSkills::SecSkills()
552 {
553     reserve( HEROESMAXSKILL );
554 }
555 
SecSkills(int race)556 Skill::SecSkills::SecSkills( int race )
557 {
558     reserve( HEROESMAXSKILL );
559 
560     if ( race & Race::ALL ) {
561         const stats_t * ptr = GameStatic::GetSkillStats( race );
562 
563         if ( ptr ) {
564             if ( ptr->initial_secondary.archery )
565                 AddSkill( Secondary( Secondary::ARCHERY, ptr->initial_secondary.archery ) );
566             if ( ptr->initial_secondary.diplomacy )
567                 AddSkill( Secondary( Secondary::DIPLOMACY, ptr->initial_secondary.diplomacy ) );
568             if ( ptr->initial_secondary.eagleeye )
569                 AddSkill( Secondary( Secondary::EAGLEEYE, ptr->initial_secondary.eagleeye ) );
570             if ( ptr->initial_secondary.estates )
571                 AddSkill( Secondary( Secondary::ESTATES, ptr->initial_secondary.estates ) );
572             if ( ptr->initial_secondary.logistics )
573                 AddSkill( Secondary( Secondary::LOGISTICS, ptr->initial_secondary.logistics ) );
574             if ( ptr->initial_secondary.luck )
575                 AddSkill( Secondary( Secondary::LUCK, ptr->initial_secondary.luck ) );
576             if ( ptr->initial_secondary.mysticism )
577                 AddSkill( Secondary( Secondary::MYSTICISM, ptr->initial_secondary.mysticism ) );
578             if ( ptr->initial_secondary.pathfinding )
579                 AddSkill( Secondary( Secondary::PATHFINDING, ptr->initial_secondary.pathfinding ) );
580             if ( ptr->initial_secondary.leadership )
581                 AddSkill( Secondary( Secondary::LEADERSHIP, ptr->initial_secondary.leadership ) );
582             if ( ptr->initial_secondary.ballistics )
583                 AddSkill( Secondary( Secondary::BALLISTICS, ptr->initial_secondary.ballistics ) );
584             if ( ptr->initial_secondary.navigation )
585                 AddSkill( Secondary( Secondary::NAVIGATION, ptr->initial_secondary.navigation ) );
586             if ( ptr->initial_secondary.scouting )
587                 AddSkill( Secondary( Secondary::SCOUTING, ptr->initial_secondary.scouting ) );
588             if ( ptr->initial_secondary.necromancy )
589                 AddSkill( Secondary( Secondary::NECROMANCY, ptr->initial_secondary.necromancy ) );
590             if ( ptr->initial_secondary.wisdom )
591                 AddSkill( Secondary( Secondary::WISDOM, ptr->initial_secondary.wisdom ) );
592         }
593     }
594 }
595 
GetLevel(int skill) const596 int Skill::SecSkills::GetLevel( int skill ) const
597 {
598     const_iterator it = std::find_if( begin(), end(), [skill]( const Secondary & v ) { return v.isSkill( skill ); } );
599 
600     return it == end() ? Level::NONE : ( *it ).Level();
601 }
602 
GetValues(int skill) const603 u32 Skill::SecSkills::GetValues( int skill ) const
604 {
605     const_iterator it = std::find_if( begin(), end(), [skill]( const Secondary & v ) { return v.isSkill( skill ); } );
606 
607     return it == end() ? 0 : ( *it ).GetValues();
608 }
609 
Count(void) const610 int Skill::SecSkills::Count( void ) const
611 {
612     return static_cast<int>( std::count_if( begin(), end(), []( const Secondary & v ) { return v.isValid(); } ) ); // it's safe to cast as number is small
613 }
614 
GetTotalLevel() const615 int Skill::SecSkills::GetTotalLevel() const
616 {
617     int result = 0;
618     for ( const Skill::Secondary & skill : *this ) {
619         if ( skill.isValid() ) {
620             result += skill.Level();
621         }
622     }
623     return result;
624 }
625 
AddSkill(const Skill::Secondary & skill)626 void Skill::SecSkills::AddSkill( const Skill::Secondary & skill )
627 {
628     if ( skill.isValid() ) {
629         const int skillValue = skill.Skill();
630         iterator it = std::find_if( begin(), end(), [skillValue]( const Secondary & v ) { return v.isSkill( skillValue ); } );
631         if ( it != end() )
632             ( *it ).SetLevel( skill.Level() );
633         else {
634             it = std::find_if( begin(), end(), []( const Secondary & v ) { return !v.isValid(); } );
635             if ( it != end() )
636                 ( *it ).Set( skill );
637             else if ( size() < HEROESMAXSKILL )
638                 push_back( skill );
639         }
640     }
641 }
642 
FindSkill(int skill)643 Skill::Secondary * Skill::SecSkills::FindSkill( int skill )
644 {
645     iterator it = std::find_if( begin(), end(), [skill]( const Secondary & v ) { return v.isSkill( skill ); } );
646     return it != end() ? &( *it ) : nullptr;
647 }
648 
ToVector(void)649 std::vector<Skill::Secondary> & Skill::SecSkills::ToVector( void )
650 {
651     std::vector<Secondary> & v = *this;
652     return v;
653 }
654 
String(void) const655 std::string Skill::SecSkills::String( void ) const
656 {
657     std::string output;
658 
659     for ( const_iterator it = begin(); it != end(); ++it ) {
660         output += it->GetName();
661         output += ", ";
662     }
663 
664     return output;
665 }
666 
FillMax(const Skill::Secondary & skill)667 void Skill::SecSkills::FillMax( const Skill::Secondary & skill )
668 {
669     if ( size() < HEROESMAXSKILL )
670         resize( HEROESMAXSKILL, skill );
671 }
672 
SecondaryGetWeightSkillFromRace(int race,int skill)673 int Skill::SecondaryGetWeightSkillFromRace( int race, int skill )
674 {
675     const stats_t * ptr = GameStatic::GetSkillStats( race );
676 
677     if ( ptr ) {
678         if ( skill == Secondary::PATHFINDING )
679             return ptr->mature_secondary.pathfinding;
680         else if ( skill == Secondary::ARCHERY )
681             return ptr->mature_secondary.archery;
682         else if ( skill == Secondary::LOGISTICS )
683             return ptr->mature_secondary.logistics;
684         else if ( skill == Secondary::SCOUTING )
685             return ptr->mature_secondary.scouting;
686         else if ( skill == Secondary::DIPLOMACY )
687             return ptr->mature_secondary.diplomacy;
688         else if ( skill == Secondary::NAVIGATION )
689             return ptr->mature_secondary.navigation;
690         else if ( skill == Secondary::LEADERSHIP )
691             return ptr->mature_secondary.leadership;
692         else if ( skill == Secondary::WISDOM )
693             return ptr->mature_secondary.wisdom;
694         else if ( skill == Secondary::MYSTICISM )
695             return ptr->mature_secondary.mysticism;
696         else if ( skill == Secondary::LUCK )
697             return ptr->mature_secondary.luck;
698         else if ( skill == Secondary::BALLISTICS )
699             return ptr->mature_secondary.ballistics;
700         else if ( skill == Secondary::EAGLEEYE )
701             return ptr->mature_secondary.eagleeye;
702         else if ( skill == Secondary::NECROMANCY )
703             return ptr->mature_secondary.necromancy;
704         else if ( skill == Secondary::ESTATES )
705             return ptr->mature_secondary.estates;
706     }
707 
708     return 0;
709 }
710 
SecondaryPriorityFromRace(int race,const std::vector<int> & exclude,uint32_t seed)711 int Skill::SecondaryPriorityFromRace( int race, const std::vector<int> & exclude, uint32_t seed )
712 {
713     Rand::Queue parts( MAXSECONDARYSKILL );
714 
715     for ( auto skill : secskills )
716         if ( exclude.end() == std::find( exclude.begin(), exclude.end(), skill ) )
717             parts.Push( skill, SecondaryGetWeightSkillFromRace( race, skill ) );
718 
719     return parts.Size() ? parts.GetWithSeed( seed ) : Secondary::UNKNOWN;
720 }
721 
722 /* select secondary skills for level up */
FindSkillsForLevelUp(int race,uint32_t seedSkill1,uint32_t seedSkill2,Secondary & sec1,Secondary & sec2) const723 void Skill::SecSkills::FindSkillsForLevelUp( int race, uint32_t seedSkill1, uint32_t seedSkill2, Secondary & sec1, Secondary & sec2 ) const
724 {
725     std::vector<int> exclude_skills;
726     exclude_skills.reserve( MAXSECONDARYSKILL + HEROESMAXSKILL );
727 
728     // exclude for expert
729     for ( const_iterator it = begin(); it != end(); ++it )
730         if ( ( *it ).Level() == Level::EXPERT )
731             exclude_skills.push_back( ( *it ).Skill() );
732 
733     // exclude is full, add other.
734     if ( HEROESMAXSKILL <= Count() ) {
735         std::copy_if( secskills, std::end( secskills ), std::back_inserter( exclude_skills ), [this]( int skill ) { return Level::NONE == GetLevel( skill ); } );
736     }
737 
738     sec1.SetSkill( SecondaryPriorityFromRace( race, exclude_skills, seedSkill1 ) );
739 
740     if ( Secondary::UNKNOWN != sec1.Skill() ) {
741         exclude_skills.push_back( sec1.Skill() );
742         sec2.SetSkill( SecondaryPriorityFromRace( race, exclude_skills, seedSkill2 ) );
743 
744         sec1.SetLevel( GetLevel( sec1.Skill() ) );
745         sec2.SetLevel( GetLevel( sec2.Skill() ) );
746 
747         sec1.NextLevel();
748         sec2.NextLevel();
749     }
750 }
751 
StringAppendModifiers(std::string & str,int value)752 void StringAppendModifiers( std::string & str, int value )
753 {
754     if ( value < 0 )
755         str.append( " " ); // '-' present
756     else if ( value > 0 )
757         str.append( " +" );
758 
759     str.append( std::to_string( value ) );
760 }
761 
GetLeadershipModifiers(int level,std::string * strs=nullptr)762 int Skill::GetLeadershipModifiers( int level, std::string * strs = nullptr )
763 {
764     Secondary skill( Secondary::LEADERSHIP, level );
765 
766     if ( skill.GetValues() && strs ) {
767         strs->append( skill.GetName() );
768         StringAppendModifiers( *strs, skill.GetValues() );
769         strs->append( "\n" );
770     }
771 
772     return skill.GetValues();
773 }
774 
GetLuckModifiers(int level,std::string * strs=nullptr)775 int Skill::GetLuckModifiers( int level, std::string * strs = nullptr )
776 {
777     Secondary skill( Secondary::LUCK, level );
778 
779     if ( skill.GetValues() && strs ) {
780         strs->append( skill.GetName() );
781         StringAppendModifiers( *strs, skill.GetValues() );
782         strs->append( "\n" );
783     }
784 
785     return skill.GetValues();
786 }
787 
GetNecromancyBonus(const HeroBase & hero)788 uint32_t Skill::GetNecromancyBonus( const HeroBase & hero )
789 {
790     const uint32_t shrineCount = world.GetKingdom( hero.GetColor() ).GetCountNecromancyShrineBuild();
791     const uint32_t artifactCount = hero.artifactCount( Artifact::SPADE_NECROMANCY );
792     // cap bonus at 7
793     return std::min( 7u, shrineCount + artifactCount );
794 }
795 
GetNecromancyPercent(const HeroBase & hero)796 uint32_t Skill::GetNecromancyPercent( const HeroBase & hero )
797 {
798     uint32_t percent = hero.GetSecondaryValues( Skill::Secondary::NECROMANCY );
799     percent += 10 * GetNecromancyBonus( hero );
800     // cap at 100% bonus
801     return std::min( percent, 100u );
802 }
803 
operator <<(StreamBase & msg,const Primary & skill)804 StreamBase & Skill::operator<<( StreamBase & msg, const Primary & skill )
805 {
806     return msg << skill.attack << skill.defense << skill.knowledge << skill.power;
807 }
808 
operator >>(StreamBase & msg,Primary & skill)809 StreamBase & Skill::operator>>( StreamBase & msg, Primary & skill )
810 {
811     return msg >> skill.attack >> skill.defense >> skill.knowledge >> skill.power;
812 }
813 
operator >>(StreamBase & sb,Secondary & st)814 StreamBase & Skill::operator>>( StreamBase & sb, Secondary & st )
815 {
816     return sb >> st.first >> st.second;
817 }
818 
operator <<(StreamBase & sb,const SecSkills & ss)819 StreamBase & Skill::operator<<( StreamBase & sb, const SecSkills & ss )
820 {
821     const std::vector<Secondary> & v = ss;
822     return sb << v;
823 }
824 
operator >>(StreamBase & sb,SecSkills & ss)825 StreamBase & Skill::operator>>( StreamBase & sb, SecSkills & ss )
826 {
827     std::vector<Secondary> & v = ss;
828     sb >> v;
829 
830     if ( v.size() > HEROESMAXSKILL )
831         v.resize( HEROESMAXSKILL );
832     return sb;
833 }
834