UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cp_nation.cpp
Go to the documentation of this file.
1 
7 /*
8 Copyright (C) 2002-2020 UFO: Alien Invasion.
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 
19 See the GNU General Public License for more details.
20 
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 */
25 
26 #include "../../cl_shared.h"
27 #include "../../../shared/parse.h"
28 #include "../../../shared/shared.h"
29 #include "cp_campaign.h"
30 #include "cp_geoscape.h"
31 #include "cp_ufo.h"
32 #include "cp_time.h"
33 #include "save/save_nation.h"
34 #include "../../ui/node/ui_node_linechart.h" /* lineStrip_t */
35 #include "cp_missions.h"
36 
37 /* nation happiness constants */
38 #define HAPPINESS_ALIEN_MISSION_LOSS -0.02
39 #define HAPPINESS_MAX_MISSION_IMPACT 0.07
40 
46 {
47  const int nationIndex = rand() % ccs.numNations;
48  int i = 0;
49  NAT_Foreach(nation) {
50  if (i == nationIndex)
51  return nation;
52  i++;
53  }
54 
55  return nullptr;
56 }
57 
63 nation_t* NAT_GetNationByID (const char* nationID)
64 {
65  if (nationID == nullptr) {
66  cgi->Com_Printf("NAT_GetNationByID: nullptr nationID\n");
67  return nullptr;
68  }
69  NAT_Foreach(nation) {
70  if (Q_streq(nation->id, nationID))
71  return nation;
72  }
73 
74  cgi->Com_Printf("NAT_GetNationByID: Could not find nation '%s'\n", nationID);
75  return nullptr;
76 }
77 
83 void NAT_UpdateHappinessForAllNations (const float minhappiness)
84 {
85  MIS_Foreach(mission) {
86  nation_t* nation = GEO_GetNation(mission->pos);
87  /* Difficulty modifier range is [0, 0.02f] */
88 
89  /* Some non-water location have no nation */
90  if (!nation)
91  continue;
92 
93  float happinessFactor;
94  const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation);
95  switch (mission->stage) {
97  case STAGE_SUBVERT_GOV:
98  case STAGE_RECON_GROUND:
99  case STAGE_SPREAD_XVI:
100  case STAGE_HARVEST:
101  happinessFactor = HAPPINESS_ALIEN_MISSION_LOSS;
102  break;
103  default:
104  /* mission is not active on earth or does not have any influence
105  * on the nation happiness, skip this mission */
106  continue;
107  }
108 
109  NAT_SetHappiness(minhappiness, nation, stats->happiness + happinessFactor);
110  cgi->Com_DPrintf(DEBUG_CLIENT, "Happiness of nation %s decreased: %.02f\n", nation->name, stats->happiness);
111  }
112 }
113 
120 int NAT_GetFunding (const nation_t* const nation, int month)
121 {
122  assert(month >= 0);
123  assert(month < MONTHS_PER_YEAR);
124  return nation->maxFunding * nation->stats[month].happiness;
125 }
126 
132 const nationInfo_t* NAT_GetCurrentMonthInfo (const nation_t* const nation)
133 {
134  return &nation->stats[0];
135 }
136 
143 const char* NAT_GetHappinessString (const float happiness)
144 {
145  if (happiness < 0.015)
146  return _("Giving up");
147  else if (happiness < 0.025)
148  return _("Furious");
149  else if (happiness < 0.04)
150  return _("Angry");
151  else if (happiness < 0.06)
152  return _("Mad");
153  else if (happiness < 0.10)
154  return _("Upset");
155  else if (happiness < 0.20)
156  return _("Tolerant");
157  else if (happiness < 0.30)
158  return _("Neutral");
159  else if (happiness < 0.50)
160  return _("Content");
161  else if (happiness < 0.70)
162  return _("Pleased");
163  else if (happiness < 0.95)
164  return _("Happy");
165  else
166  return _("Exuberant");
167 
168 }
169 
176 const char* NAT_GetCurrentHappinessString (const nation_t* nation)
177 {
178  const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation);
179  return NAT_GetHappinessString(stats->happiness);
180 }
181 
188 void NAT_SetHappiness (const float minhappiness, nation_t* nation, const float happiness)
189 {
190  const char* oldString = NAT_GetCurrentHappinessString(nation);
191  const char* newString;
192  nationInfo_t* stats = &nation->stats[0];
193  const float oldHappiness = stats->happiness;
194  const float middleHappiness = (minhappiness + 1.0) / 2;
195  notify_t notifyType = NT_NUM_NOTIFYTYPE;
196 
197  stats->happiness = happiness;
198  if (stats->happiness < 0.0f)
199  stats->happiness = 0.0f;
200  else if (stats->happiness > 1.0f)
201  stats->happiness = 1.0f;
202 
203  newString = NAT_GetCurrentHappinessString(nation);
204 
205  if (oldString != newString) {
207  _("Nation %s changed happiness from %s to %s"), _(nation->name), oldString, newString);
208  notifyType = NT_HAPPINESS_CHANGED;
209  } else if (oldHappiness > middleHappiness && happiness < middleHappiness) {
211  _("Nation %s changed happiness to %s"), _(nation->name), newString);
212  notifyType = NT_HAPPINESS_PLEASED;
213  } else if (happiness < minhappiness && oldHappiness > minhappiness) {
215  _("Happiness of nation %s is %s and less than minimal happiness allowed to the campaign"), _(nation->name), newString);
216  notifyType = NT_HAPPINESS_MIN;
217  } else {
218  return;
219  }
220 
221  MSO_CheckAddNewMessage(notifyType, _("Nation changed happiness"), cp_messageBuffer);
222 }
223 
229 {
231 
232  NAT_Foreach(nation) {
234  cgi->XML_AddString(s, SAVE_NATION_ID, nation->id);
235  for (int j = 0; j < MONTHS_PER_YEAR; j++) {
236  const nationInfo_t* stats = &nation->stats[j];
237 
238  if (!stats->inuse)
239  continue;
240 
242  cgi->XML_AddInt(ss, SAVE_NATION_MONTH_IDX, j);
243  cgi->XML_AddFloat(ss, SAVE_NATION_HAPPINESS, stats->happiness);
244  cgi->XML_AddInt(ss, SAVE_NATION_XVI, stats->xviInfection);
245  }
246  }
247  return true;
248 }
249 
263 void CP_HandleNationData (float minHappiness, mission_t* mis, const nation_t* affectedNation, const missionResults_t* results, bool won)
264 {
265  const float civilianSum = (float) (results->civiliansSurvived + results->civiliansKilled + results->civiliansKilledFriendlyFire);
266  const float alienSum = (float) (results->aliensSurvived + results->aliensKilled + results->aliensStunned);
267  float performance;
268  float performanceAlien;
269  float performanceCivilian;
270  float deltaHappiness = 0.0f;
271  float happinessDivisor = 5.0f;
272  float victoryBonusPerAlien = 0.1f;
273 
274  if (mis->mapDef->victoryBonusPerAlien) {
275  victoryBonusPerAlien = mis->mapDef->victoryBonusPerAlien;
276  }
277 
280  if (civilianSum <= 1) {
281  performanceCivilian = 0.0f;
282  } else {
283  performanceCivilian = (2 * civilianSum - results->civiliansKilled - 2
284  * results->civiliansKilledFriendlyFire) * 3 / (2 * civilianSum) - 2;
285  }
286 
287  /* Calculate how well the mission went. */
291  if (won) {
292  performanceAlien = (results->aliensKilled + results->aliensStunned) * victoryBonusPerAlien;
293  } else {
294  performanceAlien = results->aliensKilled + results->aliensStunned - alienSum;
295  }
296  performance = performanceCivilian + performanceAlien;
297 
298  /* Calculate the actual happiness delta. The bigger the mission, the more potential influence. */
299  deltaHappiness = 0.004 * civilianSum + 0.004 * alienSum;
300 
301  /* There is a maximum base happiness delta. */
302  if (deltaHappiness > HAPPINESS_MAX_MISSION_IMPACT)
303  deltaHappiness = HAPPINESS_MAX_MISSION_IMPACT;
304 
305  NAT_Foreach(nation) {
306  const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation);
307  float happinessFactor;
308 
309  /* update happiness. */
310  if (nation == affectedNation)
311  happinessFactor = deltaHappiness;
312  else
313  happinessFactor = deltaHappiness / happinessDivisor;
314 
315  NAT_SetHappiness(minHappiness, nation, stats->happiness + performance * happinessFactor);
316  }
317 }
318 
324 {
325  xmlNode_t* n;
326  xmlNode_t* s;
327 
329  if (!n)
330  return false;
331 
332  /* nations loop */
334  xmlNode_t* ss;
336 
337  if (!nation)
338  return false;
339 
340  /* month loop */
341  for (ss = cgi->XML_GetNode(s, SAVE_NATION_MONTH); ss; ss = cgi->XML_GetNextNode(ss, s, SAVE_NATION_MONTH)) {
342  int monthIDX = cgi->XML_GetInt(ss, SAVE_NATION_MONTH_IDX, MONTHS_PER_YEAR);
343  nationInfo_t* stats = &nation->stats[monthIDX];
344 
345  if (monthIDX < 0 || monthIDX >= MONTHS_PER_YEAR)
346  return false;
347 
348  stats->inuse = true;
349  stats->happiness = cgi->XML_GetFloat(ss, SAVE_NATION_HAPPINESS, 0.0);
350  stats->xviInfection = cgi->XML_GetInt(ss, SAVE_NATION_XVI, 0);
351  }
352  }
353  return true;
354 }
355 
356 /*==========================================
357 Parsing
358 ==========================================*/
359 
360 static const value_t nation_vals[] = {
361  {"name", V_TRANSLATION_STRING, offsetof(nation_t, name), 0},
362  {"pos", V_POS, offsetof(nation_t, pos), MEMBER_SIZEOF(nation_t, pos)},
363  {"color", V_COLOR, offsetof(nation_t, color), MEMBER_SIZEOF(nation_t, color)},
364  {"funding", V_INT, offsetof(nation_t, maxFunding), MEMBER_SIZEOF(nation_t, maxFunding)},
365  {"happiness", V_FLOAT, offsetof(nation_t, stats[0].happiness), MEMBER_SIZEOF(nation_t, stats[0].happiness)},
366  {"soldiers", V_INT, offsetof(nation_t, maxSoldiers), MEMBER_SIZEOF(nation_t, maxSoldiers)},
367  {"scientists", V_INT, offsetof(nation_t, maxScientists), MEMBER_SIZEOF(nation_t, maxScientists)},
368  {"workers", V_INT, offsetof(nation_t, maxWorkers), MEMBER_SIZEOF(nation_t, maxWorkers)},
369  {"pilots", V_INT, offsetof(nation_t, maxPilots), MEMBER_SIZEOF(nation_t, maxPilots)},
370 
371  {nullptr, V_NULL, 0, 0}
372 };
373 
382 void CL_ParseNations (const char* name, const char** text)
383 {
384  /* search for nations with same name */
385  NAT_Foreach(n) {
386  if (Q_streq(name, n->id)) {
387  cgi->Com_Printf("CL_ParseNations: nation def \"%s\" with same name found, second ignored\n", name);
388  return;
389  }
390  }
391 
392  /* initialize the nation */
393  nation_t nation;
394  OBJZERO(nation);
395  nation.idx = ccs.numNations;
396  nation.stats[0].inuse = true;
397 
398  if (cgi->Com_ParseBlock(name, text, &nation, nation_vals, cp_campaignPool)) {
399  ccs.numNations++;
400 
401  cgi->Com_DPrintf(DEBUG_CLIENT, "...found nation %s\n", name);
402  nation.id = cgi->PoolStrDup(name, cp_campaignPool, 0);
403  LIST_Add(&ccs.nations, nation);
404  }
405 }
406 
411 city_t* CITY_GetById (const char* cityId)
412 {
413  LIST_Foreach(ccs.cities, city_t, city) {
414  if (Q_streq(cityId, city->id))
415  return city;
416  }
417  return nullptr;
418 }
419 
425 {
426  LIST_Foreach(ccs.cities, city_t, city) {
427  if (Vector2Equal(pos, city->pos))
428  return city;
429  }
430  return nullptr;
431 }
432 
433 static const value_t city_vals[] = {
434  {"name", V_TRANSLATION_STRING, offsetof(city_t, name), 0},
435  {"pos", V_POS, offsetof(city_t, pos), MEMBER_SIZEOF(city_t, pos)},
436 
437  {nullptr, V_NULL, 0, 0}
438 };
439 
445 void CITY_Parse (const char* name, const char** text)
446 {
447  city_t newCity;
448 
449  /* search for cities with same name */
450  if (CITY_GetById(name)) {
451  cgi->Com_Printf("CITY_Parse: city def \"%s\" with same name found, second ignored\n", name);
452  return;
453  }
454 
455  OBJZERO(newCity);
456  newCity.idx = ccs.numCities;
457 
458  if (cgi->Com_ParseBlock(name, text, &newCity, city_vals, cp_campaignPool)) {
459  ccs.numCities++;
460  newCity.id = cgi->PoolStrDup(name, cp_campaignPool, 0);
461  /* Add city to the list */
462  LIST_Add(&ccs.cities, newCity);
463  }
464 }
465 
471 {
472  int error = 0;
473 
474  /* Check if there is at least one map fitting city parameter for terror mission */
475  LIST_Foreach(ccs.cities, city_t, city) {
476  bool cityCanBeUsed = false;
477  bool parametersFit = false;
478  ufoType_t ufoTypes[UFO_MAX];
479  int numTypes;
480  const mapDef_t* md;
481 
482  if (!city->name) {
483  error++;
484  cgi->Com_Printf("...... city '%s' has no name\n", city->id);
485  }
486 
487  if (MapIsWater(GEO_GetColor(city->pos, MAPTYPE_TERRAIN, nullptr))) {
488  error++;
489  cgi->Com_Printf("...... city '%s' has a position in the water\n", city->id);
490  }
491 
493 
495  if (md->storyRelated)
496  continue;
497 
498  if (GEO_PositionFitsTCPNTypes(city->pos, md->terrains, md->cultures, md->populations, nullptr)) {
499  /* this map fits city parameter, check if we have some terror mission UFOs available for this map */
500  parametersFit = true;
501 
502  /* no UFO on this map (LIST_ContainsString doesn't like empty string) */
503  if (!md->ufos) {
504  continue;
505  }
506 
507  /* loop must be backward, as we remove items */
508  for (int i = numTypes - 1 ; i >= 0; i--) {
509  if (cgi->LIST_ContainsString(md->ufos, cgi->Com_UFOTypeToShortName(ufoTypes[i]))) {
510  REMOVE_ELEM(ufoTypes, i, numTypes);
511  }
512  }
513  }
514  if (numTypes == 0) {
515  cityCanBeUsed = true;
516  break;
517  }
518  }
519 
520  if (!cityCanBeUsed) {
521  error++;
522  cgi->Com_Printf("...... city '%s' can't be used in game: it has no map fitting parameters\n", city->id);
523  if (parametersFit) {
524  cgi->Com_Printf(" (No map fitting");
525  for (int i = 0 ; i < numTypes; i++)
526  cgi->Com_Printf(" %s", cgi->Com_UFOTypeToShortName(ufoTypes[i]));
527  cgi->Com_Printf(")\n");
528  }
530  }
531  }
532 
533  return !error;
534 }
535 
536 /*=====================================
537 Menu functions
538 =====================================*/
539 
543 static void NAT_ListStats_f (void)
544 {
545  const int argCount = cgi->Cmd_Argc();
546  if (argCount < 2) {
547  cgi->Com_Printf("Usage: %s <confunc> [nationID] [monthIDX]\n", cgi->Cmd_Argv(0));
548  return;
549  }
550  char callback[MAX_VAR];
551  Q_strncpyz(callback, cgi->Cmd_Argv(1), sizeof(callback));
552 
553  NAT_Foreach(nation) {
554  if (argCount >= 3 && !Q_streq(nation->id, cgi->Cmd_Argv(1)))
555  continue;
556 
557  for (int monthIDX = 0; monthIDX < MONTHS_PER_YEAR; monthIDX++) {
558  if (!nation->stats[monthIDX].inuse)
559  break;
560 
561  if (argCount >= 4 && monthIDX != -1 * atoi(cgi->Cmd_Argv(3)))
562  continue;
563 
564  cgi->UI_ExecuteConfunc("%s %s \"%s\" %d %.4f \"%s\" %d \"%f, %f, %f, %f\"",
565  callback,
566  nation->id,
567  _(nation->name),
568  monthIDX,
569  nation->stats[monthIDX].happiness,
570  NAT_GetHappinessString(nation->stats[monthIDX].happiness),
571  NAT_GetFunding(nation, monthIDX),
572  nation->color[0],
573  nation->color[1],
574  nation->color[2],
575  nation->color[3]
576  );
577  }
578  }
579 }
580 
584 static void NAT_DrawCharts_f (void)
585 {
586  const int argCount = cgi->Cmd_Argc();
587  if (argCount < 5) {
588  cgi->Com_Printf("Usage: %s <stat_type> <chartnode> <width> <height>\n", cgi->Cmd_Argv(0));
589  return;
590  }
591 
592  char type[MAX_VAR];
593  Q_strncpyz(type, cgi->Cmd_Argv(1), MAX_VAR);
594  char nodePath[255];
595  Q_strncpyz(nodePath, cgi->Cmd_Argv(2), 255);
596  const int width = atoi(cgi->Cmd_Argv(3));
597  const int height = atoi(cgi->Cmd_Argv(4));
598  if (width <= 0 || height <= 0)
599  return;
600 
601  const int dx = (int)(width / MONTHS_PER_YEAR);
602 
603  /* Calculate chart bounds (maximums) */
604  int maxFunding;
605  float maxXVI;
606  int nationIdx = 0;
607  NAT_Foreach(nation) {
608  for (int monthIdx = 0; monthIdx < MONTHS_PER_YEAR; monthIdx++) {
609  if (!nation->stats[monthIdx].inuse)
610  break;
611 
612  if (nationIdx == 0 && monthIdx == 0) {
613  maxFunding = NAT_GetFunding(nation, monthIdx);
614  maxXVI = nation->stats[monthIdx].xviInfection;
615  } else {
616  if (maxFunding < NAT_GetFunding(nation, monthIdx))
617  maxFunding = NAT_GetFunding(nation, monthIdx);
618  if (maxXVI < nation->stats[monthIdx].xviInfection)
619  maxXVI = nation->stats[monthIdx].xviInfection;
620  }
621  }
622  nationIdx++;
623  }
624 
625  const float dyFunding = (0 != maxFunding) ? (float) height / maxFunding : 1;
626  const float dyXvi = (0 != maxXVI) ? (float) height / maxXVI : 1;
627 
628  uiNode_t* chart = cgi->UI_GetNodeByPath(nodePath);
629  if (chart == nullptr) {
630  cgi->Com_Printf("chart node not found\n");
631  return;
632  }
633  /* Fill the points */
634  NAT_Foreach(nation) {
635  cgi->UI_ExecuteConfunc("ui_nation_graph_add_line %s %s %f %f %f %f %s %d",
636  nation->id,
637  "true",
638  nation->color[0],
639  nation->color[1],
640  nation->color[2],
641  nation->color[3],
642  "true",
643  12
644  );
645 
646  int monthIdx;
647  for (monthIdx = 0; monthIdx < MONTHS_PER_YEAR; monthIdx++) {
648  if (!nation->stats[monthIdx].inuse)
649  break;
650 
651  if (Q_streq("funding", type)) {
652  const int funding = NAT_GetFunding(nation, monthIdx);
653  cgi->UI_ExecuteConfunc("ui_nation_graph_add_point %s %d %f",
654  nation->id, (monthIdx * dx), height - dyFunding * funding);
655  } else if (Q_streq("happiness", type)) {
656  cgi->UI_ExecuteConfunc("ui_nation_graph_add_point %s %d %f",
657  nation->id, (monthIdx * dx), height - (height * nation->stats[monthIdx].happiness));
658  } else if (Q_streq("xvi", type)) {
659  const int xviInfection= nation->stats[monthIdx].xviInfection;
660  cgi->UI_ExecuteConfunc("ui_nation_graph_add_point %s %d %f",
661  nation->id, (monthIdx * dx), height - dyXvi * xviInfection);
662  }
663  }
664  nationIdx++;
665  }
666 }
667 
668 #ifdef DEBUG
669 
673 static void NAT_ListCities_f (void)
674 {
675  LIST_Foreach(ccs.cities, city_t, city) {
676  cgi->Com_Printf("City '%s' -- position (%0.1f, %0.1f)\n", city->id, city->pos[0], city->pos[1]);
678  }
679 }
680 
685 static void NAT_NationList_f (void)
686 {
687  NAT_Foreach(nation) {
688  cgi->Com_Printf("Nation ID: %s\n", nation->id);
689  cgi->Com_Printf("...max-funding %i c\n", nation->maxFunding);
690  cgi->Com_Printf("...happiness %0.2f\n", nation->stats[0].happiness);
691  cgi->Com_Printf("...xviInfection %i\n", nation->stats[0].xviInfection);
692  cgi->Com_Printf("...max-soldiers %i\n", nation->maxSoldiers);
693  cgi->Com_Printf("...max-scientists %i\n", nation->maxScientists);
694  cgi->Com_Printf("...max-workers %i\n", nation->maxWorkers);
695  cgi->Com_Printf("...max-pilots %i\n", nation->maxPilots);
696  cgi->Com_Printf("...color r:%.2f g:%.2f b:%.2f a:%.2f\n", nation->color[0], nation->color[1], nation->color[2], nation->color[3]);
697  cgi->Com_Printf("...pos x:%.0f y:%.0f\n", nation->pos[0], nation->pos[1]);
698  }
699 }
700 #endif
701 
714 void NAT_HandleBudget (const campaign_t* campaign)
715 {
716  char message[1024];
717  int cost;
718  int totalIncome = 0;
719  int totalExpenditure = 0;
720  int initialCredits = ccs.credits;
721  const salary_t* salary = &campaign->salaries;
722 
723  NAT_Foreach(nation) {
724  const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation);
725  const int funding = NAT_GetFunding(nation, 0);
726  int newScientists = 0, newSoldiers = 0, newPilots = 0, newWorkers = 0;
727 
728  totalIncome += funding;
729 
730  for (int j = 0; 0.25 + j < (float) nation->maxScientists * stats->happiness * ccs.curCampaign->employeeRate; j++) {
731  /* Create a scientist, but don't auto-hire her. */
732  E_CreateEmployee(EMPL_SCIENTIST, nation, nullptr);
733  newScientists++;
734  }
735 
736  if (stats->happiness > 0) {
737  for (int j = 0; 0.25 + j < (float) nation->maxSoldiers * stats->happiness * ccs.curCampaign->employeeRate; j++) {
738  /* Create a soldier. */
739  E_CreateEmployee(EMPL_SOLDIER, nation, nullptr);
740  newSoldiers++;
741  }
742  }
743  /* pilots */
744  if (stats->happiness > 0) {
745  for (int j = 0; 0.25 + j < (float) nation->maxPilots * stats->happiness * ccs.curCampaign->employeeRate; j++) {
746  /* Create a pilot. */
747  E_CreateEmployee(EMPL_PILOT, nation, nullptr);
748  newPilots++;
749  }
750  }
751 
752  for (int j = 0; 0.25 + j * 2 < (float) nation->maxWorkers * stats->happiness * ccs.curCampaign->employeeRate; j++) {
753  /* Create a worker. */
754  E_CreateEmployee(EMPL_WORKER, nation, nullptr);
755  newWorkers++;
756  }
757 
758  Com_sprintf(message, sizeof(message), _("Gained %i %s, %i %s, %i %s, %i %s, and %i %s from nation %s (%s)"),
759  funding, ngettext("credit", "credits", funding),
760  newScientists, ngettext("scientist", "scientists", newScientists),
761  newSoldiers, ngettext("soldier", "soldiers", newSoldiers),
762  newPilots, ngettext("pilot", "pilots", newPilots),
763  newWorkers, ngettext("worker", "workers", newWorkers),
764  _(nation->name), NAT_GetCurrentHappinessString(nation));
765  MS_AddNewMessage(_("Notice"), message);
766  }
767 
768  for (int i = 0; i < MAX_EMPL; i++) {
769  int count = 0;
770  cost = 0;
771  E_Foreach(i, employee) {
772  if (!employee->isHired())
773  continue;
774  cost += employee->salary();
775  count++;
776  }
777  totalExpenditure += cost;
778 
779  if (cost == 0)
780  continue;
781 
782  Com_sprintf(message, sizeof(message), _("Paid %i credits to: %s"), cost, E_GetEmployeeString((employeeType_t)i, count));
783  MS_AddNewMessage(_("Notice"), message);
784  }
785 
786  cost = 0;
787  AIR_Foreach(aircraft) {
788  if (aircraft->status == AIR_CRASHED)
789  continue;
790  cost += aircraft->price * salary->aircraftFactor / salary->aircraftDivisor;
791  }
792  totalExpenditure += cost;
793 
794  if (cost != 0) {
795  Com_sprintf(message, sizeof(message), _("Paid %i credits for aircraft"), cost);
796  MS_AddNewMessage(_("Notice"), message);
797  }
798 
799  base_t* base = nullptr;
800  while ((base = B_GetNext(base)) != nullptr) {
801  cost = CP_GetSalaryUpKeepBase(salary, base);
802  totalExpenditure += cost;
803 
804  Com_sprintf(message, sizeof(message), _("Paid %i credits for upkeep of %s"), cost, base->name);
805  MS_AddNewMessage(_("Notice"), message);
806  }
807 
808  if (initialCredits < 0) {
809  const float interest = initialCredits * campaign->salaries.debtInterest;
810 
811  cost = (int)ceil(interest);
812  Com_sprintf(message, sizeof(message), _("Paid %i credits in interest on your debt."), cost);
813  totalExpenditure += cost;
814  MS_AddNewMessage(_("Notice"), message);
815  }
816  CP_UpdateCredits(ccs.credits - totalExpenditure + totalIncome);
817  CP_GameTimeStop();
818 }
819 
827 {
832  NAT_Foreach(nation) {
833  for (int i = MONTHS_PER_YEAR - 1; i > 0; i--) { /* Reverse copy to not overwrite with wrong data */
834  nation->stats[i] = nation->stats[i - 1];
835  }
836  }
837 }
838 
839 static const cmdList_t nationCmds[] = {
840  {"nation_getstats", NAT_ListStats_f, "Returns nation happiness and funding stats through a UI callback."},
841  {"nation_drawcharts", NAT_DrawCharts_f, "Draws nation happiness and funding charts."},
842 #ifdef DEBUG
843  {"debug_listcities", NAT_ListCities_f, "Debug function to list all cities in game."},
844  {"debug_listnations", NAT_NationList_f, "List all nations on the game console"},
845 #endif
846  {nullptr, nullptr, nullptr}
847 };
848 
852 void NAT_InitStartup (void)
853 {
854  cgi->Cmd_TableAddList(nationCmds);
855 }
856 
860 void NAT_Shutdown (void)
861 {
862  cgi->LIST_Delete(&ccs.cities);
863  cgi->LIST_Delete(&ccs.nations);
864 
865  cgi->Cmd_TableRemoveList(nationCmds);
866 }
const nationInfo_t * NAT_GetCurrentMonthInfo(const nation_t *const nation)
Get the current month nation stats.
Definition: cp_nation.cpp:132
nation_t * NAT_GetNationByID(const char *nationID)
Return a nation-pointer by the nations id.
Definition: cp_nation.cpp:63
uiNode_t *IMPORT * UI_GetNodeByPath(const char *path)
#define Vector2Equal(a, b)
Definition: vector.h:67
linkedList_t * terrains
Definition: q_shared.h:486
int maxFunding
Definition: cp_nation.h:58
uiMessageListNodeMessage_t * MSO_CheckAddNewMessage(const notify_t messagecategory, const char *title, const char *text, messageType_t type, technology_t *pedia, bool popup)
Adds a new message to message stack. It uses message settings to verify whether sound should be playe...
#define SAVE_NATION_XVI
Definition: save_nation.h:33
Nation definition.
Definition: cp_nation.h:44
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
nation_t * NAT_GetRandom(void)
Return a pointer to a random nation.
Definition: cp_nation.cpp:45
float debtInterest
Definition: cp_campaign.h:159
float victoryBonusPerAlien
Definition: q_shared.h:468
const char * NAT_GetHappinessString(const float happiness)
Translates the nation happiness float value to a string.
Definition: cp_nation.cpp:143
#define E_Foreach(employeeType, var)
Definition: cp_employee.h:122
XML tag constants for savegame.
#define MIS_Foreach(var)
iterates through missions
Definition: cp_missions.h:118
#define _(String)
Definition: cl_shared.h:43
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
linkedList_t * populations
Definition: q_shared.h:487
char name[MAX_VAR]
Definition: cp_base.h:86
static void NAT_DrawCharts_f(void)
Console command for UI to draw charts.
Definition: cp_nation.cpp:584
int credits
Definition: cp_campaign.h:242
#define MONTHS_PER_YEAR
Definition: common.h:299
int numNations
Definition: cp_campaign.h:296
int xviInfection
Definition: cp_nation.h:38
short ufoType_t
Definition: scripts.h:146
#define SAVE_NATION_NATIONS
Definition: save_nation.h:27
const byte * GEO_GetColor(const vec2_t pos, mapType_t type, bool *coast)
Returns the color value from geoscape of a certain mask (terrain, culture or population) at a given p...
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
const char * name
Definition: cp_nation.h:46
const char * id
Definition: cp_nation.h:45
mission definition
Definition: cp_missions.h:85
uiMessageListNodeMessage_t * MS_AddNewMessage(const char *title, const char *text, messageType_t type, technology_t *pedia, bool popup, bool playSound)
Adds a new message to message stack.
Definition: cp_messages.cpp:61
static const cmdList_t nationCmds[]
Definition: cp_nation.cpp:839
memPool_t * cp_campaignPool
Definition: cp_campaign.cpp:61
void CP_GameTimeStop(void)
Stop game time speed.
Definition: cp_time.cpp:126
#define HAPPINESS_ALIEN_MISSION_LOSS
Definition: cp_nation.cpp:38
float happiness
Definition: cp_nation.h:37
A base with all it's data.
Definition: cp_base.h:84
void CP_HandleNationData(float minHappiness, mission_t *mis, const nation_t *affectedNation, const missionResults_t *results, bool won)
Updates each nation's happiness. Should be called at the completion or expiration of every mission...
Definition: cp_nation.cpp:263
const linkedList_t *IMPORT * LIST_ContainsString(const linkedList_t *list, const char *string)
#define xmlNode_t
Definition: xml.h:24
#define SAVE_NATION_HAPPINESS
Definition: save_nation.h:32
#define REMOVE_ELEM(array, index, n)
Definition: common.h:408
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
nationInfo_t stats[MONTHS_PER_YEAR]
Definition: cp_nation.h:52
bool NAT_LoadXML(xmlNode_t *p)
Nation loading xml callback.
Definition: cp_nation.cpp:323
#define ngettext(x, y, cnt)
Definition: g_local.h:40
#define DEBUG_CLIENT
Definition: defines.h:59
#define NAT_Foreach(var)
iterates trough nations
Definition: cp_nation.h:80
void NAT_SetHappiness(const float minhappiness, nation_t *nation, const float happiness)
Updates the nation happiness.
Definition: cp_nation.cpp:188
#define OBJZERO(obj)
Definition: shared.h:178
int UFO_GetAvailableUFOsForMission(const interestCategory_t missionType, ufoType_t *ufoTypes, bool checkInterest)
Fill an array with available UFOs for the mission type.
Definition: cp_ufo.cpp:153
#define MAX_VAR
Definition: shared.h:36
static const value_t nation_vals[]
Definition: cp_nation.cpp:360
void NAT_BackupMonthlyData(void)
Backs up each nation's relationship values.
Definition: cp_nation.cpp:826
Campaign missions headers.
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition: cp_base.cpp:285
int numCities
Definition: cp_campaign.h:300
static const value_t city_vals[]
Definition: cp_nation.cpp:433
Employee * E_CreateEmployee(employeeType_t type, const nation_t *nation, const ugv_t *ugvType)
Creates an entry of a new employee in the global list and assignes it to no building/base.
#define SAVE_NATION_MONTH
Definition: save_nation.h:30
const cgame_import_t * cgi
bool GEO_PositionFitsTCPNTypes(const vec2_t pos, const linkedList_t *terrainTypes, const linkedList_t *cultureTypes, const linkedList_t *populationTypes, const linkedList_t *nations)
Checks for a given location, if it fulfills all criteria given via parameters (terrain, culture, population, nation type)
const char *IMPORT * Com_UFOTypeToShortName(ufoType_t type)
#define UFO_MAX
Definition: scripts.h:147
employeeType_t
The types of employees.
Definition: cp_employee.h:30
void CP_UpdateCredits(int credits)
Sets credits and update mn_credits cvar.
bool storyRelated
Definition: q_shared.h:489
linkedList_t * cities
Definition: cp_campaign.h:299
ccs_t ccs
Definition: cp_campaign.cpp:62
Definition: cmd.h:86
void NAT_UpdateHappinessForAllNations(const float minhappiness)
Lower happiness of nations depending on alien activity.
Definition: cp_nation.cpp:83
int aircraftDivisor
Definition: cp_campaign.h:157
#define SAVE_NATION_MONTH_IDX
Definition: save_nation.h:31
Campaign geoscape time header.
#define MapDef_ForeachSingleplayerCampaign(var)
Definition: cl_shared.h:83
void NAT_Shutdown(void)
Closing actions for nation-subsystem.
Definition: cp_nation.cpp:860
Header for Geoscape management.
Atomic structure used to define most of the UI.
Definition: ui_nodes.h:80
#define HAPPINESS_MAX_MISSION_IMPACT
Definition: cp_nation.cpp:39
City definition.
Definition: cp_nation.h:68
char cp_messageBuffer[MAX_MESSAGE_TEXT]
Definition: cp_messages.cpp:31
int idx
Definition: cp_nation.h:47
QGL_EXTERN GLuint count
Definition: r_gl.h:99
CGAME_HARD_LINKED_FUNCTIONS linkedList_t * LIST_Add(linkedList_t **listDest, void const *data, size_t length)
#define SAVE_NATION_NATION
Definition: save_nation.h:28
int civiliansKilledFriendlyFire
Definition: cp_missions.h:77
int idx
Definition: cp_nation.h:71
Definition: scripts.h:49
bool inuse
Definition: cp_nation.h:34
int NAT_GetFunding(const nation_t *const nation, int month)
Get the funding of a nation at a certain month.
Definition: cp_nation.cpp:120
QGL_EXTERN GLint i
Definition: r_gl.h:113
xmlNode_t *IMPORT * XML_GetNextNode(xmlNode_t *current, xmlNode_t *parent, const char *name)
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
int aircraftFactor
Definition: cp_campaign.h:156
bool NAT_SaveXML(xmlNode_t *p)
Nation saving callback.
Definition: cp_nation.cpp:228
linkedList_t * nations
Definition: cp_campaign.h:295
const char * E_GetEmployeeString(employeeType_t type, int n)
Convert employeeType_t to translated string.
linkedList_t * ufos
Definition: q_shared.h:491
void NAT_InitStartup(void)
Init actions for nation-subsystem.
Definition: cp_nation.cpp:852
#define MEMBER_SIZEOF(TYPE, MEMBER)
Definition: scripts.h:34
void GEO_PrintParameterStringByPos(const vec2_t pos)
Prints positions parameter in console.
#define MapIsWater(color)
Definition: cp_geoscape.h:32
linkedList_t * cultures
Definition: q_shared.h:488
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it's safe to delete the returned entry from the list while looping over ...
Definition: list.h:41
vec_t vec2_t[2]
Definition: ufotypes.h:38
Header file for single player campaign control.
enum nt_s notify_t
Notify types.
float employeeRate
Definition: cp_campaign.h:206
Definition: scripts.h:52
int CP_GetSalaryUpKeepBase(const salary_t *salary, const base_t *base)
city_t * CITY_GetById(const char *cityId)
Finds a city by it's scripted identifier.
Definition: cp_nation.cpp:411
xmlNode_t *IMPORT * XML_AddNode(xmlNode_t *parent, const char *name)
#define Q_streq(a, b)
Definition: shared.h:136
#define AIR_Foreach(var)
iterates trough all aircraft
Definition: cp_aircraft.h:192
char *IMPORT * PoolStrDup(const char *in, memPool_t *pool, const int tagNum)
#define SAVE_NATION_ID
Definition: save_nation.h:29
city_t * CITY_GetByPos(vec2_t pos)
Finds a city by it's geoscape coordinates.
Definition: cp_nation.cpp:424
mapDef_t * mapDef
Definition: cp_missions.h:88
campaign_t * curCampaign
Definition: cp_campaign.h:377
void CL_ParseNations(const char *name, const char **text)
Parse the nation data from script file.
Definition: cp_nation.cpp:382
static void NAT_ListStats_f(void)
Console command for UI to gather nation statistics.
Definition: cp_nation.cpp:543
Structure with mission info needed to create results summary at menu won.
Definition: cp_missions.h:60
nation_t * GEO_GetNation(const vec2_t pos)
Translate nation map color to nation.
const char *IMPORT * Cmd_Argv(int n)
Definition: scripts.h:55
const char *IMPORT * XML_GetString(xmlNode_t *parent, const char *name)
bool NAT_ScriptSanityCheck(void)
Checks the parsed nations and cities for errors.
Definition: cp_nation.cpp:470
xmlNode_t *IMPORT * XML_GetNode(xmlNode_t *parent, const char *name)
void NAT_HandleBudget(const campaign_t *campaign)
Update the nation data from all parsed nation each month.
Definition: cp_nation.cpp:714
void CITY_Parse(const char *name, const char **text)
Parse the city data from script file.
Definition: cp_nation.cpp:445
const char * id
Definition: cp_nation.h:69
salary_t salaries
Definition: cp_campaign.h:195
const char * NAT_GetCurrentHappinessString(const nation_t *nation)
Translates the current nation happiness float value to a string.
Definition: cp_nation.cpp:176
Detailed information about the nation relationship (currently per month, but could be used elsewhere)...
Definition: cp_nation.h:33