UFO: Alien Invasion
cp_nation.cpp
Go to the documentation of this file.
1 
7 /*
8 Copyright (C) 2002-2022 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 "../../DateTime.h"
27 #include "../../cl_shared.h"
28 #include "../../../shared/parse.h"
29 #include "../../../shared/shared.h"
30 #include "cp_campaign.h"
31 #include "cp_geoscape.h"
32 #include "cp_ufo.h"
33 #include "cp_time.h"
34 #include "save/save_nation.h"
35 #include "../../ui/node/ui_node_linechart.h" /* lineStrip_t */
36 #include "cp_missions.h"
37 
38 /* nation happiness constants */
39 #define HAPPINESS_ALIEN_MISSION_LOSS -0.02
40 #define HAPPINESS_MAX_MISSION_IMPACT 0.07
41 
47 {
48  const int nationIndex = rand() % ccs.numNations;
49  int i = 0;
50  NAT_Foreach(nation) {
51  if (i == nationIndex)
52  return nation;
53  i++;
54  }
55 
56  return nullptr;
57 }
58 
64 nation_t* NAT_GetNationByID (const char* nationID)
65 {
66  if (nationID == nullptr) {
67  cgi->Com_Printf("NAT_GetNationByID: nullptr nationID\n");
68  return nullptr;
69  }
70  NAT_Foreach(nation) {
71  if (Q_streq(nation->id, nationID))
72  return nation;
73  }
74 
75  cgi->Com_Printf("NAT_GetNationByID: Could not find nation '%s'\n", nationID);
76  return nullptr;
77 }
78 
84 void NAT_UpdateHappinessForAllNations (const float minhappiness)
85 {
86  MIS_Foreach(mission) {
87  nation_t* nation = GEO_GetNation(mission->pos);
88  /* Difficulty modifier range is [0, 0.02f] */
89 
90  /* Some non-water location have no nation */
91  if (!nation)
92  continue;
93 
94  float happinessFactor;
95  const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation);
96  switch (mission->stage) {
98  case STAGE_SUBVERT_GOV:
99  case STAGE_RECON_GROUND:
100  case STAGE_SPREAD_XVI:
101  case STAGE_HARVEST:
102  happinessFactor = HAPPINESS_ALIEN_MISSION_LOSS;
103  break;
104  default:
105  /* mission is not active on earth or does not have any influence
106  * on the nation happiness, skip this mission */
107  continue;
108  }
109 
110  NAT_SetHappiness(minhappiness, nation, stats->happiness + happinessFactor);
111  cgi->Com_DPrintf(DEBUG_CLIENT, "Happiness of nation %s decreased: %.02f\n", nation->name, stats->happiness);
112  }
113 }
114 
121 int NAT_GetFunding (const nation_t* const nation, int month)
122 {
123  assert(month >= 0);
124  assert(month < DateTime::MONTHS_PER_YEAR);
125  return nation->maxFunding * nation->stats[month].happiness;
126 }
127 
133 const nationInfo_t* NAT_GetCurrentMonthInfo (const nation_t* const nation)
134 {
135  return &nation->stats[0];
136 }
137 
144 const char* NAT_GetHappinessString (const float happiness)
145 {
146  if (happiness < 0.015)
147  return _("Giving up");
148  else if (happiness < 0.025)
149  return _("Furious");
150  else if (happiness < 0.04)
151  return _("Angry");
152  else if (happiness < 0.06)
153  return _("Mad");
154  else if (happiness < 0.10)
155  return _("Upset");
156  else if (happiness < 0.20)
157  return _("Tolerant");
158  else if (happiness < 0.30)
159  return _("Neutral");
160  else if (happiness < 0.50)
161  return _("Content");
162  else if (happiness < 0.70)
163  return _("Pleased");
164  else if (happiness < 0.95)
165  return _("Happy");
166  else
167  return _("Exuberant");
168 
169 }
170 
177 const char* NAT_GetCurrentHappinessString (const nation_t* nation)
178 {
179  const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation);
180  return NAT_GetHappinessString(stats->happiness);
181 }
182 
189 void NAT_SetHappiness (const float minhappiness, nation_t* nation, const float happiness)
190 {
191  const char* oldString = NAT_GetCurrentHappinessString(nation);
192  const char* newString;
193  nationInfo_t* stats = &nation->stats[0];
194  const float oldHappiness = stats->happiness;
195  const float middleHappiness = (minhappiness + 1.0) / 2;
196  notify_t notifyType = NT_NUM_NOTIFYTYPE;
197 
198  stats->happiness = happiness;
199  if (stats->happiness < 0.0f)
200  stats->happiness = 0.0f;
201  else if (stats->happiness > 1.0f)
202  stats->happiness = 1.0f;
203 
204  newString = NAT_GetCurrentHappinessString(nation);
205 
206  if (oldString != newString) {
208  _("Nation %s changed happiness from %s to %s"), _(nation->name), oldString, newString);
209  notifyType = NT_HAPPINESS_CHANGED;
210  } else if (oldHappiness > middleHappiness && happiness < middleHappiness) {
212  _("Nation %s changed happiness to %s"), _(nation->name), newString);
213  notifyType = NT_HAPPINESS_PLEASED;
214  } else if (happiness < minhappiness && oldHappiness > minhappiness) {
216  _("Happiness of nation %s is %s and less than minimal happiness allowed to the campaign"), _(nation->name), newString);
217  notifyType = NT_HAPPINESS_MIN;
218  } else {
219  return;
220  }
221 
222  MSO_CheckAddNewMessage(notifyType, _("Nation changed happiness"), cp_messageBuffer);
223 }
224 
230 {
232 
233  NAT_Foreach(nation) {
235  cgi->XML_AddString(s, SAVE_NATION_ID, nation->id);
236  for (int j = 0; j < DateTime::MONTHS_PER_YEAR; j++) {
237  const nationInfo_t* stats = &nation->stats[j];
238 
239  if (!stats->inuse)
240  continue;
241 
243  cgi->XML_AddInt(ss, SAVE_NATION_MONTH_IDX, j);
244  cgi->XML_AddFloat(ss, SAVE_NATION_HAPPINESS, stats->happiness);
245  cgi->XML_AddInt(ss, SAVE_NATION_XVI, stats->xviInfection);
246  }
247  }
248  return true;
249 }
250 
264 void CP_HandleNationData (float minHappiness, mission_t* mis, const nation_t* affectedNation, const missionResults_t* results, bool won)
265 {
266  const float civilianSum = (float) (results->civiliansSurvived + results->civiliansKilled + results->civiliansKilledFriendlyFire);
267  const float alienSum = (float) (results->aliensSurvived + results->aliensKilled + results->aliensStunned);
268  float performance;
269  float performanceAlien;
270  float performanceCivilian;
271  float deltaHappiness = 0.0f;
272  float happinessDivisor = 5.0f;
273  float victoryBonusPerAlien = 0.1f;
274 
275  if (mis->mapDef->victoryBonusPerAlien) {
276  victoryBonusPerAlien = mis->mapDef->victoryBonusPerAlien;
277  }
278 
281  if (civilianSum <= 1) {
282  performanceCivilian = 0.0f;
283  } else {
284  performanceCivilian = (2 * civilianSum - results->civiliansKilled - 2
285  * results->civiliansKilledFriendlyFire) * 3 / (2 * civilianSum) - 2;
286  }
287 
288  /* Calculate how well the mission went. */
292  if (won) {
293  performanceAlien = (results->aliensKilled + results->aliensStunned) * victoryBonusPerAlien;
294  } else {
295  performanceAlien = results->aliensKilled + results->aliensStunned - alienSum;
296  }
297  performance = performanceCivilian + performanceAlien;
298 
299  /* Calculate the actual happiness delta. The bigger the mission, the more potential influence. */
300  deltaHappiness = 0.004 * civilianSum + 0.004 * alienSum;
301 
302  /* There is a maximum base happiness delta. */
303  if (deltaHappiness > HAPPINESS_MAX_MISSION_IMPACT)
304  deltaHappiness = HAPPINESS_MAX_MISSION_IMPACT;
305 
306  NAT_Foreach(nation) {
307  const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation);
308  float happinessFactor;
309 
310  /* update happiness. */
311  if (nation == affectedNation)
312  happinessFactor = deltaHappiness;
313  else
314  happinessFactor = deltaHappiness / happinessDivisor;
315 
316  NAT_SetHappiness(minHappiness, nation, stats->happiness + performance * happinessFactor);
317  }
318 }
319 
325 {
326  xmlNode_t* n;
327  xmlNode_t* s;
328 
330  if (!n)
331  return false;
332 
333  /* nations loop */
335  xmlNode_t* ss;
337 
338  if (!nation)
339  return false;
340 
341  /* month loop */
342  for (ss = cgi->XML_GetNode(s, SAVE_NATION_MONTH); ss; ss = cgi->XML_GetNextNode(ss, s, SAVE_NATION_MONTH)) {
343  int monthIDX = cgi->XML_GetInt(ss, SAVE_NATION_MONTH_IDX, DateTime::MONTHS_PER_YEAR);
344  nationInfo_t* stats = &nation->stats[monthIDX];
345 
346  if (monthIDX < 0 || monthIDX >= DateTime::MONTHS_PER_YEAR)
347  return false;
348 
349  stats->inuse = true;
350  stats->happiness = cgi->XML_GetFloat(ss, SAVE_NATION_HAPPINESS, 0.0);
351  stats->xviInfection = cgi->XML_GetInt(ss, SAVE_NATION_XVI, 0);
352  }
353  }
354  return true;
355 }
356 
357 /*==========================================
358 Parsing
359 ==========================================*/
360 
361 static const value_t nation_vals[] = {
362  {"name", V_TRANSLATION_STRING, offsetof(nation_t, name), 0},
363  {"pos", V_POS, offsetof(nation_t, pos), MEMBER_SIZEOF(nation_t, pos)},
364  {"color", V_COLOR, offsetof(nation_t, color), MEMBER_SIZEOF(nation_t, color)},
365  {"funding", V_INT, offsetof(nation_t, maxFunding), MEMBER_SIZEOF(nation_t, maxFunding)},
366  {"happiness", V_FLOAT, offsetof(nation_t, stats[0].happiness), MEMBER_SIZEOF(nation_t, stats[0].happiness)},
367  {"soldiers", V_INT, offsetof(nation_t, maxSoldiers), MEMBER_SIZEOF(nation_t, maxSoldiers)},
368  {"scientists", V_INT, offsetof(nation_t, maxScientists), MEMBER_SIZEOF(nation_t, maxScientists)},
369  {"workers", V_INT, offsetof(nation_t, maxWorkers), MEMBER_SIZEOF(nation_t, maxWorkers)},
370  {"pilots", V_INT, offsetof(nation_t, maxPilots), MEMBER_SIZEOF(nation_t, maxPilots)},
371 
372  {nullptr, V_NULL, 0, 0}
373 };
374 
383 void CL_ParseNations (const char* name, const char** text)
384 {
385  /* search for nations with same name */
386  NAT_Foreach(n) {
387  if (Q_streq(name, n->id)) {
388  cgi->Com_Printf("CL_ParseNations: nation def \"%s\" with same name found, second ignored\n", name);
389  return;
390  }
391  }
392 
393  /* initialize the nation */
394  nation_t nation;
395  OBJZERO(nation);
396  nation.idx = ccs.numNations;
397  nation.stats[0].inuse = true;
398 
399  if (cgi->Com_ParseBlock(name, text, &nation, nation_vals, cp_campaignPool)) {
400  ccs.numNations++;
401 
402  cgi->Com_DPrintf(DEBUG_CLIENT, "...found nation %s\n", name);
403  nation.id = cgi->PoolStrDup(name, cp_campaignPool, 0);
404  LIST_Add(&ccs.nations, nation);
405  }
406 }
407 
412 city_t* CITY_GetById (const char* cityId)
413 {
414  LIST_Foreach(ccs.cities, city_t, city) {
415  if (Q_streq(cityId, city->id))
416  return city;
417  }
418  return nullptr;
419 }
420 
426 {
427  LIST_Foreach(ccs.cities, city_t, city) {
428  if (Vector2Equal(pos, city->pos))
429  return city;
430  }
431  return nullptr;
432 }
433 
434 static const value_t city_vals[] = {
435  {"name", V_TRANSLATION_STRING, offsetof(city_t, name), 0},
436  {"pos", V_POS, offsetof(city_t, pos), MEMBER_SIZEOF(city_t, pos)},
437 
438  {nullptr, V_NULL, 0, 0}
439 };
440 
446 void CITY_Parse (const char* name, const char** text)
447 {
448  city_t newCity;
449 
450  /* search for cities with same name */
451  if (CITY_GetById(name)) {
452  cgi->Com_Printf("CITY_Parse: city def \"%s\" with same name found, second ignored\n", name);
453  return;
454  }
455 
456  OBJZERO(newCity);
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 < DateTime::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];
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 / DateTime::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 < DateTime::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 < DateTime::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 = DateTime::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 }
xmlNode_t *IMPORT * XML_GetNode(xmlNode_t *parent, const char *name)
const nationInfo_t * NAT_GetCurrentMonthInfo(const nation_t *const nation)
Get the current month nation stats.
Definition: cp_nation.cpp:133
nation_t * NAT_GetNationByID(const char *nationID)
Return a nation-pointer by the nations id.
Definition: cp_nation.cpp:64
#define Vector2Equal(a, b)
Definition: vector.h:67
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
mission definition
Definition: cp_missions.h:86
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:46
A base with all it&#39;s data.
Definition: cp_base.h:84
int numNations
Definition: cp_campaign.h:297
const char * NAT_GetHappinessString(const float happiness)
Translates the nation happiness float value to a string.
Definition: cp_nation.cpp:144
const char * id
Definition: cp_nation.h:47
#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:119
#define _(String)
Definition: cl_shared.h:44
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
static void NAT_DrawCharts_f(void)
Console command for UI to draw charts.
Definition: cp_nation.cpp:584
Detailed information about the nation relationship (currently per month, but could be used elsewhere)...
Definition: cp_nation.h:35
short ufoType_t
Definition: scripts.h:145
bool inuse
Definition: cp_nation.h:36
char name[MAX_VAR]
Definition: cp_base.h:86
linkedList_t * ufos
Definition: q_shared.h:491
#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 *IMPORT * Cmd_Argv(int n)
Nation definition.
Definition: cp_nation.h:46
float victoryBonusPerAlien
Definition: q_shared.h:468
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:63
static const cmdList_t nationCmds[]
Definition: cp_nation.cpp:839
memPool_t * cp_campaignPool
Definition: cp_campaign.cpp:62
void CP_GameTimeStop(void)
Stop game time speed.
Definition: cp_time.cpp:126
#define HAPPINESS_ALIEN_MISSION_LOSS
Definition: cp_nation.cpp:39
linkedList_t * populations
Definition: q_shared.h:487
void CP_HandleNationData(float minHappiness, mission_t *mis, const nation_t *affectedNation, const missionResults_t *results, bool won)
Updates each nation&#39;s happiness. Should be called at the completion or expiration of every mission...
Definition: cp_nation.cpp:264
char *IMPORT * PoolStrDup(const char *in, memPool_t *pool, const int tagNum)
City definition.
Definition: cp_nation.h:70
#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:384
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
int xviInfection
Definition: cp_nation.h:40
bool NAT_LoadXML(xmlNode_t *p)
Nation loading xml callback.
Definition: cp_nation.cpp:324
#define ngettext(x, y, cnt)
Definition: g_local.h:40
float happiness
Definition: cp_nation.h:39
#define DEBUG_CLIENT
Definition: defines.h:59
#define NAT_Foreach(var)
iterates trough nations
Definition: cp_nation.h:80
int numCities
Definition: cp_campaign.h:301
void NAT_SetHappiness(const float minhappiness, nation_t *nation, const float happiness)
Updates the nation happiness.
Definition: cp_nation.cpp:189
#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:361
int aircraftFactor
Definition: cp_campaign.h:157
void NAT_BackupMonthlyData(void)
Backs up each nation&#39;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:286
static const value_t city_vals[]
Definition: cp_nation.cpp:434
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.
const char *IMPORT * XML_GetString(xmlNode_t *parent, const char *name)
#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)
#define UFO_MAX
Definition: scripts.h:146
employeeType_t
The types of employees.
Definition: cp_employee.h:30
void CP_UpdateCredits(int credits)
Sets credits and update mn_credits cvar.
ccs_t ccs
Definition: cp_campaign.cpp:63
void NAT_UpdateHappinessForAllNations(const float minhappiness)
Lower happiness of nations depending on alien activity.
Definition: cp_nation.cpp:84
salary_t salaries
Definition: cp_campaign.h:196
#define SAVE_NATION_MONTH_IDX
Definition: save_nation.h:31
Campaign geoscape time header.
#define MapDef_ForeachSingleplayerCampaign(var)
Definition: cl_shared.h:84
xmlNode_t *IMPORT * XML_GetNextNode(xmlNode_t *current, xmlNode_t *parent, const char *name)
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:40
int aircraftDivisor
Definition: cp_campaign.h:158
char cp_messageBuffer[MAX_MESSAGE_TEXT]
Definition: cp_messages.cpp:33
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
static const short MONTHS_PER_YEAR
Definition: DateTime.h:39
Definition: scripts.h:49
int NAT_GetFunding(const nation_t *const nation, int month)
Get the funding of a nation at a certain month.
Definition: cp_nation.cpp:121
campaign_t * curCampaign
Definition: cp_campaign.h:378
const linkedList_t *IMPORT * LIST_ContainsString(const linkedList_t *list, const char *string)
QGL_EXTERN GLint i
Definition: r_gl.h:113
float employeeRate
Definition: cp_campaign.h:207
Structure with mission info needed to create results summary at menu won.
Definition: cp_missions.h:61
nationInfo_t stats[DateTime::MONTHS_PER_YEAR]
Definition: cp_nation.h:54
int civiliansKilledFriendlyFire
Definition: cp_missions.h:78
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
Definition: cmd.h:86
bool NAT_SaveXML(xmlNode_t *p)
Nation saving callback.
Definition: cp_nation.cpp:229
int maxFunding
Definition: cp_nation.h:60
const char * E_GetEmployeeString(employeeType_t type, int n)
Convert employeeType_t to translated string.
void NAT_InitStartup(void)
Init actions for nation-subsystem.
Definition: cp_nation.cpp:852
linkedList_t * nations
Definition: cp_campaign.h:296
linkedList_t * cities
Definition: cp_campaign.h:300
#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
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it&#39;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.
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&#39;s scripted identifier.
Definition: cp_nation.cpp:412
float debtInterest
Definition: cp_campaign.h:160
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:193
#define SAVE_NATION_ID
Definition: save_nation.h:29
city_t * CITY_GetByPos(vec2_t pos)
Finds a city by it&#39;s geoscape coordinates.
Definition: cp_nation.cpp:425
int credits
Definition: cp_campaign.h:243
void CL_ParseNations(const char *name, const char **text)
Parse the nation data from script file.
Definition: cp_nation.cpp:383
bool storyRelated
Definition: q_shared.h:489
linkedList_t * terrains
Definition: q_shared.h:486
linkedList_t * cultures
Definition: q_shared.h:488
static void NAT_ListStats_f(void)
Console command for UI to gather nation statistics.
Definition: cp_nation.cpp:543
int idx
Definition: cp_nation.h:49
const char *IMPORT * Com_UFOTypeToShortName(ufoType_t type)
const char * name
Definition: cp_nation.h:48
nation_t * GEO_GetNation(const vec2_t pos)
Translate nation map color to nation.
Definition: scripts.h:55
bool NAT_ScriptSanityCheck(void)
Checks the parsed nations and cities for errors.
Definition: cp_nation.cpp:470
uiNode_t *IMPORT * UI_GetNodeByPath(const char *path)
mapDef_t * mapDef
Definition: cp_missions.h:89
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:446
const char * NAT_GetCurrentHappinessString(const nation_t *nation)
Translates the current nation happiness float value to a string.
Definition: cp_nation.cpp:177
notify_t
Notify types.
const char * id
Definition: cp_nation.h:71