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