1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name player.cpp - The player source file. */
12 //
13 // (c) Copyright 1998-2019 by Lutz Sammer, Jimmy Salmon, Nehal Mistry
14 // and Andrettin
15 //
16 // This program is free software; you can redistribute it and/or modify
17 // it under the terms of the GNU General Public License as published by
18 // the Free Software Foundation; only version 2 of the License.
19 //
20 // This program is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 // GNU General Public License for more details.
24 //
25 // You should have received a copy of the GNU General Public License
26 // along with this program; if not, write to the Free Software
27 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 // 02111-1307, USA.
29 //
30
31 //@{
32
33 /*----------------------------------------------------------------------------
34 -- Includes
35 ----------------------------------------------------------------------------*/
36
37 #include <stdarg.h>
38
39 #include "stratagus.h"
40
41 #include "player.h"
42
43 #include "action/action_upgradeto.h"
44 #include "actions.h"
45 #include "age.h"
46 #include "ai.h"
47 //Wyrmgus start
48 #include "../ai/ai_local.h" //for using AiHelpers
49 #include "civilization.h"
50 #include "commands.h" //for faction setting
51 #include "currency.h"
52 #include "editor.h"
53 #include "font.h"
54 #include "game.h"
55 #include "iocompat.h"
56 //Wyrmgus end
57 #include "iolib.h"
58 //Wyrmgus start
59 #include "grand_strategy.h"
60 #include "luacallback.h"
61 //Wyrmgus end
62 #include "map/map.h"
63 #include "map/map_layer.h"
64 #include "map/site.h"
65 #include "network.h"
66 #include "netconnect.h"
67 //Wyrmgus start
68 #include "parameters.h"
69 #include "quest.h"
70 #include "religion/deity.h"
71 #include "religion/deity_domain.h"
72 #include "religion/religion.h"
73 #include "settings.h"
74 //Wyrmgus end
75 #include "sound.h"
76 #include "time/calendar.h"
77 #include "time/time_of_day.h"
78 #include "translate.h"
79 #include "ui/button_action.h"
80 #include "ui/ui.h"
81 #include "unit/unit.h"
82 //Wyrmgus start
83 #include "unit/unit_find.h"
84 #include "unit/unittype.h"
85 //Wyrmgus end
86 #include "unitsound.h"
87 #include "upgrade/dependency.h"
88 //Wyrmgus start
89 #include "upgrade/upgrade.h"
90 //Wyrmgus end
91 #include "upgrade/upgrade_modifier.h"
92 #include "video.h"
93 #include "world.h"
94
95 //Wyrmgus start
96 #include "../ai/ai_local.h"
97 //Wyrmgus end
98
99 /*----------------------------------------------------------------------------
100 -- Documentation
101 ----------------------------------------------------------------------------*/
102
103 /**
104 ** @class CPlayer player.h
105 **
106 ** \#include "player.h"
107 **
108 ** This structure contains all information about a player in game.
109 **
110 ** The player structure members:
111 **
112 ** CPlayer::Player
113 **
114 ** This is the unique slot number. It is not possible that two
115 ** players have the same slot number at the same time. The slot
116 ** numbers are reused in the future. This means if a player is
117 ** defeated, a new player can join using this slot. Currently
118 ** #PlayerMax (16) players are supported. This member is used to
119 ** access bit fields.
120 ** Slot #PlayerNumNeutral (15) is reserved for the neutral units
121 ** like gold-mines or critters.
122 **
123 ** @note Should call this member Slot?
124 **
125 ** CPlayer::Name
126 **
127 ** Name of the player used for displays and network game.
128 ** It is restricted to 15 characters plus final zero.
129 **
130 ** CPlayer::Type
131 **
132 ** Type of the player. This field is setup from the level (map).
133 ** We support currently #PlayerNeutral,
134 ** #PlayerNobody, #PlayerComputer, #PlayerPerson,
135 ** #PlayerRescuePassive and #PlayerRescueActive.
136 ** @see #PlayerTypes.
137 **
138 ** CPlayer::RaceName
139 **
140 ** Name of the race to which the player belongs, used to select
141 ** the user interface and the AI.
142 ** We have 'orc', 'human', 'alliance' or 'mythical'. Should
143 ** only be used during configuration and not during runtime.
144 **
145 ** CPlayer::Race
146 **
147 ** Race number of the player. This field is setup from the level
148 ** map. This number is mapped with #PlayerRaces to the symbolic
149 ** name CPlayer::RaceName.
150 **
151 ** CPlayer::AiName
152 **
153 ** AI name for computer. This field is setup
154 ** from the map. Used to select the AI for the computer
155 ** player.
156 **
157 ** CPlayer::Team
158 **
159 ** Team of player. Selected during network game setup. All players
160 ** of the same team are allied and enemy to all other teams.
161 ** @note It is planned to show the team on the map.
162 **
163 ** CPlayer::Enemy
164 **
165 ** A bit field which contains the enemies of this player.
166 ** If CPlayer::Enemy & (1<<CPlayer::Player) != 0 its an enemy.
167 ** Setup during startup using the CPlayer::Team, can later be
168 ** changed with diplomacy. CPlayer::Enemy and CPlayer::Allied
169 ** are combined, if none bit is set, the player is neutral.
170 ** @note You can be allied to a player, which sees you as enemy.
171 **
172 ** CPlayer::Allied
173 **
174 ** A bit field which contains the allies of this player.
175 ** If CPlayer::Allied & (1<<CPlayer::Player) != 0 its an allied.
176 ** Setup during startup using the Player:Team, can later be
177 ** changed with diplomacy. CPlayer::Enemy and CPlayer::Allied
178 ** are combined, if none bit is set, the player is neutral.
179 ** @note You can be allied to a player, which sees you as enemy.
180 **
181 ** CPlayer::SharedVision
182 **
183 ** A bit field which contains shared vision for this player.
184 ** Shared vision only works when it's activated both ways. Really.
185 **
186 ** CPlayer::StartX CPlayer::StartY
187 **
188 ** The tile map coordinates of the player start position. 0,0 is
189 ** the upper left on the map. This members are setup from the
190 ** map and only important for the game start.
191 ** Ignored if game starts with level settings. Used to place
192 ** the initial workers if you play with 1 or 3 workers.
193 **
194 ** CPlayer::Resources[::MaxCosts]
195 **
196 ** How many resources the player owns. Needed for building
197 ** units and structures.
198 ** @see _costs_, TimeCost, GoldCost, WoodCost, OilCost, MaxCosts.
199 **
200 ** CPlayer::MaxResources[::MaxCosts]
201 **
202 ** How many resources the player can store at the moment.
203 **
204 ** CPlayer::Incomes[::MaxCosts]
205 **
206 ** Income of the resources, when they are delivered at a store.
207 ** @see _costs_, TimeCost, GoldCost, WoodCost, OilCost, MaxCosts.
208 **
209 ** CPlayer::LastResources[::MaxCosts]
210 **
211 ** Keeps track of resources in time (used for calculating
212 ** CPlayer::Revenue, see below)
213 **
214 ** CPlayer::Revenue[::MaxCosts]
215 **
216 ** Production of resources per minute (or estimates)
217 ** Used just as information (statistics) for the player...
218 **
219 ** CPlayer::UnitTypesCount[::UnitTypeMax]
220 **
221 ** Total count for each different unit type. Used by the AI and
222 ** for dependencies checks. The addition of all counts should
223 ** be CPlayer::TotalNumUnits.
224 ** @note Should not use the maximum number of unit-types here,
225 ** only the real number of unit-types used.
226 **
227 ** CPlayer::AiEnabled
228 **
229 ** If the player is controlled by the computer and this flag is
230 ** true, than the player is handled by the AI on this local
231 ** computer.
232 **
233 ** @note Currently the AI is calculated parallel on all computers
234 ** in a network play. It is planned to change this.
235 **
236 ** CPlayer::Ai
237 **
238 ** AI structure pointer. Please look at #PlayerAi for more
239 ** information.
240 **
241 ** CPlayer::Units
242 **
243 ** A table of all (CPlayer::TotalNumUnits) units of the player.
244 **
245 ** CPlayer::TotalNumUnits
246 **
247 ** Total number of units (incl. buildings) in the CPlayer::Units
248 ** table.
249 **
250 ** CPlayer::Demand
251 **
252 ** Total unit demand, used to demand limit.
253 ** A player can only build up to CPlayer::Food units and not more
254 ** than CPlayer::FoodUnitLimit units.
255 **
256 ** @note that CPlayer::NumFoodUnits > CPlayer::Food, when enough
257 ** farms are destroyed.
258 **
259 ** CPlayer::NumBuildings
260 **
261 ** Total number buildings, units that don't need food.
262 **
263 ** CPlayer::Food
264 **
265 ** Number of food available/produced. Player can't train more
266 ** CPlayer::NumFoodUnits than this.
267 ** @note that all limits are always checked.
268 **
269 ** CPlayer::FoodUnitLimit
270 **
271 ** Number of food units allowed. Player can't train more
272 ** CPlayer::NumFoodUnits than this.
273 ** @note that all limits are always checked.
274 **
275 ** CPlayer::BuildingLimit
276 **
277 ** Number of buildings allowed. Player can't build more
278 ** CPlayer::NumBuildings than this.
279 ** @note that all limits are always checked.
280 **
281 ** CPlayer::TotalUnitLimit
282 **
283 ** Number of total units allowed. Player can't have more
284 ** CPlayer::NumFoodUnits+CPlayer::NumBuildings=CPlayer::TotalNumUnits
285 ** this.
286 ** @note that all limits are always checked.
287 **
288 ** CPlayer::Score
289 **
290 ** Total number of points. You can get points for killing units,
291 ** destroying buildings ...
292 **
293 ** CPlayer::TotalUnits
294 **
295 ** Total number of units made.
296 **
297 ** CPlayer::TotalBuildings
298 **
299 ** Total number of buildings made.
300 **
301 ** CPlayer::TotalResources[::MaxCosts]
302 **
303 ** Total number of resources collected.
304 ** @see _costs_, TimeCost, GoldCost, WoodCost, OilCost, MaxCosts.
305 **
306 ** CPlayer::TotalRazings
307 **
308 ** Total number of buildings destroyed.
309 **
310 ** CPlayer::TotalKills
311 **
312 ** Total number of kills.
313 **
314 ** CPlayer::Color
315 **
316 ** Color of units of this player on the minimap. Index number
317 ** into the global palette.
318 **
319 ** CPlayer::UnitColors
320 **
321 ** Unit colors of this player. Contains the hardware dependent
322 ** pixel values for the player colors (palette index #208-#211).
323 ** Setup from the global palette.
324 ** @note Index #208-#211 are various SHADES of the team color
325 ** (#208 is brightest shade, #211 is darkest shade) .... these
326 ** numbers are NOT red=#208, blue=#209, etc
327 **
328 ** CPlayer::Allow
329 **
330 ** Contains which unit-types and upgrades are allowed for the
331 ** player. Possible values are:
332 ** @li 'A' -- allowed,
333 ** @li 'F' -- forbidden,
334 ** @li 'R' -- acquired, perhaps other values
335 ** @li 'Q' -- acquired but forbidden (does it make sense?:))
336 ** @li 'E' -- enabled, allowed by level but currently forbidden
337 ** @see CAllow
338 **
339 ** CPlayer::UpgradeTimers
340 **
341 ** Timer for the upgrades. One timer for all possible upgrades.
342 ** Initial 0 counted up by the upgrade action, until it reaches
343 ** the upgrade time.
344 ** @see _upgrade_timers_
345 ** @note it is planned to combine research for faster upgrades.
346 */
347
348 /*----------------------------------------------------------------------------
349 -- Variables
350 ----------------------------------------------------------------------------*/
351
352 int NumPlayers; /// How many player slots used
353 CPlayer Players[PlayerMax]; /// All players in play
354 CPlayer *ThisPlayer; /// Player on this computer
355 PlayerRace PlayerRaces; /// Player races
356
357 bool NoRescueCheck; /// Disable rescue check
358
359 /**
360 ** Colors used for minimap.
361 */
362 //Wyrmgus start
363 //std::vector<CColor> PlayerColorsRGB[PlayerMax];
364 //std::vector<IntColor> PlayerColors[PlayerMax];
365
366 //std::string PlayerColorNames[PlayerMax];
367 std::vector<CColor> PlayerColorsRGB[PlayerColorMax];
368 std::vector<IntColor> PlayerColors[PlayerColorMax];
369 std::string PlayerColorNames[PlayerColorMax];
370 std::vector<int> ConversiblePlayerColors;
371 //Wyrmgus end
372
373 /**
374 ** Which indexes to replace with player color
375 */
376 int PlayerColorIndexStart;
377 int PlayerColorIndexCount;
378
379 //Wyrmgus start
380 std::map<std::string, int> FactionStringToIndex;
381 std::map<std::string, int> DynastyStringToIndex;
382 std::map<std::string, CLanguage *> LanguageIdentToPointer;
383
384 bool LanguageCacheOutdated = false;
385 //Wyrmgus end
386
387 /*----------------------------------------------------------------------------
388 -- Functions
389 ----------------------------------------------------------------------------*/
390
391 /**
392 ** Clean up the PlayerRaces names.
393 */
Clean()394 void PlayerRace::Clean()
395 {
396 //Wyrmgus start
397 if (!CCivilization::Civilizations.empty()) { //don't clean the languages if first defining the civilizations
398 for (size_t i = 0; i < this->Languages.size(); ++i) {
399 for (size_t j = 0; j < this->Languages[i]->LanguageWords.size(); ++j) {
400 for (size_t k = 0; k < this->Languages[i]->Dialects.size(); ++k) { //remove word from dialects, so that they don't try to delete it too
401 this->Languages[i]->Dialects[k]->RemoveWord(this->Languages[i]->LanguageWords[j]);
402 }
403
404 delete this->Languages[i]->LanguageWords[j];
405 }
406 this->Languages[i]->LanguageWords.clear();
407
408 this->Languages[i]->NameTranslations.clear();
409 }
410 }
411 //Wyrmgus end
412 for (size_t i = 0; i != CCivilization::Civilizations.size(); ++i) {
413 this->Name[i].clear();
414 this->Display[i].clear();
415 this->Visible[i] = false;
416 //Wyrmgus start
417 this->CivilizationUpgrades[i].clear();
418 this->CivilizationClassUnitTypes[i].clear();
419 this->CivilizationClassUpgrades[i].clear();
420 this->Playable[i] = false;
421 this->Species[i].clear();
422 this->DefaultColor[i].clear();
423 this->DevelopsFrom[i].clear();
424 this->DevelopsTo[i].clear();
425 this->CivilizationUIFillers[i].clear();
426 //Wyrmgus end
427 }
428 //Wyrmgus start
429 for (size_t i = 0; i < PlayerRaces.Factions.size(); ++i) {
430 delete this->Factions[i];
431 }
432 this->Factions.clear();
433 for (size_t i = 0; i < PlayerRaces.Dynasties.size(); ++i) {
434 delete this->Dynasties[i];
435 }
436 this->Dynasties.clear();
437 //Wyrmgus end
438 }
439
440 //Wyrmgus start
GetFactionIndexByName(const std::string & faction_ident) const441 int PlayerRace::GetFactionIndexByName(const std::string &faction_ident) const
442 {
443 if (faction_ident.empty()) {
444 return -1;
445 }
446
447 if (FactionStringToIndex.find(faction_ident) != FactionStringToIndex.end()) {
448 return FactionStringToIndex[faction_ident];
449 } else {
450 return -1;
451 }
452 }
453
GetFaction(const std::string & faction_ident) const454 CFaction *PlayerRace::GetFaction(const std::string &faction_ident) const
455 {
456 if (faction_ident.empty()) {
457 return nullptr;
458 }
459
460 if (FactionStringToIndex.find(faction_ident) != FactionStringToIndex.end()) {
461 return PlayerRaces.Factions[FactionStringToIndex[faction_ident]];
462 } else {
463 return nullptr;
464 }
465 }
466
GetDynasty(const std::string & dynasty_ident) const467 CDynasty *PlayerRace::GetDynasty(const std::string &dynasty_ident) const
468 {
469 if (dynasty_ident.empty()) {
470 return nullptr;
471 }
472
473 if (DynastyStringToIndex.find(dynasty_ident) != DynastyStringToIndex.end()) {
474 return PlayerRaces.Dynasties[DynastyStringToIndex[dynasty_ident]];
475 } else {
476 return nullptr;
477 }
478 }
479
GetLanguage(const std::string & language_ident) const480 CLanguage *PlayerRace::GetLanguage(const std::string &language_ident) const
481 {
482 if (LanguageIdentToPointer.find(language_ident) != LanguageIdentToPointer.end()) {
483 return LanguageIdentToPointer[language_ident];
484 }
485 return nullptr;
486 }
487
GetCivilizationClassUnitType(int civilization,int class_id)488 int PlayerRace::GetCivilizationClassUnitType(int civilization, int class_id)
489 {
490 if (civilization == -1 || class_id == -1) {
491 return -1;
492 }
493
494 if (CivilizationClassUnitTypes[civilization].find(class_id) != CivilizationClassUnitTypes[civilization].end()) {
495 return CivilizationClassUnitTypes[civilization][class_id];
496 }
497
498 if (CCivilization::Civilizations[civilization]->ParentCivilization) {
499 return GetCivilizationClassUnitType(CCivilization::Civilizations[civilization]->ParentCivilization->ID, class_id);
500 }
501
502 return -1;
503 }
504
GetCivilizationClassUpgrade(int civilization,int class_id)505 int PlayerRace::GetCivilizationClassUpgrade(int civilization, int class_id)
506 {
507 if (civilization == -1 || class_id == -1) {
508 return -1;
509 }
510
511 if (CivilizationClassUpgrades[civilization].find(class_id) != CivilizationClassUpgrades[civilization].end()) {
512 return CivilizationClassUpgrades[civilization][class_id];
513 }
514
515 if (CCivilization::Civilizations[civilization]->ParentCivilization) {
516 return GetCivilizationClassUpgrade(CCivilization::Civilizations[civilization]->ParentCivilization->ID, class_id);
517 }
518
519 return -1;
520 }
521
GetFactionClassUnitType(int faction,int class_id)522 int PlayerRace::GetFactionClassUnitType(int faction, int class_id)
523 {
524 if (faction == -1 || class_id == -1) {
525 return -1;
526 }
527
528 if (PlayerRaces.Factions[faction]->ClassUnitTypes.find(class_id) != PlayerRaces.Factions[faction]->ClassUnitTypes.end()) {
529 return PlayerRaces.Factions[faction]->ClassUnitTypes[class_id];
530 }
531
532 if (PlayerRaces.Factions[faction]->ParentFaction != -1) {
533 return GetFactionClassUnitType(PlayerRaces.Factions[faction]->ParentFaction, class_id);
534 }
535
536 return GetCivilizationClassUnitType(PlayerRaces.Factions[faction]->Civilization->ID, class_id);
537 }
538
GetFactionClassUpgrade(int faction,int class_id)539 int PlayerRace::GetFactionClassUpgrade(int faction, int class_id)
540 {
541 if (faction == -1 || class_id == -1) {
542 return -1;
543 }
544
545 if (PlayerRaces.Factions[faction]->ClassUpgrades.find(class_id) != PlayerRaces.Factions[faction]->ClassUpgrades.end()) {
546 return PlayerRaces.Factions[faction]->ClassUpgrades[class_id];
547 }
548
549 if (PlayerRaces.Factions[faction]->ParentFaction != -1) {
550 return GetFactionClassUpgrade(PlayerRaces.Factions[faction]->ParentFaction, class_id);
551 }
552
553 return GetCivilizationClassUpgrade(PlayerRaces.Factions[faction]->Civilization->ID, class_id);
554 }
555
GetCivilizationLanguage(int civilization)556 CLanguage *PlayerRace::GetCivilizationLanguage(int civilization)
557 {
558 if (civilization == -1) {
559 return nullptr;
560 }
561
562 if (CCivilization::Civilizations[civilization] && CCivilization::Civilizations[civilization]->Language) {
563 return CCivilization::Civilizations[civilization]->Language;
564 }
565
566 if (CCivilization::Civilizations[civilization]->ParentCivilization) {
567 return GetCivilizationLanguage(CCivilization::Civilizations[civilization]->ParentCivilization->ID);
568 }
569
570 return nullptr;
571 }
572
GetCivilizationUIFillers(int civilization)573 std::vector<CFiller> PlayerRace::GetCivilizationUIFillers(int civilization)
574 {
575 if (civilization == -1) {
576 return std::vector<CFiller>();
577 }
578
579 if (CivilizationUIFillers[civilization].size() > 0) {
580 return CivilizationUIFillers[civilization];
581 }
582
583 if (CCivilization::Civilizations[civilization]->ParentCivilization) {
584 return GetCivilizationUIFillers(CCivilization::Civilizations[civilization]->ParentCivilization->ID);
585 }
586
587 return std::vector<CFiller>();
588 }
589
GetFactionUIFillers(int faction)590 std::vector<CFiller> PlayerRace::GetFactionUIFillers(int faction)
591 {
592 if (faction == -1) {
593 return std::vector<CFiller>();
594 }
595
596 if (Factions[faction]->UIFillers.size() > 0) {
597 return Factions[faction]->UIFillers;
598 }
599
600 if (PlayerRaces.Factions[faction]->ParentFaction != -1) {
601 return GetFactionUIFillers(PlayerRaces.Factions[faction]->ParentFaction);
602 }
603
604 return GetCivilizationUIFillers(PlayerRaces.Factions[faction]->Civilization->ID);
605 }
606
607 /**
608 ** "Translate" (that is, adapt) a proper name from one culture (civilization) to another.
609 */
TranslateName(const std::string & name,CLanguage * language)610 std::string PlayerRace::TranslateName(const std::string &name, CLanguage *language)
611 {
612 std::string new_name;
613
614 if (!language || name.empty()) {
615 return new_name;
616 }
617
618 // try to translate the entire name, as a particular translation for it may exist
619 if (language->NameTranslations.find(name) != language->NameTranslations.end()) {
620 return language->NameTranslations[name][SyncRand(language->NameTranslations[name].size())];
621 }
622
623 //if adapting the entire name failed, try to match prefixes and suffixes
624 if (name.size() > 1) {
625 if (name.find(" ") == std::string::npos) {
626 for (size_t i = 0; i < name.size(); ++i) {
627 std::string name_prefix = name.substr(0, i + 1);
628 std::string name_suffix = CapitalizeString(name.substr(i + 1, name.size() - (i + 1)));
629
630 // fprintf(stdout, "Trying to match prefix \"%s\" and suffix \"%s\" for translating name \"%s\" to the \"%s\" language.\n", name_prefix.c_str(), name_suffix.c_str(), name.c_str(), language->Ident.c_str());
631
632 if (language->NameTranslations.find(name_prefix) != language->NameTranslations.end() && language->NameTranslations.find(name_suffix) != language->NameTranslations.end()) { // if both a prefix and suffix have been matched
633 name_prefix = language->NameTranslations[name_prefix][SyncRand(language->NameTranslations[name_prefix].size())];
634 name_suffix = language->NameTranslations[name_suffix][SyncRand(language->NameTranslations[name_suffix].size())];
635 name_suffix = DecapitalizeString(name_suffix);
636 if (name_prefix.substr(name_prefix.size() - 2, 2) == "gs" && name_suffix.substr(0, 1) == "g") { //if the last two characters of the prefix are "gs", and the first character of the suffix is "g", then remove the final "s" from the prefix (as in "K�niggr�tz")
637 name_prefix = FindAndReplaceStringEnding(name_prefix, "gs", "g");
638 }
639 if (name_prefix.substr(name_prefix.size() - 1, 1) == "s" && name_suffix.substr(0, 1) == "s") { //if the prefix ends in "s" and the suffix begins in "s" as well, then remove the final "s" from the prefix (as in "Josefstadt", "Kronstadt" and "Leopoldstadt")
640 name_prefix = FindAndReplaceStringEnding(name_prefix, "s", "");
641 }
642
643 return name_prefix + name_suffix;
644 }
645 }
646 } else { // if the name contains a space, try to translate each of its elements separately
647 size_t previous_pos = 0;
648 new_name = name;
649 for (size_t i = 0; i < name.size(); ++i) {
650 if ((i + 1) == name.size() || name[i + 1] == ' ') {
651 std::string name_element = TranslateName(name.substr(previous_pos, i + 1 - previous_pos), language);
652
653 if (name_element.empty()) {
654 new_name = "";
655 break;
656 }
657
658 new_name = FindAndReplaceString(new_name, name.substr(previous_pos, i + 1 - previous_pos), name_element);
659
660 previous_pos = i + 2;
661 }
662 }
663 }
664 }
665
666 return new_name;
667 }
668
~CFaction()669 CFaction::~CFaction()
670 {
671 for (std::map<int, std::vector<CForceTemplate *>>::iterator iterator = this->ForceTemplates.begin(); iterator != this->ForceTemplates.end(); ++iterator) {
672 for (size_t i = 0; i < iterator->second.size(); ++i) {
673 delete iterator->second[i];
674 }
675 }
676
677 for (size_t i = 0; i < this->AiBuildingTemplates.size(); ++i) {
678 delete this->AiBuildingTemplates[i];
679 }
680
681 if (this->Conditions) {
682 delete Conditions;
683 }
684
685 this->UIFillers.clear();
686 }
687
GetUpgradePriority(const CUpgrade * upgrade) const688 int CFaction::GetUpgradePriority(const CUpgrade *upgrade) const
689 {
690 if (!upgrade) {
691 fprintf(stderr, "Error in CFaction::GetUpgradePriority: the upgrade is null.\n");
692 }
693
694 if (this->UpgradePriorities.find(upgrade) != this->UpgradePriorities.end()) {
695 return this->UpgradePriorities.find(upgrade)->second;
696 }
697
698 if (this->Civilization == nullptr) {
699 fprintf(stderr, "Error in CFaction::GetUpgradePriority: the faction has no civilization.\n");
700 }
701
702 return this->Civilization->GetUpgradePriority(upgrade);
703 }
704
GetForceTypeWeight(int force_type) const705 int CFaction::GetForceTypeWeight(int force_type) const
706 {
707 if (force_type == -1) {
708 fprintf(stderr, "Error in CFaction::GetForceTypeWeight: the force_type is -1.\n");
709 }
710
711 if (this->ForceTypeWeights.find(force_type) != this->ForceTypeWeights.end()) {
712 return this->ForceTypeWeights.find(force_type)->second;
713 }
714
715 if (this->ParentFaction != -1) {
716 return PlayerRaces.Factions[this->ParentFaction]->GetForceTypeWeight(force_type);
717 }
718
719 if (this->Civilization == nullptr) {
720 fprintf(stderr, "Error in CFaction::GetForceTypeWeight: the faction has no civilization.\n");
721 }
722
723 return this->Civilization->GetForceTypeWeight(force_type);
724 }
725
726 /**
727 ** @brief Get the faction's currency
728 **
729 ** @return The faction's currency
730 */
GetCurrency() const731 CCurrency *CFaction::GetCurrency() const
732 {
733 if (this->Currency) {
734 return this->Currency;
735 }
736
737 if (this->ParentFaction != -1) {
738 return PlayerRaces.Factions[this->ParentFaction]->GetCurrency();
739 }
740
741 if (this->Civilization != nullptr) {
742 return this->Civilization->GetCurrency();
743 }
744
745 return nullptr;
746 }
747
GetForceTemplates(int force_type) const748 std::vector<CForceTemplate *> CFaction::GetForceTemplates(int force_type) const
749 {
750 if (force_type == -1) {
751 fprintf(stderr, "Error in CFaction::GetForceTemplates: the force_type is -1.\n");
752 }
753
754 if (this->ForceTemplates.find(force_type) != this->ForceTemplates.end()) {
755 return this->ForceTemplates.find(force_type)->second;
756 }
757
758 if (this->ParentFaction != -1) {
759 return PlayerRaces.Factions[this->ParentFaction]->GetForceTemplates(force_type);
760 }
761
762 if (this->Civilization == nullptr) {
763 fprintf(stderr, "Error in CFaction::GetForceTemplates: the faction has no civilization.\n");
764 }
765
766 return this->Civilization->GetForceTemplates(force_type);
767 }
768
GetAiBuildingTemplates() const769 std::vector<CAiBuildingTemplate *> CFaction::GetAiBuildingTemplates() const
770 {
771 if (this->AiBuildingTemplates.size() > 0) {
772 return this->AiBuildingTemplates;
773 }
774
775 if (this->ParentFaction != -1) {
776 return PlayerRaces.Factions[this->ParentFaction]->GetAiBuildingTemplates();
777 }
778
779 if (this->Civilization == nullptr) {
780 fprintf(stderr, "Error in CFaction::GetAiBuildingTemplates: the faction has no civilization.\n");
781 }
782
783 return this->Civilization->GetAiBuildingTemplates();
784 }
785
GetShipNames()786 std::vector<std::string> &CFaction::GetShipNames()
787 {
788 if (this->ShipNames.size() > 0) {
789 return this->ShipNames;
790 }
791
792 if (this->ParentFaction != -1) {
793 return PlayerRaces.Factions[this->ParentFaction]->GetShipNames();
794 }
795
796 return this->Civilization->GetShipNames();
797 }
798
~CDynasty()799 CDynasty::~CDynasty()
800 {
801 if (this->Conditions) {
802 delete Conditions;
803 }
804 }
805 //Wyrmgus end
806
807 /**
808 ** Init players.
809 */
InitPlayers()810 void InitPlayers()
811 {
812 for (int p = 0; p < PlayerMax; ++p) {
813 Players[p].Index = p;
814 if (!Players[p].Type) {
815 Players[p].Type = PlayerNobody;
816 }
817 //Wyrmgus start
818 // for (int x = 0; x < PlayerColorIndexCount; ++x) {
819 // PlayerColors[p][x] = Video.MapRGB(TheScreen->format, PlayerColorsRGB[p][x]);
820 // }
821 //Wyrmgus end
822 }
823 //Wyrmgus start
824 for (int p = 0; p < PlayerColorMax; ++p) {
825 for (int x = 0; x < PlayerColorIndexCount; ++x) {
826 PlayerColors[p][x] = Video.MapRGB(TheScreen->format, PlayerColorsRGB[p][x]);
827 }
828 }
829 //Wyrmgus end
830 }
831
832 /**
833 ** Clean up players.
834 */
CleanPlayers()835 void CleanPlayers()
836 {
837 ThisPlayer = nullptr;
838 for (unsigned int i = 0; i < PlayerMax; ++i) {
839 Players[i].Clear();
840 }
841 NumPlayers = 0;
842 NoRescueCheck = false;
843 }
844
FreePlayerColors()845 void FreePlayerColors()
846 {
847 for (int i = 0; i < PlayerMax; ++i) {
848 Players[i].UnitColors.Colors.clear();
849 //Wyrmgus start
850 // PlayerColorsRGB[i].clear();
851 // PlayerColors[i].clear();
852 //Wyrmgus end
853 }
854 //Wyrmgus start
855 for (int i = 0; i < PlayerColorMax; ++i) {
856 PlayerColorsRGB[i].clear();
857 PlayerColors[i].clear();
858 }
859 //Wyrmgus end
860 }
861
862 /**
863 ** Save state of players to file.
864 **
865 ** @param file Output file.
866 **
867 ** @note FIXME: Not completely saved.
868 */
SavePlayers(CFile & file)869 void SavePlayers(CFile &file)
870 {
871 file.printf("\n--------------------------------------------\n");
872 file.printf("--- MODULE: players\n\n");
873
874 // Dump all players
875 for (int i = 0; i < NumPlayers; ++i) {
876 Players[i].Save(file);
877 }
878
879 file.printf("SetThisPlayer(%d)\n\n", ThisPlayer->Index);
880 }
881
882
Save(CFile & file) const883 void CPlayer::Save(CFile &file) const
884 {
885 const CPlayer &p = *this;
886 file.printf("Player(%d,\n", this->Index);
887 //Wyrmgus start
888 file.printf(" \"race\", \"%s\",", PlayerRaces.Name[p.Race].c_str());
889 if (p.Faction != -1) {
890 file.printf(" \"faction\", %d,", p.Faction);
891 }
892 if (p.Dynasty) {
893 file.printf(" \"dynasty\", \"%s\",", p.Dynasty->Ident.c_str());
894 }
895 if (p.Age) {
896 file.printf(" \"age\", \"%s\",", p.Age->Ident.c_str());
897 }
898 for (int i = 0; i < PlayerColorMax; ++i) {
899 if (PlayerColors[i][0] == this->Color) {
900 file.printf(" \"color\", %d,", i);
901 break;
902 }
903 }
904 //Wyrmgus end
905 file.printf(" \"name\", \"%s\",\n", p.Name.c_str());
906 file.printf(" \"type\", ");
907 switch (p.Type) {
908 case PlayerNeutral: file.printf("\"neutral\","); break;
909 case PlayerNobody: file.printf("\"nobody\","); break;
910 case PlayerComputer: file.printf("\"computer\","); break;
911 case PlayerPerson: file.printf("\"person\","); break;
912 case PlayerRescuePassive: file.printf("\"rescue-passive\","); break;
913 case PlayerRescueActive: file.printf("\"rescue-active\","); break;
914 default: file.printf("%d,", p.Type); break;
915 }
916 //Wyrmgus start
917 // file.printf(" \"race\", \"%s\",", PlayerRaces.Name[p.Race].c_str());
918 //Wyrmgus end
919 file.printf(" \"ai-name\", \"%s\",\n", p.AiName.c_str());
920 file.printf(" \"team\", %d,", p.Team);
921
922 file.printf(" \"enemy\", \"");
923 for (int j = 0; j < PlayerMax; ++j) {
924 file.printf("%c", (p.Enemy & (1 << j)) ? 'X' : '_');
925 }
926 file.printf("\", \"allied\", \"");
927 for (int j = 0; j < PlayerMax; ++j) {
928 file.printf("%c", (p.Allied & (1 << j)) ? 'X' : '_');
929 }
930 file.printf("\", \"shared-vision\", \"");
931 for (int j = 0; j < PlayerMax; ++j) {
932 file.printf("%c", (p.SharedVision & (1 << j)) ? 'X' : '_');
933 }
934 file.printf("\",\n \"start\", {%d, %d},\n", p.StartPos.x, p.StartPos.y);
935 //Wyrmgus start
936 file.printf(" \"start-map-layer\", %d,\n", p.StartMapLayer);
937 if (p.Overlord) {
938 file.printf(" \"overlord\", %d,\n", p.Overlord->Index);
939 }
940 //Wyrmgus end
941
942 // Resources
943 file.printf(" \"resources\", {");
944 for (int j = 0; j < MaxCosts; ++j) {
945 //Wyrmgus start
946 if (!p.Resources[j]) {
947 continue;
948 }
949 //Wyrmgus end
950 file.printf("\"%s\", %d, ", DefaultResourceNames[j].c_str(), p.Resources[j]);
951 }
952 // Stored Resources
953 file.printf("},\n \"stored-resources\", {");
954 for (int j = 0; j < MaxCosts; ++j) {
955 //Wyrmgus start
956 if (!p.StoredResources[j]) {
957 continue;
958 }
959 //Wyrmgus end
960 file.printf("\"%s\", %d, ", DefaultResourceNames[j].c_str(), p.StoredResources[j]);
961 }
962 // Max Resources
963 file.printf("},\n \"max-resources\", {");
964 for (int j = 0; j < MaxCosts; ++j) {
965 file.printf("\"%s\", %d, ", DefaultResourceNames[j].c_str(), p.MaxResources[j]);
966 }
967 // Last Resources
968 file.printf("},\n \"last-resources\", {");
969 for (int j = 0; j < MaxCosts; ++j) {
970 //Wyrmgus start
971 if (!p.LastResources[j]) {
972 continue;
973 }
974 //Wyrmgus end
975 file.printf("\"%s\", %d, ", DefaultResourceNames[j].c_str(), p.LastResources[j]);
976 }
977 // Incomes
978 file.printf("},\n \"incomes\", {");
979 for (int j = 0; j < MaxCosts; ++j) {
980 if (j) {
981 if (j == MaxCosts / 2) {
982 file.printf("\n ");
983 } else {
984 file.printf(" ");
985 }
986 }
987 file.printf("\"%s\", %d,", DefaultResourceNames[j].c_str(), p.Incomes[j]);
988 }
989 // Revenue
990 file.printf("},\n \"revenue\", {");
991 for (int j = 0; j < MaxCosts; ++j) {
992 //Wyrmgus start
993 // if (j) {
994 // if (j == MaxCosts / 2) {
995 // file.printf("\n ");
996 // } else {
997 // file.printf(" ");
998 // }
999 // }
1000 // file.printf("\"%s\", %d,", DefaultResourceNames[j].c_str(), p.Revenue[j]);
1001 if (p.Revenue[j]) {
1002 file.printf("\"%s\", %d, ", DefaultResourceNames[j].c_str(), p.Revenue[j]);
1003 }
1004 //Wyrmgus end
1005 }
1006
1007 //Wyrmgus start
1008 file.printf("},\n \"prices\", {");
1009 for (int j = 0; j < MaxCosts; ++j) {
1010 if (p.Prices[j]) {
1011 file.printf("\"%s\", %d, ", DefaultResourceNames[j].c_str(), p.Prices[j]);
1012 }
1013 }
1014 //Wyrmgus end
1015
1016 // UnitTypesCount done by load units.
1017
1018 file.printf("},\n \"%s\",\n", p.AiEnabled ? "ai-enabled" : "ai-disabled");
1019
1020 // Ai done by load ais.
1021 // Units done by load units.
1022 // TotalNumUnits done by load units.
1023 // NumBuildings done by load units.
1024
1025 //Wyrmgus start
1026 if (p.Revealed) {
1027 file.printf(" \"revealed\",");
1028 }
1029 //Wyrmgus end
1030
1031 file.printf(" \"supply\", %d,", p.Supply);
1032 file.printf(" \"trade-cost\", %d,", p.TradeCost);
1033 file.printf(" \"unit-limit\", %d,", p.UnitLimit);
1034 file.printf(" \"building-limit\", %d,", p.BuildingLimit);
1035 file.printf(" \"total-unit-limit\", %d,", p.TotalUnitLimit);
1036
1037 file.printf("\n \"score\", %d,", p.Score);
1038 file.printf("\n \"total-units\", %d,", p.TotalUnits);
1039 file.printf("\n \"total-buildings\", %d,", p.TotalBuildings);
1040 file.printf("\n \"total-resources\", {");
1041 for (int j = 0; j < MaxCosts; ++j) {
1042 if (j) {
1043 file.printf(" ");
1044 }
1045 file.printf("%d,", p.TotalResources[j]);
1046 }
1047 file.printf("},");
1048 file.printf("\n \"total-razings\", %d,", p.TotalRazings);
1049 file.printf("\n \"total-kills\", %d,", p.TotalKills);
1050 //Wyrmgus start
1051 file.printf("\n \"unit-type-kills\", {");
1052 for (size_t i = 0; i < UnitTypes.size(); ++i) {
1053 if (p.UnitTypeKills[i] != 0) {
1054 file.printf("\"%s\", %d, ", UnitTypes[i]->Ident.c_str(), p.UnitTypeKills[i]);
1055 }
1056 }
1057 file.printf("},");
1058 //Wyrmgus end
1059 if (p.LostTownHallTimer != 0) {
1060 file.printf("\n \"lost-town-hall-timer\", %d,", p.LostTownHallTimer);
1061 }
1062 if (p.HeroCooldownTimer != 0) {
1063 file.printf("\n \"hero-cooldown-timer\", %d,", p.HeroCooldownTimer);
1064 }
1065 //Wyrmgus end
1066
1067 file.printf("\n \"speed-resource-harvest\", {");
1068 for (int j = 0; j < MaxCosts; ++j) {
1069 if (j) {
1070 file.printf(" ");
1071 }
1072 file.printf("%d,", p.SpeedResourcesHarvest[j]);
1073 }
1074 file.printf("},");
1075 file.printf("\n \"speed-resource-return\", {");
1076 for (int j = 0; j < MaxCosts; ++j) {
1077 if (j) {
1078 file.printf(" ");
1079 }
1080 file.printf("%d,", p.SpeedResourcesReturn[j]);
1081 }
1082 file.printf("},");
1083 file.printf("\n \"speed-build\", %d,", p.SpeedBuild);
1084 file.printf("\n \"speed-train\", %d,", p.SpeedTrain);
1085 file.printf("\n \"speed-upgrade\", %d,", p.SpeedUpgrade);
1086 file.printf("\n \"speed-research\", %d,", p.SpeedResearch);
1087
1088 //Wyrmgus start
1089 /*
1090 Uint8 r, g, b;
1091
1092 SDL_GetRGB(p.Color, TheScreen->format, &r, &g, &b);
1093 file.printf("\n \"color\", { %d, %d, %d },", r, g, b);
1094 */
1095 //Wyrmgus end
1096
1097 //Wyrmgus start
1098 file.printf("\n \"current-quests\", {");
1099 for (size_t j = 0; j < p.CurrentQuests.size(); ++j) {
1100 if (j) {
1101 file.printf(" ");
1102 }
1103 file.printf("\"%s\",", p.CurrentQuests[j]->Ident.c_str());
1104 }
1105 file.printf("},");
1106
1107 file.printf("\n \"completed-quests\", {");
1108 for (size_t j = 0; j < p.CompletedQuests.size(); ++j) {
1109 if (j) {
1110 file.printf(" ");
1111 }
1112 file.printf("\"%s\",", p.CompletedQuests[j]->Ident.c_str());
1113 }
1114 file.printf("},");
1115
1116 file.printf("\n \"quest-objectives\", {");
1117 for (size_t j = 0; j < p.QuestObjectives.size(); ++j) {
1118 if (j) {
1119 file.printf(" ");
1120 }
1121 file.printf("{");
1122 file.printf("\"quest\", \"%s\",", p.QuestObjectives[j]->Quest->Ident.c_str());
1123 file.printf("\"objective-type\", \"%s\",", GetQuestObjectiveTypeNameById(p.QuestObjectives[j]->ObjectiveType).c_str());
1124 file.printf("\"objective-string\", \"%s\",", p.QuestObjectives[j]->ObjectiveString.c_str());
1125 file.printf("\"quantity\", %d,", p.QuestObjectives[j]->Quantity);
1126 file.printf("\"counter\", %d,", p.QuestObjectives[j]->Counter);
1127 if (p.QuestObjectives[j]->Resource != -1) {
1128 file.printf("\"resource\", \"%s\",", DefaultResourceNames[p.QuestObjectives[j]->Resource].c_str());
1129 }
1130 if (p.QuestObjectives[j]->UnitClass != -1) {
1131 file.printf("\"unit-class\", \"%s\",", UnitTypeClasses[p.QuestObjectives[j]->UnitClass].c_str());
1132 }
1133 for (const CUnitType *unit_type : p.QuestObjectives[j]->UnitTypes) {
1134 file.printf("\"unit-type\", \"%s\",", unit_type->Ident.c_str());
1135 }
1136 if (p.QuestObjectives[j]->Upgrade) {
1137 file.printf("\"upgrade\", \"%s\",", p.QuestObjectives[j]->Upgrade->Ident.c_str());
1138 }
1139 if (p.QuestObjectives[j]->Character) {
1140 file.printf("\"character\", \"%s\",", p.QuestObjectives[j]->Character->Ident.c_str());
1141 }
1142 if (p.QuestObjectives[j]->Unique) {
1143 file.printf("\"unique\", \"%s\",", p.QuestObjectives[j]->Unique->Ident.c_str());
1144 }
1145 if (p.QuestObjectives[j]->Settlement) {
1146 file.printf("\"settlement\", \"%s\",", p.QuestObjectives[j]->Settlement->Ident.c_str());
1147 }
1148 if (p.QuestObjectives[j]->Faction) {
1149 file.printf("\"faction\", \"%s\",", p.QuestObjectives[j]->Faction->Ident.c_str());
1150 }
1151 file.printf("},");
1152 }
1153 file.printf("},");
1154
1155 file.printf("\n \"modifiers\", {");
1156 for (size_t j = 0; j < p.Modifiers.size(); ++j) {
1157 if (j) {
1158 file.printf(" ");
1159 }
1160 file.printf("\"%s\", %d,", p.Modifiers[j].first->Ident.c_str(), p.Modifiers[j].second);
1161 }
1162 file.printf("},");
1163 //Wyrmgus end
1164
1165 file.printf("\n \"autosell-resources\", {");
1166 for (size_t j = 0; j < p.AutosellResources.size(); ++j) {
1167 if (j) {
1168 file.printf(" ");
1169 }
1170 file.printf("\"%s\",", DefaultResourceNames[p.AutosellResources[j]].c_str());
1171 }
1172 file.printf("},");
1173
1174 // UnitColors done by init code.
1175 // Allow saved by allow.
1176
1177 file.printf("\n \"timers\", {");
1178 //Wyrmgus start
1179 bool first = true;
1180 //Wyrmgus end
1181 for (int j = 0; j < UpgradeMax; ++j) {
1182 //Wyrmgus start
1183 // if (j) {
1184 // file.printf(" ,");
1185 // }
1186 // file.printf("%d", p.UpgradeTimers.Upgrades[j]);
1187 if (p.UpgradeTimers.Upgrades[j]) {
1188 if (first) {
1189 first = false;
1190 } else {
1191 file.printf(", ");
1192 }
1193 file.printf("\"%s\", %d", AllUpgrades[j]->Ident.c_str(), p.UpgradeTimers.Upgrades[j]);
1194 }
1195 //Wyrmgus end
1196 }
1197 file.printf("}");
1198
1199 file.printf(")\n\n");
1200
1201 DebugPrint("FIXME: must save unit-stats?\n");
1202 }
1203
1204 /**
1205 ** Create a new player.
1206 **
1207 ** @param type Player type (Computer,Human,...).
1208 */
CreatePlayer(int type)1209 void CreatePlayer(int type)
1210 {
1211 if (NumPlayers == PlayerMax) { // already done for bigmaps!
1212 return;
1213 }
1214 CPlayer &player = Players[NumPlayers];
1215 player.Index = NumPlayers;
1216
1217 player.Init(type);
1218 }
1219
1220 //Wyrmgus start
GetFactionPlayer(const CFaction * faction)1221 CPlayer *GetFactionPlayer(const CFaction *faction)
1222 {
1223 if (!faction) {
1224 return nullptr;
1225 }
1226
1227 for (int i = 0; i < NumPlayers; ++i) {
1228 if (Players[i].Race == faction->Civilization->ID && Players[i].Faction == faction->ID) {
1229 return &Players[i];
1230 }
1231 }
1232
1233 return nullptr;
1234 }
1235
GetOrAddFactionPlayer(const CFaction * faction)1236 CPlayer *GetOrAddFactionPlayer(const CFaction *faction)
1237 {
1238 CPlayer *faction_player = GetFactionPlayer(faction);
1239 if (faction_player) {
1240 return faction_player;
1241 }
1242
1243 // no player belonging to this faction, so let's make an unused player slot be created for it
1244
1245 for (int i = 0; i < NumPlayers; ++i) {
1246 if (Players[i].Type == PlayerNobody) {
1247 Players[i].Type = PlayerComputer;
1248 Players[i].SetCivilization(faction->Civilization->ID);
1249 Players[i].SetFaction(faction);
1250 Players[i].AiEnabled = true;
1251 Players[i].AiName = faction->DefaultAI;
1252 Players[i].Team = 1;
1253 Players[i].Resources[CopperCost] = 2500; // give the new player enough resources to start up
1254 Players[i].Resources[WoodCost] = 2500;
1255 Players[i].Resources[StoneCost] = 2500;
1256 return &Players[i];
1257 }
1258 }
1259
1260 fprintf(stderr, "Cannot add player for faction \"%s\": no player slots available.\n", faction->Ident.c_str());
1261
1262 return nullptr;
1263 }
1264 //Wyrmgus end
1265
Init(int type)1266 void CPlayer::Init(/* PlayerTypes */ int type)
1267 {
1268 std::vector<CUnit *>().swap(this->Units);
1269 std::vector<CUnit *>().swap(this->FreeWorkers);
1270 //Wyrmgus start
1271 std::vector<CUnit *>().swap(this->LevelUpUnits);
1272 //Wyrmgus end
1273
1274 // Take first slot for person on this computer,
1275 // fill other with computer players.
1276 if (type == PlayerPerson && !NetPlayers) {
1277 if (!ThisPlayer) {
1278 ThisPlayer = this;
1279 } else {
1280 type = PlayerComputer;
1281 }
1282 }
1283 if (NetPlayers && NumPlayers == NetLocalPlayerNumber) {
1284 ThisPlayer = &Players[NetLocalPlayerNumber];
1285 }
1286
1287 if (NumPlayers == PlayerMax) {
1288 static int already_warned;
1289
1290 if (!already_warned) {
1291 DebugPrint("Too many players\n");
1292 already_warned = 1;
1293 }
1294 return;
1295 }
1296
1297 // Make simple teams:
1298 // All person players are enemies.
1299 int team;
1300 switch (type) {
1301 case PlayerNeutral:
1302 case PlayerNobody:
1303 default:
1304 team = 0;
1305 this->SetName("Neutral");
1306 break;
1307 case PlayerComputer:
1308 team = 1;
1309 this->SetName("Computer");
1310 break;
1311 case PlayerPerson:
1312 team = 2 + NumPlayers;
1313 this->SetName("Person");
1314 break;
1315 case PlayerRescuePassive:
1316 case PlayerRescueActive:
1317 // FIXME: correct for multiplayer games?
1318 this->SetName("Computer");
1319 team = 2 + NumPlayers;
1320 break;
1321 }
1322 DebugPrint("CreatePlayer name %s\n" _C_ this->Name.c_str());
1323
1324 this->Type = type;
1325 this->Race = 0;
1326 this->Faction = -1;
1327 this->Religion = nullptr;
1328 this->Dynasty = nullptr;
1329 this->Age = nullptr;
1330 this->Overlord = nullptr;
1331 this->Team = team;
1332 this->Enemy = 0;
1333 this->Allied = 0;
1334 this->AiName = "ai-passive";
1335
1336 // Calculate enemy/allied mask.
1337 for (int i = 0; i < NumPlayers; ++i) {
1338 switch (type) {
1339 case PlayerNeutral:
1340 case PlayerNobody:
1341 default:
1342 break;
1343 case PlayerComputer:
1344 // Computer allied with computer and enemy of all persons.
1345 //Wyrmgus start
1346 /*
1347 if (Players[i].Type == PlayerComputer) {
1348 this->Allied |= (1 << i);
1349 Players[i].Allied |= (1 << NumPlayers);
1350 } else if (Players[i].Type == PlayerPerson || Players[i].Type == PlayerRescueActive) {
1351 */
1352 // make computer players be hostile to each other by default
1353 if (Players[i].Type == PlayerComputer || Players[i].Type == PlayerPerson || Players[i].Type == PlayerRescueActive) {
1354 //Wyrmgus end
1355 this->Enemy |= (1 << i);
1356 Players[i].Enemy |= (1 << NumPlayers);
1357 }
1358 break;
1359 case PlayerPerson:
1360 // Humans are enemy of all?
1361 if (Players[i].Type == PlayerComputer || Players[i].Type == PlayerPerson) {
1362 this->Enemy |= (1 << i);
1363 Players[i].Enemy |= (1 << NumPlayers);
1364 } else if (Players[i].Type == PlayerRescueActive || Players[i].Type == PlayerRescuePassive) {
1365 this->Allied |= (1 << i);
1366 Players[i].Allied |= (1 << NumPlayers);
1367 }
1368 break;
1369 case PlayerRescuePassive:
1370 // Rescue passive are allied with persons
1371 if (Players[i].Type == PlayerPerson) {
1372 this->Allied |= (1 << i);
1373 Players[i].Allied |= (1 << NumPlayers);
1374 }
1375 break;
1376 case PlayerRescueActive:
1377 // Rescue active are allied with persons and enemies of computer
1378 if (Players[i].Type == PlayerComputer) {
1379 this->Enemy |= (1 << i);
1380 Players[i].Enemy |= (1 << NumPlayers);
1381 } else if (Players[i].Type == PlayerPerson) {
1382 this->Allied |= (1 << i);
1383 Players[i].Allied |= (1 << NumPlayers);
1384 }
1385 break;
1386 }
1387 }
1388
1389 // Initial default incomes.
1390 for (int i = 0; i < MaxCosts; ++i) {
1391 this->Incomes[i] = CResource::Resources[i]->DefaultIncome;
1392 }
1393
1394 this->TradeCost = DefaultTradeCost;
1395
1396 // Initial max resource amounts.
1397 for (int i = 0; i < MaxCosts; ++i) {
1398 this->MaxResources[i] = CResource::Resources[i]->DefaultMaxAmount;
1399 }
1400
1401 //Wyrmgus start
1402 this->UnitTypesCount.clear();
1403 this->UnitTypesUnderConstructionCount.clear();
1404 this->UnitTypesAiActiveCount.clear();
1405 this->Heroes.clear();
1406 this->Deities.clear();
1407 this->UnitsByType.clear();
1408 this->AiActiveUnitsByType.clear();
1409 //Wyrmgus end
1410
1411 this->Supply = 0;
1412 this->Demand = 0;
1413 this->NumBuildings = 0;
1414 //Wyrmgus start
1415 this->NumBuildingsUnderConstruction = 0;
1416 this->NumTownHalls = 0;
1417 //Wyrmgus end
1418 this->Score = 0;
1419 //Wyrmgus start
1420 this->LostTownHallTimer = 0;
1421 this->HeroCooldownTimer = 0;
1422 //Wyrmgus end
1423
1424 this->Color = PlayerColors[NumPlayers][0];
1425
1426 if (Players[NumPlayers].Type == PlayerComputer || Players[NumPlayers].Type == PlayerRescueActive) {
1427 this->AiEnabled = true;
1428 } else {
1429 this->AiEnabled = false;
1430 }
1431 //Wyrmgus start
1432 this->Revealed = false;
1433 //Wyrmgus end
1434 ++NumPlayers;
1435 }
1436
1437 /**
1438 ** Change player name.
1439 **
1440 ** @param name New name.
1441 */
SetName(const std::string & name)1442 void CPlayer::SetName(const std::string &name)
1443 {
1444 Name = name;
1445 }
1446
1447 //Wyrmgus start
SetCivilization(int civilization)1448 void CPlayer::SetCivilization(int civilization)
1449 {
1450 if (this->Race != -1 && (GameRunning || GameEstablishing)) {
1451 if (!PlayerRaces.CivilizationUpgrades[this->Race].empty() && this->Allow.Upgrades[CUpgrade::Get(PlayerRaces.CivilizationUpgrades[this->Race])->ID] == 'R') {
1452 UpgradeLost(*this, CUpgrade::Get(PlayerRaces.CivilizationUpgrades[this->Race])->ID);
1453 }
1454 }
1455
1456 int old_civilization = this->Race;
1457 int old_faction = this->Faction;
1458
1459 if (GameRunning) {
1460 this->SetFaction(nullptr);
1461 } else {
1462 this->Faction = -1;
1463 }
1464
1465 this->Race = civilization;
1466
1467 //if the civilization of the person player changed, update the UI
1468 if ((ThisPlayer && ThisPlayer->Index == this->Index) || (!ThisPlayer && this->Index == 0)) {
1469 //load proper UI
1470 char buf[256];
1471 snprintf(buf, sizeof(buf), "if (LoadCivilizationUI ~= nil) then LoadCivilizationUI(\"%s\") end;", PlayerRaces.Name[this->Race].c_str());
1472 CclCommand(buf);
1473
1474 UI.Load();
1475 SetDefaultTextColors(UI.NormalFontColor, UI.ReverseFontColor);
1476 }
1477
1478 if (this->Race != -1) {
1479 CUpgrade *civilization_upgrade = CUpgrade::Get(PlayerRaces.CivilizationUpgrades[this->Race]);
1480 if (civilization_upgrade && this->Allow.Upgrades[civilization_upgrade->ID] != 'R') {
1481 UpgradeAcquire(*this, civilization_upgrade);
1482 }
1483 }
1484 }
1485
1486 /**
1487 ** Change player faction.
1488 **
1489 ** @param faction New faction.
1490 */
SetFaction(const CFaction * faction)1491 void CPlayer::SetFaction(const CFaction *faction)
1492 {
1493 int old_faction_id = this->Faction;
1494
1495 if (faction && faction->Civilization->ID != this->Race) {
1496 this->SetCivilization(faction->Civilization->ID);
1497 }
1498
1499 if (this->Faction != -1) {
1500 if (!PlayerRaces.Factions[this->Faction]->FactionUpgrade.empty() && this->Allow.Upgrades[CUpgrade::Get(PlayerRaces.Factions[this->Faction]->FactionUpgrade)->ID] == 'R') {
1501 UpgradeLost(*this, CUpgrade::Get(PlayerRaces.Factions[this->Faction]->FactionUpgrade)->ID);
1502 }
1503
1504 int faction_type_upgrade_id = UpgradeIdByIdent("upgrade-" + GetFactionTypeNameById(PlayerRaces.Factions[this->Faction]->Type));
1505 if (faction_type_upgrade_id != -1 && this->Allow.Upgrades[faction_type_upgrade_id] == 'R') {
1506 UpgradeLost(*this, faction_type_upgrade_id);
1507 }
1508 }
1509
1510 int faction_id = faction ? faction->ID : -1;
1511
1512 if (old_faction_id != -1 && faction_id != -1) {
1513 for (size_t i = 0; i < UpgradeClasses.size(); ++i) {
1514 if (PlayerRaces.GetFactionClassUpgrade(old_faction_id, i) != PlayerRaces.GetFactionClassUpgrade(faction_id, i)) { //if the upgrade for a certain class is different for the new faction than the old faction (and it has been acquired), remove the modifiers of the old upgrade and apply the modifiers of the new
1515 if (PlayerRaces.GetFactionClassUpgrade(old_faction_id, i) != -1 && this->Allow.Upgrades[PlayerRaces.GetFactionClassUpgrade(old_faction_id, i)] == 'R') {
1516 UpgradeLost(*this, PlayerRaces.GetFactionClassUpgrade(old_faction_id, i));
1517
1518 if (PlayerRaces.GetFactionClassUpgrade(faction_id, i) != -1) {
1519 UpgradeAcquire(*this, AllUpgrades[PlayerRaces.GetFactionClassUpgrade(faction_id, i)]);
1520 }
1521 }
1522 }
1523 }
1524 }
1525
1526 bool personal_names_changed = true;
1527 bool ship_names_changed = true;
1528 if (this->Faction != -1 && faction_id != -1) {
1529 ship_names_changed = PlayerRaces.Factions[this->Faction]->GetShipNames() != PlayerRaces.Factions[faction_id]->GetShipNames();
1530 personal_names_changed = false; // setting to a faction of the same civilization
1531 }
1532
1533 this->Faction = faction_id;
1534
1535 if (this->Index == ThisPlayer->Index) {
1536 UI.Load();
1537 }
1538
1539 if (this->Faction == -1) {
1540 return;
1541 }
1542
1543 if (!IsNetworkGame()) { //only set the faction's name as the player's name if this is a single player game
1544 this->SetName(PlayerRaces.Factions[this->Faction]->Name);
1545 }
1546 if (this->Faction != -1) {
1547 int color = -1;
1548 for (size_t i = 0; i < PlayerRaces.Factions[faction_id]->Colors.size(); ++i) {
1549 if (!IsPlayerColorUsed(PlayerRaces.Factions[faction_id]->Colors[i])) {
1550 color = PlayerRaces.Factions[faction_id]->Colors[i];
1551 break;
1552 }
1553 }
1554 if (color == -1) { //if all of the faction's colors are used, get a unused player color
1555 for (int i = 0; i < PlayerColorMax; ++i) {
1556 if (!IsPlayerColorUsed(i)) {
1557 color = i;
1558 break;
1559 }
1560 }
1561 }
1562
1563 if (color != -1) {
1564 if (this->Color != PlayerColors[color][0]) {
1565 this->Color = PlayerColors[color][0];
1566 this->UnitColors.Colors = PlayerColorsRGB[color];
1567 }
1568 }
1569
1570 if (!PlayerRaces.Factions[this->Faction]->FactionUpgrade.empty()) {
1571 CUpgrade *faction_upgrade = CUpgrade::Get(PlayerRaces.Factions[this->Faction]->FactionUpgrade);
1572 if (faction_upgrade && this->Allow.Upgrades[faction_upgrade->ID] != 'R') {
1573 if (GameEstablishing) {
1574 AllowUpgradeId(*this, faction_upgrade->ID, 'R');
1575 } else {
1576 UpgradeAcquire(*this, faction_upgrade);
1577 }
1578 }
1579 }
1580
1581 int faction_type_upgrade_id = UpgradeIdByIdent("upgrade-" + GetFactionTypeNameById(PlayerRaces.Factions[this->Faction]->Type));
1582 if (faction_type_upgrade_id != -1 && this->Allow.Upgrades[faction_type_upgrade_id] != 'R') {
1583 if (GameEstablishing) {
1584 AllowUpgradeId(*this, faction_type_upgrade_id, 'R');
1585 } else {
1586 UpgradeAcquire(*this, AllUpgrades[faction_type_upgrade_id]);
1587 }
1588 }
1589 } else {
1590 fprintf(stderr, "Invalid faction \"%s\" tried to be set for player %d of civilization \"%s\".\n", faction->Name.c_str(), this->Index, PlayerRaces.Name[this->Race].c_str());
1591 }
1592
1593 for (int i = 0; i < this->GetUnitCount(); ++i) {
1594 CUnit &unit = this->GetUnit(i);
1595 if (!unit.Unique && unit.Type->PersonalNames.size() == 0) {
1596 if (!unit.Type->BoolFlag[ORGANIC_INDEX].value && unit.Type->UnitType == UnitTypeNaval && ship_names_changed) {
1597 unit.UpdatePersonalName();
1598 }
1599 }
1600 if (personal_names_changed && unit.Type->BoolFlag[ORGANIC_INDEX].value && !unit.Character && unit.Type->Civilization != -1 && PlayerRaces.Species[unit.Type->Civilization] == PlayerRaces.Species[faction->Civilization->ID] && unit.Type->Slot == PlayerRaces.GetFactionClassUnitType(faction->ID, unit.Type->Class)) {
1601 unit.UpdatePersonalName();
1602 }
1603 unit.UpdateSoldUnits();
1604 unit.UpdateButtonIcons();
1605 }
1606 }
1607
1608 /**
1609 ** Change player faction to a randomly chosen one.
1610 **
1611 ** @param faction New faction.
1612 */
SetRandomFaction()1613 void CPlayer::SetRandomFaction()
1614 {
1615 // set random one from the civilization's factions
1616 std::vector<CFaction *> local_factions;
1617
1618 for (size_t i = 0; i < PlayerRaces.Factions.size(); ++i) {
1619 CFaction *faction = PlayerRaces.Factions[i];
1620 if (faction->Civilization->ID != this->Race) {
1621 continue;
1622 }
1623 if (!faction->Playable) {
1624 continue;
1625 }
1626 if (!this->CanFoundFaction(faction)) {
1627 continue;
1628 }
1629
1630 int faction_type = faction->Type;
1631 bool has_writing = this->HasUpgradeClass(GetUpgradeClassIndexByName("writing"));
1632 if (
1633 !(faction_type == FactionTypeTribe && !has_writing)
1634 && !(faction_type == FactionTypePolity && has_writing)
1635 ) {
1636 continue;
1637 }
1638
1639 local_factions.push_back(faction);
1640 }
1641
1642 if (local_factions.size() > 0) {
1643 CFaction *chosen_faction = local_factions[SyncRand(local_factions.size())];
1644 this->SetFaction(chosen_faction);
1645 } else {
1646 this->SetFaction(nullptr);
1647 }
1648 }
1649
1650 /**
1651 ** @brief Change player dynasty.
1652 **
1653 ** @param dynasty New dynasty.
1654 */
SetDynasty(CDynasty * dynasty)1655 void CPlayer::SetDynasty(CDynasty *dynasty)
1656 {
1657 CDynasty *old_dynasty = this->Dynasty;
1658
1659 if (this->Dynasty) {
1660 if (this->Dynasty->DynastyUpgrade && this->Allow.Upgrades[this->Dynasty->DynastyUpgrade->ID] == 'R') {
1661 UpgradeLost(*this, this->Dynasty->DynastyUpgrade->ID);
1662 }
1663 }
1664
1665 this->Dynasty = dynasty;
1666
1667 if (!this->Dynasty) {
1668 return;
1669 }
1670
1671 if (this->Dynasty->DynastyUpgrade) {
1672 if (this->Allow.Upgrades[this->Dynasty->DynastyUpgrade->ID] != 'R') {
1673 if (GameEstablishing) {
1674 AllowUpgradeId(*this, this->Dynasty->DynastyUpgrade->ID, 'R');
1675 } else {
1676 UpgradeAcquire(*this, this->Dynasty->DynastyUpgrade);
1677 }
1678 }
1679 }
1680
1681 for (int i = 0; i < this->GetUnitCount(); ++i) {
1682 CUnit &unit = this->GetUnit(i);
1683 unit.UpdateSoldUnits(); //in case conditions changed (i.e. some heroes may require a certain dynasty)
1684 }
1685 }
1686
1687 /**
1688 ** @brief Check which age fits the player's current situation best, and set it as the player's age
1689 */
CheckAge()1690 void CPlayer::CheckAge()
1691 {
1692 //pick an age which fits the player, giving priority to the first ones (ages are already sorted by priority)
1693
1694 for (CAge *potential_age : CAge::Ages) {
1695 if (!CheckDependencies(potential_age, this)) {
1696 continue;
1697 }
1698
1699 this->SetAge(potential_age);
1700 return;
1701 }
1702
1703 this->SetAge(nullptr);
1704 }
1705
1706 /**
1707 ** @brief Set the player's age
1708 **
1709 ** @param age The age to be set for the player
1710 */
SetAge(CAge * age)1711 void CPlayer::SetAge(CAge *age)
1712 {
1713 if (this->Age == age) {
1714 return;
1715 }
1716
1717 this->Age = age;
1718
1719 if (this == ThisPlayer) {
1720 if (this->Age) {
1721 UI.AgePanel.Text = this->Age->Name;
1722 UI.AgePanel.G = this->Age->G;
1723
1724 if (GameCycle > 0 && !SaveGameLoading) {
1725 this->Notify(_("The %s has dawned upon us."), this->Age->Name.c_str());
1726 }
1727 } else {
1728 UI.AgePanel.Text.clear();
1729 UI.AgePanel.G = nullptr;
1730 }
1731 }
1732
1733 CAge::CheckCurrentAge();
1734 }
1735
1736 /**
1737 ** @brief Get the player's currency
1738 **
1739 ** @return The player's currency
1740 */
GetCurrency() const1741 CCurrency *CPlayer::GetCurrency() const
1742 {
1743 if (this->Faction != -1) {
1744 return PlayerRaces.Factions[this->Faction]->GetCurrency();
1745 }
1746
1747 if (this->Race != -1) {
1748 return CCivilization::Civilizations[this->Race]->GetCurrency();
1749 }
1750
1751 return nullptr;
1752 }
1753
ShareUpgradeProgress(CPlayer & player,CUnit & unit)1754 void CPlayer::ShareUpgradeProgress(CPlayer &player, CUnit &unit)
1755 {
1756 std::vector<CUpgrade *> upgrade_list = this->GetResearchableUpgrades();
1757 std::vector<CUpgrade *> potential_upgrades;
1758
1759 for (size_t i = 0; i < upgrade_list.size(); ++i) {
1760 if (this->Allow.Upgrades[upgrade_list[i]->ID] != 'R') {
1761 continue;
1762 }
1763
1764 if (upgrade_list[i]->Class == -1) {
1765 continue;
1766 }
1767
1768 int upgrade_id = PlayerRaces.GetFactionClassUpgrade(player.Faction, upgrade_list[i]->Class);
1769 if (upgrade_id == -1) {
1770 continue;
1771 }
1772
1773 CUpgrade *upgrade = AllUpgrades[upgrade_id];
1774
1775 if (player.Allow.Upgrades[upgrade->ID] != 'A' || !CheckDependencies(upgrade, &player)) {
1776 continue;
1777 }
1778
1779 if (player.UpgradeRemovesExistingUpgrade(upgrade, player.AiEnabled)) {
1780 continue;
1781 }
1782
1783 potential_upgrades.push_back(upgrade);
1784 }
1785
1786 if (potential_upgrades.size() > 0) {
1787 CUpgrade *chosen_upgrade = potential_upgrades[SyncRand(potential_upgrades.size())];
1788
1789 if (!chosen_upgrade->Name.empty()) {
1790 player.Notify(NotifyGreen, unit.tilePos, unit.MapLayer->ID, _("%s acquired through contact with %s"), chosen_upgrade->Name.c_str(), this->Name.c_str());
1791 }
1792 if (&player == ThisPlayer) {
1793 CSound *sound = GameSounds.ResearchComplete[player.Race].Sound;
1794
1795 if (sound) {
1796 PlayGameSound(sound, MaxSampleVolume);
1797 }
1798 }
1799 if (player.AiEnabled) {
1800 AiResearchComplete(unit, chosen_upgrade);
1801 }
1802 UpgradeAcquire(player, chosen_upgrade);
1803 }
1804 }
1805
IsPlayerColorUsed(int color)1806 bool CPlayer::IsPlayerColorUsed(int color)
1807 {
1808 bool color_used = false;
1809 for (int i = 0; i < PlayerMax; ++i) {
1810 if (this->Index != i && Players[i].Faction != -1 && Players[i].Type != PlayerNobody && Players[i].Color == PlayerColors[color][0]) {
1811 color_used = true;
1812 }
1813 }
1814 return color_used;
1815 }
1816
HasUpgradeClass(const int upgrade_class) const1817 bool CPlayer::HasUpgradeClass(const int upgrade_class) const
1818 {
1819 if (this->Race == -1 || upgrade_class == -1) {
1820 return false;
1821 }
1822
1823 int upgrade_id = -1;
1824
1825 if (this->Faction != -1) {
1826 upgrade_id = PlayerRaces.GetFactionClassUpgrade(this->Faction, upgrade_class);
1827 } else {
1828 upgrade_id = PlayerRaces.GetCivilizationClassUpgrade(this->Race, upgrade_class);
1829 }
1830
1831 if (upgrade_id != -1 && this->Allow.Upgrades[upgrade_id] == 'R') {
1832 return true;
1833 }
1834
1835 return false;
1836 }
1837
HasSettlement(const CSite * settlement) const1838 bool CPlayer::HasSettlement(const CSite *settlement) const
1839 {
1840 if (!settlement) {
1841 return false;
1842 }
1843
1844 if (settlement->SiteUnit && settlement->SiteUnit->Player == this) {
1845 return true;
1846 }
1847
1848 return false;
1849 }
1850
HasSettlementNearWaterZone(int water_zone) const1851 bool CPlayer::HasSettlementNearWaterZone(int water_zone) const
1852 {
1853 std::vector<CUnit *> settlement_unit_table;
1854
1855 int town_hall_type_id = PlayerRaces.GetFactionClassUnitType(this->Faction, GetUnitTypeClassIndexByName("town-hall"));
1856 if (town_hall_type_id == -1) {
1857 return false;
1858 }
1859 CUnitType *town_hall_type = UnitTypes[town_hall_type_id];
1860
1861 int stronghold_type_id = PlayerRaces.GetFactionClassUnitType(this->Faction, GetUnitTypeClassIndexByName("stronghold"));
1862 CUnitType *stronghold_type = nullptr;
1863 if (stronghold_type_id != -1) {
1864 stronghold_type = UnitTypes[stronghold_type_id];
1865 }
1866
1867 FindPlayerUnitsByType(*this, *town_hall_type, settlement_unit_table, true);
1868
1869 if (stronghold_type) {
1870 FindPlayerUnitsByType(*this, *stronghold_type, settlement_unit_table, true); //adds strongholds to the table
1871 }
1872 for (size_t i = 0; i < settlement_unit_table.size(); ++i) {
1873 CUnit *settlement_unit = settlement_unit_table[i];
1874 if (!settlement_unit->IsAliveOnMap()) {
1875 continue;
1876 }
1877
1878 int settlement_landmass = Map.GetTileLandmass(settlement_unit->tilePos, settlement_unit->MapLayer->ID);
1879 if (std::find(Map.BorderLandmasses[settlement_landmass].begin(), Map.BorderLandmasses[settlement_landmass].end(), water_zone) == Map.BorderLandmasses[settlement_landmass].end()) { //settlement's landmass doesn't even border the water zone, continue
1880 continue;
1881 }
1882
1883 Vec2i pos(0, 0);
1884 if (FindTerrainType(0, 0, 8, *this, settlement_unit->tilePos, &pos, settlement_unit->MapLayer->ID, water_zone)) {
1885 return true;
1886 }
1887 }
1888
1889 return false;
1890 }
1891
GetNearestSettlement(const Vec2i & pos,int z,const Vec2i & size) const1892 CSite *CPlayer::GetNearestSettlement(const Vec2i &pos, int z, const Vec2i &size) const
1893 {
1894 CUnit *best_hall = nullptr;
1895 int best_distance = -1;
1896
1897 for (size_t i = 0; i < Map.SiteUnits.size(); ++i) {
1898 CUnit *settlement_unit = Map.SiteUnits[i];
1899 if (!settlement_unit || !settlement_unit->IsAliveOnMap() || !settlement_unit->Type->BoolFlag[TOWNHALL_INDEX].value || z != settlement_unit->MapLayer->ID) {
1900 continue;
1901 }
1902 if (!this->HasNeutralFactionType() && this != settlement_unit->Player) {
1903 continue;
1904 }
1905 int distance = MapDistance(size, pos, z, settlement_unit->Type->TileSize, settlement_unit->tilePos, settlement_unit->MapLayer->ID);
1906 if (!best_hall || distance < best_distance) {
1907 best_hall = settlement_unit;
1908 best_distance = distance;
1909 }
1910 }
1911
1912 if (best_hall) {
1913 return best_hall->Settlement;
1914 } else {
1915 return nullptr;
1916 }
1917 }
1918
HasUnitBuilder(const CUnitType * type,const CSite * settlement) const1919 bool CPlayer::HasUnitBuilder(const CUnitType *type, const CSite *settlement) const
1920 {
1921 if (type->BoolFlag[BUILDING_INDEX].value && type->Slot < (int) AiHelpers.Build.size()) {
1922 for (size_t j = 0; j < AiHelpers.Build[type->Slot].size(); ++j) {
1923 if (this->GetUnitTypeCount(AiHelpers.Build[type->Slot][j]) > 0) {
1924 return true;
1925 }
1926 }
1927 } else if (!type->BoolFlag[BUILDING_INDEX].value && type->Slot < (int) AiHelpers.Train.size()) {
1928 for (size_t j = 0; j < AiHelpers.Train[type->Slot].size(); ++j) {
1929 if (this->GetUnitTypeCount(AiHelpers.Train[type->Slot][j]) > 0) {
1930 return true;
1931 }
1932 }
1933 }
1934 if (type->Slot < (int) AiHelpers.Upgrade.size()) {
1935 for (size_t j = 0; j < AiHelpers.Upgrade[type->Slot].size(); ++j) {
1936 if (this->GetUnitTypeCount(AiHelpers.Upgrade[type->Slot][j]) > 0) {
1937 if (!settlement) {
1938 return true;
1939 } else {
1940 for (int i = 0; i < this->GetUnitCount(); ++i) {
1941 CUnit &unit = this->GetUnit(i);
1942 if (unit.Type == AiHelpers.Upgrade[type->Slot][j] && unit.Settlement == settlement) {
1943 return true;
1944 }
1945 }
1946 }
1947 }
1948 }
1949 }
1950 return false;
1951 }
1952
HasUpgradeResearcher(const CUpgrade * upgrade) const1953 bool CPlayer::HasUpgradeResearcher(const CUpgrade *upgrade) const
1954 {
1955 if (upgrade->ID < (int) AiHelpers.Research.size()) {
1956 for (size_t j = 0; j < AiHelpers.Research[upgrade->ID].size(); ++j) {
1957 CUnitType *researcher_type = AiHelpers.Research[upgrade->ID][j];
1958 if (this->GetUnitTypeCount(researcher_type) > 0 || HasUnitBuilder(researcher_type)) {
1959 return true;
1960 }
1961 }
1962 }
1963 return false;
1964 }
1965
1966 /**
1967 ** Check if the player can found a particular faction.
1968 **
1969 ** @param faction New faction.
1970 */
CanFoundFaction(CFaction * faction,bool pre)1971 bool CPlayer::CanFoundFaction(CFaction *faction, bool pre)
1972 {
1973 if (CurrentQuest != nullptr) {
1974 return false;
1975 }
1976
1977 if (!faction->FactionUpgrade.empty()) {
1978 CUpgrade *faction_upgrade = CUpgrade::Get(faction->FactionUpgrade);
1979
1980 if (faction_upgrade) {
1981 if (!CheckDependencies(faction_upgrade, this, false, pre)) {
1982 return false;
1983 }
1984 } else {
1985 fprintf(stderr, "Faction upgrade \"%s\" doesn't exist.\n", faction->FactionUpgrade.c_str());
1986 }
1987 }
1988
1989 for (int i = 0; i < PlayerMax; ++i) {
1990 if (this->Index != i && Players[i].Type != PlayerNobody && Players[i].Race == faction->Civilization->ID && Players[i].Faction == faction->ID) {
1991 // faction is already in use
1992 return false;
1993 }
1994 }
1995
1996 if (!pre) {
1997 //check if the required core settlements are owned by the player
1998 if (CurrentCampaign != nullptr) { //only check for settlements in the Scenario mode
1999 for (size_t i = 0; i < faction->Cores.size(); ++i) {
2000 if (!faction->Cores[i]->SiteUnit || faction->Cores[i]->SiteUnit->Player != this || faction->Cores[i]->SiteUnit->CurrentAction() == UnitActionBuilt) {
2001 return false;
2002 }
2003 }
2004 }
2005
2006 if (faction->Conditions) {
2007 CclCommand("trigger_player = " + std::to_string((long long) this->Index) + ";");
2008 faction->Conditions->pushPreamble();
2009 faction->Conditions->run(1);
2010 if (faction->Conditions->popBoolean() == false) {
2011 return false;
2012 }
2013 }
2014 }
2015
2016 return true;
2017 }
2018
2019 /**
2020 ** Check if the player can choose a particular dynasty.
2021 **
2022 ** @param dynasty New dynasty.
2023 */
CanChooseDynasty(CDynasty * dynasty,bool pre)2024 bool CPlayer::CanChooseDynasty(CDynasty *dynasty, bool pre)
2025 {
2026 if (CurrentQuest != nullptr) {
2027 return false;
2028 }
2029
2030 if (dynasty->DynastyUpgrade) {
2031 if (!CheckDependencies(dynasty->DynastyUpgrade, this, false, pre)) {
2032 return false;
2033 }
2034 } else {
2035 return false;
2036 }
2037
2038 if (!pre) {
2039 if (dynasty->Conditions) {
2040 CclCommand("trigger_player = " + std::to_string((long long) this->Index) + ";");
2041 dynasty->Conditions->pushPreamble();
2042 dynasty->Conditions->run(1);
2043 if (dynasty->Conditions->popBoolean() == false) {
2044 return false;
2045 }
2046 }
2047 }
2048
2049 return true;
2050 }
2051
2052 /**
2053 ** Check if the player can recruit a particular hero.
2054 **
2055 ** @param character Hero.
2056 */
CanRecruitHero(const CCharacter * character,bool ignore_neutral) const2057 bool CPlayer::CanRecruitHero(const CCharacter *character, bool ignore_neutral) const
2058 {
2059 if (character->Deity != nullptr) { //character is a deity
2060 return false;
2061 }
2062
2063 if (!character->Civilization || character->Civilization->ID != this->Race) {
2064 return false;
2065 }
2066
2067 if (!CheckDependencies(character->Type, this, true)) {
2068 return false;
2069 }
2070
2071 if (!character->Factions.empty() && (this->Faction == -1 || std::find(character->Factions.begin(), character->Factions.end(), PlayerRaces.Factions[this->Faction]) == character->Factions.end())) {
2072 return false;
2073 }
2074
2075 if (character->Conditions) {
2076 CclCommand("trigger_player = " + std::to_string((long long) this->Index) + ";");
2077 character->Conditions->pushPreamble();
2078 character->Conditions->run(1);
2079 if (character->Conditions->popBoolean() == false) {
2080 return false;
2081 }
2082 }
2083
2084 if (!character->CanAppear(ignore_neutral)) {
2085 return false;
2086 }
2087
2088 return true;
2089 }
2090
2091 /**
2092 ** Check if the upgrade removes an existing upgrade of the player.
2093 **
2094 ** @param upgrade Upgrade.
2095 */
UpgradeRemovesExistingUpgrade(const CUpgrade * upgrade,bool ignore_lower_priority) const2096 bool CPlayer::UpgradeRemovesExistingUpgrade(const CUpgrade *upgrade, bool ignore_lower_priority) const
2097 {
2098 for (size_t z = 0; z < upgrade->UpgradeModifiers.size(); ++z) {
2099 for (size_t j = 0; j < upgrade->UpgradeModifiers[z]->RemoveUpgrades.size(); ++j) {
2100 const CUpgrade *removed_upgrade = upgrade->UpgradeModifiers[z]->RemoveUpgrades[j];
2101 bool has_upgrade = this->AiEnabled ? AiHasUpgrade(*this->Ai, removed_upgrade, true) : (UpgradeIdAllowed(*this, removed_upgrade->ID) == 'R');
2102 if (has_upgrade) {
2103 if (ignore_lower_priority && this->Faction != -1 && PlayerRaces.Factions[this->Faction]->GetUpgradePriority(removed_upgrade) < PlayerRaces.Factions[this->Faction]->GetUpgradePriority(upgrade)) {
2104 continue;
2105 }
2106 return true;
2107 }
2108 }
2109 }
2110
2111 return false;
2112 }
2113
GetFactionTitleName() const2114 std::string CPlayer::GetFactionTitleName() const
2115 {
2116 if (this->Race == -1 || this->Faction == -1) {
2117 return "";
2118 }
2119
2120 CFaction *faction = PlayerRaces.Factions[this->Faction];
2121 int faction_tier = faction->DefaultTier;
2122 int government_type = faction->DefaultGovernmentType;
2123
2124 if (faction->Type == FactionTypePolity) {
2125 if (!faction->Titles[government_type][faction_tier].empty()) {
2126 return faction->Titles[government_type][faction_tier];
2127 } else {
2128 if (government_type == GovernmentTypeMonarchy) {
2129 if (faction_tier == FactionTierBarony) {
2130 return "Barony";
2131 } else if (faction_tier == FactionTierCounty) {
2132 return "County";
2133 } else if (faction_tier == FactionTierDuchy) {
2134 return "Duchy";
2135 } else if (faction_tier == FactionTierGrandDuchy) {
2136 return "Grand Duchy";
2137 } else if (faction_tier == FactionTierKingdom) {
2138 return "Kingdom";
2139 } else if (faction_tier == FactionTierEmpire) {
2140 return "Empire";
2141 }
2142 } else if (government_type == GovernmentTypeRepublic) {
2143 return "Republic";
2144 } else if (government_type == GovernmentTypeTheocracy) {
2145 return "Theocracy";
2146 }
2147 }
2148 }
2149
2150 return "";
2151 }
2152
GetCharacterTitleName(int title_type,int gender) const2153 std::string CPlayer::GetCharacterTitleName(int title_type, int gender) const
2154 {
2155 if (this->Race == -1 || this->Faction == -1 || title_type == -1 || gender == -1) {
2156 return "";
2157 }
2158
2159 CCivilization *civilization = CCivilization::Civilizations[this->Race];
2160 CFaction *faction = PlayerRaces.Factions[this->Faction];
2161 int faction_tier = faction->DefaultTier;
2162 int government_type = faction->DefaultGovernmentType;
2163
2164 if (faction->Type == FactionTypePolity) {
2165 if (!faction->MinisterTitles[title_type][gender][government_type][faction_tier].empty()) {
2166 return faction->MinisterTitles[title_type][gender][government_type][faction_tier];
2167 } else if (!faction->MinisterTitles[title_type][NoGender][government_type][faction_tier].empty()) {
2168 return faction->MinisterTitles[title_type][NoGender][government_type][faction_tier];
2169 } else if (!faction->MinisterTitles[title_type][gender][GovernmentTypeNoGovernmentType][faction_tier].empty()) {
2170 return faction->MinisterTitles[title_type][gender][GovernmentTypeNoGovernmentType][faction_tier];
2171 } else if (!faction->MinisterTitles[title_type][NoGender][GovernmentTypeNoGovernmentType][faction_tier].empty()) {
2172 return faction->MinisterTitles[title_type][NoGender][GovernmentTypeNoGovernmentType][faction_tier];
2173 } else if (!faction->MinisterTitles[title_type][gender][government_type][FactionTierNoFactionTier].empty()) {
2174 return faction->MinisterTitles[title_type][gender][government_type][FactionTierNoFactionTier];
2175 } else if (!faction->MinisterTitles[title_type][NoGender][government_type][FactionTierNoFactionTier].empty()) {
2176 return faction->MinisterTitles[title_type][NoGender][government_type][FactionTierNoFactionTier];
2177 } else if (!faction->MinisterTitles[title_type][gender][GovernmentTypeNoGovernmentType][FactionTierNoFactionTier].empty()) {
2178 return faction->MinisterTitles[title_type][gender][GovernmentTypeNoGovernmentType][FactionTierNoFactionTier];
2179 } else if (!faction->MinisterTitles[title_type][NoGender][GovernmentTypeNoGovernmentType][FactionTierNoFactionTier].empty()) {
2180 return faction->MinisterTitles[title_type][NoGender][GovernmentTypeNoGovernmentType][FactionTierNoFactionTier];
2181 } else if (!civilization->MinisterTitles[title_type][gender][government_type][faction_tier].empty()) {
2182 return civilization->MinisterTitles[title_type][gender][government_type][faction_tier];
2183 } else if (!civilization->MinisterTitles[title_type][NoGender][government_type][faction_tier].empty()) {
2184 return civilization->MinisterTitles[title_type][NoGender][government_type][faction_tier];
2185 } else if (!civilization->MinisterTitles[title_type][gender][GovernmentTypeNoGovernmentType][faction_tier].empty()) {
2186 return civilization->MinisterTitles[title_type][gender][GovernmentTypeNoGovernmentType][faction_tier];
2187 } else if (!civilization->MinisterTitles[title_type][NoGender][GovernmentTypeNoGovernmentType][faction_tier].empty()) {
2188 return civilization->MinisterTitles[title_type][NoGender][GovernmentTypeNoGovernmentType][faction_tier];
2189 } else if (!civilization->MinisterTitles[title_type][gender][government_type][FactionTierNoFactionTier].empty()) {
2190 return civilization->MinisterTitles[title_type][gender][government_type][FactionTierNoFactionTier];
2191 } else if (!civilization->MinisterTitles[title_type][NoGender][government_type][FactionTierNoFactionTier].empty()) {
2192 return civilization->MinisterTitles[title_type][NoGender][government_type][FactionTierNoFactionTier];
2193 } else if (!civilization->MinisterTitles[title_type][gender][GovernmentTypeNoGovernmentType][FactionTierNoFactionTier].empty()) {
2194 return civilization->MinisterTitles[title_type][gender][GovernmentTypeNoGovernmentType][FactionTierNoFactionTier];
2195 } else if (!civilization->MinisterTitles[title_type][NoGender][GovernmentTypeNoGovernmentType][FactionTierNoFactionTier].empty()) {
2196 return civilization->MinisterTitles[title_type][NoGender][GovernmentTypeNoGovernmentType][FactionTierNoFactionTier];
2197 }
2198 }
2199
2200 if (title_type == CharacterTitleHeadOfState) {
2201 if (faction->Type == FactionTypeTribe) {
2202 if (gender != FemaleGender) {
2203 return "Chieftain";
2204 } else {
2205 return "Chieftess";
2206 }
2207 } else if (faction->Type == FactionTypePolity) {
2208 std::string faction_title = this->GetFactionTitleName();
2209
2210 if (faction_title == "Barony") {
2211 if (gender != FemaleGender) {
2212 return "Baron";
2213 } else {
2214 return "Baroness";
2215 }
2216 } else if (faction_title == "Lordship") {
2217 if (gender != FemaleGender) {
2218 return "Lord";
2219 } else {
2220 return "Lady";
2221 }
2222 } else if (faction_title == "County") {
2223 if (gender != FemaleGender) {
2224 return "Count";
2225 } else {
2226 return "Countess";
2227 }
2228 } else if (faction_title == "City-State") {
2229 return "Archon";
2230 } else if (faction_title == "Duchy") {
2231 if (gender != FemaleGender) {
2232 return "Duke";
2233 } else {
2234 return "Duchess";
2235 }
2236 } else if (faction_title == "Principality") {
2237 if (gender != FemaleGender) {
2238 return "Prince";
2239 } else {
2240 return "Princess";
2241 }
2242 } else if (faction_title == "Margraviate") {
2243 return "Margrave";
2244 } else if (faction_title == "Landgraviate") {
2245 return "Landgrave";
2246 } else if (faction_title == "Grand Duchy") {
2247 if (gender != FemaleGender) {
2248 return "Grand Duke";
2249 } else {
2250 return "Grand Duchess";
2251 }
2252 } else if (faction_title == "Archduchy") {
2253 if (gender != FemaleGender) {
2254 return "Archduke";
2255 } else {
2256 return "Archduchess";
2257 }
2258 } else if (faction_title == "Kingdom") {
2259 if (gender != FemaleGender) {
2260 return "King";
2261 } else {
2262 return "Queen";
2263 }
2264 } else if (faction_title == "Khanate") {
2265 return "Khan";
2266 } else if (faction_title == "Empire") {
2267 if (gender != FemaleGender) {
2268 return "Emperor";
2269 } else {
2270 return "Empress";
2271 }
2272 } else if (faction_title == "Republic") {
2273 return "Consul";
2274 } else if (faction_title == "Confederation") {
2275 return "Chancellor";
2276 } else if (faction_title == "Theocracy") {
2277 if (gender != FemaleGender) {
2278 return "High Priest";
2279 } else {
2280 return "High Priestess";
2281 }
2282 } else if (faction_title == "Bishopric") {
2283 return "Bishop";
2284 } else if (faction_title == "Archbishopric") {
2285 return "Archbishop";
2286 }
2287 }
2288 } else if (title_type == CharacterTitleHeadOfGovernment) {
2289 return "Prime Minister";
2290 } else if (title_type == CharacterTitleEducationMinister) {
2291 // return "Education Minister"; //education minister sounds too modern, considering the technology tree we have up to now only goes to the medieval era
2292 return "Master Educator";
2293 } else if (title_type == CharacterTitleFinanceMinister) {
2294 // return "Finance Minister"; //finance minister sounds too modern, considering the technology tree we have up to now only goes to the medieval era
2295 return "Treasurer";
2296 } else if (title_type == CharacterTitleForeignMinister) {
2297 // return "Foreign Minister"; //foreign minister sounds too modern, considering the technology tree we have up to now only goes to the medieval era
2298 return "Chancellor";
2299 } else if (title_type == CharacterTitleIntelligenceMinister) {
2300 // return "Intelligence Minister"; //intelligence minister sounds too modern, considering the technology tree we have up to now only goes to the medieval era
2301 return "Spymaster";
2302 } else if (title_type == CharacterTitleInteriorMinister) {
2303 // return "Interior Minister"; //interior minister sounds too modern, considering the technology tree we have up to now only goes to the medieval era
2304 return "High Constable";
2305 } else if (title_type == CharacterTitleJusticeMinister) {
2306 // return "Justice Minister"; //justice minister sounds too modern, considering the technology tree we have up to now only goes to the medieval era
2307 return "Master of Laws";
2308 } else if (title_type == CharacterTitleWarMinister) {
2309 // return "War Minister"; //war minister sounds too modern, considering the technology tree we have up to now only goes to the medieval era
2310 return "Marshal";
2311 } else if (title_type == CharacterTitleGovernor) {
2312 return "Governor";
2313 } else if (title_type == CharacterTitleMayor) {
2314 return "Mayor";
2315 }
2316
2317 return "";
2318 }
2319
GetWorkerLandmasses(std::vector<int> & worker_landmasses,const CUnitType * building)2320 void CPlayer::GetWorkerLandmasses(std::vector<int>& worker_landmasses, const CUnitType *building)
2321 {
2322 for (unsigned int i = 0; i < AiHelpers.Build[building->Slot].size(); ++i) {
2323 if (this->GetUnitTypeAiActiveCount(AiHelpers.Build[building->Slot][i])) {
2324 std::vector<CUnit *> worker_table;
2325
2326 FindPlayerUnitsByType(*this, *AiHelpers.Build[building->Slot][i], worker_table, true);
2327
2328 for (size_t j = 0; j != worker_table.size(); ++j) {
2329 int worker_landmass = Map.GetTileLandmass(worker_table[j]->tilePos, worker_table[j]->MapLayer->ID);
2330 if (std::find(worker_landmasses.begin(), worker_landmasses.end(), worker_landmass) == worker_landmasses.end()) {
2331 worker_landmasses.push_back(worker_landmass);
2332 }
2333 }
2334 }
2335 }
2336 }
2337
GetResearchableUpgrades()2338 std::vector<CUpgrade *> CPlayer::GetResearchableUpgrades()
2339 {
2340 std::vector<CUpgrade *> researchable_upgrades;
2341 for (std::map<const CUnitType *, int>::iterator iterator = this->UnitTypesAiActiveCount.begin(); iterator != this->UnitTypesAiActiveCount.end(); ++iterator) {
2342 const CUnitType *type = iterator->first;
2343 if (type->Slot < ((int) AiHelpers.ResearchedUpgrades.size())) {
2344 for (size_t i = 0; i < AiHelpers.ResearchedUpgrades[type->Slot].size(); ++i) {
2345 CUpgrade *upgrade = AiHelpers.ResearchedUpgrades[type->Slot][i];
2346 if (std::find(researchable_upgrades.begin(), researchable_upgrades.end(), upgrade) == researchable_upgrades.end()) {
2347 researchable_upgrades.push_back(upgrade);
2348 }
2349 }
2350 }
2351 }
2352
2353 return researchable_upgrades;
2354 }
2355 //Wyrmgus end
2356
2357 /**
2358 ** Clear all player data excepts members which don't change.
2359 **
2360 ** The fields that are not cleared are
2361 ** UnitLimit, BuildingLimit, TotalUnitLimit and Allow.
2362 */
Clear()2363 void CPlayer::Clear()
2364 {
2365 Index = 0;
2366 Name.clear();
2367 Type = 0;
2368 Race = 0;
2369 Faction = -1;
2370 Religion = nullptr;
2371 Dynasty = nullptr;
2372 Age = nullptr;
2373 Overlord = nullptr;
2374 Vassals.clear();
2375 AiName.clear();
2376 Team = 0;
2377 Enemy = 0;
2378 Allied = 0;
2379 SharedVision = 0;
2380 StartPos.x = 0;
2381 StartPos.y = 0;
2382 //Wyrmgus start
2383 StartMapLayer = 0;
2384 //Wyrmgus end
2385 memset(Resources, 0, sizeof(Resources));
2386 memset(StoredResources, 0, sizeof(StoredResources));
2387 memset(MaxResources, 0, sizeof(MaxResources));
2388 memset(LastResources, 0, sizeof(LastResources));
2389 memset(Incomes, 0, sizeof(Incomes));
2390 memset(Revenue, 0, sizeof(Revenue));
2391 //Wyrmgus start
2392 memset(ResourceDemand, 0, sizeof(ResourceDemand));
2393 memset(StoredResourceDemand, 0, sizeof(StoredResourceDemand));
2394 //Wyrmgus end
2395 this->UnitTypesCount.clear();
2396 this->UnitTypesUnderConstructionCount.clear();
2397 this->UnitTypesAiActiveCount.clear();
2398 //Wyrmgus start
2399 this->Heroes.clear();
2400 this->Deities.clear();
2401 this->UnitsByType.clear();
2402 this->AiActiveUnitsByType.clear();
2403 this->AvailableQuests.clear();
2404 this->CurrentQuests.clear();
2405 this->CompletedQuests.clear();
2406 this->AutosellResources.clear();
2407 for (size_t i = 0; i < this->QuestObjectives.size(); ++i) {
2408 delete this->QuestObjectives[i];
2409 }
2410 this->QuestObjectives.clear();
2411 this->Modifiers.clear();
2412 //Wyrmgus end
2413 AiEnabled = false;
2414 //Wyrmgus start
2415 Revealed = false;
2416 //Wyrmgus end
2417 Ai = 0;
2418 this->Units.resize(0);
2419 this->FreeWorkers.resize(0);
2420 //Wyrmgus start
2421 this->LevelUpUnits.resize(0);
2422 //Wyrmgus end
2423 NumBuildings = 0;
2424 //Wyrmgus start
2425 NumBuildingsUnderConstruction = 0;
2426 NumTownHalls = 0;
2427 //Wyrmgus end
2428 Supply = 0;
2429 Demand = 0;
2430 TradeCost = 0;
2431 // FIXME: can't clear limits since it's initialized already
2432 // UnitLimit = 0;
2433 // BuildingLimit = 0;
2434 // TotalUnitLimit = 0;
2435 Score = 0;
2436 TotalUnits = 0;
2437 TotalBuildings = 0;
2438 memset(TotalResources, 0, sizeof(TotalResources));
2439 TotalRazings = 0;
2440 TotalKills = 0;
2441 //Wyrmgus start
2442 memset(UnitTypeKills, 0, sizeof(UnitTypeKills));
2443 LostTownHallTimer = 0;
2444 HeroCooldownTimer = 0;
2445 //Wyrmgus end
2446 Color = 0;
2447 UpgradeTimers.Clear();
2448 for (int i = 0; i < MaxCosts; ++i) {
2449 SpeedResourcesHarvest[i] = SPEEDUP_FACTOR;
2450 SpeedResourcesReturn[i] = SPEEDUP_FACTOR;
2451 //Wyrmgus start
2452 Prices[i] = CResource::Resources[i]->BasePrice;
2453 //Wyrmgus end
2454 }
2455 SpeedBuild = SPEEDUP_FACTOR;
2456 SpeedTrain = SPEEDUP_FACTOR;
2457 SpeedUpgrade = SPEEDUP_FACTOR;
2458 SpeedResearch = SPEEDUP_FACTOR;
2459 }
2460
2461
AddUnit(CUnit & unit)2462 void CPlayer::AddUnit(CUnit &unit)
2463 {
2464 Assert(unit.Player != this);
2465 Assert(unit.PlayerSlot == static_cast<size_t>(-1));
2466 unit.PlayerSlot = this->Units.size();
2467 this->Units.push_back(&unit);
2468 unit.Player = this;
2469 Assert(this->Units[unit.PlayerSlot] == &unit);
2470 }
2471
RemoveUnit(CUnit & unit)2472 void CPlayer::RemoveUnit(CUnit &unit)
2473 {
2474 Assert(unit.Player == this);
2475 //Wyrmgus start
2476 if (unit.PlayerSlot == -1 || this->Units[unit.PlayerSlot] != &unit) {
2477 fprintf(stderr, "Error in CPlayer::RemoveUnit: the unit's PlayerSlot doesn't match its position in the player's units array; Unit's PlayerSlot: %d, Unit Type: \"%s\".\n", unit.PlayerSlot, unit.Type ? unit.Type->Ident.c_str() : "");
2478 return;
2479 }
2480 //Wyrmgus end
2481 Assert(this->Units[unit.PlayerSlot] == &unit);
2482
2483 // unit.Player = nullptr; // we can remove dying unit...
2484 CUnit *last = this->Units.back();
2485
2486 this->Units[unit.PlayerSlot] = last;
2487 last->PlayerSlot = unit.PlayerSlot;
2488 this->Units.pop_back();
2489 unit.PlayerSlot = static_cast<size_t>(-1);
2490 Assert(last == &unit || this->Units[last->PlayerSlot] == last);
2491 }
2492
UpdateFreeWorkers()2493 void CPlayer::UpdateFreeWorkers()
2494 {
2495 FreeWorkers.clear();
2496 if (FreeWorkers.capacity() != 0) {
2497 // Just calling FreeWorkers.clear() is not always appropriate.
2498 // Certain paths may leave FreeWorkers in an invalid state, so
2499 // it's safer to re-initialize.
2500 std::vector<CUnit*>().swap(FreeWorkers);
2501 }
2502 const int nunits = this->GetUnitCount();
2503
2504 for (int i = 0; i < nunits; ++i) {
2505 CUnit &unit = this->GetUnit(i);
2506 //Wyrmgus start
2507 // if (unit.IsAlive() && unit.Type->BoolFlag[HARVESTER_INDEX].value && !unit.Removed) {
2508 if (unit.IsAlive() && unit.Type->BoolFlag[HARVESTER_INDEX].value && !unit.Removed && !unit.Type->BoolFlag[TRADER_INDEX].value) {
2509 //Wyrmgus end
2510 if (unit.CurrentAction() == UnitActionStill) {
2511 FreeWorkers.push_back(&unit);
2512 }
2513 }
2514 }
2515 }
2516
2517 //Wyrmgus start
PerformResourceTrade()2518 void CPlayer::PerformResourceTrade()
2519 {
2520 CUnit *market_unit = this->GetMarketUnit();
2521
2522 if (!market_unit) {
2523 return;
2524 }
2525
2526 for (size_t i = 0; i < this->AutosellResources.size(); ++i) {
2527 const int res = this->AutosellResources[i];
2528
2529 if ((this->Resources[res] + this->StoredResources[res]) >= 100) { //sell 100 per second, as long as there is enough of the resource stored
2530 market_unit->SellResource(res, this->Index);
2531 }
2532
2533 //increase price due to domestic demand
2534 this->StoredResourceDemand[res] += this->GetEffectiveResourceDemand(res);
2535 while (this->StoredResourceDemand[res] >= 100) {
2536 this->IncreaseResourcePrice(res);
2537 this->StoredResourceDemand[res] -= 100;
2538 }
2539 }
2540
2541 for (size_t i = 0; i < LuxuryResources.size(); ++i) {
2542 const int res = LuxuryResources[i];
2543
2544 while ((this->Resources[res] + this->StoredResources[res]) >= 100) {
2545 market_unit->SellResource(res, this->Index);
2546 }
2547
2548 //increase price due to domestic demand
2549 this->StoredResourceDemand[res] += this->GetEffectiveResourceDemand(res);
2550 while (this->StoredResourceDemand[res] >= 100) {
2551 this->IncreaseResourcePrice(res);
2552 this->StoredResourceDemand[res] -= 100;
2553 }
2554 }
2555 }
2556
2557 /**
2558 ** @brief Get whether the player has a market unit
2559 **
2560 ** @return True if the player has a market unit, or false otherwise
2561 */
HasMarketUnit() const2562 bool CPlayer::HasMarketUnit() const
2563 {
2564 const int n_m = AiHelpers.SellMarkets[0].size();
2565
2566 for (int i = 0; i < n_m; ++i) {
2567 CUnitType &market_type = *AiHelpers.SellMarkets[0][i];
2568
2569 if (this->GetUnitTypeCount(&market_type)) {
2570 return true;
2571 }
2572 }
2573
2574 return false;
2575 }
2576
2577 /**
2578 ** @brief Get the player's market unit, if any
2579 **
2580 ** @return The market unit if present, or null otherwise
2581 */
GetMarketUnit() const2582 CUnit *CPlayer::GetMarketUnit() const
2583 {
2584 CUnit *market_unit = nullptr;
2585
2586 const int n_m = AiHelpers.SellMarkets[0].size();
2587
2588 for (int i = 0; i < n_m; ++i) {
2589 CUnitType &market_type = *AiHelpers.SellMarkets[0][i];
2590
2591 if (this->GetUnitTypeCount(&market_type)) {
2592 std::vector<CUnit *> market_table;
2593 FindPlayerUnitsByType(*this, market_type, market_table);
2594 if (market_table.size() > 0) {
2595 market_unit = market_table[SyncRand() % market_table.size()];
2596 break;
2597 }
2598 }
2599 }
2600
2601 return market_unit;
2602 }
2603
GetAutosellResources() const2604 std::vector<int> CPlayer::GetAutosellResources() const
2605 {
2606 return this->AutosellResources;
2607 }
2608
AutosellResource(const int resource)2609 void CPlayer::AutosellResource(const int resource)
2610 {
2611 if (std::find(this->AutosellResources.begin(), this->AutosellResources.end(), resource) != this->AutosellResources.end()) {
2612 this->AutosellResources.erase(std::remove(this->AutosellResources.begin(), this->AutosellResources.end(), resource), this->AutosellResources.end());
2613 } else {
2614 this->AutosellResources.push_back(resource);
2615 }
2616 }
2617
UpdateLevelUpUnits()2618 void CPlayer::UpdateLevelUpUnits()
2619 {
2620 LevelUpUnits.clear();
2621 if (LevelUpUnits.capacity() != 0) {
2622 // Just calling LevelUpUnits.clear() is not always appropriate.
2623 // Certain paths may leave LevelUpUnits in an invalid state, so
2624 // it's safer to re-initialize.
2625 std::vector<CUnit*>().swap(LevelUpUnits);
2626 }
2627 const int nunits = this->GetUnitCount();
2628
2629 for (int i = 0; i < nunits; ++i) {
2630 CUnit &unit = this->GetUnit(i);
2631 if (unit.IsAlive() && unit.Variable[LEVELUP_INDEX].Value >= 1) {
2632 LevelUpUnits.push_back(&unit);
2633 }
2634 }
2635 }
2636
UpdateQuestPool()2637 void CPlayer::UpdateQuestPool()
2638 {
2639 if (CurrentCampaign == nullptr) { // in-game quests only while playing the campaign mode
2640 return;
2641 }
2642
2643 bool exausted_available_quests = (this->AvailableQuests.size() == 0);
2644
2645 this->AvailableQuests.clear();
2646
2647 std::vector<CQuest *> potential_quests;
2648 for (size_t i = 0; i < Quests.size(); ++i) {
2649 if (this->CanAcceptQuest(Quests[i])) {
2650 potential_quests.push_back(Quests[i]);
2651 }
2652 }
2653
2654 int max_quest_pool_size = 3 - ((int) this->CurrentQuests.size());
2655 for (int i = 0; i < max_quest_pool_size; ++i) { // fill the quest pool with up to three quests
2656 if (potential_quests.size() == 0) {
2657 break;
2658 }
2659 this->AvailableQuests.push_back(potential_quests[SyncRand(potential_quests.size())]);
2660 int available_quest_quantity = this->AvailableQuests.size();
2661 potential_quests.erase(std::remove(potential_quests.begin(), potential_quests.end(), this->AvailableQuests[available_quest_quantity - 1]), potential_quests.end());
2662 }
2663
2664 this->AvailableQuestsChanged();
2665
2666 // notify the player when new quests are available (but only if the player has already exausted the quests available to him, so that they aren't bothered if they choose not to engage with the quest system)
2667 if (this == ThisPlayer && GameCycle >= CYCLES_PER_MINUTE && this->AvailableQuests.size() > 0 && exausted_available_quests && this->NumTownHalls > 0) {
2668 ThisPlayer->Notify("%s", _("New quests available"));
2669 }
2670
2671 if (this->AiEnabled) { // if is an AI player, accept all quests that it can
2672 int available_quest_quantity = this->AvailableQuests.size();
2673 for (int i = (available_quest_quantity - 1); i >= 0; --i) {
2674 if (this->CanAcceptQuest(this->AvailableQuests[i])) { // something may have changed, so recheck if the player is able to accept the quest
2675 this->AcceptQuest(this->AvailableQuests[i]);
2676 }
2677 }
2678 }
2679 }
2680
AvailableQuestsChanged()2681 void CPlayer::AvailableQuestsChanged()
2682 {
2683 if (this == ThisPlayer) {
2684 for (int i = 0; i < (int) UnitButtonTable.size(); ++i) {
2685 if (UnitButtonTable[i]->Action != ButtonQuest || UnitButtonTable[i]->Value >= (int) this->AvailableQuests.size()) {
2686 continue;
2687 }
2688
2689 const CQuest *quest = this->AvailableQuests[UnitButtonTable[i]->Value];
2690 UnitButtonTable[i]->Hint = "Quest: " + quest->Name;
2691 UnitButtonTable[i]->Description = quest->Description + "\n \nObjectives:";
2692 for (size_t j = 0; j < quest->Objectives.size(); ++j) {
2693 UnitButtonTable[i]->Description += "\n- " + quest->Objectives[j]->ObjectiveString;
2694 }
2695 for (size_t j = 0; j < quest->ObjectiveStrings.size(); ++j) {
2696 UnitButtonTable[i]->Description += "\n" + quest->ObjectiveStrings[j];
2697 }
2698 if (!quest->Rewards.empty()) {
2699 UnitButtonTable[i]->Description += "\n \nRewards: " + quest->Rewards;
2700 }
2701 if (!quest->Hint.empty()) {
2702 UnitButtonTable[i]->Description += "\n \nHint: " + quest->Hint;
2703 }
2704 if (quest->HighestCompletedDifficulty > DifficultyNoDifficulty) {
2705 std::string highest_completed_difficulty;
2706 if (quest->HighestCompletedDifficulty == DifficultyEasy) {
2707 highest_completed_difficulty = "Easy";
2708 } else if (quest->HighestCompletedDifficulty == DifficultyNormal) {
2709 highest_completed_difficulty = "Normal";
2710 } else if (quest->HighestCompletedDifficulty == DifficultyHard) {
2711 highest_completed_difficulty = "Hard";
2712 } else if (quest->HighestCompletedDifficulty == DifficultyBrutal) {
2713 highest_completed_difficulty = "Brutal";
2714 }
2715 UnitButtonTable[i]->Description += "\n \nHighest Completed Difficulty: " + highest_completed_difficulty;
2716 }
2717
2718 }
2719
2720 if (!Selected.empty() && Selected[0]->Type->BoolFlag[TOWNHALL_INDEX].value) {
2721 UI.ButtonPanel.Update();
2722 }
2723 }
2724 }
2725
UpdateCurrentQuests()2726 void CPlayer::UpdateCurrentQuests()
2727 {
2728 for (size_t i = 0; i < this->QuestObjectives.size(); ++i) {
2729 CPlayerQuestObjective *objective = this->QuestObjectives[i];
2730 if (objective->ObjectiveType == HaveResourceObjectiveType) {
2731 objective->Counter = std::min(this->GetResource(objective->Resource, STORE_BOTH), objective->Quantity);
2732 } else if (objective->ObjectiveType == ResearchUpgradeObjectiveType) {
2733 objective->Counter = UpgradeIdAllowed(*this, objective->Upgrade->ID) == 'R' ? 1 : 0;
2734 } else if (objective->ObjectiveType == RecruitHeroObjectiveType) {
2735 objective->Counter = this->HasHero(objective->Character) ? 1 : 0;
2736 }
2737 }
2738
2739 for (int i = (this->CurrentQuests.size() - 1); i >= 0; --i) {
2740 std::string failed_quest = this->HasFailedQuest(this->CurrentQuests[i]);
2741 if (!failed_quest.empty()) {
2742 this->FailQuest(this->CurrentQuests[i], failed_quest);
2743 } else if (this->HasCompletedQuest(this->CurrentQuests[i])) {
2744 this->CompleteQuest(this->CurrentQuests[i]);
2745 }
2746 }
2747 }
2748
AcceptQuest(CQuest * quest)2749 void CPlayer::AcceptQuest(CQuest *quest)
2750 {
2751 if (!quest) {
2752 return;
2753 }
2754
2755 this->AvailableQuests.erase(std::remove(this->AvailableQuests.begin(), this->AvailableQuests.end(), quest), this->AvailableQuests.end());
2756 this->CurrentQuests.push_back(quest);
2757
2758 for (size_t i = 0; i < quest->Objectives.size(); ++i) {
2759 CPlayerQuestObjective *objective = new CPlayerQuestObjective;
2760 objective->ObjectiveType = quest->Objectives[i]->ObjectiveType;
2761 objective->Quest = quest->Objectives[i]->Quest;
2762 objective->ObjectiveString = quest->Objectives[i]->ObjectiveString;
2763 objective->Quantity = quest->Objectives[i]->Quantity;
2764 objective->Resource = quest->Objectives[i]->Resource;
2765 objective->UnitClass = quest->Objectives[i]->UnitClass;
2766 objective->UnitTypes = quest->Objectives[i]->UnitTypes;
2767 objective->Upgrade = quest->Objectives[i]->Upgrade;
2768 objective->Character = quest->Objectives[i]->Character;
2769 objective->Unique = quest->Objectives[i]->Unique;
2770 objective->Settlement = quest->Objectives[i]->Settlement;
2771 objective->Faction = quest->Objectives[i]->Faction;
2772 this->QuestObjectives.push_back(objective);
2773 }
2774
2775 CclCommand("trigger_player = " + std::to_string((long long) this->Index) + ";");
2776
2777 if (quest->AcceptEffects) {
2778 quest->AcceptEffects->pushPreamble();
2779 quest->AcceptEffects->run();
2780 }
2781
2782 this->AvailableQuestsChanged();
2783
2784 this->UpdateCurrentQuests();
2785 }
2786
CompleteQuest(CQuest * quest)2787 void CPlayer::CompleteQuest(CQuest *quest)
2788 {
2789 if (std::find(this->CompletedQuests.begin(), this->CompletedQuests.end(), quest) != this->CompletedQuests.end()) {
2790 return;
2791 }
2792
2793 RemoveCurrentQuest(quest);
2794
2795 this->CompletedQuests.push_back(quest);
2796 if (quest->Competitive) {
2797 quest->CurrentCompleted = true;
2798 }
2799
2800 CclCommand("trigger_player = " + std::to_string((long long) this->Index) + ";");
2801
2802 if (quest->CompletionEffects) {
2803 quest->CompletionEffects->pushPreamble();
2804 quest->CompletionEffects->run();
2805 }
2806
2807 if (this == ThisPlayer) {
2808 SetQuestCompleted(quest->Ident, GameSettings.Difficulty);
2809 SaveQuestCompletion();
2810 std::string rewards_string;
2811 if (!quest->Rewards.empty()) {
2812 rewards_string = "Rewards: " + quest->Rewards;
2813 }
2814 CclCommand("if (GenericDialog ~= nil) then GenericDialog(\"Quest Completed\", \"You have completed the " + quest->Name + " quest!\\n\\n" + rewards_string + "\", nil, \"" + quest->Icon.Name + "\", \"" + PlayerColorNames[quest->PlayerColor] + "\", " + std::to_string((long long) (quest->Icon.Icon ? quest->Icon.Icon->Frame : 0)) + ") end;");
2815 }
2816 }
2817
FailQuest(CQuest * quest,std::string fail_reason)2818 void CPlayer::FailQuest(CQuest *quest, std::string fail_reason)
2819 {
2820 this->RemoveCurrentQuest(quest);
2821
2822 CclCommand("trigger_player = " + std::to_string((long long) this->Index) + ";");
2823
2824 if (quest->FailEffects) {
2825 quest->FailEffects->pushPreamble();
2826 quest->FailEffects->run();
2827 }
2828
2829 if (this == ThisPlayer) {
2830 CclCommand("if (GenericDialog ~= nil) then GenericDialog(\"Quest Failed\", \"You have failed the " + quest->Name + " quest! " + fail_reason + "\", nil, \"" + quest->Icon.Name + "\", \"" + PlayerColorNames[quest->PlayerColor] + "\") end;");
2831 }
2832 }
2833
RemoveCurrentQuest(CQuest * quest)2834 void CPlayer::RemoveCurrentQuest(CQuest *quest)
2835 {
2836 this->CurrentQuests.erase(std::remove(this->CurrentQuests.begin(), this->CurrentQuests.end(), quest), this->CurrentQuests.end());
2837
2838 for (int i = (this->QuestObjectives.size() - 1); i >= 0; --i) {
2839 if (this->QuestObjectives[i]->Quest == quest) {
2840 this->QuestObjectives.erase(std::remove(this->QuestObjectives.begin(), this->QuestObjectives.end(), this->QuestObjectives[i]), this->QuestObjectives.end());
2841 }
2842 }
2843 }
2844
CanAcceptQuest(CQuest * quest)2845 bool CPlayer::CanAcceptQuest(CQuest *quest)
2846 {
2847 if (quest->Hidden || quest->CurrentCompleted || quest->Unobtainable) {
2848 return false;
2849 }
2850
2851 if (std::find(this->CurrentQuests.begin(), this->CurrentQuests.end(), quest) != this->CurrentQuests.end() || std::find(this->CompletedQuests.begin(), this->CompletedQuests.end(), quest) != this->CompletedQuests.end()) {
2852 return false;
2853 }
2854
2855 int recruit_heroes_quantity = 0;
2856 for (size_t i = 0; i < quest->Objectives.size(); ++i) {
2857 CQuestObjective *objective = quest->Objectives[i];
2858 if (objective->ObjectiveType == BuildUnitsObjectiveType || objective->ObjectiveType == BuildUnitsOfClassObjectiveType) {
2859 std::vector<const CUnitType *> unit_types = objective->UnitTypes;
2860 if (objective->ObjectiveType == BuildUnitsOfClassObjectiveType) {
2861 int unit_type_id = PlayerRaces.GetFactionClassUnitType(this->Faction, objective->UnitClass);
2862 if (unit_type_id == -1) {
2863 return false;
2864 }
2865 unit_types.clear();
2866 unit_types.push_back(UnitTypes[unit_type_id]);
2867 }
2868
2869 bool validated = false;
2870 for (const CUnitType *unit_type : unit_types) {
2871 if (objective->Settlement && !this->HasSettlement(objective->Settlement) && !unit_type->BoolFlag[TOWNHALL_INDEX].value) {
2872 continue;
2873 }
2874
2875 if (!this->HasUnitBuilder(unit_type, objective->Settlement) || !CheckDependencies(unit_type, this)) {
2876 continue;
2877 }
2878
2879 validated = true;
2880 }
2881
2882 if (!validated) {
2883 return false;
2884 }
2885 } else if (objective->ObjectiveType == ResearchUpgradeObjectiveType) {
2886 const CUpgrade *upgrade = objective->Upgrade;
2887
2888 bool has_researcher = this->HasUpgradeResearcher(upgrade);
2889
2890 if (!has_researcher && upgrade->ID < (int) AiHelpers.Research.size()) { //check if the quest includes an objective to build a researcher of the upgrade
2891 for (CQuestObjective *second_objective : quest->Objectives) {
2892 if (second_objective == objective) {
2893 continue;
2894 }
2895
2896 if (second_objective->ObjectiveType == BuildUnitsObjectiveType || second_objective->ObjectiveType == BuildUnitsOfClassObjectiveType) {
2897 std::vector<const CUnitType *> unit_types = second_objective->UnitTypes;
2898 if (second_objective->ObjectiveType == BuildUnitsOfClassObjectiveType) {
2899 int unit_type_id = PlayerRaces.GetFactionClassUnitType(this->Faction, second_objective->UnitClass);
2900 if (unit_type_id == -1) {
2901 continue;
2902 }
2903 unit_types.clear();
2904 unit_types.push_back(UnitTypes[unit_type_id]);
2905 }
2906
2907 for (const CUnitType *unit_type : unit_types) {
2908 if (std::find(AiHelpers.Research[upgrade->ID].begin(), AiHelpers.Research[upgrade->ID].end(), unit_type) != AiHelpers.Research[upgrade->ID].end()) { //if the unit type of the other objective is a researcher of this upgrade
2909 has_researcher = true;
2910 break;
2911 }
2912 }
2913
2914 if (has_researcher) {
2915 break;
2916 }
2917 }
2918 }
2919 }
2920
2921 if (!has_researcher || this->Allow.Upgrades[upgrade->ID] != 'A' || !CheckDependencies(upgrade, this)) {
2922 return false;
2923 }
2924 } else if (objective->ObjectiveType == RecruitHeroObjectiveType) {
2925 if (!this->CanRecruitHero(objective->Character, true)) {
2926 return false;
2927 }
2928 recruit_heroes_quantity++;
2929 } else if (objective->ObjectiveType == DestroyUnitsObjectiveType || objective->ObjectiveType == DestroyHeroObjectiveType || objective->ObjectiveType == DestroyUniqueObjectiveType) {
2930 if (objective->Faction) {
2931 CPlayer *faction_player = GetFactionPlayer(objective->Faction);
2932 if (faction_player == nullptr || faction_player->GetUnitCount() == 0) {
2933 return false;
2934 }
2935
2936 if (objective->Settlement && !faction_player->HasSettlement(objective->Settlement)) {
2937 return false;
2938 }
2939 }
2940
2941 if (objective->ObjectiveType == DestroyHeroObjectiveType) {
2942 if (objective->Character->CanAppear()) { //if the character "can appear" it doesn't already exist, and thus can't be destroyed
2943 return false;
2944 }
2945 } else if (objective->ObjectiveType == DestroyUniqueObjectiveType) {
2946 if (objective->Unique->CanDrop()) { //if the unique "can drop" it doesn't already exist, and thus can't be destroyed
2947 return false;
2948 }
2949 }
2950 } else if (objective->ObjectiveType == DestroyFactionObjectiveType) {
2951 CPlayer *faction_player = GetFactionPlayer(objective->Faction);
2952 if (faction_player == nullptr || faction_player->GetUnitCount() == 0) {
2953 return false;
2954 }
2955 }
2956 }
2957
2958 if (recruit_heroes_quantity > 0 && (this->Heroes.size() + recruit_heroes_quantity) > PlayerHeroMax) {
2959 return false;
2960 }
2961
2962 for (size_t i = 0; i < quest->HeroesMustSurvive.size(); ++i) {
2963 if (!this->HasHero(quest->HeroesMustSurvive[i])) {
2964 return false;
2965 }
2966 }
2967
2968 if (quest->Conditions) {
2969 CclCommand("trigger_player = " + std::to_string((long long) this->Index) + ";");
2970 quest->Conditions->pushPreamble();
2971 quest->Conditions->run(1);
2972 return quest->Conditions->popBoolean();
2973 } else {
2974 return true;
2975 }
2976 }
2977
HasCompletedQuest(CQuest * quest)2978 bool CPlayer::HasCompletedQuest(CQuest *quest)
2979 {
2980 if (quest->Uncompleteable) {
2981 return false;
2982 }
2983
2984 for (size_t i = 0; i < this->QuestObjectives.size(); ++i) {
2985 if (this->QuestObjectives[i]->Quest != quest) {
2986 continue;
2987 }
2988 if (this->QuestObjectives[i]->Quantity > 0 && this->QuestObjectives[i]->Counter < this->QuestObjectives[i]->Quantity) {
2989 return false;
2990 }
2991 }
2992
2993 return true;
2994 }
2995
HasFailedQuest(CQuest * quest)2996 std::string CPlayer::HasFailedQuest(CQuest *quest) // returns the reason for failure (empty if none)
2997 {
2998 for (size_t i = 0; i < quest->HeroesMustSurvive.size(); ++i) { // put it here, because "unfailable" quests should also fail when a hero which should survive dies
2999 if (!this->HasHero(quest->HeroesMustSurvive[i])) {
3000 return "A hero necessary for the quest has died.";
3001 }
3002 }
3003
3004 if (quest->CurrentCompleted) { // quest already completed by someone else
3005 return "Another faction has completed the quest before you could.";
3006 }
3007
3008 if (quest->Unfailable) {
3009 return "";
3010 }
3011
3012 for (size_t i = 0; i < this->QuestObjectives.size(); ++i) {
3013 CPlayerQuestObjective *objective = this->QuestObjectives[i];
3014 if (objective->Quest != quest) {
3015 continue;
3016 }
3017 if (objective->ObjectiveType == BuildUnitsObjectiveType || objective->ObjectiveType == BuildUnitsOfClassObjectiveType) {
3018 if (objective->Counter < objective->Quantity) {
3019 std::vector<const CUnitType *> unit_types = objective->UnitTypes;
3020 if (objective->ObjectiveType == BuildUnitsOfClassObjectiveType) {
3021 int unit_type_id = PlayerRaces.GetFactionClassUnitType(this->Faction, objective->UnitClass);
3022 if (unit_type_id == -1) {
3023 return "You can no longer produce the required unit.";
3024 }
3025 unit_types.clear();
3026 unit_types.push_back(UnitTypes[unit_type_id]);
3027 }
3028
3029 bool validated = false;
3030 std::string validation_error;
3031 for (const CUnitType *unit_type : unit_types) {
3032 if (objective->Settlement && !this->HasSettlement(objective->Settlement) && !unit_type->BoolFlag[TOWNHALL_INDEX].value) {
3033 validation_error = "You no longer hold the required settlement.";
3034 continue;
3035 }
3036
3037 if (!this->HasUnitBuilder(unit_type, objective->Settlement) || !CheckDependencies(unit_type, this)) {
3038 validation_error = "You can no longer produce the required unit.";
3039 continue;
3040 }
3041
3042 validated = true;
3043 }
3044
3045 if (!validated) {
3046 return validation_error;
3047 }
3048 }
3049 } else if (objective->ObjectiveType == ResearchUpgradeObjectiveType) {
3050 const CUpgrade *upgrade = objective->Upgrade;
3051
3052 if (this->Allow.Upgrades[upgrade->ID] != 'R') {
3053 bool has_researcher = this->HasUpgradeResearcher(upgrade);
3054
3055 if (!has_researcher && upgrade->ID < (int) AiHelpers.Research.size()) { //check if the quest includes an objective to build a researcher of the upgrade
3056 for (CPlayerQuestObjective *second_objective : this->QuestObjectives) {
3057 if (second_objective->Quest != quest || second_objective == objective || second_objective->Counter >= second_objective->Quantity) { //if the objective has been fulfilled, then there should be a researcher, if there isn't it is due to i.e. the researcher having been destroyed later on, or upgraded to another type, and then the quest should fail if the upgrade can no longer be researched
3058 continue;
3059 }
3060
3061 if (second_objective->ObjectiveType == BuildUnitsObjectiveType || second_objective->ObjectiveType == BuildUnitsOfClassObjectiveType) {
3062 std::vector<const CUnitType *> unit_types = second_objective->UnitTypes;
3063 if (second_objective->ObjectiveType == BuildUnitsOfClassObjectiveType) {
3064 int unit_type_id = PlayerRaces.GetFactionClassUnitType(this->Faction, second_objective->UnitClass);
3065 if (unit_type_id == -1) {
3066 continue;
3067 }
3068 unit_types.clear();
3069 unit_types.push_back(UnitTypes[unit_type_id]);
3070 }
3071
3072 for (const CUnitType *unit_type : unit_types) {
3073 if (std::find(AiHelpers.Research[upgrade->ID].begin(), AiHelpers.Research[upgrade->ID].end(), unit_type) != AiHelpers.Research[upgrade->ID].end()) { //if the unit type of the other objective is a researcher of this upgrade
3074 has_researcher = true;
3075 break;
3076 }
3077 }
3078
3079 if (has_researcher) {
3080 break;
3081 }
3082 }
3083 }
3084 }
3085
3086 if (!has_researcher || this->Allow.Upgrades[upgrade->ID] != 'A' || !CheckDependencies(upgrade, this)) {
3087 return "You can no longer research the required upgrade.";
3088 }
3089 }
3090 } else if (objective->ObjectiveType == RecruitHeroObjectiveType) {
3091 if (!this->HasHero(objective->Character) && !this->CanRecruitHero(objective->Character, true)) {
3092 return "The hero can no longer be recruited.";
3093 }
3094 } else if (objective->ObjectiveType == DestroyUnitsObjectiveType || objective->ObjectiveType == DestroyHeroObjectiveType || objective->ObjectiveType == DestroyUniqueObjectiveType) {
3095 if (objective->Faction && objective->Counter < objective->Quantity) {
3096 CPlayer *faction_player = GetFactionPlayer(objective->Faction);
3097 if (faction_player == nullptr || faction_player->GetUnitCount() == 0) {
3098 return "The target no longer exists.";
3099 }
3100
3101 if (objective->Settlement && !faction_player->HasSettlement(objective->Settlement)) {
3102 return "The target no longer exists.";
3103 }
3104 }
3105
3106 if (objective->ObjectiveType == DestroyHeroObjectiveType) {
3107 if (objective->Counter == 0 && objective->Character->CanAppear()) { // if is supposed to destroy a character, but it is nowhere to be found, fail the quest
3108 return "The target no longer exists.";
3109 }
3110 } else if (objective->ObjectiveType == DestroyUniqueObjectiveType) {
3111 if (objective->Counter == 0 && objective->Unique->CanDrop()) { // if is supposed to destroy a unique, but it is nowhere to be found, fail the quest
3112 return "The target no longer exists.";
3113 }
3114 }
3115 } else if (objective->ObjectiveType == DestroyFactionObjectiveType) {
3116 if (objective->Counter == 0) { // if is supposed to destroy a faction, but it is nowhere to be found, fail the quest
3117 CPlayer *faction_player = GetFactionPlayer(objective->Faction);
3118 if (faction_player == nullptr || faction_player->GetUnitCount() == 0) {
3119 return "The target no longer exists.";
3120 }
3121 }
3122 }
3123 }
3124
3125 return "";
3126 }
3127
AddModifier(CUpgrade * modifier,int cycles)3128 void CPlayer::AddModifier(CUpgrade *modifier, int cycles)
3129 {
3130 if (this->Allow.Upgrades[modifier->ID] == 'R') {
3131 for (size_t i = 0; i < this->Modifiers.size(); ++i) { //if already has the modifier, make it have the greater duration of the new or old one
3132 if (this->Modifiers[i].first == modifier) {
3133 this->Modifiers[i].second = std::max(this->Modifiers[i].second, (int) (GameCycle + cycles));
3134 }
3135 }
3136 } else {
3137 this->Modifiers.push_back(std::pair<CUpgrade *, int>(modifier, GameCycle + cycles));
3138 UpgradeAcquire(*this, modifier);
3139 }
3140
3141 }
3142
RemoveModifier(CUpgrade * modifier)3143 void CPlayer::RemoveModifier(CUpgrade *modifier)
3144 {
3145 if (this->Allow.Upgrades[modifier->ID] == 'R') {
3146 UpgradeLost(*this, modifier->ID);
3147 for (size_t i = 0; i < this->Modifiers.size(); ++i) { //if already has the modifier, make it have the greater duration of the new or old one
3148 if (this->Modifiers[i].first == modifier) {
3149 this->Modifiers.erase(std::remove(this->Modifiers.begin(), this->Modifiers.end(), this->Modifiers[i]), this->Modifiers.end());
3150 break;
3151 }
3152 }
3153 }
3154
3155 }
3156
AtPeace() const3157 bool CPlayer::AtPeace() const
3158 {
3159 for (int i = 0; i < PlayerNumNeutral; ++i) {
3160 if (this->IsEnemy(Players[i]) && this->HasContactWith(Players[i]) && Players[i].GetUnitCount() > 0) {
3161 return false;
3162 }
3163 }
3164
3165 return true;
3166 }
3167 //Wyrmgus end
3168
UnitBegin() const3169 std::vector<CUnit *>::const_iterator CPlayer::UnitBegin() const
3170 {
3171 return Units.begin();
3172 }
3173
UnitBegin()3174 std::vector<CUnit *>::iterator CPlayer::UnitBegin()
3175 {
3176 return Units.begin();
3177 }
3178
UnitEnd() const3179 std::vector<CUnit *>::const_iterator CPlayer::UnitEnd() const
3180 {
3181 return Units.end();
3182 }
3183
UnitEnd()3184 std::vector<CUnit *>::iterator CPlayer::UnitEnd()
3185 {
3186 return Units.end();
3187 }
3188
GetUnit(int index) const3189 CUnit &CPlayer::GetUnit(int index) const
3190 {
3191 return *Units[index];
3192 }
3193
GetUnitCount() const3194 int CPlayer::GetUnitCount() const
3195 {
3196 return static_cast<int>(Units.size());
3197 }
3198
3199
3200
3201 /*----------------------------------------------------------------------------
3202 -- Resource management
3203 ----------------------------------------------------------------------------*/
3204
3205 /**
3206 ** Gets the player resource.
3207 **
3208 ** @param resource Resource to get.
3209 ** @param type Storing type
3210 **
3211 ** @note Storing types: 0 - overall store, 1 - store buildings, 2 - both
3212 */
GetResource(const int resource,const int type)3213 int CPlayer::GetResource(const int resource, const int type)
3214 {
3215 switch (type) {
3216 case STORE_OVERALL:
3217 return this->Resources[resource];
3218 case STORE_BUILDING:
3219 return this->StoredResources[resource];
3220 case STORE_BOTH:
3221 return this->Resources[resource] + this->StoredResources[resource];
3222 default:
3223 DebugPrint("Wrong resource type\n");
3224 return -1;
3225 }
3226 }
3227
3228 /**
3229 ** Adds/subtracts some resources to/from the player store
3230 **
3231 ** @param resource Resource to add/subtract.
3232 ** @param value How many of this resource (can be negative).
3233 ** @param store If true, sets the building store resources, else the overall resources.
3234 */
ChangeResource(const int resource,const int value,const bool store)3235 void CPlayer::ChangeResource(const int resource, const int value, const bool store)
3236 {
3237 if (value < 0) {
3238 const int fromStore = std::min(this->StoredResources[resource], abs(value));
3239 this->StoredResources[resource] -= fromStore;
3240 this->Resources[resource] -= abs(value) - fromStore;
3241 this->Resources[resource] = std::max(this->Resources[resource], 0);
3242 } else {
3243 if (store && this->MaxResources[resource] != -1) {
3244 this->StoredResources[resource] += std::min(value, this->MaxResources[resource] - this->StoredResources[resource]);
3245 } else {
3246 this->Resources[resource] += value;
3247 }
3248 }
3249 }
3250
3251 /**
3252 ** Change the player resource.
3253 **
3254 ** @param resource Resource to change.
3255 ** @param value How many of this resource.
3256 ** @param type Resource types: 0 - overall store, 1 - store buildings, 2 - both
3257 */
SetResource(const int resource,const int value,const int type)3258 void CPlayer::SetResource(const int resource, const int value, const int type)
3259 {
3260 if (type == STORE_BOTH) {
3261 if (this->MaxResources[resource] != -1) {
3262 const int toRes = std::max(0, value - this->StoredResources[resource]);
3263 this->Resources[resource] = std::max(0, toRes);
3264 this->StoredResources[resource] = std::min(value - toRes, this->MaxResources[resource]);
3265 } else {
3266 this->Resources[resource] = value;
3267 }
3268 } else if (type == STORE_BUILDING && this->MaxResources[resource] != -1) {
3269 this->StoredResources[resource] = std::min(value, this->MaxResources[resource]);
3270 } else if (type == STORE_OVERALL) {
3271 this->Resources[resource] = value;
3272 }
3273 }
3274
3275 /**
3276 ** Check, if there enough resources for action.
3277 **
3278 ** @param resource Resource to change.
3279 ** @param value How many of this resource.
3280 */
CheckResource(const int resource,const int value)3281 bool CPlayer::CheckResource(const int resource, const int value)
3282 {
3283 int result = this->Resources[resource];
3284 if (this->MaxResources[resource] != -1) {
3285 result += this->StoredResources[resource];
3286 }
3287 return result < value ? false : true;
3288 }
3289
3290 //Wyrmgus start
3291 /**
3292 ** Increase resource price
3293 **
3294 ** @param resource Resource.
3295 */
IncreaseResourcePrice(const int resource)3296 void CPlayer::IncreaseResourcePrice(const int resource)
3297 {
3298 int price_change = CResource::Resources[resource]->BasePrice / std::max(this->Prices[resource], 100);
3299 price_change = std::max(1, price_change);
3300 this->Prices[resource] += price_change;
3301 }
3302
3303 /**
3304 ** Decrease resource price
3305 **
3306 ** @param resource Resource.
3307 */
DecreaseResourcePrice(const int resource)3308 void CPlayer::DecreaseResourcePrice(const int resource)
3309 {
3310 int price_change = this->Prices[resource] / CResource::Resources[resource]->BasePrice;
3311 price_change = std::max(1, price_change);
3312 this->Prices[resource] -= price_change;
3313 this->Prices[resource] = std::max(1, this->Prices[resource]);
3314 }
3315
3316 /**
3317 ** Converges prices with another player (and returns how many convergences were effected)
3318 */
ConvergePricesWith(CPlayer & player,int max_convergences)3319 int CPlayer::ConvergePricesWith(CPlayer &player, int max_convergences)
3320 {
3321 int convergences = 0;
3322
3323 bool converged = true;
3324 while (converged) {
3325 converged = false;
3326
3327 for (int i = 1; i < MaxCosts; ++i) {
3328 if (!CResource::Resources[i]->BasePrice) {
3329 continue;
3330 }
3331
3332 int convergence_increase = 100;
3333
3334 if (this->Prices[i] < player.Prices[i] && convergences < max_convergences) {
3335 this->IncreaseResourcePrice(i);
3336 convergences += convergence_increase;
3337 converged = true;
3338
3339 if (this->Prices[i] < player.Prices[i] && convergences < max_convergences) { //now do the convergence for the other side as well, if possible
3340 player.DecreaseResourcePrice(i);
3341 convergences += convergence_increase;
3342 converged = true;
3343 }
3344 } else if (this->Prices[i] > player.Prices[i] && convergences < max_convergences) {
3345 this->DecreaseResourcePrice(i);
3346 convergences += convergence_increase;
3347 converged = true;
3348
3349 if (this->Prices[i] > player.Prices[i] && convergences < max_convergences) { //do the convergence for the other side as well, if possible
3350 player.IncreaseResourcePrice(i);
3351 convergences += convergence_increase;
3352 converged = true;
3353 }
3354 }
3355 }
3356 }
3357
3358 return convergences;
3359 }
3360
3361 /**
3362 ** Get the price of a resource for the player
3363 **
3364 ** @param resource Resource.
3365 */
GetResourcePrice(const int resource) const3366 int CPlayer::GetResourcePrice(const int resource) const
3367 {
3368 if (resource == CopperCost) {
3369 return 100;
3370 }
3371
3372 return this->Prices[resource];
3373 }
3374
3375 /**
3376 ** Get the effective resource demand for the player, given the current prices
3377 **
3378 ** @param resource Resource.
3379 */
GetEffectiveResourceDemand(const int resource) const3380 int CPlayer::GetEffectiveResourceDemand(const int resource) const
3381 {
3382 int resource_demand = this->ResourceDemand[resource];
3383
3384 if (this->Prices[resource]) {
3385 resource_demand *= CResource::Resources[resource]->BasePrice;
3386 resource_demand /= this->Prices[resource];
3387 }
3388
3389 if (CResource::Resources[resource]->DemandElasticity != 100) {
3390 resource_demand = this->ResourceDemand[resource] + ((resource_demand - this->ResourceDemand[resource]) * CResource::Resources[resource]->DemandElasticity / 100);
3391 }
3392
3393 resource_demand = std::max(resource_demand, 0);
3394
3395 return resource_demand;
3396 }
3397
3398 /**
3399 ** Get the effective sell price of a resource
3400 */
GetEffectiveResourceSellPrice(const int resource,int traded_quantity) const3401 int CPlayer::GetEffectiveResourceSellPrice(const int resource, int traded_quantity) const
3402 {
3403 if (resource == CopperCost) {
3404 return 100;
3405 }
3406
3407 int price = traded_quantity * this->Prices[resource] / 100 * (100 - this->TradeCost) / 100;
3408 price = std::max(1, price);
3409 return price;
3410 }
3411
3412 /**
3413 ** Get the effective buy quantity of a resource
3414 */
GetEffectiveResourceBuyPrice(const int resource,int traded_quantity) const3415 int CPlayer::GetEffectiveResourceBuyPrice(const int resource, int traded_quantity) const
3416 {
3417 int price = traded_quantity * this->Prices[resource] / 100 * 100 / (100 - this->TradeCost);
3418 price = std::max(1, price);
3419 return price;
3420 }
3421
3422 /**
3423 ** Get the total price difference between this player and another one
3424 */
GetTotalPriceDifferenceWith(const CPlayer & player) const3425 int CPlayer::GetTotalPriceDifferenceWith(const CPlayer &player) const
3426 {
3427 int difference = 0;
3428 for (int i = 1; i < MaxCosts; ++i) {
3429 if (!CResource::Resources[i]->BasePrice) {
3430 continue;
3431 }
3432 difference += abs(this->Prices[i] - player.Prices[i]);
3433 }
3434
3435 return difference;
3436 }
3437
3438 /**
3439 ** Get the trade potential between this player and another one
3440 */
GetTradePotentialWith(const CPlayer & player) const3441 int CPlayer::GetTradePotentialWith(const CPlayer &player) const
3442 {
3443 int trade_potential = 0;
3444 for (int i = 1; i < MaxCosts; ++i) {
3445 if (!CResource::Resources[i]->BasePrice) {
3446 continue;
3447 }
3448 int price_difference = abs(this->Prices[i] - player.Prices[i]);
3449 trade_potential += price_difference * 100;
3450 }
3451
3452 trade_potential = std::max(trade_potential, 10);
3453
3454 return trade_potential;
3455 }
3456 //Wyrmgus end
3457
GetUnitTotalCount(const CUnitType & type) const3458 int CPlayer::GetUnitTotalCount(const CUnitType &type) const
3459 {
3460 int count = this->GetUnitTypeCount(&type);
3461 for (std::vector<CUnit *>::const_iterator it = this->UnitBegin(); it != this->UnitEnd(); ++it) {
3462 //Wyrmgus start
3463 if (*it == nullptr) {
3464 fprintf(stderr, "Error in CPlayer::GetUnitTotalCount: unit of player %d is null.\n", this->Index);
3465 continue;
3466 }
3467 //Wyrmgus end
3468 CUnit &unit = **it;
3469
3470 if (unit.CurrentAction() == UnitActionUpgradeTo) {
3471 COrder_UpgradeTo &order = dynamic_cast<COrder_UpgradeTo &>(*unit.CurrentOrder());
3472 if (order.GetUnitType().Slot == type.Slot) {
3473 ++count;
3474 }
3475 }
3476 }
3477 return count;
3478 }
3479
3480 /**
3481 ** Check if the unit-type didn't break any unit limits.
3482 **
3483 ** @param type Type of unit.
3484 **
3485 ** @return True if enough, negative on problem.
3486 **
3487 ** @note The return values of the PlayerCheck functions are inconsistent.
3488 */
CheckLimits(const CUnitType & type) const3489 int CPlayer::CheckLimits(const CUnitType &type) const
3490 {
3491 // Check game limits.
3492 if (type.BoolFlag[BUILDING_INDEX].value && NumBuildings >= BuildingLimit) {
3493 Notify("%s", _("Building Limit Reached"));
3494 return -1;
3495 }
3496 if (!type.BoolFlag[BUILDING_INDEX].value && (this->GetUnitCount() - NumBuildings) >= UnitLimit) {
3497 Notify("%s", _("Unit Limit Reached"));
3498 return -2;
3499 }
3500 //Wyrmgus start
3501 // if (this->Demand + type.Stats[this->Index].Variables[DEMAND_INDEX].Value > this->Supply && type.Stats[this->Index].Variables[DEMAND_INDEX].Value) {
3502 if (this->Demand + (type.Stats[this->Index].Variables[DEMAND_INDEX].Value * (type.TrainQuantity ? type.TrainQuantity : 1)) > this->Supply && type.Stats[this->Index].Variables[DEMAND_INDEX].Value) {
3503 //Wyrmgus end
3504 //Wyrmgus start
3505 // Notify("%s", _("Insufficient Supply, increase Supply."));
3506 Notify("%s", _("Insufficient Food Supply, increase Food Supply."));
3507 //Wyrmgus end
3508 return -3;
3509 }
3510 if (this->GetUnitCount() >= TotalUnitLimit) {
3511 Notify("%s", _("Total Unit Limit Reached"));
3512 return -4;
3513 }
3514 if (GetUnitTotalCount(type) >= Allow.Units[type.Slot]) {
3515 Notify(_("Limit of %d reached for this unit type"), Allow.Units[type.Slot]);
3516 return -6;
3517 }
3518 return 1;
3519 }
3520
3521 /**
3522 ** Check if enough resources for are available.
3523 **
3524 ** @param costs How many costs.
3525 **
3526 ** @return False if all enough, otherwise a bit mask.
3527 **
3528 ** @note The return values of the PlayerCheck functions are inconsistent.
3529 */
CheckCosts(const int * costs,bool notify) const3530 int CPlayer::CheckCosts(const int *costs, bool notify) const
3531 {
3532 //Wyrmgus start
3533 bool sound_played = false;
3534 //Wyrmgus end
3535 int err = 0;
3536 for (int i = 1; i < MaxCosts; ++i) {
3537 if (this->Resources[i] + this->StoredResources[i] >= costs[i]) {
3538 continue;
3539 }
3540 if (notify) {
3541 const char *name = DefaultResourceNames[i].c_str();
3542 const char *actionName = CResource::Resources[i]->ActionName.c_str();
3543
3544 //Wyrmgus start
3545 // Notify(_("Not enough %s...%s more %s."), _(name), _(actionName), _(name));
3546 Notify(_("Not enough %s... %s more %s."), _(name), _(actionName), _(name)); //added extra space to look better
3547 //Wyrmgus end
3548
3549 //Wyrmgus start
3550 // if (this == ThisPlayer && GameSounds.NotEnoughRes[this->Race][i].Sound) {
3551 if (this == ThisPlayer && GameSounds.NotEnoughRes[this->Race][i].Sound && !sound_played) {
3552 sound_played = true;
3553 //Wyrmgus end
3554 PlayGameSound(GameSounds.NotEnoughRes[this->Race][i].Sound, MaxSampleVolume);
3555 }
3556 }
3557 err |= 1 << i;
3558 }
3559 return err;
3560 }
3561
3562 /**
3563 ** Check if enough resources for new unit is available.
3564 **
3565 ** @param type Type of unit.
3566 **
3567 ** @return False if all enough, otherwise a bit mask.
3568 */
3569 //Wyrmgus start
3570 //int CPlayer::CheckUnitType(const CUnitType &type) const
CheckUnitType(const CUnitType & type,bool hire) const3571 int CPlayer::CheckUnitType(const CUnitType &type, bool hire) const
3572 //Wyrmgus end
3573 {
3574 //Wyrmgus start
3575 // return this->CheckCosts(type.Stats[this->Index].Costs);
3576 int type_costs[MaxCosts];
3577 this->GetUnitTypeCosts(&type, type_costs, hire);
3578 return this->CheckCosts(type_costs);
3579 //Wyrmgus end
3580 }
3581
3582 /**
3583 ** Add costs to the resources
3584 **
3585 ** @param costs How many costs.
3586 */
AddCosts(const int * costs)3587 void CPlayer::AddCosts(const int *costs)
3588 {
3589 for (int i = 1; i < MaxCosts; ++i) {
3590 ChangeResource(i, costs[i], false);
3591 }
3592 }
3593
3594 /**
3595 ** Add the costs of an unit type to resources
3596 **
3597 ** @param type Type of unit.
3598 */
3599 //Wyrmgus start
3600 //void CPlayer::AddUnitType(const CUnitType &type)
AddUnitType(const CUnitType & type,bool hire)3601 void CPlayer::AddUnitType(const CUnitType &type, bool hire)
3602 //Wyrmgus end
3603 {
3604 //Wyrmgus start
3605 // AddCosts(type.Stats[this->Index].Costs);
3606 int type_costs[MaxCosts];
3607 this->GetUnitTypeCosts(&type, type_costs, hire);
3608 AddCostsFactor(type_costs, 100);
3609 //Wyrmgus end
3610 }
3611
3612 /**
3613 ** Add a factor of costs to the resources
3614 **
3615 ** @param costs How many costs.
3616 ** @param factor Factor of the costs to apply.
3617 */
AddCostsFactor(const int * costs,int factor)3618 void CPlayer::AddCostsFactor(const int *costs, int factor)
3619 {
3620 if (!factor) {
3621 return;
3622 }
3623
3624 for (int i = 1; i < MaxCosts; ++i) {
3625 ChangeResource(i, costs[i] * factor / 100, true);
3626 }
3627 }
3628
3629 /**
3630 ** Subtract costs from the resources
3631 **
3632 ** @param costs How many costs.
3633 */
SubCosts(const int * costs)3634 void CPlayer::SubCosts(const int *costs)
3635 {
3636 for (int i = 1; i < MaxCosts; ++i) {
3637 ChangeResource(i, -costs[i], true);
3638 }
3639 }
3640
3641 /**
3642 ** Subtract the costs of new unit from resources
3643 **
3644 ** @param type Type of unit.
3645 */
3646 //Wyrmgus start
3647 //void CPlayer::SubUnitType(const CUnitType &type)
SubUnitType(const CUnitType & type,bool hire)3648 void CPlayer::SubUnitType(const CUnitType &type, bool hire)
3649 //Wyrmgus end
3650 {
3651 //Wyrmgus start
3652 // this->SubCosts(type.Stats[this->Index].Costs);
3653 int type_costs[MaxCosts];
3654 this->GetUnitTypeCosts(&type, type_costs, hire);
3655 this->SubCostsFactor(type_costs, 100);
3656 //Wyrmgus end
3657 }
3658
3659 /**
3660 ** Subtract a factor of costs from the resources
3661 **
3662 ** @param costs How many costs.
3663 ** @param factor Factor of the costs to apply.
3664 */
SubCostsFactor(const int * costs,int factor)3665 void CPlayer::SubCostsFactor(const int *costs, int factor)
3666 {
3667 for (int i = 1; i < MaxCosts; ++i) {
3668 ChangeResource(i, -costs[i] * 100 / factor);
3669 }
3670 }
3671
3672 //Wyrmgus start
3673 /**
3674 ** Gives the cost of a unit type for the player
3675 */
GetUnitTypeCosts(const CUnitType * type,int * type_costs,bool hire,bool ignore_one) const3676 void CPlayer::GetUnitTypeCosts(const CUnitType *type, int *type_costs, bool hire, bool ignore_one) const
3677 {
3678 for (int i = 0; i < MaxCosts; ++i) {
3679 type_costs[i] = 0;
3680 }
3681 if (hire) {
3682 type_costs[CopperCost] = type->Stats[this->Index].GetPrice();
3683 } else {
3684 for (int i = 0; i < MaxCosts; ++i) {
3685 type_costs[i] = type->Stats[this->Index].Costs[i];
3686 }
3687 }
3688 for (int i = 0; i < MaxCosts; ++i) {
3689 if (type->TrainQuantity) {
3690 type_costs[i] *= type->TrainQuantity;
3691 }
3692 if (type->CostModifier) {
3693 int type_count = this->GetUnitTypeCount(type) + this->GetUnitTypeUnderConstructionCount(type);
3694 if (ignore_one) {
3695 type_count--;
3696 }
3697 for (int j = 0; j < type_count; ++j) {
3698 type_costs[i] *= 100 + type->CostModifier;
3699 type_costs[i] /= 100;
3700 }
3701 }
3702 }
3703 }
3704
GetUnitTypeCostsMask(const CUnitType * type,bool hire) const3705 int CPlayer::GetUnitTypeCostsMask(const CUnitType *type, bool hire) const
3706 {
3707 int costs_mask = 0;
3708
3709 int type_costs[MaxCosts];
3710 AiPlayer->Player->GetUnitTypeCosts(type, type_costs, hire);
3711
3712 for (int i = 1; i < MaxCosts; ++i) {
3713 if (type_costs[i] > 0) {
3714 costs_mask |= 1 << i;
3715 }
3716 }
3717
3718 return costs_mask;
3719 }
3720
3721 /**
3722 ** Gives the cost of an upgrade for the player
3723 */
GetUpgradeCosts(const CUpgrade * upgrade,int * upgrade_costs)3724 void CPlayer::GetUpgradeCosts(const CUpgrade *upgrade, int *upgrade_costs)
3725 {
3726 for (int i = 0; i < MaxCosts; ++i) {
3727 upgrade_costs[i] = upgrade->Costs[i];
3728 for (size_t j = 0; j < upgrade->ScaledCostUnits.size(); ++j) {
3729 upgrade_costs[i] += upgrade->ScaledCosts[i] * this->GetUnitTypeCount(upgrade->ScaledCostUnits[j]);
3730 }
3731 }
3732 }
3733
GetUpgradeCostsMask(const CUpgrade * upgrade) const3734 int CPlayer::GetUpgradeCostsMask(const CUpgrade *upgrade) const
3735 {
3736 int costs_mask = 0;
3737
3738 int upgrade_costs[MaxCosts];
3739 AiPlayer->Player->GetUpgradeCosts(upgrade, upgrade_costs);
3740
3741 for (int i = 1; i < MaxCosts; ++i) {
3742 if (upgrade_costs[i] > 0) {
3743 costs_mask |= 1 << i;
3744 }
3745 }
3746
3747 return costs_mask;
3748 }
3749
3750 //Wyrmgus end
3751
SetUnitTypeCount(const CUnitType * type,int quantity)3752 void CPlayer::SetUnitTypeCount(const CUnitType *type, int quantity)
3753 {
3754 if (!type) {
3755 return;
3756 }
3757
3758 if (quantity <= 0) {
3759 if (this->UnitTypesCount.find(type) != this->UnitTypesCount.end()) {
3760 this->UnitTypesCount.erase(type);
3761 }
3762 } else {
3763 this->UnitTypesCount[type] = quantity;
3764 }
3765 }
3766
ChangeUnitTypeCount(const CUnitType * type,int quantity)3767 void CPlayer::ChangeUnitTypeCount(const CUnitType *type, int quantity)
3768 {
3769 this->SetUnitTypeCount(type, this->GetUnitTypeCount(type) + quantity);
3770 }
3771
GetUnitTypeCount(const CUnitType * type) const3772 int CPlayer::GetUnitTypeCount(const CUnitType *type) const
3773 {
3774 if (type && this->UnitTypesCount.find(type) != this->UnitTypesCount.end()) {
3775 return this->UnitTypesCount.find(type)->second;
3776 } else {
3777 return 0;
3778 }
3779 }
3780
SetUnitTypeUnderConstructionCount(const CUnitType * type,int quantity)3781 void CPlayer::SetUnitTypeUnderConstructionCount(const CUnitType *type, int quantity)
3782 {
3783 if (!type) {
3784 return;
3785 }
3786
3787 if (quantity <= 0) {
3788 if (this->UnitTypesUnderConstructionCount.find(type) != this->UnitTypesUnderConstructionCount.end()) {
3789 this->UnitTypesUnderConstructionCount.erase(type);
3790 }
3791 } else {
3792 this->UnitTypesUnderConstructionCount[type] = quantity;
3793 }
3794 }
3795
ChangeUnitTypeUnderConstructionCount(const CUnitType * type,int quantity)3796 void CPlayer::ChangeUnitTypeUnderConstructionCount(const CUnitType *type, int quantity)
3797 {
3798 this->SetUnitTypeUnderConstructionCount(type, this->GetUnitTypeUnderConstructionCount(type) + quantity);
3799 }
3800
GetUnitTypeUnderConstructionCount(const CUnitType * type) const3801 int CPlayer::GetUnitTypeUnderConstructionCount(const CUnitType *type) const
3802 {
3803 if (type && this->UnitTypesUnderConstructionCount.find(type) != this->UnitTypesUnderConstructionCount.end()) {
3804 return this->UnitTypesUnderConstructionCount.find(type)->second;
3805 } else {
3806 return 0;
3807 }
3808 }
3809
SetUnitTypeAiActiveCount(const CUnitType * type,int quantity)3810 void CPlayer::SetUnitTypeAiActiveCount(const CUnitType *type, int quantity)
3811 {
3812 if (!type) {
3813 return;
3814 }
3815
3816 if (quantity <= 0) {
3817 if (this->UnitTypesAiActiveCount.find(type) != this->UnitTypesAiActiveCount.end()) {
3818 this->UnitTypesAiActiveCount.erase(type);
3819 }
3820 } else {
3821 this->UnitTypesAiActiveCount[type] = quantity;
3822 }
3823 }
3824
ChangeUnitTypeAiActiveCount(const CUnitType * type,int quantity)3825 void CPlayer::ChangeUnitTypeAiActiveCount(const CUnitType *type, int quantity)
3826 {
3827 this->SetUnitTypeAiActiveCount(type, this->GetUnitTypeAiActiveCount(type) + quantity);
3828 }
3829
GetUnitTypeAiActiveCount(const CUnitType * type) const3830 int CPlayer::GetUnitTypeAiActiveCount(const CUnitType *type) const
3831 {
3832 if (type && this->UnitTypesAiActiveCount.find(type) != this->UnitTypesAiActiveCount.end()) {
3833 return this->UnitTypesAiActiveCount.find(type)->second;
3834 } else {
3835 return 0;
3836 }
3837 }
3838
IncreaseCountsForUnit(CUnit * unit,bool type_change)3839 void CPlayer::IncreaseCountsForUnit(CUnit *unit, bool type_change)
3840 {
3841 const CUnitType *type = unit->Type;
3842
3843 this->ChangeUnitTypeCount(type, 1);
3844 this->UnitsByType[type].push_back(unit);
3845
3846 if (unit->Active) {
3847 this->ChangeUnitTypeAiActiveCount(type, 1);
3848 this->AiActiveUnitsByType[type].push_back(unit);
3849 }
3850
3851 if (type->BoolFlag[TOWNHALL_INDEX].value) {
3852 this->NumTownHalls++;
3853 }
3854
3855 for (int i = 0; i < MaxCosts; ++i) {
3856 this->ResourceDemand[i] += type->Stats[this->Index].ResourceDemand[i];
3857 }
3858
3859 if (this->AiEnabled && type->BoolFlag[COWARD_INDEX].value && !type->BoolFlag[HARVESTER_INDEX].value && !type->CanTransport() && type->Spells.size() == 0 && Map.Info.IsPointOnMap(unit->tilePos, unit->MapLayer) && unit->CanMove() && unit->Active && unit->GroupId != 0 && unit->Variable[SIGHTRANGE_INDEX].Value > 0) { //assign coward, non-worker, non-transporter, non-spellcaster units to be scouts
3860 this->Ai->Scouts.push_back(unit);
3861 }
3862
3863 if (!type_change) {
3864 if (unit->Character != nullptr) {
3865 this->Heroes.push_back(unit);
3866 }
3867 }
3868 }
3869
DecreaseCountsForUnit(CUnit * unit,bool type_change)3870 void CPlayer::DecreaseCountsForUnit(CUnit *unit, bool type_change)
3871 {
3872 const CUnitType *type = unit->Type;
3873
3874 this->ChangeUnitTypeCount(type, -1);
3875
3876 this->UnitsByType[type].erase(std::remove(this->UnitsByType[type].begin(), this->UnitsByType[type].end(), unit), this->UnitsByType[type].end());
3877
3878 if (this->UnitsByType[type].empty()) {
3879 this->UnitsByType.erase(type);
3880 }
3881
3882 if (unit->Active) {
3883 this->ChangeUnitTypeAiActiveCount(type, -1);
3884
3885 this->AiActiveUnitsByType[type].erase(std::remove(this->AiActiveUnitsByType[type].begin(), this->AiActiveUnitsByType[type].end(), unit), this->AiActiveUnitsByType[type].end());
3886
3887 if (this->AiActiveUnitsByType[type].empty()) {
3888 this->AiActiveUnitsByType.erase(type);
3889 }
3890 }
3891
3892 if (type->BoolFlag[TOWNHALL_INDEX].value) {
3893 this->NumTownHalls--;
3894 }
3895
3896 for (int i = 0; i < MaxCosts; ++i) {
3897 this->ResourceDemand[i] -= type->Stats[this->Index].ResourceDemand[i];
3898 }
3899
3900 if (this->AiEnabled && this->Ai && std::find(this->Ai->Scouts.begin(), this->Ai->Scouts.end(), unit) != this->Ai->Scouts.end()) {
3901 this->Ai->Scouts.erase(std::remove(this->Ai->Scouts.begin(), this->Ai->Scouts.end(), unit), this->Ai->Scouts.end());
3902 }
3903
3904 if (!type_change) {
3905 if (unit->Character != nullptr) {
3906 this->Heroes.erase(std::remove(this->Heroes.begin(), this->Heroes.end(), unit), this->Heroes.end());
3907 }
3908 }
3909 }
3910
3911 /**
3912 ** Have unit of type.
3913 **
3914 ** @param type Type of unit.
3915 **
3916 ** @return How many exists, false otherwise.
3917 */
HaveUnitTypeByType(const CUnitType & type) const3918 int CPlayer::HaveUnitTypeByType(const CUnitType &type) const
3919 {
3920 return this->GetUnitTypeCount(&type);
3921 }
3922
3923 /**
3924 ** Have unit of type.
3925 **
3926 ** @param ident Identifier of unit-type that should be lookuped.
3927 **
3928 ** @return How many exists, false otherwise.
3929 **
3930 ** @note This function should not be used during run time.
3931 */
HaveUnitTypeByIdent(const std::string & ident) const3932 int CPlayer::HaveUnitTypeByIdent(const std::string &ident) const
3933 {
3934 return this->GetUnitTypeCount(UnitTypeByIdent(ident));
3935 }
3936
3937 /**
3938 ** Initialize the Ai for all players.
3939 */
PlayersInitAi()3940 void PlayersInitAi()
3941 {
3942 for (int player = 0; player < NumPlayers; ++player) {
3943 if (Players[player].AiEnabled) {
3944 AiInit(Players[player]);
3945 }
3946 }
3947 }
3948
3949 /**
3950 ** Handle AI of all players each game cycle.
3951 */
PlayersEachCycle()3952 void PlayersEachCycle()
3953 {
3954 for (int player = 0; player < NumPlayers; ++player) {
3955 CPlayer &p = Players[player];
3956
3957 //Wyrmgus start
3958 if (p.LostTownHallTimer && !p.Revealed && p.LostTownHallTimer < ((int) GameCycle) && ThisPlayer->HasContactWith(p)) {
3959 p.Revealed = true;
3960 for (int j = 0; j < NumPlayers; ++j) {
3961 if (player != j && Players[j].Type != PlayerNobody) {
3962 Players[j].Notify(_("%s's units have been revealed!"), p.Name.c_str());
3963 } else {
3964 Players[j].Notify("%s", _("Your units have been revealed!"));
3965 }
3966 }
3967 }
3968
3969
3970 for (size_t i = 0; i < p.Modifiers.size(); ++i) { //if already has the modifier, make it have the greater duration of the new or old one
3971 if ((unsigned long) p.Modifiers[i].second < GameCycle) {
3972 p.RemoveModifier(p.Modifiers[i].first); //only remove one modifier per cycle, to prevent too many upgrade changes from happening at the same cycle (for performance reasons)
3973 break;
3974 }
3975 }
3976
3977 if (p.HeroCooldownTimer) {
3978 p.HeroCooldownTimer--;
3979 }
3980 //Wyrmgus end
3981
3982 if (p.AiEnabled) {
3983 AiEachCycle(p);
3984 }
3985 }
3986 }
3987
3988 /**
3989 ** Handle AI of a player each second.
3990 **
3991 ** @param playerIdx the player to update AI
3992 */
PlayersEachSecond(int playerIdx)3993 void PlayersEachSecond(int playerIdx)
3994 {
3995 CPlayer &player = Players[playerIdx];
3996
3997 if ((GameCycle / CYCLES_PER_SECOND) % 10 == 0) {
3998 for (int res = 0; res < MaxCosts; ++res) {
3999 player.Revenue[res] = player.Resources[res] + player.StoredResources[res] - player.LastResources[res];
4000 player.Revenue[res] *= 6; // estimate per minute
4001 player.LastResources[res] = player.Resources[res] + player.StoredResources[res];
4002 }
4003 }
4004 if (player.AiEnabled) {
4005 AiEachSecond(player);
4006 }
4007
4008 player.UpdateFreeWorkers();
4009 //Wyrmgus start
4010 player.PerformResourceTrade();
4011 player.UpdateCurrentQuests();
4012 //Wyrmgus end
4013 }
4014
4015 /**
4016 ** Handle AI of a player each half minute.
4017 **
4018 ** @param playerIdx the player to update AI
4019 */
PlayersEachHalfMinute(int playerIdx)4020 void PlayersEachHalfMinute(int playerIdx)
4021 {
4022 CPlayer &player = Players[playerIdx];
4023
4024 if (player.AiEnabled) {
4025 AiEachHalfMinute(player);
4026 }
4027
4028 player.UpdateQuestPool(); // every half minute, update the quest pool
4029 }
4030
4031 /**
4032 ** Handle AI of a player each minute.
4033 **
4034 ** @param playerIdx the player to update AI
4035 */
PlayersEachMinute(int playerIdx)4036 void PlayersEachMinute(int playerIdx)
4037 {
4038 CPlayer &player = Players[playerIdx];
4039
4040 if (player.AiEnabled) {
4041 AiEachMinute(player);
4042 }
4043 }
4044
4045 /**
4046 ** Change current color set to new player.
4047 **
4048 ** FIXME: use function pointer here.
4049 **
4050 ** @param player Pointer to player.
4051 ** @param sprite The sprite in which the colors should be changed.
4052 */
4053 //Wyrmgus start
4054 //void GraphicPlayerPixels(CPlayer &player, const CGraphic &sprite)
GraphicPlayerPixels(int player,const CGraphic & sprite)4055 void GraphicPlayerPixels(int player, const CGraphic &sprite)
4056 //Wyrmgus end
4057 {
4058 //Wyrmgus start
4059 if (sprite.Grayscale) {
4060 return;
4061 }
4062 //Wyrmgus end
4063
4064 Assert(PlayerColorIndexCount);
4065
4066 SDL_LockSurface(sprite.Surface);
4067 //Wyrmgus start
4068 // std::vector<SDL_Color> sdlColors(player.UnitColors.Colors.begin(), player.UnitColors.Colors.end());
4069 std::vector<SDL_Color> sdlColors(PlayerColorsRGB[player].begin(), PlayerColorsRGB[player].end());
4070
4071 //convert colors according to time of day
4072 int time_of_day_red = 0;
4073 int time_of_day_green = 0;
4074 int time_of_day_blue = 0;
4075
4076 if (sprite.TimeOfDay) {
4077 if (sprite.TimeOfDay->Dawn) {
4078 time_of_day_red = -20;
4079 time_of_day_green = -20;
4080 time_of_day_blue = 0;
4081 } else if (sprite.TimeOfDay->Day) {
4082 time_of_day_red = 0;
4083 time_of_day_green = 0;
4084 time_of_day_blue = 0;
4085 } else if (sprite.TimeOfDay->Dusk) {
4086 time_of_day_red = 0;
4087 time_of_day_green = -20;
4088 time_of_day_blue = -20;
4089 } else if (sprite.TimeOfDay->Night) {
4090 time_of_day_red = -45;
4091 time_of_day_green = -35;
4092 time_of_day_blue = -10;
4093 }
4094 }
4095
4096 if (sprite.TimeOfDay && (time_of_day_red != 0 || time_of_day_green != 0 || time_of_day_blue != 0)) {
4097 for (int i = 0; i < PlayerColorIndexCount; ++i) {
4098 sdlColors[i].r = std::max<int>(0,std::min<int>(255,int(sdlColors[i].r) + time_of_day_red));
4099 sdlColors[i].g = std::max<int>(0,std::min<int>(255,int(sdlColors[i].g) + time_of_day_green));
4100 sdlColors[i].b = std::max<int>(0,std::min<int>(255,int(sdlColors[i].b) + time_of_day_blue));
4101 }
4102 }
4103 //Wyrmgus end
4104 SDL_SetColors(sprite.Surface, &sdlColors[0], PlayerColorIndexStart, PlayerColorIndexCount);
4105 if (sprite.SurfaceFlip) {
4106 SDL_SetColors(sprite.SurfaceFlip, &sdlColors[0], PlayerColorIndexStart, PlayerColorIndexCount);
4107 }
4108 SDL_UnlockSurface(sprite.Surface);
4109 }
4110
4111 /**
4112 ** Setup the player colors for the current palette.
4113 **
4114 ** @todo FIXME: could be called before PixelsXX is setup.
4115 */
SetPlayersPalette()4116 void SetPlayersPalette()
4117 {
4118 for (int i = 0; i < PlayerMax; ++i) {
4119 //Wyrmgus start
4120 // Players[i].UnitColors.Colors = PlayerColorsRGB[i];
4121 if (Players[i].Faction == -1) {
4122 Players[i].UnitColors.Colors = PlayerColorsRGB[i];
4123 }
4124 //Wyrmgus end
4125 }
4126 }
4127
4128 /**
4129 ** Output debug information for players.
4130 */
DebugPlayers()4131 void DebugPlayers()
4132 {
4133 #ifdef DEBUG
4134 DebugPrint("Nr Color I Name Type Race Ai\n");
4135 DebugPrint("-- -------- - -------- ------------ ------- -----\n");
4136 for (int i = 0; i < PlayerMax; ++i) {
4137 if (Players[i].Type == PlayerNobody) {
4138 continue;
4139 }
4140 const char *playertype;
4141
4142 switch (Players[i].Type) {
4143 case 0: playertype = "Don't know 0"; break;
4144 case 1: playertype = "Don't know 1"; break;
4145 case 2: playertype = "neutral "; break;
4146 case 3: playertype = "nobody "; break;
4147 case 4: playertype = "computer "; break;
4148 case 5: playertype = "person "; break;
4149 case 6: playertype = "rescue pas. "; break;
4150 case 7: playertype = "rescue akt. "; break;
4151 default : playertype = "?unknown? "; break;
4152 }
4153 DebugPrint("%2d: %8.8s %c %-8.8s %s %7s %s\n" _C_ i _C_ PlayerColorNames[i].c_str() _C_
4154 ThisPlayer == &Players[i] ? '*' :
4155 Players[i].AiEnabled ? '+' : ' ' _C_
4156 Players[i].Name.c_str() _C_ playertype _C_
4157 PlayerRaces.Name[Players[i].Race].c_str() _C_
4158 Players[i].AiName.c_str());
4159 }
4160 #endif
4161 }
4162
4163 /**
4164 ** Notify player about a problem.
4165 **
4166 ** @param type Problem type
4167 ** @param pos Map tile position
4168 ** @param fmt Message format
4169 ** @param ... Message varargs
4170 **
4171 ** @todo FIXME: We must also notfiy allied players.
4172 */
Notify(int type,const Vec2i & pos,int z,const char * fmt,...) const4173 void CPlayer::Notify(int type, const Vec2i &pos, int z, const char *fmt, ...) const
4174 {
4175 Assert(Map.Info.IsPointOnMap(pos, z));
4176 char temp[128];
4177 Uint32 color;
4178 va_list va;
4179
4180 // Notify me, and my TEAM members
4181 if (this != ThisPlayer && !IsTeamed(*ThisPlayer)) {
4182 return;
4183 }
4184
4185 va_start(va, fmt);
4186 temp[sizeof(temp) - 1] = '\0';
4187 vsnprintf(temp, sizeof(temp) - 1, fmt, va);
4188 va_end(va);
4189 switch (type) {
4190 case NotifyRed:
4191 color = ColorRed;
4192 break;
4193 case NotifyYellow:
4194 color = ColorYellow;
4195 break;
4196 case NotifyGreen:
4197 color = ColorGreen;
4198 break;
4199 default: color = ColorWhite;
4200 }
4201 //Wyrmgus start
4202 // UI.Minimap.AddEvent(pos, color);
4203 UI.Minimap.AddEvent(pos, z, color);
4204 //Wyrmgus end
4205 if (this == ThisPlayer) {
4206 //Wyrmgus start
4207 // SetMessageEvent(pos, "%s", temp);
4208 SetMessageEvent(pos, z, "%s", temp);
4209 //Wyrmgus end
4210 } else {
4211 //Wyrmgus start
4212 // SetMessageEvent(pos, "(%s): %s", Name.c_str(), temp);
4213 SetMessageEvent(pos, z, "(%s): %s", Name.c_str(), temp);
4214 //Wyrmgus end
4215 }
4216 }
4217
4218 /**
4219 ** Notify player about a problem.
4220 **
4221 ** @param type Problem type
4222 ** @param pos Map tile position
4223 ** @param fmt Message format
4224 ** @param ... Message varargs
4225 **
4226 ** @todo FIXME: We must also notfiy allied players.
4227 */
Notify(const char * fmt,...) const4228 void CPlayer::Notify(const char *fmt, ...) const
4229 {
4230 // Notify me, and my TEAM members
4231 if (this != ThisPlayer && !IsTeamed(*ThisPlayer)) {
4232 return;
4233 }
4234 char temp[128];
4235 va_list va;
4236
4237 va_start(va, fmt);
4238 temp[sizeof(temp) - 1] = '\0';
4239 vsnprintf(temp, sizeof(temp) - 1, fmt, va);
4240 va_end(va);
4241 if (this == ThisPlayer) {
4242 SetMessage("%s", temp);
4243 } else {
4244 SetMessage("(%s): %s", Name.c_str(), temp);
4245 }
4246 }
4247
SetDiplomacyNeutralWith(const CPlayer & player)4248 void CPlayer::SetDiplomacyNeutralWith(const CPlayer &player)
4249 {
4250 this->Enemy &= ~(1 << player.Index);
4251 this->Allied &= ~(1 << player.Index);
4252
4253 //Wyrmgus start
4254 if (GameCycle > 0 && player.Index == ThisPlayer->Index) {
4255 ThisPlayer->Notify(_("%s changed their diplomatic stance with us to Neutral"), _(this->Name.c_str()));
4256 }
4257 //Wyrmgus end
4258 }
4259
SetDiplomacyAlliedWith(const CPlayer & player)4260 void CPlayer::SetDiplomacyAlliedWith(const CPlayer &player)
4261 {
4262 this->Enemy &= ~(1 << player.Index);
4263 this->Allied |= 1 << player.Index;
4264
4265 //Wyrmgus start
4266 if (GameCycle > 0 && player.Index == ThisPlayer->Index) {
4267 ThisPlayer->Notify(_("%s changed their diplomatic stance with us to Ally"), _(this->Name.c_str()));
4268 }
4269 //Wyrmgus end
4270 }
4271
4272 //Wyrmgus start
4273 //void CPlayer::SetDiplomacyEnemyWith(const CPlayer &player)
SetDiplomacyEnemyWith(CPlayer & player)4274 void CPlayer::SetDiplomacyEnemyWith(CPlayer &player)
4275 //Wyrmgus end
4276 {
4277 this->Enemy |= 1 << player.Index;
4278 this->Allied &= ~(1 << player.Index);
4279
4280 //Wyrmgus start
4281 if (GameCycle > 0 && player.Index == ThisPlayer->Index) {
4282 ThisPlayer->Notify(_("%s changed their diplomatic stance with us to Enemy"), _(this->Name.c_str()));
4283 }
4284
4285 // if either player is the overlord of another (indirect or otherwise), break the vassalage bond after the declaration of war
4286 if (this->IsOverlordOf(player, true)) {
4287 player.SetOverlord(nullptr);
4288 } else if (player.IsOverlordOf(*this, true)) {
4289 this->SetOverlord(nullptr);
4290 }
4291 //Wyrmgus end
4292 }
4293
SetDiplomacyCrazyWith(const CPlayer & player)4294 void CPlayer::SetDiplomacyCrazyWith(const CPlayer &player)
4295 {
4296 this->Enemy |= 1 << player.Index;
4297 this->Allied |= 1 << player.Index;
4298
4299 //Wyrmgus start
4300 if (GameCycle > 0 && player.Index == ThisPlayer->Index) {
4301 ThisPlayer->Notify(_("%s changed their diplomatic stance with us to Crazy"), _(this->Name.c_str()));
4302 }
4303 //Wyrmgus end
4304 }
4305
ShareVisionWith(const CPlayer & player)4306 void CPlayer::ShareVisionWith(const CPlayer &player)
4307 {
4308 this->SharedVision |= (1 << player.Index);
4309
4310 //Wyrmgus start
4311 if (GameCycle > 0 && player.Index == ThisPlayer->Index) {
4312 ThisPlayer->Notify(_("%s is now sharing vision with us"), _(this->Name.c_str()));
4313 }
4314 //Wyrmgus end
4315 }
4316
UnshareVisionWith(const CPlayer & player)4317 void CPlayer::UnshareVisionWith(const CPlayer &player)
4318 {
4319 this->SharedVision &= ~(1 << player.Index);
4320
4321 //Wyrmgus start
4322 if (GameCycle > 0 && player.Index == ThisPlayer->Index) {
4323 ThisPlayer->Notify(_("%s is no longer sharing vision with us"), _(this->Name.c_str()));
4324 }
4325 //Wyrmgus end
4326 }
4327
4328 //Wyrmgus start
SetOverlord(CPlayer * player)4329 void CPlayer::SetOverlord(CPlayer *player)
4330 {
4331 if (this->Overlord) {
4332 this->Overlord->Vassals.erase(std::remove(this->Overlord->Vassals.begin(), this->Overlord->Vassals.end(), this), this->Overlord->Vassals.end());
4333 }
4334
4335 this->Overlord = player;
4336
4337 if (this->Overlord) {
4338 this->Overlord->Vassals.push_back(this);
4339 if (!SaveGameLoading) {
4340 this->SetDiplomacyAlliedWith(*this->Overlord);
4341 this->Overlord->SetDiplomacyAlliedWith(*this);
4342 CommandDiplomacy(this->Index, DiplomacyAllied, this->Overlord->Index);
4343 CommandDiplomacy(this->Overlord->Index, DiplomacyAllied, this->Index);
4344 CommandSharedVision(this->Index, true, this->Overlord->Index);
4345 CommandSharedVision(this->Overlord->Index, true, this->Index);
4346 }
4347 }
4348 }
4349 //Wyrmgus end
4350
4351 /**
4352 ** Check if the player is an enemy
4353 */
IsEnemy(const CPlayer & player) const4354 bool CPlayer::IsEnemy(const CPlayer &player) const
4355 {
4356 //Wyrmgus start
4357 // return IsEnemy(player.Index);
4358 return IsEnemy(player.Index) || player.IsEnemy(this->Index); // be hostile to the other player if they are hostile, even if the diplomatic stance hasn't been changed
4359 //Wyrmgus end
4360 }
4361
4362 /**
4363 ** Check if the unit is an enemy
4364 */
IsEnemy(const CUnit & unit) const4365 bool CPlayer::IsEnemy(const CUnit &unit) const
4366 {
4367 //Wyrmgus start
4368 if (
4369 unit.Player->Type == PlayerNeutral
4370 && unit.Type->BoolFlag[PREDATOR_INDEX].value
4371 && this->Type != PlayerNeutral
4372 ) {
4373 return true;
4374 }
4375
4376 if (
4377 this != unit.Player
4378 && this->Type != PlayerNeutral
4379 && unit.CurrentAction() == UnitActionAttack
4380 && unit.CurrentOrder()->HasGoal()
4381 && unit.CurrentOrder()->GetGoal()->Player == this
4382 && !unit.CurrentOrder()->GetGoal()->Type->BoolFlag[HIDDENOWNERSHIP_INDEX].value
4383 ) {
4384 return true;
4385 }
4386
4387 if (unit.Player->Index != this->Index && this->Type != PlayerNeutral && unit.Type->BoolFlag[HIDDENOWNERSHIP_INDEX].value && unit.IsAgressive() && !this->HasNeutralFactionType()) {
4388 return true;
4389 }
4390 //Wyrmgus end
4391
4392 return IsEnemy(*unit.Player);
4393 }
4394
4395 /**
4396 ** Check if the player is an ally
4397 */
IsAllied(const CPlayer & player) const4398 bool CPlayer::IsAllied(const CPlayer &player) const
4399 {
4400 return (Allied & (1 << player.Index)) != 0;
4401 }
4402
4403 /**
4404 ** Check if the unit is an ally
4405 */
IsAllied(const CUnit & unit) const4406 bool CPlayer::IsAllied(const CUnit &unit) const
4407 {
4408 return IsAllied(*unit.Player);
4409 }
4410
4411
IsVisionSharing() const4412 bool CPlayer::IsVisionSharing() const
4413 {
4414 return SharedVision != 0;
4415 }
4416
4417 /**
4418 ** Check if the player shares vision with the player
4419 */
IsSharedVision(const CPlayer & player) const4420 bool CPlayer::IsSharedVision(const CPlayer &player) const
4421 {
4422 return (SharedVision & (1 << player.Index)) != 0;
4423 }
4424
4425 /**
4426 ** Check if the player shares vision with the unit
4427 */
IsSharedVision(const CUnit & unit) const4428 bool CPlayer::IsSharedVision(const CUnit &unit) const
4429 {
4430 return IsSharedVision(*unit.Player);
4431 }
4432
4433 /**
4434 ** Check if the both players share vision
4435 */
IsBothSharedVision(const CPlayer & player) const4436 bool CPlayer::IsBothSharedVision(const CPlayer &player) const
4437 {
4438 return (SharedVision & (1 << player.Index)) != 0
4439 && (player.SharedVision & (1 << Index)) != 0;
4440 }
4441
4442 /**
4443 ** Check if the player and the unit share vision
4444 */
IsBothSharedVision(const CUnit & unit) const4445 bool CPlayer::IsBothSharedVision(const CUnit &unit) const
4446 {
4447 return IsBothSharedVision(*unit.Player);
4448 }
4449
4450 /**
4451 ** Check if the player is teamed
4452 */
IsTeamed(const CPlayer & player) const4453 bool CPlayer::IsTeamed(const CPlayer &player) const
4454 {
4455 return Team == player.Team;
4456 }
4457
4458 /**
4459 ** Check if the unit is teamed
4460 */
IsTeamed(const CUnit & unit) const4461 bool CPlayer::IsTeamed(const CUnit &unit) const
4462 {
4463 return IsTeamed(*unit.Player);
4464 }
4465
4466 //Wyrmgus start
4467 /**
4468 ** Check if the player is the overlord of another
4469 */
IsOverlordOf(const CPlayer & player,bool include_indirect) const4470 bool CPlayer::IsOverlordOf(const CPlayer &player, bool include_indirect) const
4471 {
4472 if (!player.Overlord) {
4473 return false;
4474 }
4475
4476 if (this == player.Overlord) {
4477 return true;
4478 }
4479
4480 if (include_indirect) { //if include_indirect is true, search this player's other vassals to see if the player is an indirect overlord of the other
4481 for (size_t i = 0; i < this->Vassals.size(); ++i) {
4482 if (this->Vassals[i]->IsOverlordOf(player, include_indirect)) {
4483 return true;
4484 }
4485 }
4486 }
4487
4488 return false;
4489 }
4490
4491 /**
4492 ** Check if the player is the vassal of another
4493 */
IsVassalOf(const CPlayer & player,bool include_indirect) const4494 bool CPlayer::IsVassalOf(const CPlayer &player, bool include_indirect) const
4495 {
4496 if (!this->Overlord) {
4497 return false;
4498 }
4499
4500 if (this->Overlord == &player) {
4501 return true;
4502 }
4503
4504 if (include_indirect) { //if include_indirect is true, search this player's other vassals to see if the player is an indirect overlord of the other
4505 if (this->Overlord->IsVassalOf(player, include_indirect)) {
4506 return true;
4507 }
4508 }
4509
4510 return false;
4511 }
4512
4513 /**
4514 ** Check if the player has contact with another (used for determining which players show up in the player list and etc.)
4515 */
HasContactWith(const CPlayer & player) const4516 bool CPlayer::HasContactWith(const CPlayer &player) const
4517 {
4518 return player.StartMapLayer == this->StartMapLayer || (player.StartMapLayer < (int) Map.MapLayers.size() && this->StartMapLayer < (int) Map.MapLayers.size() && Map.MapLayers[player.StartMapLayer]->World == Map.MapLayers[this->StartMapLayer]->World && Map.MapLayers[player.StartMapLayer]->Plane == Map.MapLayers[this->StartMapLayer]->Plane);
4519 }
4520
4521 /**
4522 ** Check if the player's faction type is a neutral one
4523 */
HasNeutralFactionType() const4524 bool CPlayer::HasNeutralFactionType() const
4525 {
4526 if (
4527 this->Race != -1
4528 && this->Faction != -1
4529 && (PlayerRaces.Factions[this->Faction]->Type == FactionTypeMercenaryCompany || PlayerRaces.Factions[this->Faction]->Type == FactionTypeHolyOrder || PlayerRaces.Factions[this->Faction]->Type == FactionTypeTradingCompany)
4530 ) {
4531 return true;
4532 }
4533
4534 return false;
4535 }
4536
4537 /**
4538 ** Check if the player can use the buildings of another, for neutral building functions (i.e. unit training)
4539 */
HasBuildingAccess(const CPlayer & player,int button_action) const4540 bool CPlayer::HasBuildingAccess(const CPlayer &player, int button_action) const
4541 {
4542 if (player.IsEnemy(*this)) {
4543 return false;
4544 }
4545
4546 if (player.Type == PlayerNeutral) {
4547 return true;
4548 }
4549
4550 if (
4551 player.HasNeutralFactionType()
4552 && (player.Overlord == nullptr || this->IsOverlordOf(player, true) || player.Overlord->IsAllied(*this))
4553 ) {
4554 if (PlayerRaces.Factions[player.Faction]->Type != FactionTypeHolyOrder || (button_action != ButtonTrain && button_action != ButtonBuy) || std::find(this->Deities.begin(), this->Deities.end(), PlayerRaces.Factions[player.Faction]->HolyOrderDeity) != this->Deities.end()) { //if the faction is a holy order, the player must have chosen its respective deity
4555 return true;
4556 }
4557 }
4558
4559 return false;
4560 }
4561
HasHero(const CCharacter * hero) const4562 bool CPlayer::HasHero(const CCharacter *hero) const
4563 {
4564 if (!hero) {
4565 return false;
4566 }
4567
4568 for (const CUnit *hero_unit : this->Heroes) {
4569 if (hero_unit->Character == hero) {
4570 return true;
4571 }
4572 }
4573
4574 return false;
4575 }
4576
SetFactionStringToIndex(const std::string & faction_name,int faction_id)4577 void SetFactionStringToIndex(const std::string &faction_name, int faction_id)
4578 {
4579 FactionStringToIndex[faction_name] = faction_id;
4580 }
4581
NetworkSetFaction(int player,const std::string & faction_name)4582 void NetworkSetFaction(int player, const std::string &faction_name)
4583 {
4584 int faction = PlayerRaces.GetFactionIndexByName(faction_name);
4585 SendCommandSetFaction(player, faction);
4586 }
4587
GetPlayerColorIndexByName(const std::string & player_color_name)4588 int GetPlayerColorIndexByName(const std::string &player_color_name)
4589 {
4590 for (int c = 0; c < PlayerColorMax; ++c) {
4591 if (PlayerColorNames[c] == player_color_name) {
4592 return c;
4593 }
4594 }
4595 return -1;
4596 }
4597
GetFactionTypeNameById(int faction_type)4598 std::string GetFactionTypeNameById(int faction_type)
4599 {
4600 if (faction_type == FactionTypeNoFactionType) {
4601 return "no-faction-type";
4602 } else if (faction_type == FactionTypeTribe) {
4603 return "tribe";
4604 } else if (faction_type == FactionTypePolity) {
4605 return "polity";
4606 } else if (faction_type == FactionTypeMercenaryCompany) {
4607 return "mercenary-company";
4608 } else if (faction_type == FactionTypeHolyOrder) {
4609 return "holy-order";
4610 } else if (faction_type == FactionTypeTradingCompany) {
4611 return "trading-company";
4612 }
4613
4614 return "";
4615 }
4616
GetFactionTypeIdByName(const std::string & faction_type)4617 int GetFactionTypeIdByName(const std::string &faction_type)
4618 {
4619 if (faction_type == "no-faction-type") {
4620 return FactionTypeNoFactionType;
4621 } else if (faction_type == "tribe") {
4622 return FactionTypeTribe;
4623 } else if (faction_type == "polity") {
4624 return FactionTypePolity;
4625 } else if (faction_type == "mercenary-company") {
4626 return FactionTypeMercenaryCompany;
4627 } else if (faction_type == "holy-order") {
4628 return FactionTypeHolyOrder;
4629 } else if (faction_type == "trading-company") {
4630 return FactionTypeTradingCompany;
4631 }
4632
4633 return -1;
4634 }
4635
GetGovernmentTypeNameById(int government_type)4636 std::string GetGovernmentTypeNameById(int government_type)
4637 {
4638 if (government_type == GovernmentTypeNoGovernmentType) {
4639 return "no-government-type";
4640 } else if (government_type == GovernmentTypeMonarchy) {
4641 return "monarchy";
4642 } else if (government_type == GovernmentTypeRepublic) {
4643 return "republic";
4644 } else if (government_type == GovernmentTypeTheocracy) {
4645 return "theocracy";
4646 }
4647
4648 return "";
4649 }
4650
GetGovernmentTypeIdByName(const std::string & government_type)4651 int GetGovernmentTypeIdByName(const std::string &government_type)
4652 {
4653 if (government_type == "no-government-type") {
4654 return GovernmentTypeNoGovernmentType;
4655 } else if (government_type == "monarchy") {
4656 return GovernmentTypeMonarchy;
4657 } else if (government_type == "republic") {
4658 return GovernmentTypeRepublic;
4659 } else if (government_type == "theocracy") {
4660 return GovernmentTypeTheocracy;
4661 }
4662
4663 return -1;
4664 }
4665
GetForceTypeNameById(int force_type)4666 std::string GetForceTypeNameById(int force_type)
4667 {
4668 if (force_type == LandForceType) {
4669 return "land-force";
4670 } else if (force_type == NavalForceType) {
4671 return "naval-force";
4672 } else if (force_type == AirForceType) {
4673 return "air-force";
4674 }
4675
4676 return "";
4677 }
4678
GetForceTypeIdByName(const std::string & force_type)4679 int GetForceTypeIdByName(const std::string &force_type)
4680 {
4681 if (force_type == "land-force") {
4682 return LandForceType;
4683 } else if (force_type == "naval-force") {
4684 return NavalForceType;
4685 } else if (force_type == "air-force") {
4686 return AirForceType;
4687 }
4688
4689 return -1;
4690 }
4691
GetWordTypeNameById(int word_type)4692 std::string GetWordTypeNameById(int word_type)
4693 {
4694 if (word_type == WordTypeNoun) {
4695 return "noun";
4696 } else if (word_type == WordTypeVerb) {
4697 return "verb";
4698 } else if (word_type == WordTypeAdjective) {
4699 return "adjective";
4700 } else if (word_type == WordTypePronoun) {
4701 return "pronoun";
4702 } else if (word_type == WordTypeAdverb) {
4703 return "adverb";
4704 } else if (word_type == WordTypeConjunction) {
4705 return "conjunction";
4706 } else if (word_type == WordTypeAdposition) {
4707 return "adposition";
4708 } else if (word_type == WordTypeArticle) {
4709 return "article";
4710 } else if (word_type == WordTypeNumeral) {
4711 return "numeral";
4712 } else if (word_type == WordTypeAffix) {
4713 return "affix";
4714 }
4715
4716 return "";
4717 }
4718
GetWordTypeIdByName(const std::string & word_type)4719 int GetWordTypeIdByName(const std::string &word_type)
4720 {
4721 if (word_type == "noun") {
4722 return WordTypeNoun;
4723 } else if (word_type == "verb") {
4724 return WordTypeVerb;
4725 } else if (word_type == "adjective") {
4726 return WordTypeAdjective;
4727 } else if (word_type == "pronoun") {
4728 return WordTypePronoun;
4729 } else if (word_type == "adverb") {
4730 return WordTypeAdverb;
4731 } else if (word_type == "conjunction") {
4732 return WordTypeConjunction;
4733 } else if (word_type == "adposition") {
4734 return WordTypeAdposition;
4735 } else if (word_type == "article") {
4736 return WordTypeArticle;
4737 } else if (word_type == "numeral") {
4738 return WordTypeNumeral;
4739 } else if (word_type == "affix") {
4740 return WordTypeAffix;
4741 }
4742
4743 return -1;
4744 }
4745
GetArticleTypeNameById(int article_type)4746 std::string GetArticleTypeNameById(int article_type)
4747 {
4748 if (article_type == ArticleTypeNoArticle) {
4749 return "no-article";
4750 } else if (article_type == ArticleTypeDefinite) {
4751 return "definite";
4752 } else if (article_type == ArticleTypeIndefinite) {
4753 return "indefinite";
4754 }
4755
4756 return "";
4757 }
4758
GetArticleTypeIdByName(const std::string & article_type)4759 int GetArticleTypeIdByName(const std::string &article_type)
4760 {
4761 if (article_type == "no-article") {
4762 return ArticleTypeNoArticle;
4763 } else if (article_type == "definite") {
4764 return ArticleTypeDefinite;
4765 } else if (article_type == "indefinite") {
4766 return ArticleTypeIndefinite;
4767 }
4768
4769 return -1;
4770 }
4771
GetGrammaticalCaseNameById(int grammatical_case)4772 std::string GetGrammaticalCaseNameById(int grammatical_case)
4773 {
4774 if (grammatical_case == GrammaticalCaseNoCase) {
4775 return "no-case";
4776 } else if (grammatical_case == GrammaticalCaseNominative) {
4777 return "nominative";
4778 } else if (grammatical_case == GrammaticalCaseAccusative) {
4779 return "accusative";
4780 } else if (grammatical_case == GrammaticalCaseDative) {
4781 return "dative";
4782 } else if (grammatical_case == GrammaticalCaseGenitive) {
4783 return "genitive";
4784 }
4785
4786 return "";
4787 }
4788
GetGrammaticalCaseIdByName(const std::string & grammatical_case)4789 int GetGrammaticalCaseIdByName(const std::string &grammatical_case)
4790 {
4791 if (grammatical_case == "no-case") {
4792 return GrammaticalCaseNoCase;
4793 } else if (grammatical_case == "nominative") {
4794 return GrammaticalCaseNominative;
4795 } else if (grammatical_case == "accusative") {
4796 return GrammaticalCaseAccusative;
4797 } else if (grammatical_case == "dative") {
4798 return GrammaticalCaseDative;
4799 } else if (grammatical_case == "genitive") {
4800 return GrammaticalCaseGenitive;
4801 }
4802
4803 return -1;
4804 }
4805
GetGrammaticalNumberNameById(int grammatical_number)4806 std::string GetGrammaticalNumberNameById(int grammatical_number)
4807 {
4808 if (grammatical_number == GrammaticalNumberNoNumber) {
4809 return "no-number";
4810 } else if (grammatical_number == GrammaticalNumberSingular) {
4811 return "singular";
4812 } else if (grammatical_number == GrammaticalNumberPlural) {
4813 return "plural";
4814 }
4815
4816 return "";
4817 }
4818
GetGrammaticalNumberIdByName(const std::string & grammatical_number)4819 int GetGrammaticalNumberIdByName(const std::string &grammatical_number)
4820 {
4821 if (grammatical_number == "no-number") {
4822 return GrammaticalNumberNoNumber;
4823 } else if (grammatical_number == "singular") {
4824 return GrammaticalNumberSingular;
4825 } else if (grammatical_number == "plural") {
4826 return GrammaticalNumberPlural;
4827 }
4828
4829 return -1;
4830 }
4831
GetGrammaticalPersonNameById(int grammatical_person)4832 std::string GetGrammaticalPersonNameById(int grammatical_person)
4833 {
4834 if (grammatical_person == GrammaticalPersonFirstPerson) {
4835 return "first-person";
4836 } else if (grammatical_person == GrammaticalPersonSecondPerson) {
4837 return "second-person";
4838 } else if (grammatical_person == GrammaticalPersonThirdPerson) {
4839 return "third-person";
4840 }
4841
4842 return "";
4843 }
4844
GetGrammaticalPersonIdByName(const std::string & grammatical_person)4845 int GetGrammaticalPersonIdByName(const std::string &grammatical_person)
4846 {
4847 if (grammatical_person == "first-person") {
4848 return GrammaticalPersonFirstPerson;
4849 } else if (grammatical_person == "second-person") {
4850 return GrammaticalPersonSecondPerson;
4851 } else if (grammatical_person == "third-person") {
4852 return GrammaticalPersonThirdPerson;
4853 }
4854
4855 return -1;
4856 }
4857
GetGrammaticalGenderNameById(int grammatical_gender)4858 std::string GetGrammaticalGenderNameById(int grammatical_gender)
4859 {
4860 if (grammatical_gender == GrammaticalGenderNoGender) {
4861 return "no-gender";
4862 } else if (grammatical_gender == GrammaticalGenderMasculine) {
4863 return "masculine";
4864 } else if (grammatical_gender == GrammaticalGenderFeminine) {
4865 return "feminine";
4866 } else if (grammatical_gender == GrammaticalGenderNeuter) {
4867 return "neuter";
4868 }
4869
4870 return "";
4871 }
4872
GetGrammaticalGenderIdByName(const std::string & grammatical_gender)4873 int GetGrammaticalGenderIdByName(const std::string &grammatical_gender)
4874 {
4875 if (grammatical_gender == "no-gender") {
4876 return GrammaticalGenderNoGender;
4877 } else if (grammatical_gender == "masculine") {
4878 return GrammaticalGenderMasculine;
4879 } else if (grammatical_gender == "feminine") {
4880 return GrammaticalGenderFeminine;
4881 } else if (grammatical_gender == "neuter") {
4882 return GrammaticalGenderNeuter;
4883 }
4884
4885 return -1;
4886 }
4887
GetGrammaticalTenseNameById(int grammatical_tense)4888 std::string GetGrammaticalTenseNameById(int grammatical_tense)
4889 {
4890 if (grammatical_tense == GrammaticalTenseNoTense) {
4891 return "no-tense";
4892 } else if (grammatical_tense == GrammaticalTensePresent) {
4893 return "present";
4894 } else if (grammatical_tense == GrammaticalTensePast) {
4895 return "past";
4896 } else if (grammatical_tense == GrammaticalTenseFuture) {
4897 return "future";
4898 }
4899
4900 return "";
4901 }
4902
GetGrammaticalTenseIdByName(const std::string & grammatical_tense)4903 int GetGrammaticalTenseIdByName(const std::string &grammatical_tense)
4904 {
4905 if (grammatical_tense == "no-tense") {
4906 return GrammaticalTenseNoTense;
4907 } else if (grammatical_tense == "present") {
4908 return GrammaticalTensePresent;
4909 } else if (grammatical_tense == "past") {
4910 return GrammaticalTensePast;
4911 } else if (grammatical_tense == "future") {
4912 return GrammaticalTenseFuture;
4913 }
4914
4915 return -1;
4916 }
4917
GetGrammaticalMoodNameById(int grammatical_mood)4918 std::string GetGrammaticalMoodNameById(int grammatical_mood)
4919 {
4920 if (grammatical_mood == GrammaticalMoodIndicative) {
4921 return "indicative";
4922 } else if (grammatical_mood == GrammaticalMoodSubjunctive) {
4923 return "subjunctive";
4924 }
4925
4926 return "";
4927 }
4928
GetGrammaticalMoodIdByName(const std::string & grammatical_mood)4929 int GetGrammaticalMoodIdByName(const std::string &grammatical_mood)
4930 {
4931 if (grammatical_mood == "indicative") {
4932 return GrammaticalMoodIndicative;
4933 } else if (grammatical_mood == "subjunctive") {
4934 return GrammaticalMoodSubjunctive;
4935 }
4936
4937 return -1;
4938 }
4939
GetComparisonDegreeNameById(int comparison_degree)4940 std::string GetComparisonDegreeNameById(int comparison_degree)
4941 {
4942 if (comparison_degree == ComparisonDegreePositive) {
4943 return "positive";
4944 } else if (comparison_degree == ComparisonDegreeComparative) {
4945 return "comparative";
4946 } else if (comparison_degree == ComparisonDegreeSuperlative) {
4947 return "superlative";
4948 }
4949
4950 return "";
4951 }
4952
GetComparisonDegreeIdByName(const std::string & comparison_degree)4953 int GetComparisonDegreeIdByName(const std::string &comparison_degree)
4954 {
4955 if (comparison_degree == "positive") {
4956 return ComparisonDegreePositive;
4957 } else if (comparison_degree == "comparative") {
4958 return ComparisonDegreeComparative;
4959 } else if (comparison_degree == "superlative") {
4960 return ComparisonDegreeSuperlative;
4961 }
4962
4963 return -1;
4964 }
4965
GetAffixTypeNameById(int affix_type)4966 std::string GetAffixTypeNameById(int affix_type)
4967 {
4968 if (affix_type == AffixTypePrefix) {
4969 return "prefix";
4970 } else if (affix_type == AffixTypeSuffix) {
4971 return "suffix";
4972 } else if (affix_type == AffixTypeInfix) {
4973 return "infix";
4974 }
4975
4976 return "";
4977 }
4978
GetAffixTypeIdByName(const std::string & affix_type)4979 int GetAffixTypeIdByName(const std::string &affix_type)
4980 {
4981 if (affix_type == "prefix") {
4982 return AffixTypePrefix;
4983 } else if (affix_type == "suffix") {
4984 return AffixTypeSuffix;
4985 } else if (affix_type == "infix") {
4986 return AffixTypeInfix;
4987 }
4988
4989 return -1;
4990 }
4991
GetWordJunctionTypeNameById(int word_junction_type)4992 std::string GetWordJunctionTypeNameById(int word_junction_type)
4993 {
4994 if (word_junction_type == WordJunctionTypeNoWordJunction) {
4995 return "no-word-junction";
4996 } else if (word_junction_type == WordJunctionTypeCompound) {
4997 return "compound";
4998 } else if (word_junction_type == WordJunctionTypeSeparate) {
4999 return "separate";
5000 }
5001
5002 return "";
5003 }
5004
GetWordJunctionTypeIdByName(const std::string & word_junction_type)5005 int GetWordJunctionTypeIdByName(const std::string &word_junction_type)
5006 {
5007 if (word_junction_type == "no-word-junction") {
5008 return WordJunctionTypeNoWordJunction;
5009 } else if (word_junction_type == "compound") {
5010 return WordJunctionTypeCompound;
5011 } else if (word_junction_type == "separate") {
5012 return WordJunctionTypeSeparate;
5013 }
5014
5015 return -1;
5016 }
5017
GetWord(const std::string word,int word_type,std::vector<std::string> & word_meanings) const5018 LanguageWord *CLanguage::GetWord(const std::string word, int word_type, std::vector<std::string>& word_meanings) const
5019 {
5020 for (size_t i = 0; i < this->LanguageWords.size(); ++i) {
5021 if (
5022 this->LanguageWords[i]->Word == word
5023 && (word_type == -1 || this->LanguageWords[i]->Type == word_type)
5024 && (word_meanings.size() == 0 || this->LanguageWords[i]->Meanings == word_meanings)
5025 ) {
5026 return this->LanguageWords[i];
5027 }
5028 }
5029
5030 return nullptr;
5031 }
5032
GetArticle(int gender,int grammatical_case,int article_type,int grammatical_number)5033 std::string CLanguage::GetArticle(int gender, int grammatical_case, int article_type, int grammatical_number)
5034 {
5035 for (size_t i = 0; i < this->LanguageWords.size(); ++i) {
5036 if (this->LanguageWords[i]->Type != WordTypeArticle || this->LanguageWords[i]->ArticleType != article_type) {
5037 continue;
5038 }
5039
5040 if (grammatical_number != -1 && this->LanguageWords[i]->GrammaticalNumber != -1 && this->LanguageWords[i]->GrammaticalNumber != grammatical_number) {
5041 continue;
5042 }
5043
5044 if (gender == -1 || this->LanguageWords[i]->Gender == -1 || gender == this->LanguageWords[i]->Gender) {
5045 if (grammatical_case == GrammaticalCaseNominative && !this->LanguageWords[i]->Nominative.empty()) {
5046 return this->LanguageWords[i]->Nominative;
5047 } else if (grammatical_case == GrammaticalCaseAccusative && !this->LanguageWords[i]->Accusative.empty()) {
5048 return this->LanguageWords[i]->Accusative;
5049 } else if (grammatical_case == GrammaticalCaseDative && !this->LanguageWords[i]->Dative.empty()) {
5050 return this->LanguageWords[i]->Dative;
5051 } else if (grammatical_case == GrammaticalCaseGenitive && !this->LanguageWords[i]->Genitive.empty()) {
5052 return this->LanguageWords[i]->Genitive;
5053 }
5054 }
5055 }
5056 return "";
5057 }
5058
GetNounEnding(int grammatical_number,int grammatical_case,int word_junction_type)5059 std::string CLanguage::GetNounEnding(int grammatical_number, int grammatical_case, int word_junction_type)
5060 {
5061 if (word_junction_type == -1) {
5062 word_junction_type = WordJunctionTypeNoWordJunction;
5063 }
5064
5065 if (!this->NounEndings[grammatical_number][grammatical_case][word_junction_type].empty()) {
5066 return this->NounEndings[grammatical_number][grammatical_case][word_junction_type];
5067 } else if (!this->NounEndings[grammatical_number][grammatical_case][WordJunctionTypeNoWordJunction].empty()) {
5068 return this->NounEndings[grammatical_number][grammatical_case][WordJunctionTypeNoWordJunction];
5069 }
5070
5071 return "";
5072 }
5073
GetAdjectiveEnding(int article_type,int grammatical_case,int grammatical_number,int grammatical_gender)5074 std::string CLanguage::GetAdjectiveEnding(int article_type, int grammatical_case, int grammatical_number, int grammatical_gender)
5075 {
5076 if (grammatical_number == -1) {
5077 grammatical_number = GrammaticalNumberNoNumber;
5078 }
5079
5080 if (grammatical_gender == -1) {
5081 grammatical_gender = GrammaticalGenderNoGender;
5082 }
5083
5084 if (!this->AdjectiveEndings[article_type][grammatical_case][grammatical_number][grammatical_gender].empty()) {
5085 return this->AdjectiveEndings[article_type][grammatical_case][grammatical_number][grammatical_gender];
5086 } else if (!this->AdjectiveEndings[article_type][grammatical_case][grammatical_number][GrammaticalGenderNoGender].empty()) {
5087 return this->AdjectiveEndings[article_type][grammatical_case][grammatical_number][GrammaticalGenderNoGender];
5088 } else if (!this->AdjectiveEndings[article_type][grammatical_case][GrammaticalNumberNoNumber][GrammaticalGenderNoGender].empty()) {
5089 return this->AdjectiveEndings[article_type][grammatical_case][GrammaticalNumberNoNumber][GrammaticalGenderNoGender];
5090 }
5091
5092 return "";
5093 }
5094
RemoveWord(LanguageWord * word)5095 void CLanguage::RemoveWord(LanguageWord *word)
5096 {
5097 if (std::find(this->LanguageWords.begin(), this->LanguageWords.end(), word) != this->LanguageWords.end()) {
5098 this->LanguageWords.erase(std::remove(this->LanguageWords.begin(), this->LanguageWords.end(), word), this->LanguageWords.end());
5099 }
5100 }
5101
HasMeaning(const std::string & meaning)5102 bool LanguageWord::HasMeaning(const std::string &meaning)
5103 {
5104 return std::find(this->Meanings.begin(), this->Meanings.end(), meaning) != this->Meanings.end();
5105 }
5106
GetNounInflection(int grammatical_number,int grammatical_case,int word_junction_type)5107 std::string LanguageWord::GetNounInflection(int grammatical_number, int grammatical_case, int word_junction_type)
5108 {
5109 if (this->NumberCaseInflections.find(std::tuple<int, int>(grammatical_number, grammatical_case)) != this->NumberCaseInflections.end()) {
5110 return this->NumberCaseInflections.find(std::tuple<int, int>(grammatical_number, grammatical_case))->second;
5111 }
5112
5113 return this->Word + this->Language->GetNounEnding(grammatical_number, grammatical_case, word_junction_type);
5114 }
5115
GetVerbInflection(int grammatical_number,int grammatical_person,int grammatical_tense,int grammatical_mood)5116 std::string LanguageWord::GetVerbInflection(int grammatical_number, int grammatical_person, int grammatical_tense, int grammatical_mood)
5117 {
5118 if (this->NumberPersonTenseMoodInflections.find(std::tuple<int, int, int, int>(grammatical_number, grammatical_person, grammatical_tense, grammatical_mood)) != this->NumberPersonTenseMoodInflections.end()) {
5119 return this->NumberPersonTenseMoodInflections.find(std::tuple<int, int, int, int>(grammatical_number, grammatical_person, grammatical_tense, grammatical_mood))->second;
5120 }
5121
5122 return this->Word;
5123 }
5124
GetAdjectiveInflection(int comparison_degree,int article_type,int grammatical_case,int grammatical_number,int grammatical_gender)5125 std::string LanguageWord::GetAdjectiveInflection(int comparison_degree, int article_type, int grammatical_case, int grammatical_number, int grammatical_gender)
5126 {
5127 std::string inflected_word;
5128
5129 if (grammatical_case == -1) {
5130 grammatical_case = GrammaticalCaseNoCase;
5131 }
5132
5133 if (!this->ComparisonDegreeCaseInflections[comparison_degree][grammatical_case].empty()) {
5134 inflected_word = this->ComparisonDegreeCaseInflections[comparison_degree][grammatical_case];
5135 } else if (!this->ComparisonDegreeCaseInflections[comparison_degree][GrammaticalCaseNoCase].empty()) {
5136 inflected_word = this->ComparisonDegreeCaseInflections[comparison_degree][GrammaticalCaseNoCase];
5137 } else {
5138 inflected_word = this->Word;
5139 }
5140
5141 if (article_type != -1 && grammatical_case != GrammaticalCaseNoCase && this->ComparisonDegreeCaseInflections[comparison_degree][grammatical_case].empty()) {
5142 inflected_word += this->Language->GetAdjectiveEnding(article_type, grammatical_case, grammatical_number, grammatical_gender);
5143 }
5144
5145 return inflected_word;
5146 }
5147
GetParticiple(int grammatical_tense)5148 std::string LanguageWord::GetParticiple(int grammatical_tense)
5149 {
5150 if (!this->Participles[grammatical_tense].empty()) {
5151 return this->Participles[grammatical_tense];
5152 }
5153
5154 return this->Word;
5155 }
5156
RemoveFromVector(std::vector<LanguageWord * > & word_vector)5157 void LanguageWord::RemoveFromVector(std::vector<LanguageWord *>& word_vector)
5158 {
5159 std::vector<LanguageWord *> word_vector_copy = word_vector;
5160
5161 if (std::find(word_vector.begin(), word_vector.end(), this) != word_vector.end()) {
5162 word_vector.erase(std::remove(word_vector.begin(), word_vector.end(), this), word_vector.end());
5163 }
5164
5165 if (word_vector.size() == 0) { // if removing the word from the vector left it empty, undo the removal
5166 word_vector = word_vector_copy;
5167 }
5168 }
5169
IsNameValidForWord(const std::string & word_name)5170 bool IsNameValidForWord(const std::string &word_name)
5171 {
5172 if (word_name.empty()) {
5173 return false;
5174 }
5175
5176 if (
5177 word_name.find('\n') != -1
5178 || word_name.find('\\') != -1
5179 || word_name.find('/') != -1
5180 || word_name.find('.') != -1
5181 || word_name.find('*') != -1
5182 || word_name.find('[') != -1
5183 || word_name.find(']') != -1
5184 || word_name.find(':') != -1
5185 || word_name.find(';') != -1
5186 || word_name.find('=') != -1
5187 || word_name.find(',') != -1
5188 || word_name.find('<') != -1
5189 || word_name.find('>') != -1
5190 || word_name.find('?') != -1
5191 || word_name.find('|') != -1
5192 ) {
5193 return false;
5194 }
5195
5196 if (word_name.find_first_not_of(' ') == std::string::npos) {
5197 return false; //name contains only spaces
5198 }
5199
5200 return true;
5201 }
5202 //Wyrmgus end
5203
5204 //@}
5205