UFO: Alien Invasion
Loading...
Searching...
No Matches
cp_nation.cpp
Go to the documentation of this file.
1
6
7/*
8Copyright (C) 2002-2025 UFO: Alien Invasion.
9
10This program is free software; you can redistribute it and/or
11modify it under the terms of the GNU General Public License
12as published by the Free Software Foundation; either version 2
13of the License, or (at your option) any later version.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18
19See the GNU General Public License for more details.
20
21You should have received a copy of the GNU General Public License
22along with this program; if not, write to the Free Software
23Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24*/
25
26#include "../../DateTime.h"
27#include "../../cl_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
64nation_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
84void 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) {
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
121int 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
134{
135 return &nation->stats[0];
136}
137
144const 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
177const char* NAT_GetCurrentHappinessString (const nation_t* nation)
178{
179 const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation);
180 return NAT_GetHappinessString(stats->happiness);
181}
182
189void 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{
231 xmlNode_t* n = cgi->XML_AddNode(p, SAVE_NATION_NATIONS);
232
233 NAT_Foreach(nation) {
234 xmlNode_t* s = cgi->XML_AddNode(n, SAVE_NATION_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
242 xmlNode_t* ss = cgi->XML_AddNode(s, SAVE_NATION_MONTH);
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
264void 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
329 n = cgi->XML_GetNode(p, SAVE_NATION_NATIONS);
330 if (!n)
331 return false;
332
333 /* nations loop */
334 for (s = cgi->XML_GetNode(n, SAVE_NATION_NATION); s; s = cgi->XML_GetNextNode(s, n, SAVE_NATION_NATION)) {
335 xmlNode_t* ss;
336 nation_t* nation = NAT_GetNationByID(cgi->XML_GetString(s, SAVE_NATION_ID));
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/*==========================================
358Parsing
359==========================================*/
360
361static 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
383void 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
412city_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
434static 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
446void 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/*=====================================
537Menu functions
538=====================================*/
539
543static 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
584static 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 / 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
673static 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
685static 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
714void 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);
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
839static 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
853{
854 cgi->Cmd_TableAddList(nationCmds);
855}
856
860void NAT_Shutdown (void)
861{
862 cgi->LIST_Delete(&ccs.cities);
863 cgi->LIST_Delete(&ccs.nations);
864
865 cgi->Cmd_TableRemoveList(nationCmds);
866}
DateTime class definition.
CGAME_HARD_LINKED_FUNCTIONS linkedList_t * LIST_Add(linkedList_t **listDest, void const *data, size_t length)
Share stuff between the different cgame implementations.
#define _(String)
Definition cl_shared.h:44
#define MapDef_ForeachSingleplayerCampaign(var)
Definition cl_shared.h:84
static const short MONTHS_PER_YEAR
Definition DateTime.h:39
#define REMOVE_ELEM(array, index, n)
Definition common.h:385
#define AIR_Foreach(var)
iterates trough all aircraft
@ AIR_CRASHED
@ INTERESTCATEGORY_TERROR_ATTACK
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition cp_base.cpp:286
memPool_t * cp_campaignPool
int CP_GetSalaryUpKeepBase(const salary_t *salary, const base_t *base)
void CP_UpdateCredits(int credits)
Sets credits and update mn_credits cvar.
ccs_t ccs
Header file for single player campaign control.
const cgame_import_t * cgi
@ MAPTYPE_TERRAIN
Definition cp_campaign.h:93
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 * E_GetEmployeeString(employeeType_t type, int n)
Convert employeeType_t to translated string.
employeeType_t
The types of employees.
Definition cp_employee.h:30
@ EMPL_SOLDIER
Definition cp_employee.h:31
@ MAX_EMPL
Definition cp_employee.h:36
@ EMPL_PILOT
Definition cp_employee.h:34
@ EMPL_WORKER
Definition cp_employee.h:33
@ EMPL_SCIENTIST
Definition cp_employee.h:32
#define E_Foreach(employeeType, var)
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,...
nation_t * GEO_GetNation(const vec2_t pos)
Translate nation map color to nation.
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...
void GEO_PrintParameterStringByPos(const vec2_t pos)
Prints positions parameter in console.
Header for Geoscape management.
#define MapIsWater(color)
Definition cp_geoscape.h:32
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...
notify_t
Notify types.
@ NT_HAPPINESS_PLEASED
@ NT_NUM_NOTIFYTYPE
@ NT_HAPPINESS_CHANGED
@ NT_HAPPINESS_MIN
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.
char cp_messageBuffer[MAX_MESSAGE_TEXT]
Campaign missions headers.
#define MIS_Foreach(var)
iterates through missions
@ STAGE_RECON_GROUND
Definition cp_missions.h:40
@ STAGE_HARVEST
Definition cp_missions.h:49
@ STAGE_SPREAD_XVI
Definition cp_missions.h:46
@ STAGE_SUBVERT_GOV
Definition cp_missions.h:44
@ STAGE_TERROR_MISSION
Definition cp_missions.h:41
bool NAT_SaveXML(xmlNode_t *p)
Nation saving callback.
void NAT_HandleBudget(const campaign_t *campaign)
Update the nation data from all parsed nation each month.
#define HAPPINESS_MAX_MISSION_IMPACT
Definition cp_nation.cpp:40
int NAT_GetFunding(const nation_t *const nation, int month)
Get the funding of a nation at a certain month.
void NAT_SetHappiness(const float minhappiness, nation_t *nation, const float happiness)
Updates the nation happiness.
nation_t * NAT_GetNationByID(const char *nationID)
Return a nation-pointer by the nations id.
Definition cp_nation.cpp:64
static const cmdList_t nationCmds[]
static const value_t nation_vals[]
void NAT_BackupMonthlyData(void)
Backs up each nation's relationship values.
bool NAT_LoadXML(xmlNode_t *p)
Nation loading xml callback.
static void NAT_DrawCharts_f(void)
Console command for UI to draw charts.
nation_t * NAT_GetRandom(void)
Return a pointer to a random nation.
Definition cp_nation.cpp:46
city_t * CITY_GetByPos(vec2_t pos)
Finds a city by it's geoscape coordinates.
void NAT_UpdateHappinessForAllNations(const float minhappiness)
Lower happiness of nations depending on alien activity.
Definition cp_nation.cpp:84
const nationInfo_t * NAT_GetCurrentMonthInfo(const nation_t *const nation)
Get the current month nation stats.
static void NAT_ListStats_f(void)
Console command for UI to gather nation statistics.
void CITY_Parse(const char *name, const char **text)
Parse the city data from script file.
void CL_ParseNations(const char *name, const char **text)
Parse the nation data from script file.
void NAT_InitStartup(void)
Init actions for nation-subsystem.
void NAT_Shutdown(void)
Closing actions for nation-subsystem.
bool NAT_ScriptSanityCheck(void)
Checks the parsed nations and cities for errors.
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....
const char * NAT_GetCurrentHappinessString(const nation_t *nation)
Translates the current nation happiness float value to a string.
#define HAPPINESS_ALIEN_MISSION_LOSS
Definition cp_nation.cpp:39
const char * NAT_GetHappinessString(const float happiness)
Translates the nation happiness float value to a string.
static const value_t city_vals[]
city_t * CITY_GetById(const char *cityId)
Finds a city by it's scripted identifier.
#define NAT_Foreach(var)
iterates trough nations
Definition cp_nation.h:80
void CP_GameTimeStop(void)
Stop game time speed.
Definition cp_time.cpp:126
Campaign geoscape time header.
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 DEBUG_CLIENT
Definition defines.h:59
#define ngettext(x, y, cnt)
Definition g_local.h:40
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
#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
Shared parsing functions.
QGL_EXTERN GLuint count
Definition r_gl.h:99
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
XML tag constants for savegame.
#define SAVE_NATION_NATIONS
Definition save_nation.h:27
#define SAVE_NATION_MONTH
Definition save_nation.h:30
#define SAVE_NATION_MONTH_IDX
Definition save_nation.h:31
#define SAVE_NATION_ID
Definition save_nation.h:29
#define SAVE_NATION_HAPPINESS
Definition save_nation.h:32
#define SAVE_NATION_NATION
Definition save_nation.h:28
#define SAVE_NATION_XVI
Definition save_nation.h:33
@ V_FLOAT
Definition scripts.h:54
@ V_TRANSLATION_STRING
Definition scripts.h:59
@ V_NULL
Definition scripts.h:49
@ V_INT
Definition scripts.h:52
@ V_COLOR
Definition scripts.h:57
@ V_POS
Definition scripts.h:55
#define MEMBER_SIZEOF(TYPE, MEMBER)
Definition scripts.h:34
#define UFO_MAX
Definition scripts.h:146
short ufoType_t
Definition scripts.h:145
#define Q_streq(a, b)
Definition shared.h:136
#define OBJZERO(obj)
Definition shared.h:178
#define MAX_VAR
Definition shared.h:36
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
A base with all it's data.
Definition cp_base.h:84
char name[MAX_VAR]
Definition cp_base.h:86
salary_t salaries
City definition.
Definition cp_nation.h:70
const char * id
Definition cp_nation.h:71
linkedList_t * ufos
Definition q_shared.h:491
bool storyRelated
Definition q_shared.h:489
linkedList_t * terrains
Definition q_shared.h:486
linkedList_t * cultures
Definition q_shared.h:488
float victoryBonusPerAlien
Definition q_shared.h:468
linkedList_t * populations
Definition q_shared.h:487
mission definition
Definition cp_missions.h:86
mapDef_t * mapDef
Definition cp_missions.h:89
Structure with mission info needed to create results summary at menu won.
Definition cp_missions.h:61
int civiliansKilledFriendlyFire
Definition cp_missions.h:78
Nation definition.
Definition cp_nation.h:46
int idx
Definition cp_nation.h:49
nationInfo_t stats[DateTime::MONTHS_PER_YEAR]
Definition cp_nation.h:54
const char * name
Definition cp_nation.h:48
const char * id
Definition cp_nation.h:47
int maxFunding
Definition cp_nation.h:60
Detailed information about the nation relationship (currently per month, but could be used elsewhere)...
Definition cp_nation.h:35
float happiness
Definition cp_nation.h:39
int xviInfection
Definition cp_nation.h:40
int aircraftFactor
float debtInterest
int aircraftDivisor
Atomic structure used to define most of the UI.
Definition ui_nodes.h:80
vec_t vec2_t[2]
Definition ufotypes.h:38
#define Vector2Equal(a, b)
Definition vector.h:67
#define xmlNode_t
Definition xml.h:24