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 grand_strategy.cpp - The grand strategy mode. */
12 //
13 //      (c) Copyright 2015-2019 by Andrettin
14 //
15 //      This program is free software; you can redistribute it and/or modify
16 //      it under the terms of the GNU General Public License as published by
17 //      the Free Software Foundation; only version 2 of the License.
18 //
19 //      This program is distributed in the hope that it will be useful,
20 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //      GNU General Public License for more details.
23 //
24 //      You should have received a copy of the GNU General Public License
25 //      along with this program; if not, write to the Free Software
26 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 //      02111-1307, USA.
28 //
29 
30 /*----------------------------------------------------------------------------
31 --  Includes
32 ----------------------------------------------------------------------------*/
33 
34 #include "stratagus.h"
35 
36 #include "grand_strategy.h"
37 
38 #include "character.h"
39 #include "civilization.h"
40 #include "game.h"	// for loading screen elements
41 #include "font.h"	// for grand strategy mode tooltip drawing
42 #include "iolib.h"
43 #include "luacallback.h"
44 #include "menus.h"
45 #include "player.h"
46 #include "results.h"
47 #include "sound_server.h"
48 #include "ui/interface.h"
49 #include "ui/ui.h"
50 #include "unit/unit.h"
51 #include "unit/unittype.h"
52 #include "upgrade/upgrade.h"
53 #include "upgrade/upgrade_modifier.h"
54 #include "util.h"
55 #include "video.h"
56 
57 #include <ctype.h>
58 
59 #include <string>
60 #include <map>
61 
62 /*----------------------------------------------------------------------------
63 --  Variables
64 ----------------------------------------------------------------------------*/
65 
66 bool GrandStrategy = false;				///if the game is in grand strategy mode
67 bool GrandStrategyGameLoading = false;
68 int GrandStrategyYear = 0;
69 std::string GrandStrategyWorld;
70 int PopulationGrowthThreshold = 1000;
71 CGrandStrategyGame GrandStrategyGame;
72 std::map<std::string, int> GrandStrategyHeroStringToIndex;
73 std::vector<CGrandStrategyEvent *> GrandStrategyEvents;
74 std::map<std::string, CGrandStrategyEvent *> GrandStrategyEventStringToPointer;
75 
76 /*----------------------------------------------------------------------------
77 --  Functions
78 ----------------------------------------------------------------------------*/
79 
DrawInterface()80 void CGrandStrategyGame::DrawInterface()
81 {
82 	if (this->PlayerFaction != nullptr && this->PlayerFaction->OwnedProvinces.size() > 0) { //draw resource bar
83 		std::vector<int> stored_resources;
84 		stored_resources.push_back(CopperCost);
85 		stored_resources.push_back(WoodCost);
86 		stored_resources.push_back(StoneCost);
87 		stored_resources.push_back(ResearchCost);
88 		stored_resources.push_back(PrestigeCost);
89 
90 		Vec2i hovered_research_icon(-1, -1);
91 		Vec2i hovered_prestige_icon(-1, -1);
92 		for (size_t i = 0; i < stored_resources.size(); ++i) {
93 			int x = 154 + (100 * i);
94 			int y = 0;
95 			UI.Resources[stored_resources[i]].G->DrawFrameClip(0, x, y, true);
96 
97 			int quantity_stored = this->PlayerFaction->Resources[stored_resources[i]];
98 			int income = 0;
99 			if (stored_resources[i] == CopperCost) {
100 				income = this->PlayerFaction->Income[stored_resources[i]];
101 			} else if (stored_resources[i] == ResearchCost || stored_resources[i] == LeadershipCost) {
102 				income = this->PlayerFaction->Income[stored_resources[i]] / this->PlayerFaction->OwnedProvinces.size();
103 			} else {
104 				income = this->PlayerFaction->Income[stored_resources[i]];
105 			}
106 			std::string income_string;
107 			if (income != 0) {
108 				if (income > 0) {
109 					income_string += "+";
110 				}
111 				income_string += std::to_string((long long) income);
112 			}
113 			std::string resource_stored_string = std::to_string((long long) quantity_stored) + income_string;
114 
115 			if (resource_stored_string.size() <= 9) {
116 				CLabel(GetGameFont()).Draw(x + 18, y + 1, resource_stored_string);
117 			} else {
118 				CLabel(GetSmallFont()).Draw(x + 18, y + 1 + 2, resource_stored_string);
119 			}
120 
121 			if (CursorScreenPos.x >= x && CursorScreenPos.x <= (x + UI.Resources[stored_resources[i]].G->getGraphicWidth()) && CursorScreenPos.y >= y && CursorScreenPos.y <= (y + UI.Resources[stored_resources[i]].G->getGraphicHeight())) {
122 				if (stored_resources[i] == ResearchCost) {
123 					hovered_research_icon.x = x;
124 					hovered_research_icon.y = y;
125 				} else if (stored_resources[i] == PrestigeCost) {
126 					hovered_prestige_icon.x = x;
127 					hovered_prestige_icon.y = y;
128 				}
129 			}
130 		}
131 		if (hovered_research_icon.x != -1 && hovered_research_icon.y != -1) {
132 			DrawGenericPopup("Gain Research by building town halls, lumber mills, smithies and temples", hovered_research_icon.x, hovered_research_icon.y);
133 		} else if (hovered_prestige_icon.x != -1 && hovered_prestige_icon.y != -1) {
134 			DrawGenericPopup("Prestige influences trade priority between nations, and factions with negative prestige cannot declare war", hovered_prestige_icon.x, hovered_prestige_icon.y);
135 		}
136 	}
137 }
138 
DoTurn()139 void CGrandStrategyGame::DoTurn()
140 {
141 	for (size_t i = 0; i < this->Provinces.size(); ++i) {
142 		if (this->Provinces[i]->Civilization != -1) { // if this province has a culture
143 			if (this->Provinces[i]->Owner != nullptr) {
144 				if (!this->Provinces[i]->HasFactionClaim(this->Provinces[i]->Owner->Civilization, this->Provinces[i]->Owner->Faction) && SyncRand(100) < 1) { // 1% chance the owner of this province will get a claim on it
145 					this->Provinces[i]->AddFactionClaim(this->Provinces[i]->Owner->Civilization, this->Provinces[i]->Owner->Faction);
146 				}
147 
148 				if (SyncRand(1000) == 0) { // 0.1% chance per year that a (randomly generated) literary work will be created in a province
149 					this->CreateWork(nullptr, nullptr, this->Provinces[i]);
150 				}
151 			}
152 		}
153 	}
154 
155 	// check if any literary works should be published this year
156 	int works_size = this->UnpublishedWorks.size();
157 	for (int i = (works_size - 1); i >= 0; --i) {
158 		CGrandStrategyHero *author = nullptr;
159 		if (this->UnpublishedWorks[i]->Author != nullptr) {
160 			author = this->GetHero(this->UnpublishedWorks[i]->Author->GetFullName());
161 			if (author != nullptr && !author->IsAlive()) {
162 				continue;
163 			}
164 		}
165 
166 		if ((author == nullptr && SyncRand(200) != 0) || (author != nullptr && SyncRand(10) != 0)) { // 0.5% chance per year that a work will be published if no author is preset, and 10% if an author is preset
167 			continue;
168 		}
169 
170 		int civilization = this->UnpublishedWorks[i]->Civilization;
171 		if (
172 			(author != nullptr && author->ProvinceOfOrigin != nullptr)
173 			|| (civilization != -1 && this->CultureProvinces.find(civilization) != this->CultureProvinces.end() && this->CultureProvinces[civilization].size() > 0)
174 		) {
175 			bool characters_existed = true;
176 			for (size_t j = 0; j < this->UnpublishedWorks[i]->Characters.size(); ++j) {
177 				CGrandStrategyHero *hero = this->GetHero(this->UnpublishedWorks[i]->Characters[j]->GetFullName());
178 
179 				if (hero == nullptr || !hero->Existed) {
180 					characters_existed = false;
181 					break;
182 				}
183 			}
184 			if (!characters_existed) {
185 				continue;
186 			}
187 
188 			if (author != nullptr && author->Province != nullptr) {
189 				this->CreateWork(this->UnpublishedWorks[i], author, author->Province);
190 			} else {
191 				this->CreateWork(this->UnpublishedWorks[i], author, this->CultureProvinces[civilization][SyncRand(this->CultureProvinces[civilization].size())]);
192 			}
193 		}
194 	}
195 }
196 
PerformTrade(CGrandStrategyFaction & importer_faction,CGrandStrategyFaction & exporter_faction,int resource)197 void CGrandStrategyGame::PerformTrade(CGrandStrategyFaction &importer_faction, CGrandStrategyFaction &exporter_faction, int resource)
198 {
199 	if (abs(importer_faction.Trade[resource]) > exporter_faction.Trade[resource]) {
200 		importer_faction.Resources[resource] += exporter_faction.Trade[resource];
201 		exporter_faction.Resources[resource] -= exporter_faction.Trade[resource];
202 
203 		importer_faction.Resources[CopperCost] -= exporter_faction.Trade[resource] * this->CommodityPrices[resource] / 100;
204 		exporter_faction.Resources[CopperCost] += exporter_faction.Trade[resource] * this->CommodityPrices[resource] / 100;
205 
206 		importer_faction.Trade[resource] += exporter_faction.Trade[resource];
207 		exporter_faction.Trade[resource] = 0;
208 	} else {
209 		importer_faction.Resources[resource] += abs(importer_faction.Trade[resource]);
210 		exporter_faction.Resources[resource] -= abs(importer_faction.Trade[resource]);
211 
212 		importer_faction.Resources[CopperCost] -= abs(importer_faction.Trade[resource]) * this->CommodityPrices[resource] / 100;
213 		exporter_faction.Resources[CopperCost] += abs(importer_faction.Trade[resource]) * this->CommodityPrices[resource] / 100;
214 
215 		exporter_faction.Trade[resource] += importer_faction.Trade[resource];
216 		importer_faction.Trade[resource] = 0;
217 	}
218 }
219 
CreateWork(CUpgrade * work,CGrandStrategyHero * author,CGrandStrategyProvince * province)220 void CGrandStrategyGame::CreateWork(CUpgrade *work, CGrandStrategyHero *author, CGrandStrategyProvince *province)
221 {
222 	if (!province || !province->Owner) {
223 		return;
224 	}
225 
226 	if (!province->Owner->HasTechnologyClass("writing")) { // only factions which have knowledge of writing can produce literary works
227 		return;
228 	}
229 
230 	if (work != nullptr) {
231 		this->UnpublishedWorks.erase(std::remove(this->UnpublishedWorks.begin(), this->UnpublishedWorks.end(), work), this->UnpublishedWorks.end()); // remove work from the vector, so that it doesn't get created again
232 	}
233 
234 	std::string work_name;
235 	if (work != nullptr) {
236 		work_name = work->Name;
237 	} else {
238 		work_name = province->GenerateWorkName();
239 		if (work_name.empty()) {
240 			return;
241 		}
242 	}
243 
244 	if (author == nullptr) {
245 		author = province->GetRandomAuthor();
246 	}
247 
248 	if (province->Owner == GrandStrategyGame.PlayerFaction || work != nullptr) { // only show foreign works that are predefined
249 		std::string work_creation_message = "if (GenericDialog ~= nil) then GenericDialog(\"" + work_name + "\", \"";
250 		if (author != nullptr) {
251 			work_creation_message += "The " + FullyDecapitalizeString(author->Type->Name) + " " + author->GetFullName() + " ";
252 		} else {
253 			work_creation_message += "A sage ";
254 		}
255 		work_creation_message += "has written the literary work \\\"" + work_name + "\\\" in ";
256 		if (province->Owner != GrandStrategyGame.PlayerFaction) {
257 			work_creation_message += "the foreign lands of ";
258 		}
259 		work_creation_message += province->Name + "!";
260 		if (work != nullptr && !work->Description.empty()) {
261 			work_creation_message += " " + FindAndReplaceString(FindAndReplaceString(work->Description, "\"", "\\\""), "\n", "\\n");
262 		}
263 		if (work != nullptr && !work->Quote.empty()) {
264 			work_creation_message += "\\n\\n" + FindAndReplaceString(FindAndReplaceString(work->Quote, "\"", "\\\""), "\n", "\\n");
265 		}
266 		work_creation_message += "\"";
267 		if (province->Owner == GrandStrategyGame.PlayerFaction) {
268 			work_creation_message += ", \"+1 Prestige\"";
269 		}
270 		work_creation_message += ") end;";
271 		CclCommand(work_creation_message);
272 	}
273 
274 	province->Owner->Resources[PrestigeCost] += 1;
275 }
276 
TradePriority(CGrandStrategyFaction & faction_a,CGrandStrategyFaction & faction_b)277 bool CGrandStrategyGame::TradePriority(CGrandStrategyFaction &faction_a, CGrandStrategyFaction &faction_b)
278 {
279 	return faction_a.Resources[PrestigeCost] > faction_b.Resources[PrestigeCost];
280 }
281 
GetHero(std::string hero_full_name)282 CGrandStrategyHero *CGrandStrategyGame::GetHero(std::string hero_full_name)
283 {
284 	if (!hero_full_name.empty() && GrandStrategyHeroStringToIndex.find(hero_full_name) != GrandStrategyHeroStringToIndex.end()) {
285 		return this->Heroes[GrandStrategyHeroStringToIndex[hero_full_name]];
286 	} else {
287 		return nullptr;
288 	}
289 }
290 
SetOwner(int civilization_id,int faction_id)291 void CGrandStrategyProvince::SetOwner(int civilization_id, int faction_id)
292 {
293 	//if new owner is the same as the current owner, return
294 	if (
295 		(this->Owner != nullptr && this->Owner->Civilization == civilization_id && this->Owner->Faction == faction_id)
296 		|| (this->Owner == nullptr && civilization_id == -1 && faction_id == -1)
297 	) {
298 		return;
299 	}
300 
301 	CGrandStrategyFaction *old_owner = this->Owner;
302 
303 	if (this->Owner != nullptr) { //if province has a previous owner, remove it from the owner's province list
304 		this->Owner->OwnedProvinces.erase(std::remove(this->Owner->OwnedProvinces.begin(), this->Owner->OwnedProvinces.end(), this->ID), this->Owner->OwnedProvinces.end());
305 	}
306 
307 	for (size_t i = 0; i < UnitTypes.size(); ++i) { //change the province's military score to be appropriate for the new faction's technologies
308 		if (IsMilitaryUnit(*UnitTypes[i])) {
309 			int old_owner_military_score_bonus = (this->Owner != nullptr ? this->Owner->MilitaryScoreBonus[i] : 0);
310 			int new_owner_military_score_bonus = (faction_id != -1 ? GrandStrategyGame.Factions[civilization_id][faction_id]->MilitaryScoreBonus[i] : 0);
311 			if (old_owner_military_score_bonus != new_owner_military_score_bonus) {
312 				this->MilitaryScore += this->Units[i] * (new_owner_military_score_bonus - old_owner_military_score_bonus);
313 				this->OffensiveMilitaryScore += this->Units[i] * new_owner_military_score_bonus - old_owner_military_score_bonus;
314 			}
315 		} else if (UnitTypes[i]->Class != -1 && UnitTypeClasses[UnitTypes[i]->Class] == "worker") {
316 			int militia_unit_type = PlayerRaces.GetCivilizationClassUnitType(UnitTypes[i]->Civilization, GetUnitTypeClassIndexByName("militia"));
317 			if (militia_unit_type != -1) {
318 				int old_owner_military_score_bonus = (this->Owner != nullptr ? this->Owner->MilitaryScoreBonus[militia_unit_type] : 0);
319 				int new_owner_military_score_bonus = (faction_id != -1 ? GrandStrategyGame.Factions[civilization_id][faction_id]->MilitaryScoreBonus[militia_unit_type] : 0);
320 				if (old_owner_military_score_bonus != new_owner_military_score_bonus) {
321 					this->MilitaryScore += this->Units[i] * ((new_owner_military_score_bonus - old_owner_military_score_bonus) / 2);
322 				}
323 			}
324 		}
325 	}
326 
327 	if (civilization_id != -1 && faction_id != -1) {
328 		this->Owner = GrandStrategyGame.Factions[civilization_id][faction_id];
329 		this->Owner->OwnedProvinces.push_back(this->ID);
330 	} else {
331 		this->Owner = nullptr;
332 	}
333 }
334 
SetSettlementBuilding(int building_id,bool has_settlement_building)335 void CGrandStrategyProvince::SetSettlementBuilding(int building_id, bool has_settlement_building)
336 {
337 	if (building_id == -1) {
338 		fprintf(stderr, "Tried to set invalid building type for the settlement of \"%s\".\n", this->Name.c_str());
339 		return;
340 	}
341 
342 	//if this province has an equivalent building for its civilization/faction, use that instead
343 	if (UnitTypes[building_id]->Civilization != -1) {
344 		if (this->GetClassUnitType(UnitTypes[building_id]->Class) != building_id && this->GetClassUnitType(UnitTypes[building_id]->Class) != -1) {
345 			building_id = this->GetClassUnitType(UnitTypes[building_id]->Class);
346 		}
347 	}
348 
349 	if (this->SettlementBuildings[building_id] == has_settlement_building) {
350 		return;
351 	}
352 
353 	this->SettlementBuildings[building_id] = has_settlement_building;
354 
355 	int change = has_settlement_building ? 1 : -1;
356 	for (int i = 0; i < MaxCosts; ++i) {
357 		if (UnitTypes[building_id]->GrandStrategyProductionEfficiencyModifier[i] != 0) {
358 			this->ProductionEfficiencyModifier[i] += UnitTypes[building_id]->GrandStrategyProductionEfficiencyModifier[i] * change;
359 		}
360 	}
361 
362 	if (UnitTypes[building_id]->Class != -1 && UnitTypeClasses[UnitTypes[building_id]->Class] == "stronghold") { //increase the military score of the province, if this building is a stronghold
363 		this->MilitaryScore += (100 * 2) * change; // two guard towers if has a stronghold
364 	}
365 }
366 
SetModifier(CUpgrade * modifier,bool has_modifier)367 void CGrandStrategyProvince::SetModifier(CUpgrade *modifier, bool has_modifier)
368 {
369 	if (this->HasModifier(modifier) == has_modifier) { // current situation already corresponds to has_modifier setting
370 		return;
371 	}
372 
373 	if (has_modifier) {
374 		this->Modifiers.push_back(modifier);
375 	} else {
376 		this->Modifiers.erase(std::remove(this->Modifiers.begin(), this->Modifiers.end(), modifier), this->Modifiers.end());
377 	}
378 
379 	int change = has_modifier ? 1 : -1;
380 
381 	for (int i = 0; i < MaxCosts; ++i) {
382 		if (modifier->GrandStrategyProductionEfficiencyModifier[i] != 0) {
383 			this->ProductionEfficiencyModifier[i] += modifier->GrandStrategyProductionEfficiencyModifier[i] * change;
384 		}
385 	}
386 }
387 
SetUnitQuantity(int unit_type_id,int quantity)388 void CGrandStrategyProvince::SetUnitQuantity(int unit_type_id, int quantity)
389 {
390 	if (unit_type_id == -1) {
391 		return;
392 	}
393 
394 //	fprintf(stderr, "Setting the quantity of unit type \"%s\" to %d in the %s province.\n", UnitTypes[unit_type_id]->Ident.c_str(), quantity, this->Name.c_str());
395 
396 	quantity = std::max(0, quantity);
397 
398 	int change = quantity - this->Units[unit_type_id];
399 
400 	this->TotalUnits += change;
401 
402 	if (IsMilitaryUnit(*UnitTypes[unit_type_id])) {
403 		this->MilitaryScore += change * (UnitTypes[unit_type_id]->DefaultStat.Variables[POINTS_INDEX].Value + (this->Owner != nullptr ? this->Owner->MilitaryScoreBonus[unit_type_id] : 0));
404 		this->OffensiveMilitaryScore += change * (UnitTypes[unit_type_id]->DefaultStat.Variables[POINTS_INDEX].Value + (this->Owner != nullptr ? this->Owner->MilitaryScoreBonus[unit_type_id] : 0));
405 	}
406 
407 	if (UnitTypes[unit_type_id]->Class != -1 && UnitTypeClasses[UnitTypes[unit_type_id]->Class] == "worker") {
408 		this->TotalWorkers += change;
409 
410 		//if this unit's civilization can change workers into militia, add half of the militia's points to the military score (one in every two workers becomes a militia when the province is attacked)
411 		int militia_unit_type = PlayerRaces.GetCivilizationClassUnitType(UnitTypes[unit_type_id]->Civilization, GetUnitTypeClassIndexByName("militia"));
412 		if (militia_unit_type != -1) {
413 			this->MilitaryScore += change * ((UnitTypes[militia_unit_type]->DefaultStat.Variables[POINTS_INDEX].Value + (this->Owner != nullptr ? this->Owner->MilitaryScoreBonus[militia_unit_type] : 0)) / 2);
414 		}
415 	}
416 
417 	this->Units[unit_type_id] = quantity;
418 }
419 
ChangeUnitQuantity(int unit_type_id,int quantity)420 void CGrandStrategyProvince::ChangeUnitQuantity(int unit_type_id, int quantity)
421 {
422 	this->SetUnitQuantity(unit_type_id, this->Units[unit_type_id] + quantity);
423 }
424 
SetPopulation(int quantity)425 void CGrandStrategyProvince::SetPopulation(int quantity)
426 {
427 	if (this->Civilization == -1) {
428 		return;
429 	}
430 
431 	int worker_unit_type = this->GetClassUnitType(GetUnitTypeClassIndexByName("worker"));
432 
433 	if (quantity > 0) {
434 		quantity /= 10000; // each population unit represents 10,000 people
435 		quantity /= 2; // only (working-age) adults are represented, so around half of the total population
436 		quantity = std::max(1, quantity);
437 	}
438 
439 //	quantity -= this->TotalUnits - this->Units[worker_unit_type]; // decrease from the quantity to be set the population that isn't composed of workers
440 	// don't take military units in consideration for this population count for now (since they don't cost food anyway)
441 	quantity -= this->TotalWorkers - this->Units[worker_unit_type]; // decrease from the quantity to be set the population that isn't composed of workers
442 	quantity = std::max(0, quantity);
443 
444 	this->SetUnitQuantity(worker_unit_type, quantity);
445 }
446 
SetHero(std::string hero_full_name,int value)447 void CGrandStrategyProvince::SetHero(std::string hero_full_name, int value)
448 {
449 	if (value == 1) {
450 		this->Movement = true;
451 	}
452 	CGrandStrategyHero *hero = GrandStrategyGame.GetHero(hero_full_name);
453 	if (hero) {
454 		//update the hero
455 		if (value == 0) {
456 			hero->Die();
457 			return;
458 		}
459 		hero->State = value;
460 	} else {
461 		//if the hero hasn't been defined yet, give an error message
462 		fprintf(stderr, "Hero \"%s\" doesn't exist.\n", hero_full_name.c_str());
463 		return;
464 	}
465 }
466 
AddFactionClaim(int civilization_id,int faction_id)467 void CGrandStrategyProvince::AddFactionClaim(int civilization_id, int faction_id)
468 {
469 	this->Claims.push_back(GrandStrategyGame.Factions[civilization_id][faction_id]);
470 	GrandStrategyGame.Factions[civilization_id][faction_id]->Claims.push_back(this);
471 }
472 
RemoveFactionClaim(int civilization_id,int faction_id)473 void CGrandStrategyProvince::RemoveFactionClaim(int civilization_id, int faction_id)
474 {
475 	this->Claims.erase(std::remove(this->Claims.begin(), this->Claims.end(), GrandStrategyGame.Factions[civilization_id][faction_id]), this->Claims.end());
476 	GrandStrategyGame.Factions[civilization_id][faction_id]->Claims.erase(std::remove(GrandStrategyGame.Factions[civilization_id][faction_id]->Claims.begin(), GrandStrategyGame.Factions[civilization_id][faction_id]->Claims.end(), this), GrandStrategyGame.Factions[civilization_id][faction_id]->Claims.end());
477 }
478 
HasBuildingClass(std::string building_class_name)479 bool CGrandStrategyProvince::HasBuildingClass(std::string building_class_name)
480 {
481 	if (this->Civilization == -1 || building_class_name.empty()) {
482 		return false;
483 	}
484 
485 	int building_type = this->GetClassUnitType(GetUnitTypeClassIndexByName(building_class_name));
486 
487 	if (building_type == -1 && building_class_name == "mercenary-camp") { //special case for mercenary camps, which are a neutral building
488 		building_type = UnitTypeIdByIdent("unit-mercenary-camp");
489 	}
490 
491 	if (building_type != -1 && this->SettlementBuildings[building_type] == true) {
492 		return true;
493 	}
494 
495 	return false;
496 }
497 
HasModifier(CUpgrade * modifier)498 bool CGrandStrategyProvince::HasModifier(CUpgrade *modifier)
499 {
500 	return std::find(this->Modifiers.begin(), this->Modifiers.end(), modifier) != this->Modifiers.end();
501 }
502 
BordersModifier(CUpgrade * modifier)503 bool CGrandStrategyProvince::BordersModifier(CUpgrade *modifier)
504 {
505 	//see if any provinces bordering this one have the modifier (count provinces reachable through water as well)
506 
507 	for (size_t i = 0; i < this->BorderProvinces.size(); ++i) {
508 		CGrandStrategyProvince *border_province = this->BorderProvinces[i];
509 		if (border_province->HasModifier(modifier)) {
510 			return true;
511 		}
512 	}
513 
514 	return false;
515 }
516 
HasFactionClaim(int civilization_id,int faction_id)517 bool CGrandStrategyProvince::HasFactionClaim(int civilization_id, int faction_id)
518 {
519 	for (size_t i = 0; i < this->Claims.size(); ++i) {
520 		if (this->Claims[i]->Civilization == civilization_id && this->Claims[i]->Faction == faction_id) {
521 			return true;
522 		}
523 	}
524 	return false;
525 }
526 
BordersProvince(CGrandStrategyProvince * province)527 bool CGrandStrategyProvince::BordersProvince(CGrandStrategyProvince *province)
528 {
529 	for (size_t i = 0; i < this->BorderProvinces.size(); ++i) {
530 		if (this->BorderProvinces[i] == province) {
531 			return true;
532 		}
533 	}
534 	return false;
535 }
536 
HasSecondaryBorderThroughWaterWith(CGrandStrategyProvince * province)537 bool CGrandStrategyProvince::HasSecondaryBorderThroughWaterWith(CGrandStrategyProvince *province)
538 {
539 	for (size_t i = 0; i < this->BorderProvinces.size(); ++i) {
540 		CGrandStrategyProvince *border_province = this->BorderProvinces[i];
541 		if (border_province->Water) {
542 			for (size_t j = 0; j < border_province->BorderProvinces.size(); ++j) {
543 				if (border_province->BorderProvinces[j] == province) {
544 					return true;
545 				}
546 			}
547 		}
548 	}
549 	return false;
550 }
551 
BordersFaction(int faction_civilization,int faction,bool check_through_water)552 bool CGrandStrategyProvince::BordersFaction(int faction_civilization, int faction, bool check_through_water)
553 {
554 	for (size_t i = 0; i < this->BorderProvinces.size(); ++i) {
555 		CGrandStrategyProvince *border_province = this->BorderProvinces[i];
556 		if (border_province->Owner == nullptr) {
557 			if (border_province->Water && check_through_water) {
558 				for (size_t j = 0; j < border_province->BorderProvinces.size(); ++j) {
559 					if (
560 						border_province->BorderProvinces[j]->Owner != nullptr
561 						&& border_province->BorderProvinces[j]->Owner->Civilization == faction_civilization
562 						&& border_province->BorderProvinces[j]->Owner->Faction == faction
563 					) {
564 						return true;
565 					}
566 				}
567 			}
568 			continue;
569 		}
570 		if (border_province->Owner->Civilization == faction_civilization && border_province->Owner->Faction == faction) {
571 			return true;
572 		}
573 	}
574 	return false;
575 }
576 
GetPopulation()577 int CGrandStrategyProvince::GetPopulation()
578 {
579 	return (this->TotalWorkers * 10000) * 2;
580 }
581 
GetClassUnitType(int class_id)582 int CGrandStrategyProvince::GetClassUnitType(int class_id)
583 {
584 	return PlayerRaces.GetCivilizationClassUnitType(this->Civilization, class_id);
585 }
586 
GetDesirabilityRating()587 int CGrandStrategyProvince::GetDesirabilityRating()
588 {
589 	int desirability = 0;
590 
591 	std::vector<int> resources;
592 	resources.push_back(CopperCost);
593 	resources.push_back(GoldCost);
594 	resources.push_back(SilverCost);
595 	resources.push_back(WoodCost);
596 	resources.push_back(StoneCost);
597 
598 	for (size_t i = 0; i < resources.size(); ++i) {
599 		desirability += this->ProductionCapacity[resources[i]] * 100 * GrandStrategyGame.CommodityPrices[resources[i]] / 100;
600 	}
601 
602 	if (this->Coastal) {
603 		desirability += 250;
604 	}
605 
606 	return desirability;
607 }
608 
GenerateWorkName()609 std::string CGrandStrategyProvince::GenerateWorkName()
610 {
611 	if (this->Owner == nullptr) {
612 		return "";
613 	}
614 
615 	std::string work_name;
616 
617 	std::vector<CGrandStrategyHero *> potential_heroes;
618 	for (size_t i = 0; i < this->Owner->HistoricalMinisters[CharacterTitleHeadOfState].size(); ++i) {
619 		potential_heroes.push_back(this->Owner->HistoricalMinisters[CharacterTitleHeadOfState][i]);
620 	}
621 
622 	if (potential_heroes.size() > 0 && SyncRand(10) != 0) { // 9 chances out of 10 that a literary work will use a hero's name as a basis
623 		work_name += potential_heroes[SyncRand(potential_heroes.size())]->Name;
624 		if (work_name.substr(work_name.size() - 1, 1) != "s") {
625 			work_name += "s";
626 		}
627 
628 		work_name += "mol";
629 	}
630 
631 	return work_name;
632 }
633 
GetRandomAuthor()634 CGrandStrategyHero *CGrandStrategyProvince::GetRandomAuthor()
635 {
636 	std::vector<CGrandStrategyHero *> potential_authors;
637 
638 	for (size_t i = 0; i < this->Heroes.size(); ++i) {
639 		if (this->Heroes[i]->Type->Class != -1 && UnitTypeClasses[this->Heroes[i]->Type->Class] == "priest") { // first see if there are any heroes in the province from a unit class likely to produce scholarly works
640 			potential_authors.push_back(this->Heroes[i]);
641 		}
642 	}
643 
644 	if (potential_authors.size() == 0) { // if the first loop through the province's heroes failed, try to get a hero from any class
645 		for (size_t i = 0; i < this->Heroes.size(); ++i) {
646 			potential_authors.push_back(this->Heroes[i]);
647 		}
648 	}
649 
650 	if (potential_authors.size() > 0) { // if a hero was found in the province, return it as the result
651 		return potential_authors[SyncRand(potential_authors.size())];
652 	} else { // if no hero was found, generate one
653 		return nullptr;
654 	}
655 }
656 
SetTechnology(int upgrade_id,bool has_technology,bool secondary_setting)657 void CGrandStrategyFaction::SetTechnology(int upgrade_id, bool has_technology, bool secondary_setting)
658 {
659 	if (this->Technologies[upgrade_id] == has_technology) {
660 		return;
661 	}
662 
663 	this->Technologies[upgrade_id] = has_technology;
664 
665 	int change = has_technology ? 1 : -1;
666 
667 	//add military score bonuses
668 	for (size_t z = 0; z < AllUpgrades[upgrade_id]->UpgradeModifiers.size(); ++z) {
669 		for (size_t i = 0; i < UnitTypes.size(); ++i) {
670 
671 			Assert(AllUpgrades[upgrade_id]->UpgradeModifiers[z]->ApplyTo[i] == '?' || AllUpgrades[upgrade_id]->UpgradeModifiers[z]->ApplyTo[i] == 'X');
672 
673 			if (AllUpgrades[upgrade_id]->UpgradeModifiers[z]->ApplyTo[i] == 'X') {
674 				if (AllUpgrades[upgrade_id]->UpgradeModifiers[z]->Modifier.Variables[POINTS_INDEX].Value) {
675 					this->MilitaryScoreBonus[i] += AllUpgrades[upgrade_id]->UpgradeModifiers[z]->Modifier.Variables[POINTS_INDEX].Value * change;
676 				}
677 			}
678 		}
679 	}
680 
681 	if (!secondary_setting) { //if this technology is not being set as a result of another technology of the same class being researched
682 		if (has_technology) { //if value is true, mark technologies from other civilizations that are of the same class as researched too, so that the player doesn't need to research the same type of technology every time
683 			if (AllUpgrades[upgrade_id]->Class != -1) {
684 				for (size_t i = 0; i < AllUpgrades.size(); ++i) {
685 					if (AllUpgrades[upgrade_id]->Class == AllUpgrades[i]->Class) {
686 						this->SetTechnology(i, has_technology, true);
687 					}
688 				}
689 			}
690 		}
691 	}
692 }
693 
SetCapital(CGrandStrategyProvince * province)694 void CGrandStrategyFaction::SetCapital(CGrandStrategyProvince *province)
695 {
696 	this->Capital = province;
697 }
698 
SetDiplomacyState(CGrandStrategyFaction * faction,int diplomacy_state_id)699 void CGrandStrategyFaction::SetDiplomacyState(CGrandStrategyFaction *faction, int diplomacy_state_id)
700 {
701 	int second_diplomacy_state_id; // usually the second diplomacy state is the same as the first, but there are asymmetrical diplomacy states (such as vassal/sovereign relationships)
702 	if (diplomacy_state_id == DiplomacyStateVassal) {
703 		second_diplomacy_state_id = DiplomacyStateOverlord;
704 	} else if (diplomacy_state_id == DiplomacyStateOverlord) {
705 		second_diplomacy_state_id = DiplomacyStateVassal;
706 	} else {
707 		second_diplomacy_state_id = diplomacy_state_id;
708 	}
709 
710 	this->DiplomacyStates[faction] = diplomacy_state_id;
711 	faction->DiplomacyStates[this] = second_diplomacy_state_id;
712 }
713 
SetMinister(int title,std::string hero_full_name)714 void CGrandStrategyFaction::SetMinister(int title, std::string hero_full_name)
715 {
716 	if (this->Ministers[title] != nullptr && std::find(this->Ministers[title]->Titles.begin(), this->Ministers[title]->Titles.end(), std::pair<int, CGrandStrategyFaction *>(title, this)) != this->Ministers[title]->Titles.end()) { // remove from the old minister's array
717 		this->Ministers[title]->Titles.erase(std::remove(this->Ministers[title]->Titles.begin(), this->Ministers[title]->Titles.end(), std::pair<int, CGrandStrategyFaction *>(title, this)), this->Ministers[title]->Titles.end());
718 	}
719 
720 	if (hero_full_name.empty()) {
721 		if (this->CanHaveSuccession(title, true)) { //if the minister died a violent death, wait until the next turn to replace him
722 			this->MinisterSuccession(title);
723 		} else {
724 			this->Ministers[title] = nullptr;
725 		}
726 	} else {
727 		CGrandStrategyHero *hero = GrandStrategyGame.GetHero(hero_full_name);
728 		if (hero) {
729 			this->Ministers[title] = const_cast<CGrandStrategyHero *>(&(*hero));
730 			hero->Titles.push_back(std::pair<int, CGrandStrategyFaction *>(title, this));
731 
732 			if (this->IsAlive()) {
733 				int titles_size = hero->Titles.size();
734 				for (int i = (titles_size - 1); i >= 0; --i) {
735 					if (!(hero->Titles[i].first == title && hero->Titles[i].second == this) && hero->Titles[i].first != CharacterTitleHeadOfState) { // a character can only have multiple head of state titles, but not others
736 						hero->Titles[i].second->SetMinister(hero->Titles[i].first, "");
737 					}
738 				}
739 			}
740 		} else {
741 			fprintf(stderr, "Tried to make \"%s\" the \"%s\" of the \"%s\", but the hero doesn't exist.\n", hero_full_name.c_str(), GetCharacterTitleNameById(title).c_str(), this->GetFullName().c_str());
742 		}
743 
744 		if (this == GrandStrategyGame.PlayerFaction) {
745 			std::string new_minister_message = "if (GenericDialog ~= nil) then GenericDialog(\"";
746 //			new_minister_message += this->GetCharacterTitle(title, this->Ministers[title]->Gender) + " " + this->Ministers[title]->GetFullName();
747 			new_minister_message += "\", \"";
748 //			new_minister_message += "A new " + FullyDecapitalizeString(this->GetCharacterTitle(title, this->Ministers[title]->Gender));
749 			if (title == CharacterTitleHeadOfState) {
750 				new_minister_message += " has come to power in our realm, ";
751 			} else {
752 				new_minister_message += " has been appointed, ";
753 			}
754 			new_minister_message += this->Ministers[title]->GetFullName() + "!\\n\\n";
755 			new_minister_message += "Type: " + this->Ministers[title]->Type->Name + "\\n" + "Trait: " + this->Ministers[title]->Trait->Name + "\\n" +  + "\\n\\n" + this->Ministers[title]->GetMinisterEffectsString(title);
756 			new_minister_message += "\") end;";
757 			CclCommand(new_minister_message);
758 		}
759 
760 		if (std::find(this->HistoricalMinisters[title].begin(), this->HistoricalMinisters[title].end(), hero) == this->HistoricalMinisters[title].end()) {
761 			this->HistoricalMinisters[title].push_back(hero);
762 		}
763 
764 		if (this->IsAlive() && (hero->Province == nullptr || hero->Province->Owner != this)) { // if the hero's province is not owned by this faction, move him to a random province owned by this faction
765 			this->GetRandomProvinceWeightedByPopulation()->SetHero(hero->GetFullName(), hero->State);
766 		}
767 	}
768 }
769 
MinisterSuccession(int title)770 void CGrandStrategyFaction::MinisterSuccession(int title)
771 {
772 	if (
773 		this->Ministers[title] != nullptr
774 		&& (PlayerRaces.Factions[this->Faction]->Type == FactionTypeTribe || this->GovernmentType == GovernmentTypeMonarchy)
775 		&& title == CharacterTitleHeadOfState
776 	) { //if is a tribe or a monarchical polity, try to perform ruler succession by descent
777 		for (size_t i = 0; i < this->Ministers[title]->Children.size(); ++i) {
778 			if (this->Ministers[title]->Children[i]->IsAlive() && this->Ministers[title]->Children[i]->IsVisible() && this->Ministers[title]->Children[i]->Gender == MaleGender) { //historically males have generally been given priority in throne inheritance (if not exclusivity), specially in the cultures currently playable in the game
779 				this->SetMinister(title, this->Ministers[title]->Children[i]->GetFullName());
780 				return;
781 			}
782 		}
783 		for (size_t i = 0; i < this->Ministers[title]->Siblings.size(); ++i) { // now check for male siblings of the current ruler
784 			if (this->Ministers[title]->Siblings[i]->IsAlive() && this->Ministers[title]->Siblings[i]->IsVisible() && this->Ministers[title]->Siblings[i]->Gender == MaleGender) {
785 				this->SetMinister(title, this->Ministers[title]->Siblings[i]->GetFullName());
786 				return;
787 			}
788 		}
789 		for (size_t i = 0; i < this->Ministers[title]->Children.size(); ++i) { //check again for children, but now allow for inheritance regardless of gender
790 			if (this->Ministers[title]->Children[i]->IsAlive() && this->Ministers[title]->Children[i]->IsVisible()) {
791 				this->SetMinister(title, this->Ministers[title]->Children[i]->GetFullName());
792 				return;
793 			}
794 		}
795 		for (size_t i = 0; i < this->Ministers[title]->Siblings.size(); ++i) { //check again for siblings, but now allow for inheritance regardless of gender
796 			if (this->Ministers[title]->Siblings[i]->IsAlive() && this->Ministers[title]->Siblings[i]->IsVisible()) {
797 				this->SetMinister(title, this->Ministers[title]->Siblings[i]->GetFullName());
798 				return;
799 			}
800 		}
801 
802 		// if no family successor was found, the title becomes extinct if it is only a titular one (an aristocratic title whose corresponding faction does not actually hold territory)
803 		if (!this->CanHaveSuccession(title, false) || title != CharacterTitleHeadOfState) {
804 			this->Ministers[title] = nullptr;
805 			return;
806 		}
807 	}
808 
809 	CGrandStrategyHero *best_candidate = nullptr;
810 	int best_score = 0;
811 
812 	for (size_t i = 0; i < GrandStrategyGame.Heroes.size(); ++i) {
813 		if (
814 			GrandStrategyGame.Heroes[i]->IsAlive()
815 			&& GrandStrategyGame.Heroes[i]->IsVisible()
816 			&& (
817 				(GrandStrategyGame.Heroes[i]->Province != nullptr && GrandStrategyGame.Heroes[i]->Province->Owner == this)
818 				|| (GrandStrategyGame.Heroes[i]->Province == nullptr && GrandStrategyGame.Heroes[i]->ProvinceOfOrigin != nullptr && GrandStrategyGame.Heroes[i]->ProvinceOfOrigin->Owner == this)
819 			)
820 			&& !GrandStrategyGame.Heroes[i]->Custom
821 			&& GrandStrategyGame.Heroes[i]->IsEligibleForTitle(title)
822 		) {
823 			int score = GrandStrategyGame.Heroes[i]->GetTitleScore(title);
824 			if (score > best_score) {
825 				best_candidate = GrandStrategyGame.Heroes[i];
826 				best_score = score;
827 			}
828 		}
829 	}
830 	if (best_candidate != nullptr) {
831 		this->SetMinister(title, best_candidate->GetFullName());
832 		return;
833 	}
834 
835 	this->Ministers[title] = nullptr;
836 }
837 
IsAlive()838 bool CGrandStrategyFaction::IsAlive()
839 {
840 	return this->OwnedProvinces.size() > 0;
841 }
842 
HasTechnologyClass(std::string technology_class_name)843 bool CGrandStrategyFaction::HasTechnologyClass(std::string technology_class_name)
844 {
845 	if (this->Civilization == -1 || technology_class_name.empty()) {
846 		return false;
847 	}
848 
849 	int technology_id = PlayerRaces.GetFactionClassUpgrade(this->Faction, GetUpgradeClassIndexByName(technology_class_name));
850 
851 	if (technology_id != -1 && this->Technologies[technology_id] == true) {
852 		return true;
853 	}
854 
855 	return false;
856 }
857 
CanHaveSuccession(int title,bool family_inheritance)858 bool CGrandStrategyFaction::CanHaveSuccession(int title, bool family_inheritance)
859 {
860 	if (!this->IsAlive() && (title != CharacterTitleHeadOfState || !family_inheritance || PlayerRaces.Factions[this->Faction]->Type == FactionTypeTribe || this->GovernmentType != GovernmentTypeMonarchy)) { // head of state titles can be inherited even if their respective factions have no provinces, but if the line dies out then the title becomes extinct; tribal titles cannot be titular-only
861 		return false;
862 	}
863 
864 	return true;
865 }
866 
IsConquestDesirable(CGrandStrategyProvince * province)867 bool CGrandStrategyFaction::IsConquestDesirable(CGrandStrategyProvince *province)
868 {
869 	if (this->OwnedProvinces.size() == 1 && province->Owner == nullptr && PlayerRaces.Factions[this->Faction]->Type == FactionTypeTribe) {
870 		if (province->GetDesirabilityRating() <= GrandStrategyGame.Provinces[this->OwnedProvinces[0]]->GetDesirabilityRating()) { // if conquering the province would trigger a migration, the conquest is only desirable if the province is worth more
871 			return false;
872 		}
873 	}
874 
875 	return true;
876 }
877 
GetTroopCostModifier()878 int CGrandStrategyFaction::GetTroopCostModifier()
879 {
880 	int modifier = 0;
881 
882 	if (this->Ministers[CharacterTitleHeadOfState] != nullptr) {
883 		modifier += this->Ministers[CharacterTitleHeadOfState]->GetTroopCostModifier();
884 	}
885 
886 	if (this->Ministers[CharacterTitleWarMinister] != nullptr) {
887 		modifier += this->Ministers[CharacterTitleWarMinister]->GetTroopCostModifier();
888 	}
889 
890 	return modifier;
891 }
892 
GetDiplomacyState(CGrandStrategyFaction * faction)893 int CGrandStrategyFaction::GetDiplomacyState(CGrandStrategyFaction *faction)
894 {
895 	if (this->DiplomacyStates.find(faction) != this->DiplomacyStates.end()) {
896 		return this->DiplomacyStates[faction];
897 	} else {
898 		return DiplomacyStatePeace;
899 	}
900 }
901 
GetDiplomacyStateProposal(CGrandStrategyFaction * faction)902 int CGrandStrategyFaction::GetDiplomacyStateProposal(CGrandStrategyFaction *faction)
903 {
904 	if (this->DiplomacyStateProposals.find(faction) != this->DiplomacyStateProposals.end()) {
905 		return this->DiplomacyStateProposals[faction];
906 	} else {
907 		return -1;
908 	}
909 }
910 
GetFullName()911 std::string CGrandStrategyFaction::GetFullName()
912 {
913 	if (PlayerRaces.Factions[this->Faction]->Type == FactionTypeTribe) {
914 		return PlayerRaces.Factions[this->Faction]->Name;
915 	} else if (PlayerRaces.Factions[this->Faction]->Type == FactionTypePolity) {
916 //		return this->GetTitle() + " of " + PlayerRaces.Factions[this->Faction]->Name;
917 	}
918 
919 	return "";
920 }
921 
GetRandomProvinceWeightedByPopulation()922 CGrandStrategyProvince *CGrandStrategyFaction::GetRandomProvinceWeightedByPopulation()
923 {
924 	std::vector<int> potential_provinces;
925 	for (size_t i = 0; i < this->OwnedProvinces.size(); ++i) {
926 		int weight = std::max(1, GrandStrategyGame.Provinces[this->OwnedProvinces[i]]->TotalWorkers);
927 		for (int j = 0; j < weight; ++j) {
928 			potential_provinces.push_back(this->OwnedProvinces[i]);
929 		}
930 	}
931 
932 	if (potential_provinces.size() > 0) {
933 		return GrandStrategyGame.Provinces[potential_provinces[SyncRand(potential_provinces.size())]];
934 	} else {
935 		return nullptr;
936 	}
937 }
938 
Die()939 void CGrandStrategyHero::Die()
940 {
941 	//show message that the hero has died
942 	/*
943 	if (this->IsVisible()) {
944 		if (GrandStrategyGame.PlayerFaction != nullptr && GrandStrategyGame.PlayerFaction->Ministers[CharacterTitleHeadOfState] == this) {
945 			char buf[256];
946 			snprintf(
947 				buf, sizeof(buf), "if (GenericDialog ~= nil) then GenericDialog(\"%s\", \"%s\") end;",
948 				(GrandStrategyGame.PlayerFaction->GetCharacterTitle(CharacterTitleHeadOfState, this->Gender) + " " + this->GetFullName() + " Dies").c_str(),
949 				("Tragic news spread throughout our realm. Our " + FullyDecapitalizeString(GrandStrategyGame.PlayerFaction->GetCharacterTitle(CharacterTitleHeadOfState, this->Gender)) + ", " + this->GetFullName() + ", has died! May his soul rest in peace.").c_str()
950 			);
951 			CclCommand(buf);
952 		} else if (this->GetFaction() == GrandStrategyGame.PlayerFaction) {
953 			char buf[256];
954 			snprintf(
955 				buf, sizeof(buf), "if (GenericDialog ~= nil) then GenericDialog(\"%s\", \"%s\") end;",
956 				(this->GetBestDisplayTitle() + " " + this->GetFullName() + " Dies").c_str(),
957 				("My lord, the " + FullyDecapitalizeString(this->GetBestDisplayTitle()) + " " + this->GetFullName() + " has died!").c_str()
958 			);
959 			CclCommand(buf);
960 		}
961 	}
962 	*/
963 
964 	this->State = 0;
965 
966 	//check if the hero has government positions in a faction, and if so, remove it from that position
967 	int titles_size = this->Titles.size();
968 	for (int i = (titles_size - 1); i >= 0; --i) {
969 		this->Titles[i].second->SetMinister(this->Titles[i].first, "");
970 	}
971 
972 	this->Province = nullptr;
973 }
974 
SetType(int unit_type_id)975 void CGrandStrategyHero::SetType(int unit_type_id)
976 {
977 	//if the hero's unit type changed
978 	if (unit_type_id != this->Type->Slot) {
979 		this->Type = UnitTypes[unit_type_id];
980 	}
981 
982 	this->UpdateAttributes();
983 }
984 
IsAlive()985 bool CGrandStrategyHero::IsAlive()
986 {
987 	return this->State != 0;
988 }
989 
IsVisible()990 bool CGrandStrategyHero::IsVisible()
991 {
992 	return this->Type->DefaultStat.Variables[GENDER_INDEX].Value == 0 || this->Gender == this->Type->DefaultStat.Variables[GENDER_INDEX].Value; // hero not visible if their unit type has a set gender which is different from the hero's (this is because of instances where i.e. females have a unit type that only has male portraits)
993 }
994 
IsGenerated()995 bool CGrandStrategyHero::IsGenerated()
996 {
997 	return !this->Custom && CCharacter::GetCharacter(this->GetFullName()) == nullptr;
998 }
999 
IsEligibleForTitle(int title)1000 bool CGrandStrategyHero::IsEligibleForTitle(int title)
1001 {
1002 	if (this->GetFaction()->GovernmentType == GovernmentTypeMonarchy && title == CharacterTitleHeadOfState && this->Type->Class != -1 && UnitTypeClasses[this->Type->Class] == "worker") { // commoners cannot become monarchs
1003 		return false;
1004 	} else if (this->GetFaction()->GovernmentType == GovernmentTypeTheocracy && title == CharacterTitleHeadOfState && this->Type->Class != -1 && UnitTypeClasses[this->Type->Class] != "priest") { // non-priests cannot rule theocracies
1005 		return false;
1006 	}
1007 
1008 	for (size_t i = 0; i < this->Titles.size(); ++i) {
1009 		if (this->Titles[i].first == CharacterTitleHeadOfState && this->Titles[i].second->IsAlive() && title != CharacterTitleHeadOfState) { // if it is not a head of state title, and this character is already the head of state of a living faction, return false
1010 			return false;
1011 		} else if (this->Titles[i].first == CharacterTitleHeadOfGovernment && title != CharacterTitleHeadOfState) { // if is already a head of government, don't accept ministerial titles of lower rank (that is, any but the title of head of state)
1012 			return false;
1013 		} else if (this->Titles[i].first != CharacterTitleHeadOfState && this->Titles[i].first != CharacterTitleHeadOfGovernment && title != CharacterTitleHeadOfState && title != CharacterTitleHeadOfGovernment) { // if is already a minister, don't accept another ministerial title of equal rank
1014 			return false;
1015 		}
1016 	}
1017 
1018 	for (size_t i = 0; i < this->ProvinceTitles.size(); ++i) {
1019 		if (this->ProvinceTitles[i].first != CharacterTitleHeadOfState && this->ProvinceTitles[i].first != CharacterTitleHeadOfGovernment && title != CharacterTitleHeadOfState && title != CharacterTitleHeadOfGovernment) { // if already has a government position, don't accept another ministerial title of equal rank
1020 			return false;
1021 		}
1022 	}
1023 
1024 	return true;
1025 }
1026 
GetTroopCostModifier()1027 int CGrandStrategyHero::GetTroopCostModifier()
1028 {
1029 	int modifier = 0;
1030 
1031 	modifier += (this->GetAttributeModifier(IntelligenceAttribute) + this->GetAttributeModifier(this->GetMartialAttribute())) / 2 * -5 / 2; //-2.5% troop cost modifier per average of the intelligence modifier and the strength/dexterity one (depending on which one the character uses)
1032 
1033 	return modifier;
1034 }
1035 
GetTitleScore(int title,CGrandStrategyProvince * province)1036 int CGrandStrategyHero::GetTitleScore(int title, CGrandStrategyProvince *province)
1037 {
1038 	int score = 0;
1039 	if (title == CharacterTitleHeadOfState) {
1040 		score = ((this->Attributes[IntelligenceAttribute] + ((this->Attributes[this->GetMartialAttribute()] + this->Attributes[IntelligenceAttribute]) / 2) + this->Attributes[CharismaAttribute]) / 3) + 1;
1041 	} else if (title == CharacterTitleHeadOfGovernment) {
1042 		score = ((this->Attributes[IntelligenceAttribute] + ((this->Attributes[this->GetMartialAttribute()] + this->Attributes[IntelligenceAttribute]) / 2) + this->Attributes[CharismaAttribute]) / 3) + 1;
1043 	} else if (title == CharacterTitleEducationMinister) {
1044 		score = this->Attributes[IntelligenceAttribute];
1045 	} else if (title == CharacterTitleFinanceMinister) {
1046 		score = this->Attributes[IntelligenceAttribute];
1047 	} else if (title == CharacterTitleWarMinister) {
1048 		score = (this->Attributes[this->GetMartialAttribute()] + this->Attributes[IntelligenceAttribute]) / 2;
1049 	} else if (title == CharacterTitleInteriorMinister) {
1050 		score = this->Attributes[IntelligenceAttribute];
1051 	} else if (title == CharacterTitleJusticeMinister) {
1052 		score = this->Attributes[IntelligenceAttribute];
1053 	} else if (title == CharacterTitleForeignMinister) {
1054 		score = (this->Attributes[CharismaAttribute] + this->Attributes[IntelligenceAttribute]) / 2;
1055 	} else if (title == CharacterTitleGovernor) {
1056 		score = ((this->Attributes[IntelligenceAttribute] + ((this->Attributes[this->GetMartialAttribute()] + this->Attributes[IntelligenceAttribute]) / 2) + this->Attributes[CharismaAttribute]) / 3);
1057 		if (province != nullptr && (province == this->Province || province == this->ProvinceOfOrigin)) {
1058 			score += 1;
1059 		}
1060 	}
1061 
1062 	if (this->Civilization->ID != this->GetFaction()->Civilization) {
1063 		score -= 1; //characters of a different culture than the faction they are in have a smaller chance of getting a ministerial or gubernatorial position
1064 	}
1065 
1066 	return score;
1067 }
1068 
GetMinisterEffectsString(int title)1069 std::string CGrandStrategyHero::GetMinisterEffectsString(int title)
1070 {
1071 	std::string minister_effects_string;
1072 
1073 	bool first = true;
1074 
1075 	if (title == CharacterTitleHeadOfState || title == CharacterTitleWarMinister) {
1076 		int modifier = this->GetTroopCostModifier();
1077 		if (modifier != 0) {
1078 			if (!first) {
1079 				minister_effects_string += ", ";
1080 			} else {
1081 				first = false;
1082 			}
1083 			if (modifier > 0) {
1084 				minister_effects_string += "+";
1085 			}
1086 			minister_effects_string += std::to_string((long long) modifier) + "% Troop Cost";
1087 		}
1088 	}
1089 
1090 	return minister_effects_string;
1091 }
1092 
GetBestDisplayTitle()1093 std::string CGrandStrategyHero::GetBestDisplayTitle()
1094 {
1095 	std::string best_title = this->Type->Name;
1096 	int best_title_type = MaxCharacterTitles;
1097 	/*
1098 	for (size_t i = 0; i < this->Titles.size(); ++i) {
1099 		if (this->Titles[i].second != this->GetFaction()) {
1100 			continue;
1101 		}
1102 		if (this->Titles[i].first < best_title_type) {
1103 			best_title = this->GetFaction()->GetCharacterTitle(this->Titles[i].first, this->Gender);
1104 			best_title_type = this->Titles[i].first;
1105 		}
1106 	}
1107 	for (size_t i = 0; i < this->ProvinceTitles.size(); ++i) {
1108 		if (this->ProvinceTitles[i].first < best_title_type) {
1109 			best_title = this->GetFaction()->GetCharacterTitle(this->ProvinceTitles[i].first, this->Gender);
1110 			best_title_type = this->ProvinceTitles[i].first;
1111 		}
1112 	}
1113 	*/
1114 	return best_title;
1115 }
1116 
GetFaction()1117 CGrandStrategyFaction *CGrandStrategyHero::GetFaction()
1118 {
1119 	if (this->Province != nullptr) {
1120 		return this->Province->Owner;
1121 	} else {
1122 		return this->ProvinceOfOrigin->Owner;
1123 	}
1124 
1125 	return nullptr;
1126 }
1127 
~CGrandStrategyEvent()1128 CGrandStrategyEvent::~CGrandStrategyEvent()
1129 {
1130 	delete Conditions;
1131 
1132 	for (size_t i = 0; i < this->OptionConditions.size(); ++i) {
1133 		delete this->OptionConditions[i];
1134 	}
1135 
1136 	for (size_t i = 0; i < this->OptionEffects.size(); ++i) {
1137 		delete this->OptionEffects[i];
1138 	}
1139 }
1140 
Trigger(CGrandStrategyFaction * faction)1141 void CGrandStrategyEvent::Trigger(CGrandStrategyFaction *faction)
1142 {
1143 //	fprintf(stderr, "Triggering event \"%s\" for faction %s.\n", this->Name.c_str(), PlayerRaces.Factions[faction->Faction]->Name.c_str());
1144 
1145 	CclCommand("EventFaction = GetFactionFromName(\"" + PlayerRaces.Factions[faction->Faction]->Ident + "\");");
1146 	CclCommand("GrandStrategyEvent(EventFaction, \"" + this->Name + "\");");
1147 	CclCommand("EventFaction = nil;");
1148 	CclCommand("EventProvince = nil;");
1149 	CclCommand("SecondEventProvince = nil;");
1150 
1151 	if (!this->Persistent) {
1152 		GrandStrategyGame.AvailableEvents.erase(std::remove(GrandStrategyGame.AvailableEvents.begin(), GrandStrategyGame.AvailableEvents.end(), this), GrandStrategyGame.AvailableEvents.end());
1153 	}
1154 }
1155 
CanTrigger(CGrandStrategyFaction * faction)1156 bool CGrandStrategyEvent::CanTrigger(CGrandStrategyFaction *faction)
1157 {
1158 //	fprintf(stderr, "Checking for triggers for event \"%s\" for faction %s.\n", this->Name.c_str(), PlayerRaces.Factions[faction->Faction]->Name.c_str());
1159 
1160 	if (this->MinYear && GrandStrategyYear < this->MinYear) {
1161 		return false;
1162 	}
1163 
1164 	if (this->MaxYear && GrandStrategyYear > this->MaxYear) {
1165 		return false;
1166 	}
1167 
1168 	if (this->Conditions) {
1169 		this->Conditions->pushPreamble();
1170 		this->Conditions->run(1);
1171 		if (this->Conditions->popBoolean() == false) {
1172 			return false;
1173 		}
1174 	}
1175 
1176 	return true;
1177 }
1178 
1179 /**
1180 **  Get the ID of a province
1181 */
GetProvinceId(std::string province_name)1182 int GetProvinceId(std::string province_name)
1183 {
1184 	if (!province_name.empty()) {
1185 		for (size_t i = 0; i < GrandStrategyGame.Provinces.size(); ++i) {
1186 			if (GrandStrategyGame.Provinces[i]->Name == province_name) {
1187 				return i;
1188 			}
1189 		}
1190 	}
1191 
1192 	return -1;
1193 }
1194 
SetProvinceOwner(std::string province_name,std::string civilization_name,std::string faction_name)1195 void SetProvinceOwner(std::string province_name, std::string civilization_name, std::string faction_name)
1196 {
1197 	int province_id = GetProvinceId(province_name);
1198 	CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1199 	int faction_id = PlayerRaces.GetFactionIndexByName(faction_name);
1200 
1201 	if (!civilization || province_id == -1 || !GrandStrategyGame.Provinces[province_id]) {
1202 		return;
1203 	}
1204 
1205 	GrandStrategyGame.Provinces[province_id]->SetOwner(civilization->ID, faction_id);
1206 }
1207 
SetProvinceSettlementBuilding(std::string province_name,std::string settlement_building_ident,bool has_settlement_building)1208 void SetProvinceSettlementBuilding(std::string province_name, std::string settlement_building_ident, bool has_settlement_building)
1209 {
1210 	int province_id = GetProvinceId(province_name);
1211 	int settlement_building = UnitTypeIdByIdent(settlement_building_ident);
1212 
1213 	if (province_id != -1 && GrandStrategyGame.Provinces[province_id] && settlement_building != -1) {
1214 		GrandStrategyGame.Provinces[province_id]->SetSettlementBuilding(settlement_building, has_settlement_building);
1215 	}
1216 }
1217 
SetProvinceUnitQuantity(std::string province_name,std::string unit_type_ident,int quantity)1218 void SetProvinceUnitQuantity(std::string province_name, std::string unit_type_ident, int quantity)
1219 {
1220 	int province_id = GetProvinceId(province_name);
1221 	int unit_type = UnitTypeIdByIdent(unit_type_ident);
1222 
1223 	if (province_id != -1 && GrandStrategyGame.Provinces[province_id] && unit_type != -1) {
1224 		GrandStrategyGame.Provinces[province_id]->SetUnitQuantity(unit_type, quantity);
1225 	}
1226 }
1227 
ChangeProvinceUnitQuantity(std::string province_name,std::string unit_type_ident,int quantity)1228 void ChangeProvinceUnitQuantity(std::string province_name, std::string unit_type_ident, int quantity)
1229 {
1230 	int province_id = GetProvinceId(province_name);
1231 	int unit_type = UnitTypeIdByIdent(unit_type_ident);
1232 
1233 	if (province_id != -1 && GrandStrategyGame.Provinces[province_id] && unit_type != -1) {
1234 		GrandStrategyGame.Provinces[province_id]->ChangeUnitQuantity(unit_type, quantity);
1235 	}
1236 }
1237 
SetProvinceHero(std::string province_name,std::string hero_full_name,int value)1238 void SetProvinceHero(std::string province_name, std::string hero_full_name, int value)
1239 {
1240 	int province_id = GetProvinceId(province_name);
1241 
1242 	if (province_id != -1 && GrandStrategyGame.Provinces[province_id]) {
1243 		GrandStrategyGame.Provinces[province_id]->SetHero(hero_full_name, value);
1244 	}
1245 }
1246 
SetProvinceFood(std::string province_name,int quantity)1247 void SetProvinceFood(std::string province_name, int quantity)
1248 {
1249 	int province_id = GetProvinceId(province_name);
1250 
1251 	if (province_id != -1 && GrandStrategyGame.Provinces[province_id]) {
1252 		GrandStrategyGame.Provinces[province_id]->PopulationGrowthProgress = std::max(0, quantity);
1253 	}
1254 }
1255 
ChangeProvinceFood(std::string province_name,int quantity)1256 void ChangeProvinceFood(std::string province_name, int quantity)
1257 {
1258 	int province_id = GetProvinceId(province_name);
1259 
1260 	if (province_id != -1 && GrandStrategyGame.Provinces[province_id]) {
1261 		GrandStrategyGame.Provinces[province_id]->PopulationGrowthProgress += quantity;
1262 		GrandStrategyGame.Provinces[province_id]->PopulationGrowthProgress = std::max(0, GrandStrategyGame.Provinces[province_id]->PopulationGrowthProgress);
1263 	}
1264 }
1265 
AddProvinceClaim(std::string province_name,std::string civilization_name,std::string faction_name)1266 void AddProvinceClaim(std::string province_name, std::string civilization_name, std::string faction_name)
1267 {
1268 	int province_id = GetProvinceId(province_name);
1269 
1270 	if (province_id != -1 && GrandStrategyGame.Provinces[province_id]) {
1271 		CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1272 		if (civilization) {
1273 			int faction = PlayerRaces.GetFactionIndexByName(faction_name);
1274 			if (faction != -1) {
1275 				GrandStrategyGame.Provinces[province_id]->AddFactionClaim(civilization->ID, faction);
1276 			} else {
1277 				fprintf(stderr, "Can't find %s faction (%s) to add claim to province %s.\n", faction_name.c_str(), civilization_name.c_str(), province_name.c_str());
1278 			}
1279 		} else {
1280 			fprintf(stderr, "Can't find %s civilization to add the claim of its %s faction claim to province %s.\n", civilization_name.c_str(), faction_name.c_str(), province_name.c_str());
1281 		}
1282 	} else {
1283 		fprintf(stderr, "Can't find %s province to add %s faction (%s) claim to.\n", province_name.c_str(), faction_name.c_str(), civilization_name.c_str());
1284 	}
1285 }
1286 
RemoveProvinceClaim(std::string province_name,std::string civilization_name,std::string faction_name)1287 void RemoveProvinceClaim(std::string province_name, std::string civilization_name, std::string faction_name)
1288 {
1289 	int province_id = GetProvinceId(province_name);
1290 
1291 	if (province_id != -1 && GrandStrategyGame.Provinces[province_id]) {
1292 		CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1293 		if (civilization) {
1294 			int faction = PlayerRaces.GetFactionIndexByName(faction_name);
1295 			if (faction != -1) {
1296 				GrandStrategyGame.Provinces[province_id]->RemoveFactionClaim(civilization->ID, faction);
1297 			}
1298 		}
1299 	}
1300 }
1301 
InitializeGrandStrategyGame(bool show_loading)1302 void InitializeGrandStrategyGame(bool show_loading)
1303 {
1304 	//initialize literary works
1305 	for (size_t i = 0; i < AllUpgrades.size(); ++i) {
1306 		if (AllUpgrades[i]->Work == -1 || AllUpgrades[i]->UniqueOnly) { // literary works that can only appear in unique items wouldn't be publishable
1307 			continue;
1308 		}
1309 
1310 		GrandStrategyGame.UnpublishedWorks.push_back(AllUpgrades[i]);
1311 	}
1312 }
1313 
FinalizeGrandStrategyInitialization()1314 void FinalizeGrandStrategyInitialization()
1315 {
1316 	//initialize literary works
1317 	int works_size = GrandStrategyGame.UnpublishedWorks.size();
1318 	for (int i = (works_size - 1); i >= 0; --i) {
1319 		if (GrandStrategyGame.UnpublishedWorks[i]->Year != 0 && GrandStrategyYear >= GrandStrategyGame.UnpublishedWorks[i]->Year) { //if the game is starting after the publication date of this literary work, remove it from the work list
1320 			GrandStrategyGame.UnpublishedWorks.erase(std::remove(GrandStrategyGame.UnpublishedWorks.begin(), GrandStrategyGame.UnpublishedWorks.end(), GrandStrategyGame.UnpublishedWorks[i]), GrandStrategyGame.UnpublishedWorks.end());
1321 		}
1322 	}
1323 
1324 	for (size_t i = 0; i < GrandStrategyGame.Provinces.size(); ++i) {
1325 		CGrandStrategyProvince *province = GrandStrategyGame.Provinces[i];
1326 		CProvince *base_province = GetProvince(province->Name);
1327 
1328 		// add historical population from regions to provinces here, for a lack of a better place (all province's region belongings need to be defined before this takes place, so this can't happen during the province definitions)
1329 		for (size_t j = 0; j < base_province->Regions.size(); ++j) {
1330 			for (std::map<int, int>::iterator iterator = base_province->Regions[j]->HistoricalPopulation.begin(); iterator != base_province->Regions[j]->HistoricalPopulation.end(); ++iterator) {
1331 				if (base_province->HistoricalPopulation.find(iterator->first) == base_province->HistoricalPopulation.end()) { // if the province doesn't have historical population information for a given year but the region does, then use the region's population quantity divided by the number of provinces the region has
1332 					base_province->HistoricalPopulation[iterator->first] = iterator->second / base_province->Regions[j]->Provinces.size();
1333 				}
1334 			}
1335 		}
1336 
1337 		for (std::map<int, int>::reverse_iterator iterator = base_province->HistoricalPopulation.rbegin(); iterator != base_province->HistoricalPopulation.rend(); ++iterator) {
1338 			if (GrandStrategyYear >= iterator->first) {
1339 				province->SetPopulation(iterator->second);
1340 				break;
1341 			}
1342 		}
1343 
1344 		for (std::map<CUpgrade *, std::map<int, bool>>::iterator iterator = base_province->HistoricalModifiers.begin(); iterator != base_province->HistoricalModifiers.end(); ++iterator) {
1345 			for (std::map<int, bool>::reverse_iterator second_iterator = iterator->second.rbegin(); second_iterator != iterator->second.rend(); ++second_iterator) {
1346 				if (GrandStrategyYear >= second_iterator->first) {
1347 					province->SetModifier(iterator->first, second_iterator->second);
1348 					break;
1349 				}
1350 			}
1351 		}
1352 	}
1353 }
1354 
SetGrandStrategyWorld(std::string world)1355 void SetGrandStrategyWorld(std::string world)
1356 {
1357 	GrandStrategyWorld = world;
1358 }
1359 
DoGrandStrategyTurn()1360 void DoGrandStrategyTurn()
1361 {
1362 	GrandStrategyGame.DoTurn();
1363 }
1364 
ProvinceBordersProvince(std::string province_name,std::string second_province_name)1365 bool ProvinceBordersProvince(std::string province_name, std::string second_province_name)
1366 {
1367 	int province = GetProvinceId(province_name);
1368 	int second_province = GetProvinceId(second_province_name);
1369 
1370 	return GrandStrategyGame.Provinces[province]->BordersProvince(GrandStrategyGame.Provinces[second_province]);
1371 }
1372 
ProvinceBordersFaction(std::string province_name,std::string faction_civilization_name,std::string faction_name)1373 bool ProvinceBordersFaction(std::string province_name, std::string faction_civilization_name, std::string faction_name)
1374 {
1375 	int province = GetProvinceId(province_name);
1376 	CCivilization *civilization = CCivilization::GetCivilization(faction_civilization_name);
1377 	int faction = PlayerRaces.GetFactionIndexByName(faction_name);
1378 
1379 	if (!civilization || faction == -1) {
1380 		return false;
1381 	}
1382 
1383 	return GrandStrategyGame.Provinces[province]->BordersFaction(civilization->ID, faction);
1384 }
1385 
ProvinceHasBuildingClass(std::string province_name,std::string building_class)1386 bool ProvinceHasBuildingClass(std::string province_name, std::string building_class)
1387 {
1388 	int province_id = GetProvinceId(province_name);
1389 
1390 	return GrandStrategyGame.Provinces[province_id]->HasBuildingClass(building_class);
1391 }
1392 
GetProvinceCivilization(std::string province_name)1393 std::string GetProvinceCivilization(std::string province_name)
1394 {
1395 	int province_id = GetProvinceId(province_name);
1396 
1397 	if (GrandStrategyGame.Provinces[province_id]->Civilization != -1) {
1398 		return PlayerRaces.Name[GrandStrategyGame.Provinces[province_id]->Civilization];
1399 	} else {
1400 		return "";
1401 	}
1402 }
1403 
GetProvinceSettlementBuilding(std::string province_name,std::string building_ident)1404 bool GetProvinceSettlementBuilding(std::string province_name, std::string building_ident)
1405 {
1406 	int province_id = GetProvinceId(province_name);
1407 	int building_id = UnitTypeIdByIdent(building_ident);
1408 
1409 	return GrandStrategyGame.Provinces[province_id]->SettlementBuildings[building_id];
1410 }
1411 
GetProvinceUnitQuantity(std::string province_name,std::string unit_type_ident)1412 int GetProvinceUnitQuantity(std::string province_name, std::string unit_type_ident)
1413 {
1414 	int province_id = GetProvinceId(province_name);
1415 	int unit_type = UnitTypeIdByIdent(unit_type_ident);
1416 
1417 	return GrandStrategyGame.Provinces[province_id]->Units[unit_type];
1418 }
1419 
GetProvinceHero(std::string province_name,std::string hero_full_name)1420 int GetProvinceHero(std::string province_name, std::string hero_full_name)
1421 {
1422 	int province_id = GetProvinceId(province_name);
1423 
1424 	if (province_id == -1) {
1425 		fprintf(stderr, "Can't find %s province.\n", province_name.c_str());
1426 		return 0;
1427 	}
1428 
1429 	CGrandStrategyHero *hero = GrandStrategyGame.GetHero(hero_full_name);
1430 	if (hero) {
1431 		if (hero->Province != nullptr && hero->Province->ID == province_id) {
1432 			return hero->State;
1433 		}
1434 	} else {
1435 		fprintf(stderr, "Hero \"%s\" doesn't exist.\n", hero_full_name.c_str());
1436 	}
1437 
1438 	return 0;
1439 }
1440 
GetProvinceMilitaryScore(std::string province_name,bool attacker,bool count_defenders)1441 int GetProvinceMilitaryScore(std::string province_name, bool attacker, bool count_defenders)
1442 {
1443 	int province_id = GetProvinceId(province_name);
1444 
1445 	int military_score = 0;
1446 	if (province_id != -1) {
1447 		if (attacker) {
1448 			military_score = GrandStrategyGame.Provinces[province_id]->AttackingMilitaryScore;
1449 		} else if (count_defenders) {
1450 			military_score = GrandStrategyGame.Provinces[province_id]->MilitaryScore;
1451 		} else {
1452 			military_score = GrandStrategyGame.Provinces[province_id]->OffensiveMilitaryScore;
1453 		}
1454 	}
1455 
1456 	return std::max(1, military_score); // military score must be at least one, since it is a divider in some instances, and we don't want to divide by 0
1457 }
1458 
GetProvinceOwner(std::string province_name)1459 std::string GetProvinceOwner(std::string province_name)
1460 {
1461 	int province_id = GetProvinceId(province_name);
1462 
1463 	if (province_id == -1 || GrandStrategyGame.Provinces[province_id]->Owner == nullptr) {
1464 		return "";
1465 	}
1466 
1467 	return PlayerRaces.Factions[GrandStrategyGame.Provinces[province_id]->Owner->Faction]->Ident;
1468 }
1469 
SetFactionGovernmentType(std::string civilization_name,std::string faction_name,std::string government_type_name)1470 void SetFactionGovernmentType(std::string civilization_name, std::string faction_name, std::string government_type_name)
1471 {
1472 	CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1473 
1474 	int government_type_id = GetGovernmentTypeIdByName(government_type_name);
1475 
1476 	if (government_type_id == -1) {
1477 		return;
1478 	}
1479 
1480 	if (civilization) {
1481 		int faction = PlayerRaces.GetFactionIndexByName(faction_name);
1482 		if (faction != -1) {
1483 			GrandStrategyGame.Factions[civilization->ID][faction]->GovernmentType = government_type_id;
1484 		}
1485 	}
1486 }
1487 
SetFactionDiplomacyStateProposal(std::string civilization_name,std::string faction_name,std::string second_civilization_name,std::string second_faction_name,std::string diplomacy_state_name)1488 void SetFactionDiplomacyStateProposal(std::string civilization_name, std::string faction_name, std::string second_civilization_name, std::string second_faction_name, std::string diplomacy_state_name)
1489 {
1490 	CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1491 	CCivilization *second_civilization = CCivilization::GetCivilization(second_civilization_name);
1492 
1493 	int diplomacy_state_id = GetDiplomacyStateIdByName(diplomacy_state_name);
1494 
1495 	if (civilization && second_civilization) {
1496 		int faction = PlayerRaces.GetFactionIndexByName(faction_name);
1497 		int second_faction = PlayerRaces.GetFactionIndexByName(second_faction_name);
1498 		if (faction != -1 && second_faction != -1) {
1499 			GrandStrategyGame.Factions[civilization->ID][faction]->DiplomacyStateProposals[GrandStrategyGame.Factions[second_civilization->ID][second_faction]] = diplomacy_state_id;
1500 		}
1501 	}
1502 }
1503 
GetFactionDiplomacyStateProposal(std::string civilization_name,std::string faction_name,std::string second_civilization_name,std::string second_faction_name)1504 std::string GetFactionDiplomacyStateProposal(std::string civilization_name, std::string faction_name, std::string second_civilization_name, std::string second_faction_name)
1505 {
1506 	CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1507 	CCivilization *second_civilization = CCivilization::GetCivilization(second_civilization_name);
1508 
1509 	if (civilization && second_civilization) {
1510 		int faction = PlayerRaces.GetFactionIndexByName(faction_name);
1511 		int second_faction = PlayerRaces.GetFactionIndexByName(second_faction_name);
1512 		if (faction != -1 && second_faction != -1) {
1513 			return GetDiplomacyStateNameById(GrandStrategyGame.Factions[civilization->ID][faction]->GetDiplomacyStateProposal(GrandStrategyGame.Factions[second_civilization->ID][second_faction]));
1514 		}
1515 	}
1516 
1517 	return "";
1518 }
1519 
SetFactionTier(std::string civilization_name,std::string faction_name,std::string faction_tier_name)1520 void SetFactionTier(std::string civilization_name, std::string faction_name, std::string faction_tier_name)
1521 {
1522 	CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1523 
1524 	int faction_tier_id = GetFactionTierIdByName(faction_tier_name);
1525 
1526 	if (faction_tier_id == -1) {
1527 		return;
1528 	}
1529 
1530 	if (civilization) {
1531 		int faction = PlayerRaces.GetFactionIndexByName(faction_name);
1532 		if (faction != -1) {
1533 			GrandStrategyGame.Factions[civilization->ID][faction]->FactionTier = faction_tier_id;
1534 		}
1535 	}
1536 }
1537 
GetFactionTier(std::string civilization_name,std::string faction_name)1538 std::string GetFactionTier(std::string civilization_name, std::string faction_name)
1539 {
1540 	CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1541 
1542 	if (civilization) {
1543 		int faction = PlayerRaces.GetFactionIndexByName(faction_name);
1544 		if (faction != -1) {
1545 			return GetFactionTierNameById(GrandStrategyGame.Factions[civilization->ID][faction]->FactionTier);
1546 		}
1547 	}
1548 
1549 	return "";
1550 }
1551 
IsGrandStrategyUnit(const CUnitType & type)1552 bool IsGrandStrategyUnit(const CUnitType &type)
1553 {
1554 	if (!type.BoolFlag[BUILDING_INDEX].value && type.DefaultStat.Variables[DEMAND_INDEX].Value > 0 && type.Class != -1 && UnitTypeClasses[type.Class] != "caravan") {
1555 		return true;
1556 	}
1557 	return false;
1558 }
1559 
IsMilitaryUnit(const CUnitType & type)1560 bool IsMilitaryUnit(const CUnitType &type)
1561 {
1562 	if (IsGrandStrategyUnit(type) && type.Class != -1 && UnitTypeClasses[type.Class] != "worker") {
1563 		return true;
1564 	}
1565 	return false;
1566 }
1567 
SetFactionMinister(std::string civilization_name,std::string faction_name,std::string title_name,std::string hero_full_name)1568 void SetFactionMinister(std::string civilization_name, std::string faction_name, std::string title_name, std::string hero_full_name)
1569 {
1570 	CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1571 	int faction = -1;
1572 	if (civilization) {
1573 		faction = PlayerRaces.GetFactionIndexByName(faction_name);
1574 	}
1575 	int title = GetCharacterTitleIdByName(title_name);
1576 
1577 	if (faction == -1 || title == -1) {
1578 		return;
1579 	}
1580 
1581 	GrandStrategyGame.Factions[civilization->ID][faction]->SetMinister(title, TransliterateText(hero_full_name));
1582 }
1583 
GetFactionMinister(std::string civilization_name,std::string faction_name,std::string title_name)1584 std::string GetFactionMinister(std::string civilization_name, std::string faction_name, std::string title_name)
1585 {
1586 	CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1587 	int faction = -1;
1588 	if (civilization) {
1589 		faction = PlayerRaces.GetFactionIndexByName(faction_name);
1590 	}
1591 	int title = GetCharacterTitleIdByName(title_name);
1592 
1593 	if (faction == -1 || title == -1) {
1594 		return "";
1595 	}
1596 
1597 	if (GrandStrategyGame.Factions[civilization->ID][faction]->Ministers[title] != nullptr) {
1598 		return GrandStrategyGame.Factions[civilization->ID][faction]->Ministers[title]->GetFullName();
1599 	} else {
1600 		return "";
1601 	}
1602 }
1603 
KillGrandStrategyHero(std::string hero_full_name)1604 void KillGrandStrategyHero(std::string hero_full_name)
1605 {
1606 	CGrandStrategyHero *hero = GrandStrategyGame.GetHero(hero_full_name);
1607 	if (hero) {
1608 		hero->Die();
1609 	} else {
1610 		fprintf(stderr, "Hero \"%s\" doesn't exist.\n", hero_full_name.c_str());
1611 	}
1612 }
1613 
GrandStrategyHeroExisted(std::string hero_full_name)1614 void GrandStrategyHeroExisted(std::string hero_full_name)
1615 {
1616 	CGrandStrategyHero *hero = GrandStrategyGame.GetHero(hero_full_name);
1617 	if (hero) {
1618 		hero->Existed = true;
1619 	} else {
1620 		fprintf(stderr, "Hero \"%s\" doesn't exist.\n", hero_full_name.c_str());
1621 	}
1622 }
1623 
GrandStrategyHeroIsAlive(std::string hero_full_name)1624 bool GrandStrategyHeroIsAlive(std::string hero_full_name)
1625 {
1626 	CGrandStrategyHero *hero = GrandStrategyGame.GetHero(hero_full_name);
1627 	if (hero) {
1628 		return hero->IsAlive();
1629 	} else {
1630 		fprintf(stderr, "Hero \"%s\" doesn't exist.\n", hero_full_name.c_str());
1631 	}
1632 	return false;
1633 }
1634 
GrandStrategyWorkCreated(std::string work_ident)1635 void GrandStrategyWorkCreated(std::string work_ident)
1636 {
1637 	CUpgrade *work = CUpgrade::Get(work_ident);
1638 	if (work) {
1639 		GrandStrategyGame.UnpublishedWorks.erase(std::remove(GrandStrategyGame.UnpublishedWorks.begin(), GrandStrategyGame.UnpublishedWorks.end(), work), GrandStrategyGame.UnpublishedWorks.end()); // remove work from the vector, so that it doesn't get created again
1640 	} else {
1641 		fprintf(stderr, "Work \"%s\" doesn't exist.\n", work_ident.c_str());
1642 	}
1643 }
1644 
MakeGrandStrategyEventAvailable(std::string event_name)1645 void MakeGrandStrategyEventAvailable(std::string event_name)
1646 {
1647 	CGrandStrategyEvent *event = GetGrandStrategyEvent(event_name);
1648 	if (event) {
1649 		GrandStrategyGame.AvailableEvents.push_back(event);
1650 	} else {
1651 		fprintf(stderr, "Grand strategy event \"%s\" doesn't exist.\n", event_name.c_str());
1652 	}
1653 }
1654 
GetGrandStrategyEventTriggered(std::string event_name)1655 bool GetGrandStrategyEventTriggered(std::string event_name)
1656 {
1657 	CGrandStrategyEvent *event = GetGrandStrategyEvent(event_name);
1658 	if (event) {
1659 		return std::find(GrandStrategyGame.AvailableEvents.begin(), GrandStrategyGame.AvailableEvents.end(), event) == GrandStrategyGame.AvailableEvents.end();
1660 	} else {
1661 		fprintf(stderr, "Grand strategy event \"%s\" doesn't exist.\n", event_name.c_str());
1662 		return false;
1663 	}
1664 }
1665 
CleanGrandStrategyEvents()1666 void CleanGrandStrategyEvents()
1667 {
1668 	for (size_t i = 0; i < GrandStrategyEvents.size(); ++i) {
1669 		delete GrandStrategyEvents[i];
1670 	}
1671 	GrandStrategyEvents.clear();
1672 	GrandStrategyEventStringToPointer.clear();
1673 }
1674 
GetGrandStrategyEvent(std::string event_name)1675 CGrandStrategyEvent *GetGrandStrategyEvent(std::string event_name)
1676 {
1677 	if (GrandStrategyEventStringToPointer.find(event_name) != GrandStrategyEventStringToPointer.end()) {
1678 		return GrandStrategyEventStringToPointer[event_name];
1679 	}
1680 
1681 	return nullptr;
1682 }
1683 
GetDiplomacyStateNameById(int diplomacy_state)1684 std::string GetDiplomacyStateNameById(int diplomacy_state)
1685 {
1686 	if (diplomacy_state == DiplomacyStatePeace) {
1687 		return "peace";
1688 	} else if (diplomacy_state == DiplomacyStateWar) {
1689 		return "war";
1690 	} else if (diplomacy_state == DiplomacyStateAlliance) {
1691 		return "alliance";
1692 	} else if (diplomacy_state == DiplomacyStateVassal) {
1693 		return "vassal";
1694 	} else if (diplomacy_state == DiplomacyStateOverlord) {
1695 		return "overlord";
1696 	} else if (diplomacy_state == -1) {
1697 		return "";
1698 	}
1699 
1700 	return "";
1701 }
1702 
GetDiplomacyStateIdByName(std::string diplomacy_state)1703 int GetDiplomacyStateIdByName(std::string diplomacy_state)
1704 {
1705 	if (diplomacy_state == "peace") {
1706 		return DiplomacyStatePeace;
1707 	} else if (diplomacy_state == "war") {
1708 		return DiplomacyStateWar;
1709 	} else if (diplomacy_state == "alliance") {
1710 		return DiplomacyStateAlliance;
1711 	} else if (diplomacy_state == "vassal") {
1712 		return DiplomacyStateVassal;
1713 	} else if (diplomacy_state == "overlord") {
1714 		return DiplomacyStateOverlord;
1715 	}
1716 
1717 	return -1;
1718 }
1719 
GetFactionTierNameById(int faction_tier)1720 std::string GetFactionTierNameById(int faction_tier)
1721 {
1722 	if (faction_tier == FactionTierNoFactionTier) {
1723 		return "no-faction-tier";
1724 	} else if (faction_tier == FactionTierBarony) {
1725 		return "barony";
1726 	} else if (faction_tier == FactionTierCounty) {
1727 		return "county";
1728 	} else if (faction_tier == FactionTierDuchy) {
1729 		return "duchy";
1730 	} else if (faction_tier == FactionTierGrandDuchy) {
1731 		return "grand-duchy";
1732 	} else if (faction_tier == FactionTierKingdom) {
1733 		return "kingdom";
1734 	} else if (faction_tier == FactionTierEmpire) {
1735 		return "empire";
1736 	}
1737 
1738 	return "";
1739 }
1740 
GetFactionTierIdByName(std::string faction_tier)1741 int GetFactionTierIdByName(std::string faction_tier)
1742 {
1743 	if (faction_tier == "no-faction-tier") {
1744 		return FactionTierNoFactionTier;
1745 	} else if (faction_tier == "barony") {
1746 		return FactionTierBarony;
1747 	} else if (faction_tier == "county") {
1748 		return FactionTierCounty;
1749 	} else if (faction_tier == "duchy") {
1750 		return FactionTierDuchy;
1751 	} else if (faction_tier == "grand-duchy") {
1752 		return FactionTierGrandDuchy;
1753 	} else if (faction_tier == "kingdom") {
1754 		return FactionTierKingdom;
1755 	} else if (faction_tier == "empire") {
1756 		return FactionTierEmpire;
1757 	}
1758 
1759 	return -1;
1760 }
1761