UFO: Alien Invasion
Loading...
Searching...
No Matches
cp_missions.cpp
Go to the documentation of this file.
1
5
6/*
7Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23*/
24
25#include "../../DateTime.h"
26#include "../../cl_shared.h"
27#include "../../cl_team.h"
28#include "../cl_game.h"
29#include "../../ui/ui_dataids.h"
30#include "cp_campaign.h"
31#include "cp_character.h"
32#include "cp_geoscape.h"
33#include "cp_ufo.h"
34#include "cp_alienbase.h"
35#include "cp_alien_interest.h"
36#include "cp_missions.h"
37#include "cp_mission_triggers.h"
38#include "cp_time.h"
39#include "cp_xvi.h"
40#include "save/save_missions.h"
41#include "save/save_interest.h"
53
60
62const int MAX_POS_LOOP = 10;
63
65static const float MIN_CRASHEDUFO_CONDITION = 0.2f;
66static const float MAX_CRASHEDUFO_CONDITION = 0.81f;
67
68/*====================================
69*
70* Prepare battlescape
71*
72====================================*/
73
79void BATTLE_SetVars (const battleParam_t* battleParameters)
80{
81 cgi->Cvar_SetValue("ai_singleplayeraliens", battleParameters->aliens);
82 cgi->Cvar_SetValue("ai_numcivilians", battleParameters->civilians);
83 cgi->Cvar_Set("ai_civilianteam", "%s", battleParameters->civTeam);
84 cgi->Cvar_Set("ai_equipment", "%s", battleParameters->alienEquipment);
85
86 /* now store the alien teams in the shared cgi->csi->struct to let the game dll
87 * have access to this data, too */
88 cgi->csi->numAlienTeams = 0;
89 for (int i = 0; i < battleParameters->alienTeamGroup->numAlienTeams; i++) {
90 cgi->csi->alienTeams[i] = battleParameters->alienTeamGroup->alienTeams[i];
91 cgi->csi->alienChrTemplates[i] = battleParameters->alienTeamGroup->alienChrTemplates[i];
92 cgi->csi->numAlienTeams++;
93 if (cgi->csi->numAlienTeams >= MAX_TEAMS_PER_MISSION)
94 break;
95 }
96}
97
107void BATTLE_Start (mission_t* mission, const battleParam_t* battleParameters)
108{
109 assert(mission->mapDef->mapTheme);
110
111 /* set the mapZone - this allows us to replace the ground texture
112 * with the suitable terrain texture - just use tex_terrain/dummy for the
113 * brushes you want the terrain textures on
114 * @sa R_ModLoadTexinfo */
115 cgi->Cvar_Set("sv_mapzone", "%s", battleParameters->zoneType);
116
117 /* do a quicksave */
118 cgi->Cmd_ExecuteString("game_quicksave");
119
120 if (mission->crashed)
121 cgi->Cvar_Set("sv_hurtaliens", "1");
122 else
123 cgi->Cvar_Set("sv_hurtaliens", "0");
124
125 cgi->Cvar_Set("r_overridematerial", "");
126
127 /* base attack maps starts with a dot */
128 if (mission->mapDef->mapTheme[0] == '.') {
129 const base_t* base = mission->data.base;
130
132 cgi->Com_Printf("Baseattack map on non-baseattack mission! (id=%s, category=%d)\n", mission->id, mission->category);
133 /* assemble a random base */
134 if (!base)
135 cgi->Com_Error(ERR_DROP, "Baseattack map without base!");
136 /* base must be under attack and might not have been destroyed in the meantime. */
137 char maps[2048];
138 char coords[2048];
139 B_AssembleMap(maps, sizeof(maps), coords, sizeof(coords), base);
140 cgi->Cvar_Set("r_overridematerial", "baseattack");
141 cgi->Cbuf_AddText("map %s \"%s\" \"%s\"\n", (GEO_IsNight(base->pos) ? "night" : "day"), maps, coords);
142
143 return;
144 }
145
146 /* Set difficulty level for the battle */
147 assert(ccs.curCampaign);
148 cgi->Cvar_Delete("g_difficulty");
149 cgi->Cvar_SetValue("g_difficulty", ccs.curCampaign->difficulty);
150
151 const char* param = battleParameters->param ? battleParameters->param : (const char*)cgi->LIST_GetRandom(mission->mapDef->params);
152 cgi->Cbuf_AddText("map %s %s %s\n", (GEO_IsNight(mission->pos) ? "night" : "day"),
153 mission->mapDef->mapTheme, param ? param : "");
154}
155
162static bool CP_IsAlienTeamForCategory (const alienTeamCategory_t* cat, const interestCategory_t missionCat)
163{
164 for (int i = 0; i < cat->numMissionCategories; i++) {
165 if (missionCat == cat->missionCategories[i])
166 return true;
167 }
168
169 return false;
170}
171
177static void CP_SetAlienTeamByInterest (mission_t* mission, battleParam_t* battleParameters)
178{
179 const int MAX_AVAILABLE_GROUPS = 4;
180 alienTeamGroup_t* availableGroups[MAX_AVAILABLE_GROUPS];
181 int numAvailableGroup = 0;
182
183 /* Find all available alien team groups */
184 for (int i = 0; i < ccs.numAlienCategories; i++) {
185 alienTeamCategory_t* cat = &ccs.alienCategories[i];
186
187 /* Check if this alien team category may be used */
188 if (!CP_IsAlienTeamForCategory(cat, mission->category))
189 continue;
190
191 /* Find all available team groups for current alien interest
192 * use mission->initialOverallInterest and not ccs.overallInterest:
193 * the alien team should not change depending on when you encounter it */
194 for (int j = 0; j < cat->numAlienTeamGroups; j++) {
197 availableGroups[numAvailableGroup++] = &cat->alienTeamGroups[j];
198 }
199 }
200
201 if (!numAvailableGroup) {
202 CP_MissionRemove(mission);
203 cgi->Com_Error(ERR_DROP, "CP_SetAlienTeamByInterest: no available alien team for mission '%s': interest = %i -- category = %i",
204 mission->id, mission->initialOverallInterest, mission->category);
205 }
206
207 /* Pick up one group randomly */
208 int pick = rand() % numAvailableGroup;
209
210 /* store this group for latter use */
211 battleParameters->alienTeamGroup = availableGroups[pick];
212}
213
221static bool CP_IsAlienEquipmentSelectable (const mission_t* mission, const equipDef_t* equip, linkedList_t* equipPack)
222{
223 if (mission->initialOverallInterest > equip->maxInterest || mission->initialOverallInterest < equip->minInterest)
224 return false;
225
226 LIST_Foreach(equipPack, const char, name) {
227 if (Q_strstart(equip->id, name))
228 return true;
229 }
230
231 return false;
232}
233
243static void CP_SetAlienEquipmentByInterest (const mission_t* mission, linkedList_t* equipPack, battleParam_t* battleParameters)
244{
245 int i, availableEquipDef = 0;
246
247 /* look for all available fitting alien equipment definitions
248 * use mission->initialOverallInterest and not ccs.overallInterest: the alien equipment should not change depending on
249 * when you encounter it */
250 for (i = 0; i < cgi->csi->numEDs; i++) {
251 const equipDef_t* ed = &cgi->csi->eds[i];
252 if (CP_IsAlienEquipmentSelectable(mission, ed, equipPack))
253 availableEquipDef++;
254 }
255
256 cgi->Com_DPrintf(DEBUG_CLIENT, "CP_SetAlienEquipmentByInterest: %i available equipment packs for mission %s\n", availableEquipDef, mission->id);
257
258 if (!availableEquipDef)
259 cgi->Com_Error(ERR_DROP, "CP_SetAlienEquipmentByInterest: no available alien equipment for mission '%s'", mission->id);
260
261 /* Choose an alien equipment definition -- between 0 and availableStage - 1 */
262 const int randomNum = rand() % availableEquipDef;
263
264 availableEquipDef = 0;
265 for (i = 0; i < cgi->csi->numEDs; i++) {
266 const equipDef_t* ed = &cgi->csi->eds[i];
267 if (CP_IsAlienEquipmentSelectable(mission, ed, equipPack)) {
268 if (availableEquipDef == randomNum) {
269 Com_sprintf(battleParameters->alienEquipment, sizeof(battleParameters->alienEquipment), "%s", ed->id);
270 break;
271 }
272 availableEquipDef++;
273 }
274 }
275}
276
283static void MIS_CreateAlienTeam (mission_t* mission, battleParam_t* battleParam)
284{
285 int numAliens;
286
287 assert(mission->posAssigned);
288
289 CP_SetAlienTeamByInterest(mission, battleParam);
290 CP_SetAlienEquipmentByInterest(mission, ccs.alienCategories[battleParam->alienTeamGroup->categoryIdx].equipment, battleParam);
291
292 const int min = battleParam->alienTeamGroup->minAlienCount;
293 const int max = battleParam->alienTeamGroup->maxAlienCount;
294 const int diff = max - min;
295
296 numAliens = min + rand() % (diff + 1);
297 numAliens = std::max(1, numAliens);
298 if (mission->ufo && mission->ufo->maxTeamSize && numAliens > mission->ufo->maxTeamSize)
299 numAliens = mission->ufo->maxTeamSize;
300 if (numAliens > mission->mapDef->maxAliens)
301 numAliens = mission->mapDef->maxAliens;
302 battleParam->aliens = numAliens;
303}
304
310static void CP_CreateCivilianTeam (const mission_t* mission, battleParam_t* param)
311{
312 nation_t* nation;
313
314 assert(mission->posAssigned);
315
317
318 nation = GEO_GetNation(mission->pos);
319 param->nation = nation;
320 if (mission->mapDef->civTeam != nullptr) {
321 Q_strncpyz(param->civTeam, mission->mapDef->civTeam, sizeof(param->civTeam));
322 } else if (nation) {
324 Q_strncpyz(param->civTeam, nation->id, sizeof(param->civTeam));
325 } else {
326 Q_strncpyz(param->civTeam, "europe", sizeof(param->civTeam));
327 }
328}
329
339void CP_CreateBattleParameters (mission_t* mission, battleParam_t* param, const aircraft_t* aircraft)
340{
341 assert(mission->posAssigned);
342 assert(mission->mapDef);
343
344 MIS_CreateAlienTeam(mission, param);
345 CP_CreateCivilianTeam(mission, param);
346
347 /* Reset parameters */
348 cgi->Free(param->param);
349 param->param = nullptr;
350 param->retriable = true;
351
352 cgi->Cvar_Set("rm_ufo", "");
353 cgi->Cvar_Set("rm_drop", "");
354 cgi->Cvar_Set("rm_crashed", "");
355
356 param->mission = mission;
357 const byte* color = GEO_GetColor(mission->pos, MAPTYPE_TERRAIN, nullptr);
358 param->zoneType = cgi->csi->terrainDefs.getTerrainName(color); /* store to terrain type for texture replacement */
359 /* Hack: Alienbase is fully indoors (underground) so no weather effects, maybe this should be a mapdef property? */
360 if (mission->category == INTERESTCATEGORY_ALIENBASE)
361 cgi->Cvar_Set("r_weather", "0");
362 else
363 cgi->Cvar_Set("r_weather", "%s", cgi->csi->terrainDefs.getWeather(color));
364
365 /* Is there a UFO to recover ? */
366 if (mission->ufo) {
367 const aircraft_t* ufo = mission->ufo;
368 const char* shortUFOType;
369 float ufoCondition;
370
371 if (mission->crashed) {
372 shortUFOType = cgi->Com_UFOCrashedTypeToShortName(ufo->getUfoType());
373 /* Set random map UFO if this is a random map */
374 if (mission->mapDef->mapTheme[0] == '+') {
375 /* set battleParameters.param to the ufo type: used for ufocrash random map */
376 if (Q_streq(mission->mapDef->id, "ufocrash"))
377 param->param = cgi->PoolStrDup(shortUFOType, cp_campaignPool, 0);
378 }
380 } else {
381 shortUFOType = cgi->Com_UFOTypeToShortName(ufo->getUfoType());
382 ufoCondition = 1.0f;
383 }
384
385 Com_sprintf(mission->onwin, sizeof(mission->onwin), "ui_push popup_uforecovery \"%s\" \"%s\" \"%s\" \"%s\" %3.2f",
386 UFO_GetName(ufo), ufo->id, ufo->model, (mission->crashed ? _("landed") : _("crashed")), ufoCondition);
387 /* Set random map UFO if this is a random map */
388 if (mission->mapDef->mapTheme[0] == '+' && !cgi->LIST_IsEmpty(mission->mapDef->ufos)) {
389 /* set rm_ufo to the ufo type used */
390 cgi->Cvar_Set("rm_ufo", "%s", cgi->Com_GetRandomMapAssemblyNameForCraft(shortUFOType));
391 }
392 }
393
394 /* Set random map aircraft if this is a random map */
395 if (mission->mapDef->mapTheme[0] == '+') {
396 if (mission->category == INTERESTCATEGORY_RESCUE) {
397 cgi->Cvar_Set("rm_crashed", "%s", cgi->Com_GetRandomMapAssemblyNameForCrashedCraft(mission->data.aircraft->id));
398 }
399 if (cgi->LIST_ContainsString(mission->mapDef->aircraft, aircraft->id))
400 cgi->Cvar_Set("rm_drop", "%s", cgi->Com_GetRandomMapAssemblyNameForCraft(aircraft->id));
401 }
402}
403
404/*====================================
405*
406* Get information from mission list
407*
408====================================*/
409
415mission_t* CP_GetMissionByIDSilent (const char* missionId)
416{
417 if (!missionId)
418 return nullptr;
419
420 MIS_Foreach(mission) {
421 if (Q_streq(mission->id, missionId))
422 return mission;
423 }
424
425 return nullptr;
426}
427
433mission_t* CP_GetMissionByID (const char* missionId)
434{
435 mission_t* mission = CP_GetMissionByIDSilent(missionId);
436
437 if (!missionId)
438 cgi->Com_Printf("CP_GetMissionByID: missionId was nullptr!\n");
439 else if (!mission)
440 cgi->Com_Printf("CP_GetMissionByID: Could not find mission %s\n", missionId);
441
442 return mission;
443}
444
449{
450 MIS_Foreach(mission) {
451 if (mission->idx == id)
452 return mission;
453 }
454
455 return nullptr;
456}
457
461int MIS_GetIdx (const mission_t* mis)
462{
463 return mis->idx;
464}
465
470const char* MIS_GetName (const mission_t* mission)
471{
472 assert(mission);
473
474 if (mission->category == INTERESTCATEGORY_RESCUE)
475 if (mission->data.aircraft)
476 return va(_("Crashed %s"), mission->data.aircraft->name);
477
478 const nation_t* nation = GEO_GetNation(mission->pos);
479 switch (mission->stage) {
481 if (mission->data.city)
482 return va(_("Alien terror in %s"), _(mission->data.city->name));
483 else
484 return _("Alien terror");
486 if (mission->data.base)
487 return va(_("Base attacked: %s"), mission->data.base->name);
488 else
489 return _("Base attack");
491 if (nation)
492 return va(_("Alien base in %s"), _(nation->name));
493 else
494 return _("Alien base");
495 default:
496 break;
497 }
498
499 /* mission has an ufo */
500 if (mission->ufo) {
501 /* which is crashed */
502 if (mission->crashed)
503 return va(_("Crashed %s"), UFO_GetName(mission->ufo));
504 /* not crashed but detected */
505 if (mission->ufo->detected && mission->ufo->landed)
506 return va(_("Landed %s"), UFO_GetName(mission->ufo));
507 }
508
509 /* we know nothing about the mission, maybe only it's location */
510 if (nation)
511 return va(_("Alien activity in %s"), _(nation->name));
512 else
513 return _("Alien activity");
514}
515
516#ifdef DEBUG
521static const char* CP_MissionStageToName (const missionStage_t stage)
522{
523 switch (stage) {
524 case STAGE_NOT_ACTIVE:
525 return "Not active yet";
527 return "UFO coming from orbit";
528 case STAGE_RECON_AIR:
529 return "Aerial recon underway";
531 return "Going to mission position";
533 return "Ground recon mission underway";
535 return "Terror mission underway";
536 case STAGE_BUILD_BASE:
537 return "Building base";
539 return "Attacking a base";
541 return "Subverting a government";
542 case STAGE_SUPPLY:
543 return "Supplying";
544 case STAGE_SPREAD_XVI:
545 return "Spreading XVI";
546 case STAGE_INTERCEPT:
547 return "Intercepting or attacking installation";
549 return "Leaving earth";
551 return "Base visible";
552 case STAGE_HARVEST:
553 return "Harvesting";
554 case STAGE_OVER:
555 return "Mission over";
556 }
557
558 /* Can't reach this point */
559 return "";
560}
561#endif
562
568{
569 int counterVisibleMission = 0;
570
571 MIS_Foreach(mission) {
572 if (mission->onGeoscape) {
573 counterVisibleMission++;
574 }
575 }
576
577 return counterVisibleMission;
578}
579
580
581/*====================================
582* Functions relative to geoscape
583*====================================*/
584
590const char* MIS_GetModel (const mission_t* mission)
591{
592 /* Mission shouldn't be drawn on geoscape if mapDef is not defined */
593 assert(mission->mapDef);
594
595 if (mission->crashed)
596 return "geoscape/ufocrash";
597
598 if (mission->mapDef->storyRelated && mission->category != INTERESTCATEGORY_ALIENBASE)
599 return "geoscape/icon_story";
600
601 cgi->Com_DPrintf(DEBUG_CLIENT, "Mission is %s, %d\n", mission->id, mission->category);
602 switch (mission->category) {
604 return "geoscape/icon_rescue";
607 return "geoscape/icon_build_alien_base";
610 return "geoscape/alienbase";
612 return "geoscape/icon_recon";
614 return "geoscape/icon_xvi";
616 return "geoscape/icon_harvest";
618 return "geoscape/icon_ufocarrier";
620 return "geoscape/icon_terror";
621 /* Should not be reached, these mission categories are not drawn on geoscape */
623 return "geoscape/base2";
629 break;
630 }
631 cgi->Com_Error(ERR_DROP, "Unknown mission interest category");
632}
633
640{
641 /* This function could be called before position of the mission is defined */
642 if (!mission->posAssigned)
644
645 if (mission->crashed)
647
648 if (mission->ufo && mission->ufo->detected && mission->ufo->landed)
650
651 if (mission->category == INTERESTCATEGORY_RESCUE)
653
654 switch (mission->stage) {
660 case STAGE_SPREAD_XVI:
661 case STAGE_HARVEST:
662 if (RADAR_CheckRadarSensored(mission->pos))
664 break;
667 case STAGE_INTERCEPT:
668 case STAGE_RECON_AIR:
670 case STAGE_NOT_ACTIVE:
671 case STAGE_BUILD_BASE:
673 case STAGE_OVER:
674 case STAGE_SUPPLY:
675 break;
676 }
678}
679
684{
685 if (!mission->onGeoscape && mission->category != INTERESTCATEGORY_BASE_ATTACK)
686 return;
687
688 mission->onGeoscape = false;
689
690 /* Notifications */
693}
694
701{
702 switch (mission->stage) {
704 return MSG_BASEATTACK;
706 return MSG_TERRORSITE;
707 default:
708 break;
709 }
710
711 if (mission->crashed)
712 return MSG_CRASHSITE;
713 return MSG_STANDARD;
714}
715
721static inline const char* CP_MissionGetMessage (const mission_t* mission)
722{
723 if (mission->category == INTERESTCATEGORY_RESCUE) {
724 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Go on a rescue mission for %s to save your soldiers, some of whom may still be alive."), mission->data.aircraft->name);
725 } else {
726 const nation_t* nation = GEO_GetNation(mission->pos);
727 if (nation)
728 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Alien activity has been detected in %s."), _(nation->name));
729 else
730 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Alien activity has been detected."));
731 }
732 return cp_messageBuffer;
733}
734
741void CP_MissionAddToGeoscape (mission_t* mission, bool force)
742{
744
745 /* don't show a mission spawned by a undetected ufo, unless forced : this may be done at next detection stage */
746 if (status == MISDET_CANT_BE_DETECTED || (!force && status == MISDET_MAY_BE_DETECTED && mission->ufo && !mission->ufo->detected))
747 return;
748
749#ifdef DEBUG
750 /* UFO that spawned this mission should be close of mission */
751 if (mission->ufo && ((fabs(mission->ufo->pos[0] - mission->pos[0]) > 1.0f) || (fabs(mission->ufo->pos[1] - mission->pos[1]) > 1.0f))) {
752 cgi->Com_Printf("Error: mission (stage: %s) spawned is not at the same location as UFO\n", CP_MissionStageToName(mission->stage));
753 }
754#endif
755
756 /* Notify the player */
758
759 mission->onGeoscape = true;
762}
763
771{
772 /* Probability to detect UFO each DETECTION_INTERVAL
773 * This correspond to 40 percents each 30 minutes (coded this way to be able to
774 * change DETECTION_INTERVAL without changing the way radar works) */
775 const float missionDetectionProbability = 0.000125f * DETECTION_INTERVAL;
776 bool newDetection = false;
777
778 MIS_Foreach(mission) {
780
781 /* only check mission that can be detected, and that are not already detected */
782 if (status != MISDET_MAY_BE_DETECTED || mission->onGeoscape)
783 continue;
784
785 /* if there is a ufo assigned, it must not be detected */
786 assert(!mission->ufo || !mission->ufo->detected);
787
788 if (frand() <= missionDetectionProbability) {
789 /* if mission has a UFO, detect the UFO when it takes off */
790 if (mission->ufo)
791 UFO_DetectNewUFO(mission->ufo);
792
793 /* mission is detected */
794 CP_MissionAddToGeoscape(mission, true);
795
796 newDetection = true;
797 }
798 }
799 return newDetection;
800}
801
808{
809 MIS_Foreach(mission) {
810 if (mission->onGeoscape && CP_CheckMissionVisibleOnGeoscape(mission) == MISDET_CANT_BE_DETECTED) {
811 /* remove a mission when radar is destroyed */
813 } else if (!mission->onGeoscape && CP_CheckMissionVisibleOnGeoscape(mission) == MISDET_ALWAYS_DETECTED) {
814 /* only show mission that are always detected: mission that have a probability to be detected will be
815 * tested at next half hour */
816 CP_MissionAddToGeoscape(mission, false);
817 }
818 }
819}
820
828void CP_UFORemoveFromGeoscape (mission_t* mission, bool destroyed)
829{
830 assert(mission->ufo);
831
832 /* UFO is landed even if it's destroyed: crash site mission is spawned */
833 mission->ufo->landed = true;
834
835 /* Notications */
836 AIR_AircraftsNotifyUFORemoved(mission->ufo, destroyed);
837 GEO_NotifyUFORemoved(mission->ufo, destroyed);
839
840 if (destroyed) {
841 /* remove UFO from radar and update idx of ufo in radar array */
842 RADAR_NotifyUFORemoved(mission->ufo, true);
843
844 /* Update UFO idx */
846 MIS_Foreach(removedMission) {
847 if (removedMission->ufo && (removedMission->ufo > mission->ufo))
848 removedMission->ufo--;
849 }
850
851 UFO_RemoveFromGeoscape(mission->ufo);
852 mission->ufo = nullptr;
853 } else if (mission->ufo->detected && !RADAR_CheckRadarSensored(mission->ufo->pos)) {
854 /* maybe we use a high speed time: UFO was detected, but flied out of radar
855 * range since last calculation, and mission is spawned outside radar range */
856 RADAR_NotifyUFORemoved(mission->ufo, false);
857 }
858}
859
860
861/*====================================
862*
863* Handling missions
864*
865*====================================*/
866
872{
873 /* Destroy UFO */
874 if (mission->ufo)
875 CP_UFORemoveFromGeoscape(mission, true); /* for the notifications */
876
877 /* Remove from battle parameters */
878 if (mission == ccs.battleParameters.mission)
879 ccs.battleParameters.mission = nullptr;
880
881 /* Notifications */
883
884 if (!cgi->LIST_Remove(&ccs.missions, mission))
885 cgi->Com_Error(ERR_DROP, "CP_MissionRemove: Could not find mission '%s' to remove.\n", mission->id);
886}
887
895{
896 mission->finalDate = DateTime(0, 0);
897}
898
906{
907 return mission->finalDate.getDateAsDays() != 0;
908}
909
910
911/*====================================
912*
913* Notifications
914*
915====================================*/
916
921{
922 MIS_Foreach(mission) {
923 /* Check if this is a base attack mission attacking this base */
924 if (mission->category == INTERESTCATEGORY_BASE_ATTACK && mission->data.base) {
925 if (base == mission->data.base) {
926 /* Aimed base has been destroyed, abort mission */
928 }
929 }
930 }
931}
932
938{
939 MIS_Foreach(mission) {
940 if (mission->category == INTERESTCATEGORY_INTERCEPT && mission->data.installation) {
941 if (mission->data.installation == installation)
942 CP_InterceptMissionLeave(mission, false);
943 }
944 }
945}
946
947/*====================================
948*
949* Functions common to several mission type
950*
951====================================*/
952
960static bool CP_MapIsSelectable (const mission_t* mission, const mapDef_t* md, const vec2_t pos)
961{
962 if (md->storyRelated)
963 return false;
964
965 if (!mission->ufo) {
966 /* a mission without UFO should not use a map with UFO */
967 if (!cgi->LIST_IsEmpty(md->ufos))
968 return false;
969 } else if (!cgi->LIST_IsEmpty(md->ufos)) {
970 /* A mission with UFO should use a map with UFO
971 * first check that list is not empty */
972 const ufoType_t type = mission->ufo->getUfoType();
973 const char* ufoID;
974
975 if (mission->crashed)
976 ufoID = cgi->Com_UFOCrashedTypeToShortName(type);
977 else
978 ufoID = cgi->Com_UFOTypeToShortName(type);
979
980 if (!cgi->LIST_ContainsString(md->ufos, ufoID))
981 return false;
982 }
983
984 if (pos && !GEO_PositionFitsTCPNTypes(pos, md->terrains, md->cultures, md->populations, nullptr))
985 return false;
986
987 return true;
988}
989
996bool CP_ChooseMap (mission_t* mission, const vec2_t pos)
997{
998 if (mission->mapDef)
999 return true;
1000
1001 int countMinimal = 0;
1002 int minMapDefAppearance = -1;
1003 mapDef_t* md = nullptr;
1005 /* Check if mission fulfill conditions */
1006 if (!CP_MapIsSelectable(mission, md, pos))
1007 continue;
1008
1009 if (minMapDefAppearance < 0 || md->timesAlreadyUsed < minMapDefAppearance) {
1010 minMapDefAppearance = md->timesAlreadyUsed;
1011 countMinimal = 1;
1012 continue;
1013 }
1014 if (md->timesAlreadyUsed > minMapDefAppearance)
1015 continue;
1016 countMinimal++;
1017 }
1018
1019 if (countMinimal == 0) {
1020 /* no map fulfill the conditions */
1021 if (mission->category == INTERESTCATEGORY_RESCUE) {
1022 /* default map for rescue mission is the rescue random map assembly */
1023 mission->mapDef = cgi->Com_GetMapDefinitionByID("rescue");
1024 if (!mission->mapDef)
1025 cgi->Com_Error(ERR_DROP, "Could not find mapdef: rescue");
1026 mission->mapDef->timesAlreadyUsed++;
1027 return true;
1028 }
1029 if (mission->crashed) {
1030 /* default map for crashsite mission is the crashsite random map assembly */
1031 mission->mapDef = cgi->Com_GetMapDefinitionByID("ufocrash");
1032 if (!mission->mapDef)
1033 cgi->Com_Error(ERR_DROP, "Could not find mapdef: ufocrash");
1034 mission->mapDef->timesAlreadyUsed++;
1035 return true;
1036 }
1037
1038 cgi->Com_Printf("CP_ChooseMap: Could not find map with required conditions:\n");
1039 cgi->Com_Printf(" ufo: %s -- pos: ", mission->ufo ? cgi->Com_UFOTypeToShortName(mission->ufo->getUfoType()) : "none");
1040 if (pos)
1041 cgi->Com_Printf("%s", MapIsWater(GEO_GetColor(pos, MAPTYPE_TERRAIN, nullptr)) ? " (in water) " : "");
1042 if (pos)
1043 cgi->Com_Printf("(%.02f, %.02f)\n", pos[0], pos[1]);
1044 else
1045 cgi->Com_Printf("none\n");
1046 return false;
1047 }
1048
1049 /* select a map randomly from the selected */
1050 int randomNum = rand() % countMinimal;
1051 md = nullptr;
1053 /* Check if mission fulfill conditions */
1054 if (!CP_MapIsSelectable(mission, md, pos))
1055 continue;
1056 if (md->timesAlreadyUsed > minMapDefAppearance)
1057 continue;
1058 /* There shouldn't be mission fulfilling conditions used less time than minMissionAppearance */
1059 assert(md->timesAlreadyUsed == minMapDefAppearance);
1060
1061 if (randomNum == 0) {
1062 mission->mapDef = md;
1063 break;
1064 } else {
1065 randomNum--;
1066 }
1067 }
1068
1069 /* A mission must have been selected */
1070 mission->mapDef->timesAlreadyUsed++;
1071 if (cp_missiontest->integer)
1072 cgi->Com_Printf("Selected map '%s' (among %i possible maps)\n", mission->mapDef->id, countMinimal);
1073 else
1074 cgi->Com_DPrintf(DEBUG_CLIENT, "Selected map '%s' (among %i possible maps)\n", mission->mapDef->id, countMinimal);
1075
1076 return true;
1077}
1078
1084void CP_MissionStageEnd (const campaign_t* campaign, mission_t* mission)
1085{
1086 cgi->Com_DPrintf(DEBUG_CLIENT, "Ending mission category %i, stage %i (time: %i day, %i sec)\n",
1087 mission->category, mission->stage, ccs.date.getDateAsDays(), ccs.date.getTimeAsSeconds());
1088
1089 /* Crash mission is on the map for too long: aliens die or go away. End mission */
1090 if (mission->crashed) {
1091 CP_MissionIsOver(mission);
1092 return;
1093 }
1094
1095 switch (mission->category) {
1097 CP_ReconMissionNextStage(mission);
1098 break;
1101 break;
1104 break;
1107 CP_BuildBaseMissionNextStage(campaign, mission);
1108 break;
1111 break;
1113 CP_XVIMissionNextStage(mission);
1114 break;
1117 CP_InterceptNextStage(mission);
1118 break;
1121 break;
1123 CP_RescueNextStage(mission);
1124 break;
1126 CP_UFOCarrierNextStage(mission);
1127 break;
1131 cgi->Com_Printf("CP_MissionStageEnd: Invalid type of mission (%i), remove mission '%s'\n", mission->category, mission->id);
1132 CP_MissionRemove(mission);
1133 }
1134}
1135
1141{
1142 switch (mission->category) {
1144 CP_ReconMissionIsFailure(mission);
1145 break;
1148 break;
1150 if (mission->stage <= STAGE_BASE_ATTACK)
1152 else
1154 break;
1157 if (mission->stage <= STAGE_BUILD_BASE)
1159 else
1161 break;
1163 if (mission->stage <= STAGE_SUPPLY)
1165 else
1167 break;
1169 if (mission->stage <= STAGE_SPREAD_XVI)
1170 CP_XVIMissionIsFailure(mission);
1171 else
1172 CP_XVIMissionIsSuccess(mission);
1173 break;
1176 if (mission->stage <= STAGE_INTERCEPT)
1178 else
1180 break;
1183 break;
1186 break;
1188 CP_MissionRemove(mission);
1189 break;
1193 cgi->Com_Printf("CP_MissionIsOver: Invalid type of mission (%i), remove mission\n", mission->category);
1194 CP_MissionRemove(mission);
1195 break;
1196 }
1197}
1198
1204{
1205 assert(ufocraft->mission);
1206 CP_MissionIsOver(ufocraft->mission);
1207}
1208
1215void CP_MissionEndActions (mission_t* mission, aircraft_t* aircraft, bool won)
1216{
1217 /* handle base attack mission */
1218 if (mission->stage == STAGE_BASE_ATTACK) {
1219 if (won) {
1220 /* fake an aircraft return to collect goods and aliens */
1221 B_DumpAircraftToHomeBase(aircraft);
1222
1223 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Defence of base: %s successful!"),
1224 aircraft->homebase->name);
1227 } else
1229
1230 return;
1231 }
1232
1233 if (mission->category == INTERESTCATEGORY_RESCUE) {
1234 CP_EndRescueMission(mission, aircraft, won);
1235 }
1236
1237 AIR_AircraftReturnToBase(aircraft);
1238 if (won)
1239 CP_MissionIsOver(mission);
1240}
1241
1250void CP_MissionEnd (const campaign_t* campaign, mission_t* mission, const battleParam_t* battleParameters, bool won)
1251{
1252 base_t* base;
1253 aircraft_t* aircraft;
1254 int numberOfSoldiers = 0; /* DEBUG */
1255
1256 if (mission->stage == STAGE_BASE_ATTACK) {
1257 base = mission->data.base;
1258 assert(base);
1259 /* HACK */
1260 aircraft = base->aircraftCurrent;
1261 } else {
1262 aircraft = GEO_GetMissionAircraft();
1263 base = aircraft->homebase;
1264 }
1265
1266 /* add the looted goods to base storage and market */
1267 base->storage = ccs.eMission;
1268
1269 won ? ccs.campaignStats.missionsWon++ : ccs.campaignStats.missionsLost++;
1270
1271 CP_HandleNationData(campaign->minhappiness, mission, battleParameters->nation, &(mission->missionResults), won);
1272 CP_CheckLostCondition(campaign);
1273
1274 /* update the character stats */
1275 CHAR_UpdateData(ccs.updateCharacters);
1276 cgi->LIST_Delete(&ccs.updateCharacters);
1277
1278 /* update stats */
1279 CHAR_UpdateStats(base, aircraft);
1280
1281 E_Foreach(EMPL_SOLDIER, employee) {
1282 if (AIR_IsEmployeeInAircraft(employee, aircraft))
1283 numberOfSoldiers++;
1284
1286 if (employee->isHiredInBase(base) && (employee->chr.HP <= 0))
1287 E_DeleteEmployee(employee);
1288 }
1289 cgi->Com_DPrintf(DEBUG_CLIENT, "CP_MissionEnd - num %i\n", numberOfSoldiers);
1290
1291 CP_ExecuteMissionTrigger(mission, won);
1292 CP_MissionEndActions(mission, aircraft, won);
1293}
1294
1302bool CP_CheckNextStageDestination (const campaign_t* campaign, aircraft_t* ufocraft)
1303{
1304 mission_t* mission;
1305
1306 mission = ufocraft->mission;
1307 assert(mission);
1308
1309 switch (mission->stage) {
1311 case STAGE_MISSION_GOTO:
1312 CP_MissionStageEnd(campaign, mission);
1313 return false;
1315 CP_MissionStageEnd(campaign, mission);
1316 return true;
1317 default:
1318 /* Do nothing */
1319 return false;
1320 }
1321}
1322
1328void CP_UFOProceedMission (const campaign_t* campaign, aircraft_t* ufo)
1329{
1330 /* Every UFO on geoscape must have a mission assigned */
1331 assert(ufo->mission);
1332
1333 if (ufo->mission->category == INTERESTCATEGORY_INTERCEPT && !ufo->mission->data.aircraft) {
1334 const int slotIndex = AIRFIGHT_ChooseWeapon(ufo->weapons, ufo->maxWeapons, ufo->pos, ufo->pos);
1335 /* This is an Intercept mission where UFO attacks aircraft (not installations) */
1336 /* Keep on looking targets until mission is over, unless no more ammo */
1337 ufo->status = AIR_TRANSIT;
1338 if (slotIndex != AIRFIGHT_WEAPON_CAN_NEVER_SHOOT)
1339 UFO_SetRandomDest(ufo);
1340 else
1341 CP_MissionStageEnd(campaign, ufo->mission);
1342 } else if (ufo->mission->stage < STAGE_MISSION_GOTO ||
1343 ufo->mission->stage >= STAGE_RETURN_TO_ORBIT) {
1344 UFO_SetRandomDest(ufo);
1345 } else {
1346 UFO_SendToDestination(ufo, ufo->mission->pos);
1347 }
1348}
1349
1355{
1356 /* How long the crash mission will stay before aliens leave / die */
1357 const DateTime minCrashDelay(7, 0);
1358 const DateTime maxCrashDelay(14, 0);
1359 mission_t* mission;
1360
1361 mission = ufo->mission;
1362 if (!mission)
1363 cgi->Com_Error(ERR_DROP, "CP_SpawnCrashSiteMission: No mission correspond to ufo '%s'", ufo->id);
1364
1365 mission->crashed = true;
1366
1367 /* Reset mapDef. CP_ChooseMap don't overwrite if set */
1368 mission->mapDef = nullptr;
1369 if (!CP_ChooseMap(mission, ufo->pos)) {
1370 cgi->Com_Printf("CP_SpawnCrashSiteMission: No map found, remove mission.\n");
1371 CP_MissionRemove(mission);
1372 return;
1373 }
1374
1375 Vector2Copy(ufo->pos, mission->pos);
1376 mission->posAssigned = true;
1377
1378 mission->finalDate = ccs.date + Date_Random(minCrashDelay, maxCrashDelay);
1379 /* ufo becomes invisible on geoscape, but don't remove it from ufo global array
1380 * (may be used to know what items are collected from battlefield)*/
1381 CP_UFORemoveFromGeoscape(mission, false);
1382 /* mission appear on geoscape, player can go there */
1383 CP_MissionAddToGeoscape(mission, false);
1384}
1385
1386
1396{
1397 mission_t* mission;
1398 mission_t* oldMission;
1399 int survivors = 0;
1400
1401 /* Handle events about crash */
1402 /* Do this first, if noone survived the crash => no mission to spawn */
1403
1404 bool pilotSurvived = AIR_PilotSurvivedCrash(aircraft);
1405 if (!pilotSurvived) {
1406 Employee* pilot = AIR_GetPilot(aircraft);
1408 E_DeleteEmployee(pilot);
1409 }
1410
1411 LIST_Foreach(aircraft->acTeam, Employee, employee) {
1412#if 0
1413 const character_t* chr = &employee->chr;
1414 const chrScoreGlobal_t* score = &chr->score;
1416 E_DeleteEmployee(employee);
1417#else
1418 (void)employee;
1419#endif
1420 survivors++;
1421 }
1422
1423 aircraft->status = AIR_CRASHED;
1424
1425 /* after we set this to AIR_CRASHED we can get the next 'valid'
1426 * aircraft to correct the pointer in the homebase */
1427 if (aircraft->homebase->aircraftCurrent == aircraft)
1428 aircraft->homebase->aircraftCurrent = AIR_GetFirstFromBase(aircraft->homebase);
1429
1430 /* a crashed aircraft is no longer using capacity of the hangars */
1431 const baseCapacities_t capType = AIR_GetHangarCapacityType(aircraft);
1432 if (capType != MAX_CAP)
1433 CAP_AddCurrent(aircraft->homebase, capType, -1);
1434
1435 if (GEO_IsAircraftSelected(aircraft))
1436 GEO_SetSelectedAircraft(nullptr);
1437
1438 /* Check if ufo was destroyed too */
1439 if (!ufo) {
1440 cgi->Com_Printf("CP_SpawnRescueMission: UFO was also destroyed.\n");
1442 AIR_DestroyAircraft(aircraft, pilotSurvived);
1443 return;
1444 }
1445
1446 if (survivors == 0) {
1447 AIR_DestroyAircraft(aircraft, pilotSurvived);
1448 return;
1449 }
1450
1451 /* create the mission */
1453 if (!mission)
1454 cgi->Com_Error(ERR_DROP, "CP_SpawnRescueMission: mission could not be created");
1455
1456 /* Reset mapDef. CP_ChooseMap don't overwrite if set */
1457 mission->mapDef = nullptr;
1458 if (!CP_ChooseMap(mission, aircraft->pos)) {
1459 cgi->Com_Printf("CP_SpawnRescueMission: Cannot set mapDef for mission %s, removing.\n", mission->id);
1460 CP_MissionRemove(mission);
1461 return;
1462 }
1463
1464 mission->data.aircraft = aircraft;
1465
1466 /* UFO drops it's previous mission and goes for the crashed aircraft */
1467 oldMission = ufo->mission;
1468 oldMission->ufo = nullptr;
1469 ufo->mission = mission;
1470 CP_MissionRemove(oldMission);
1471
1472 mission->ufo = ufo;
1473 mission->stage = STAGE_MISSION_GOTO;
1474 Vector2Copy(aircraft->pos, mission->pos);
1475
1476 /* Stage will finish when UFO arrives at destination */
1478}
1479
1480/*====================================
1481*
1482* Spawn new missions
1483*
1484====================================*/
1485
1490static bool MIS_IsSpawnedFromGround (const mission_t* mission)
1491{
1492 assert(mission);
1493
1494 switch (mission->category) {
1495 /* missions can't be spawned from ground */
1502 return false;
1503 /* missions can be spawned from ground */
1508 break;
1509 /* missions not spawned this way */
1515 cgi->Com_Error(ERR_DROP, "MIS_IsSpawnedFromGround: Wrong mission category %i", mission->category);
1516 }
1517
1518 /* Roll the random number */
1519 const float XVI_PARAM = 10.0f;
1520 float groundProbability;
1521 float randNumber = frand();
1522
1523 /* The higher the XVI rate, the higher the probability to have a mission spawned from ground */
1524 groundProbability = 1.0f - exp(-CP_GetAverageXVIRate() / XVI_PARAM);
1525 groundProbability = std::max(0.1f, groundProbability);
1526
1527 return randNumber < groundProbability;
1528}
1529
1538{
1539 mission->stage = STAGE_COME_FROM_ORBIT;
1540
1541 if (MIS_IsSpawnedFromGround(mission)) {
1542 mission->ufo = nullptr;
1543 /* Mission starts from ground: no UFO. Go to next stage on next frame (skip UFO arrives on earth) */
1544 mission->finalDate = ccs.date;
1545 } else {
1546 ufoType_t ufoType = CP_MissionChooseUFO(mission);
1547 if (ufoType == UFO_NONE) {
1548 CP_MissionRemove(mission);
1549 return false;
1550 }
1551 mission->ufo = UFO_AddToGeoscape(ufoType, nullptr, mission);
1552 if (!mission->ufo) {
1553 cgi->Com_Printf("CP_MissionBegin: Could not add UFO '%s', remove mission %s\n",
1554 cgi->Com_UFOTypeToShortName(ufoType), mission->id);
1555 CP_MissionRemove(mission);
1556 return false;
1557 }
1558 /* Stage will finish when UFO arrives at destination */
1560 }
1561
1562 mission->idx = ++ccs.campaignStats.missions;
1563 return true;
1564}
1565
1574{
1575 ufoType_t ufoTypes[UFO_MAX];
1576 int numTypes = 0;
1577
1578 switch (mission->category) {
1580 numTypes = UFO_GetAvailableUFOsForMission(mission->category, ufoTypes);
1581 break;
1583 numTypes = UFO_GetAvailableUFOsForMission(mission->category, ufoTypes);
1584 break;
1586 numTypes = UFO_GetAvailableUFOsForMission(mission->category, ufoTypes);
1587 break;
1591 /* This is a subverting government mission */
1593 } else {
1594 /* This is a Building base mission */
1596 }
1597 break;
1599 numTypes = UFO_GetAvailableUFOsForMission(mission->category, ufoTypes);
1600 break;
1602 numTypes = UFO_GetAvailableUFOsForMission(mission->category, ufoTypes);
1603 break;
1606 numTypes = UFO_GetAvailableUFOsForMission(mission->category, ufoTypes);
1607 break;
1609 numTypes = UFO_GetAvailableUFOsForMission(mission->category, ufoTypes);
1610 break;
1612 /* can't be spawned: alien base is the result of base building mission */
1617 cgi->Com_Error(ERR_DROP, "CP_MissionChooseUFO: Wrong mission category %i", mission->category);
1618 }
1619
1620 const short ufoIdsNum = cgi->Com_GetUFOIdsNum();
1621 if (numTypes > ufoIdsNum)
1622 cgi->Com_Error(ERR_DROP, "CP_MissionChooseUFO: Too many values UFOs (%i/%i)", numTypes, ufoIdsNum);
1623
1624 if (numTypes <= 0)
1625 return UFO_NONE;
1626
1627 /* Roll the random number */
1628 const float randNumber = frand();
1629
1630 /* If we reached this point, then mission will be spawned from space: choose UFO */
1631 int idx = (int) (numTypes * randNumber);
1632 if (idx >= numTypes) {
1633 idx = numTypes -1;
1634 }
1635
1636 return ufoTypes[idx];
1637}
1638
1645static inline void CP_SetMissionName (mission_t* mission)
1646{
1647 int num = 0;
1648
1649 do {
1650 Com_sprintf(mission->id, sizeof(mission->id), "cat%i_interest%i_%i",
1651 mission->category, mission->initialOverallInterest, num);
1652
1653 /* if mission list is empty, the id is unique for sure */
1654 if (cgi->LIST_IsEmpty(ccs.missions))
1655 return;
1656
1657 /* check whether a mission with the same id already exists in the list
1658 * if so, generate a new name by using an increased num values - otherwise
1659 * just use the mission->id we just stored - it should be unique now */
1660 if (!CP_GetMissionByIDSilent(mission->id))
1661 break;
1662
1663 num++;
1664 } while (num); /* fake condition */
1665}
1666
1675{
1676 mission_t mission;
1677 const DateTime minNextSpawningDate(0, 0);
1678 const DateTime maxNextSpawningDate(3, 0); /* Delay between 2 mission spawning */
1679
1680 /* Some event are non-occurrence */
1681 if (category <= INTERESTCATEGORY_NONE || category >= INTERESTCATEGORY_MAX)
1682 return nullptr;
1683
1684 OBJZERO(mission);
1685
1686 /* First fill common datas between all type of missions */
1687 mission.category = category;
1688 mission.initialOverallInterest = ccs.overallInterest;
1689 mission.initialIndividualInterest = ccs.interest[category];
1690 mission.stage = STAGE_NOT_ACTIVE;
1691 mission.ufo = nullptr;
1692 if (beginNow)
1693 mission.startDate = ccs.date;
1694 else
1695 mission.startDate = ccs.date + Date_Random(minNextSpawningDate, maxNextSpawningDate);
1696 mission.finalDate = mission.startDate;
1697 mission.idx = ++ccs.campaignStats.missions;
1698
1699 CP_SetMissionName(&mission);
1700
1701 /* Handle mission specific, spawn-time routines */
1702 if (mission.category == INTERESTCATEGORY_BUILDING)
1704 else if (mission.category == INTERESTCATEGORY_BASE_ATTACK)
1706 else if (mission.category == INTERESTCATEGORY_TERROR_ATTACK)
1708
1709 /* Add mission to global array */
1710 return &LIST_Add(&ccs.missions, mission);
1711}
1712
1718{
1719 int sum = 0;
1720 int i, randomNumber;
1721
1722 for (i = 0; i < INTERESTCATEGORY_MAX; i++)
1723 sum += ccs.interest[i];
1724
1725 randomNumber = (int) (frand() * (float) sum);
1726
1727 for (i = 0; randomNumber >= 0; i++)
1728 randomNumber -= ccs.interest[i];
1729
1730 return (interestCategory_t)(i - 1);
1731}
1732
1739{
1740 int spawn_delay = DELAY_BETWEEN_MISSION_SPAWNING;
1741 ccs.lastMissionSpawnedDelay++;
1742
1743 /* Halve the spawn delay in the early game so players see UFOs and get right into action */
1744 if (ccs.overallInterest < EARLY_UFO_RUSH_INTEREST) {
1745 spawn_delay = (int) (spawn_delay / 3);
1746 }
1747
1748 if (ccs.lastMissionSpawnedDelay > spawn_delay) {
1749 float nonOccurrence;
1750 /* Select the amount of missions that will be spawned in the next cycle. */
1751
1752 /* Each mission has a certain probability to not occur. This provides some randomness to the mission density.
1753 * However, once the campaign passes a certain point, this effect rapidly diminishes. This means that by the
1754 * end of the game, ALL missions will spawn, quickly overrunning the player. */
1755 if (ccs.overallInterest > FINAL_OVERALL_INTEREST)
1756 nonOccurrence = ccs.curCampaign->ufoReductionRate / pow(((ccs.overallInterest - FINAL_OVERALL_INTEREST / 30) + 1.0f), 2);
1757 else
1758 nonOccurrence = ccs.curCampaign->ufoReductionRate;
1759
1760 /* Increase the alien's interest in supplying their bases for this cycle.
1761 * The more bases, the greater their interest in supplying them. */
1763
1764 /* Calculate the amount of missions to be spawned this cycle.
1765 * Note: This is a function over css.overallInterest. It looks like this:
1766 * http://www.wolframalpha.com/input/?i=Plot%5B40%2B%285-40%29%2A%28%28x-1000%29%2F%2820-1000%29%29%5E2%2C+%7Bx%2C+0%2C+1100%7D%5D
1767 */
1768 int newMissionNum = (int) (ccs.curCampaign->maxMissions + (ccs.curCampaign->minMissions - ccs.curCampaign->maxMissions) * pow(float((ccs.overallInterest - FINAL_OVERALL_INTEREST) / (ccs.curCampaign->initialInterest - FINAL_OVERALL_INTEREST)), 2));
1769 cgi->Com_DPrintf(DEBUG_CLIENT, "interest = %d, new missions = %d\n", ccs.overallInterest, newMissionNum);
1770 for (int i = 0; i < newMissionNum; i++) {
1771 if (frand() > nonOccurrence) {
1773 CP_CreateNewMission(type, false);
1774 }
1775 }
1776
1777 ccs.lastMissionSpawnedDelay -= spawn_delay;
1778 }
1779}
1780
1787{
1788 ccs.lastMissionSpawnedDelay = DELAY_BETWEEN_MISSION_SPAWNING;
1789
1790 ccs.missionSpawnCallback();
1791}
1792
1793
1794/*====================================
1795*
1796* Debug functions
1797*
1798====================================*/
1799
1800#ifdef DEBUG
1804static void MIS_SpawnNewMissions_f (void)
1805{
1806 interestCategory_t category;
1807 int type = 0;
1808
1809 if (cgi->Cmd_Argc() < 2) {
1810 cgi->Com_Printf("Usage: %s <category> [<type>]\n", cgi->Cmd_Argv(0));
1811 for (int i = INTERESTCATEGORY_RECON; i < INTERESTCATEGORY_MAX; i++) {
1812 category = (interestCategory_t)i;
1813 cgi->Com_Printf("...%i: %s", category, INT_InterestCategoryToName(category));
1814 if (category == INTERESTCATEGORY_RECON)
1815 cgi->Com_Printf(" <0:Random, 1:Aerial, 2:Ground>");
1816 else if (category == INTERESTCATEGORY_BUILDING)
1817 cgi->Com_Printf(" <0:Subverse Government, 1:Build Base>");
1818 else if (category == INTERESTCATEGORY_INTERCEPT)
1819 cgi->Com_Printf(" <0:Intercept aircraft, 1:Attack installation>");
1820 cgi->Com_Printf("\n");
1821 }
1822 return;
1823 }
1824
1825 if (cgi->Cmd_Argc() >= 3)
1826 type = atoi(cgi->Cmd_Argv(2));
1827
1828 category = (interestCategory_t)atoi(cgi->Cmd_Argv(1));
1829
1830 if (category == INTERESTCATEGORY_MAX)
1831 return;
1832
1833 if (category == INTERESTCATEGORY_ALIENBASE) {
1834 /* spawning an alien base is special */
1835 vec2_t pos;
1836 alienBase_t* base;
1837 AB_SetAlienBasePosition(pos); /* get base position */
1838 base = AB_BuildBase(pos); /* build base */
1839 if (!base) {
1840 cgi->Com_Printf("CP_BuildBaseSetUpBase: could not create base\n");
1841 return;
1842 }
1843 CP_SpawnAlienBaseMission(base); /* make base visible */
1844 return;
1845 } else if (category == INTERESTCATEGORY_RESCUE) {
1846 const base_t* base = B_GetFoundedBaseByIDX(0);
1847 aircraft_t* aircraft;
1848 if (!base) {
1849 cgi->Com_Printf("No base yet\n");
1850 return;
1851 }
1852
1853 aircraft = AIR_GetFirstFromBase(base);
1854 if (!aircraft) {
1855 cgi->Com_Printf("No aircraft in base\n");
1856 return;
1857 }
1858 CP_SpawnRescueMission(aircraft, nullptr);
1859 return;
1860 }
1861
1862 mission_t* mission = CP_CreateNewMission(category, true);
1863 if (!mission) {
1864 cgi->Com_Printf("CP_SpawnNewMissions_f: Could not add mission, abort\n");
1865 return;
1866 }
1867
1868 if (type) {
1869 switch (category) {
1871 /* Start mission */
1872 if (!CP_MissionBegin(mission))
1873 return;
1874 if (type == 1 && mission->ufo)
1875 /* Aerial mission */
1876 CP_ReconMissionAerial(mission);
1877 else
1878 /* This is a ground mission */
1879 CP_ReconMissionGroundGo(mission);
1880 break;
1882 if (type == 1)
1883 mission->initialOverallInterest = ccs.curCampaign->alienBaseInterest + 1;
1884 break;
1886 /* Start mission */
1887 if (!CP_MissionBegin(mission))
1888 return;
1889 if (type == 1) {
1891 mission->ufo->setUfoType(ufoType);
1893 } else {
1895 }
1896 break;
1897 default:
1898 cgi->Com_Printf("Type is not implemented for this category.\n");
1899 }
1900 }
1901 cgi->Com_Printf("Spawned mission with id '%s'\n", mission->id);
1902}
1903
1907static void MIS_MissionSetMap_f (void)
1908{
1909 mapDef_t* mapDef;
1910 mission_t* mission;
1911 if (cgi->Cmd_Argc() < 3) {
1912 cgi->Com_Printf("Usage: %s <missionid> <mapdef>\n", cgi->Cmd_Argv(0));
1913 return;
1914 }
1915 mission = CP_GetMissionByID(cgi->Cmd_Argv(1));
1916 mapDef = cgi->Com_GetMapDefinitionByID(cgi->Cmd_Argv(2));
1917 if (mapDef == nullptr) {
1918 cgi->Com_Printf("Could not find mapdef for %s\n", cgi->Cmd_Argv(2));
1919 return;
1920 }
1921 mission->mapDef = mapDef;
1922}
1923
1928static void MIS_MissionList_f (void)
1929{
1930 bool noMission = true;
1931
1932 MIS_Foreach(mission) {
1933 cgi->Com_Printf("mission: '%s'\n", mission->id);
1934 cgi->Com_Printf("...category %i. '%s' -- stage %i. '%s'\n", mission->category,
1935 INT_InterestCategoryToName(mission->category), mission->stage, CP_MissionStageToName(mission->stage));
1936 cgi->Com_Printf("...mapDef: '%s'\n", mission->mapDef ? mission->mapDef->id : "No mapDef defined");
1937 cgi->Com_Printf("...start (day = %i, sec = %i), ends (day = %i, sec = %i)\n",
1939 cgi->Com_Printf("...pos (%.02f, %.02f)%s -- mission %son Geoscape\n", mission->pos[0], mission->pos[1], mission->posAssigned ? "(assigned Pos)" : "", mission->onGeoscape ? "" : "not ");
1940 if (mission->ufo)
1941 cgi->Com_Printf("...UFO: %s (%i/%i)\n", mission->ufo->id, (int) (mission->ufo - ccs.ufos), ccs.numUFOs - 1);
1942 else
1943 cgi->Com_Printf("...UFO: no UFO\n");
1944 noMission = false;
1945 }
1946 if (noMission)
1947 cgi->Com_Printf("No mission currently in game.\n");
1948}
1949
1954static void MIS_DeleteMissions_f (void)
1955{
1956 MIS_Foreach(mission) {
1957 CP_MissionRemove(mission);
1958 }
1959
1960 if (ccs.numUFOs != 0) {
1961 cgi->Com_Printf("CP_DeleteMissions_f: Error, there are still %i UFO in game afer removing all missions. Force removal.\n", ccs.numUFOs);
1962 while (ccs.numUFOs)
1964 }
1965}
1966
1971static void MIS_DeleteMission_f (void)
1972{
1973 mission_t* mission;
1974
1975 if (cgi->Cmd_Argc() < 2) {
1976 cgi->Com_Printf("Usage: %s <missionid>\n", cgi->Cmd_Argv(0));
1977 return;
1978 }
1979 mission = CP_GetMissionByID(cgi->Cmd_Argv(1));
1980
1981 if (!mission)
1982 return;
1983
1984 CP_MissionRemove(mission);
1985}
1986#endif
1987
1993{
1994 xmlNode_t* missionsNode = cgi->XML_AddNode(parent, SAVE_MISSIONS);
1995
1996 cgi->Com_RegisterConstList(saveInterestConstants);
1997 cgi->Com_RegisterConstList(saveMissionConstants);
1998 MIS_Foreach(mission) {
1999 xmlNode_t* missionNode = cgi->XML_AddNode(missionsNode, SAVE_MISSIONS_MISSION);
2000
2001 cgi->XML_AddInt(missionNode, SAVE_MISSIONS_MISSION_IDX, mission->idx);
2002 cgi->XML_AddString(missionNode, SAVE_MISSIONS_ID, mission->id);
2003 if (mission->mapDef)
2004 cgi->XML_AddString(missionNode, SAVE_MISSIONS_MAPDEF_ID, mission->mapDef->id);
2005 cgi->XML_AddBool(missionNode, SAVE_MISSIONS_ACTIVE, mission->active);
2006 cgi->XML_AddBool(missionNode, SAVE_MISSIONS_POSASSIGNED, mission->posAssigned);
2007 cgi->XML_AddBool(missionNode, SAVE_MISSIONS_CRASHED, mission->crashed);
2008 cgi->XML_AddString(missionNode, SAVE_MISSIONS_ONWIN, mission->onwin);
2009 cgi->XML_AddString(missionNode, SAVE_MISSIONS_ONLOSE, mission->onlose);
2010 cgi->XML_AddString(missionNode, SAVE_MISSIONS_CATEGORY, cgi->Com_GetConstVariable(SAVE_INTERESTCAT_NAMESPACE, mission->category));
2011 cgi->XML_AddString(missionNode, SAVE_MISSIONS_STAGE, cgi->Com_GetConstVariable(SAVE_MISSIONSTAGE_NAMESPACE, mission->stage));
2012 switch (mission->category) {
2014 if (mission->stage == STAGE_MISSION_GOTO || mission->stage == STAGE_BASE_ATTACK) {
2015 const base_t* base = mission->data.base;
2016 /* save IDX of base under attack if required */
2017 cgi->XML_AddShort(missionNode, SAVE_MISSIONS_BASEINDEX, base->idx);
2018 }
2019 break;
2021 if (mission->stage == STAGE_MISSION_GOTO || mission->stage == STAGE_INTERCEPT) {
2022 const installation_t* installation = mission->data.installation;
2023 if (installation)
2024 cgi->XML_AddShort(missionNode, SAVE_MISSIONS_INSTALLATIONINDEX, installation->idx);
2025 }
2026 break;
2028 {
2029 const aircraft_t* aircraft = mission->data.aircraft;
2030 cgi->XML_AddShort(missionNode, SAVE_MISSIONS_CRASHED_AIRCRAFT, aircraft->idx);
2031 }
2032 break;
2036 {
2037 /* save IDX of alien base if required */
2038 const alienBase_t* base = mission->data.alienBase;
2039 /* there may be no base is the mission is a subverting government */
2040 if (base)
2041 cgi->XML_AddShort(missionNode, SAVE_MISSIONS_ALIENBASEINDEX, base->idx);
2042 }
2043 break;
2044 default:
2045 break;
2046 }
2047 cgi->XML_AddShort(missionNode, SAVE_MISSIONS_INITIALOVERALLINTEREST, mission->initialOverallInterest);
2048 cgi->XML_AddShort(missionNode, SAVE_MISSIONS_INITIALINDIVIDUALINTEREST, mission->initialIndividualInterest);
2049 cgi->XML_AddDate(missionNode, SAVE_MISSIONS_STARTDATE, mission->startDate.getDateAsDays(), mission->startDate.getTimeAsSeconds());
2050 cgi->XML_AddDate(missionNode, SAVE_MISSIONS_FINALDATE, mission->finalDate.getDateAsDays(), mission->finalDate.getTimeAsSeconds());
2051 cgi->XML_AddPos2(missionNode, SAVE_MISSIONS_POS, mission->pos);
2052 cgi->XML_AddBoolValue(missionNode, SAVE_MISSIONS_ONGEOSCAPE, mission->onGeoscape);
2053 }
2054 cgi->Com_UnregisterConstList(saveInterestConstants);
2055 cgi->Com_UnregisterConstList(saveMissionConstants);
2056
2057 return true;
2058}
2059
2065{
2066 xmlNode_t* missionNode;
2067 xmlNode_t* node;
2068
2069 cgi->Com_RegisterConstList(saveInterestConstants);
2070 cgi->Com_RegisterConstList(saveMissionConstants);
2071 missionNode = cgi->XML_GetNode(parent, SAVE_MISSIONS);
2072 for (node = cgi->XML_GetNode(missionNode, SAVE_MISSIONS_MISSION); node;
2073 node = cgi->XML_GetNextNode(node, missionNode, SAVE_MISSIONS_MISSION)) {
2074 const char* name;
2075 mission_t mission;
2076 bool defaultAssigned = false;
2077 const char* categoryId = cgi->XML_GetString(node, SAVE_MISSIONS_CATEGORY);
2078 const char* stageId = cgi->XML_GetString(node, SAVE_MISSIONS_STAGE);
2079
2080 OBJZERO(mission);
2081
2082 Q_strncpyz(mission.id, cgi->XML_GetString(node, SAVE_MISSIONS_ID), sizeof(mission.id));
2083 mission.idx = cgi->XML_GetInt(node, SAVE_MISSIONS_MISSION_IDX, 0);
2084 if (mission.idx <= 0) {
2085 cgi->Com_Printf("mission has invalid or no index\n");
2086 continue;
2087 }
2088
2089 name = cgi->XML_GetString(node, SAVE_MISSIONS_MAPDEF_ID);
2090 if (name && name[0] != '\0') {
2091 mission.mapDef = cgi->Com_GetMapDefinitionByID(name);
2092 if (!mission.mapDef) {
2093 cgi->Com_Printf("Warning: mapdef \"%s\" for mission \"%s\" doesn't exist. Removing mission!\n", name, mission.id);
2094 continue;
2095 }
2096 } else {
2097 mission.mapDef = nullptr;
2098 }
2099
2100 if (!cgi->Com_GetConstIntFromNamespace(SAVE_INTERESTCAT_NAMESPACE, categoryId, (int*) &mission.category)) {
2101 cgi->Com_Printf("Invalid mission category '%s'\n", categoryId);
2102 continue;
2103 }
2104
2105 if (!cgi->Com_GetConstIntFromNamespace(SAVE_MISSIONSTAGE_NAMESPACE, stageId, (int*) &mission.stage)) {
2106 cgi->Com_Printf("Invalid mission stage '%s'\n", stageId);
2107 continue;
2108 }
2109
2110 mission.active = cgi->XML_GetBool(node, SAVE_MISSIONS_ACTIVE, false);
2111 Q_strncpyz(mission.onwin, cgi->XML_GetString(node, SAVE_MISSIONS_ONWIN), sizeof(mission.onwin));
2112 Q_strncpyz(mission.onlose, cgi->XML_GetString(node, SAVE_MISSIONS_ONLOSE), sizeof(mission.onlose));
2113
2114 mission.initialOverallInterest = cgi->XML_GetInt(node, SAVE_MISSIONS_INITIALOVERALLINTEREST, 0);
2116
2117 cgi->XML_GetPos2(node, SAVE_MISSIONS_POS, mission.pos);
2118
2119 switch (mission.category) {
2121 if (mission.stage == STAGE_MISSION_GOTO || mission.stage == STAGE_BASE_ATTACK) {
2122 /* Load IDX of base under attack */
2123 base_t* base = B_GetBaseByIDX(cgi->XML_GetInt(node, SAVE_MISSIONS_BASEINDEX, -1));
2124 if (base) {
2125 if (mission.stage == STAGE_BASE_ATTACK && !B_IsUnderAttack(base))
2126 cgi->Com_Printf("......warning: base %i (%s) is supposedly under attack but base status doesn't match!\n", base->idx, base->name);
2127 mission.data.base = base;
2128 } else
2129 cgi->Com_Printf("......warning: Missing BaseIndex\n");
2130 }
2131 break;
2133 if (mission.stage == STAGE_MISSION_GOTO || mission.stage == STAGE_INTERCEPT)
2134 mission.data.installation = INS_GetByIDX(cgi->XML_GetInt(node, SAVE_MISSIONS_INSTALLATIONINDEX, -1));
2135 break;
2137 {
2138 const int aircraftIdx = cgi->XML_GetInt(node, SAVE_MISSIONS_CRASHED_AIRCRAFT, -1);
2139 mission.data.aircraft = AIR_AircraftGetFromIDX(aircraftIdx);
2140 if (mission.data.aircraft == nullptr) {
2141 cgi->Com_Printf("Error while loading rescue mission (missionidx %i, aircraftidx: %i, category: %i, stage: %i)\n",
2142 mission.idx, aircraftIdx, mission.category, mission.stage);
2143 continue;
2144 }
2145 }
2146 break;
2148 if (mission.stage == STAGE_MISSION_GOTO || mission.stage == STAGE_TERROR_MISSION)
2149 mission.data.city = CITY_GetByPos(mission.pos);
2150 break;
2154 {
2155 int baseIdx = cgi->XML_GetInt(node, SAVE_MISSIONS_ALIENBASEINDEX, -1);
2156 alienBase_t* alienBase = AB_GetByIDX(baseIdx);
2157 if (alienBase) {
2158 if (mission.category != INTERESTCATEGORY_SUPPLY) {
2159 mission_t* duplicate = nullptr;
2160 MIS_Foreach(previousMission) {
2161 if (previousMission->category != mission.category)
2162 continue;
2163 if (previousMission->data.alienBase == alienBase) {
2164 duplicate = previousMission;
2165 break;
2166 }
2167 }
2168 if (duplicate) {
2169 cgi->Com_Printf("Error loading Alien Base mission (missionidx %i, baseidx: %i, category: %i, stage: %i): is the same as mission (missionidx %i, baseidx: %i, category: %i, stage: %i)\n",
2170 mission.idx, baseIdx, mission.category, mission.stage,
2171 duplicate->idx, duplicate->data.alienBase->idx, duplicate->category, duplicate->stage);
2172 continue;
2173 }
2174 }
2175 mission.data.alienBase = alienBase;
2176 } else {
2178 cgi->Com_Printf("Error loading Alien Base mission (missionidx %i, baseidx: %i, category: %i, stage: %i): no such base\n",
2179 mission.idx, baseIdx, mission.category, mission.stage);
2180 continue;
2181 }
2182 }
2183 }
2184 break;
2185 default:
2186 break;
2187 }
2188
2189 int date;
2190 int time;
2191 cgi->XML_GetDate(node, SAVE_MISSIONS_STARTDATE, &date , &time);
2192 mission.startDate = DateTime(date, time);
2193 cgi->XML_GetDate(node, SAVE_MISSIONS_FINALDATE, &date, &time);
2194 mission.finalDate = DateTime(date, time);
2195 cgi->XML_GetPos2(node, SAVE_MISSIONS_POS, mission.pos);
2196
2197 mission.crashed = cgi->XML_GetBool(node, SAVE_MISSIONS_CRASHED, false);
2198 mission.onGeoscape = cgi->XML_GetBool(node, SAVE_MISSIONS_ONGEOSCAPE, false);
2199
2200 if (mission.pos[0] > 0.001 || mission.pos[1] > 0.001)
2201 defaultAssigned = true;
2202 mission.posAssigned = cgi->XML_GetBool(node, SAVE_MISSIONS_POSASSIGNED, defaultAssigned);
2203 /* Add mission to global array */
2204 LIST_Add(&ccs.missions, mission);
2205 }
2206 cgi->Com_UnregisterConstList(saveInterestConstants);
2207 cgi->Com_UnregisterConstList(saveMissionConstants);
2208
2209 return true;
2210}
2211
2212static const cmdList_t debugMissionCmds[] = {
2213#ifdef DEBUG
2214 {"debug_missionsetmap", MIS_MissionSetMap_f, "Changes the map for a spawned mission"},
2215 {"debug_missionadd", MIS_SpawnNewMissions_f, "Add a new mission"},
2216 {"debug_missiondeleteall", MIS_DeleteMissions_f, "Remove all missions from global array"},
2217 {"debug_missiondelete", MIS_DeleteMission_f, "Remove mission by a given name"},
2218 {"debug_missionlist", MIS_MissionList_f, "Debug function to show all missions"},
2219#endif
2220 {nullptr, nullptr, nullptr}
2221};
2222
2227{
2229 cgi->Cmd_TableAddList(debugMissionCmds);
2230}
2231
2235void MIS_Shutdown (void)
2236{
2237 cgi->LIST_Delete(&ccs.missions);
2238
2240 cgi->Cmd_TableRemoveList(debugMissionCmds);
2241}
DateTime class definition.
Shared game type headers.
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
Class describing a point of time.
Definition DateTime.h:31
int getTimeAsSeconds() const
Return the time part of the DateTime as seconds.
Definition DateTime.cpp:54
int getDateAsDays() const
Return the date part of the DateTime as days.
Definition DateTime.cpp:46
#define ERR_DROP
Definition common.h:211
void AIR_AircraftReturnToBase(aircraft_t *aircraft)
Calculates the way back to homebase for given aircraft and returns it.
Employee * AIR_GetPilot(const aircraft_t *aircraft)
Get pilot of an aircraft.
void AIR_AircraftsNotifyUFORemoved(const aircraft_t *const ufo, bool destroyed)
Notify that a UFO has been removed.
void AIR_AircraftsNotifyMissionRemoved(const mission_t *const mission)
Notify aircraft that a mission has been removed.
bool AIR_PilotSurvivedCrash(const aircraft_t *aircraft)
Determine if an aircraft's pilot survived a crash, based on his piloting skill (and a bit of randomne...
void AIR_DestroyAircraft(aircraft_t *aircraft, bool killPilot)
Removes an aircraft from its base and the game.
baseCapacities_t AIR_GetHangarCapacityType(const aircraft_t *aircraft)
Returns capacity type needed for an aircraft.
aircraft_t * AIR_GetFirstFromBase(const base_t *b)
Iterates through the aircraft of a base.
const aircraft_t * AIR_IsEmployeeInAircraft(const Employee *employee, const aircraft_t *aircraft)
Tells you if an employee is assigned to an aircraft.
aircraft_t * AIR_AircraftGetFromIDX(int aircraftIdx)
Returns aircraft for a given global index.
@ AIR_TRANSIT
@ AIR_CRASHED
int AIRFIGHT_ChooseWeapon(const aircraftSlot_t *slot, int maxSlot, const vec2_t pos, const vec2_t targetPos)
Choose the weapon an attacking aircraft will use to fire on a target.
void AIRFIGHT_RemoveProjectileAimingAircraft(const aircraft_t *aircraft)
Set all projectile aiming a given aircraft to an idle destination.
#define AIRFIGHT_WEAPON_CAN_NEVER_SHOOT
Definition cp_airfight.h:38
void INT_ChangeIndividualInterest(float interestFactor, interestCategory_t category)
Change individual interest value.
Alien interest header.
interestCategory_t
@ INTERESTCATEGORY_BASE_ATTACK
@ INTERESTCATEGORY_INTERCEPTBOMBING
@ INTERESTCATEGORY_NONE
@ INTERESTCATEGORY_ALIENBASE
@ INTERESTCATEGORY_BUILDING
@ INTERESTCATEGORY_MAX
@ INTERESTCATEGORY_SUBVERT
@ INTERESTCATEGORY_SUPPLY
@ INTERESTCATEGORY_RECON
@ INTERESTCATEGORY_UFOCARRIER
@ INTERESTCATEGORY_XVI
@ INTERESTCATEGORY_TERROR_ATTACK
@ INTERESTCATEGORY_INTERCEPT
@ INTERESTCATEGORY_RESCUE
@ INTERESTCATEGORY_HARVEST
int AB_GetAlienBaseNumber(void)
Check number of alien bases.
void AB_SetAlienBasePosition(vec2_t pos)
Set new base position.
void CP_SpawnAlienBaseMission(alienBase_t *alienBase)
Spawn a new alien base mission after it has been discovered.
alienBase_t * AB_BuildBase(const vec2_t pos)
Build a new alien base.
alienBase_t * AB_GetByIDX(int baseIDX)
Get Alien Base per Idx.
void B_DumpAircraftToHomeBase(aircraft_t *aircraft)
Will unload all cargo to the homebase.
Definition cp_base.cpp:2084
base_t * B_GetFoundedBaseByIDX(int baseIdx)
Array bound check for the base index.
Definition cp_base.cpp:326
bool B_AssembleMap(char *maps, size_t mapsLength, char *coords, size_t coordsLength, const base_t *base)
Perform the base assembling in case of an alien attack.
Definition cp_base.cpp:563
base_t * B_GetBaseByIDX(int baseIdx)
Array bound check for the base index. Will also return unfounded bases as long as the index is in the...
Definition cp_base.cpp:313
#define B_IsUnderAttack(base)
Definition cp_base.h:53
memPool_t * cp_campaignPool
const int DETECTION_INTERVAL
delay between actions that must be executed independently of time scale
void CP_CheckLostCondition(const campaign_t *campaign)
Checks whether the player has lost the campaign.
cvar_t * cp_missiontest
ccs_t ccs
Header file for single player campaign control.
const cgame_import_t * cgi
#define DELAY_BETWEEN_MISSION_SPAWNING
The length of a single mission spawn cycle.
Definition cp_campaign.h:78
#define FINAL_OVERALL_INTEREST
Definition cp_campaign.h:73
@ MAPTYPE_TERRAIN
Definition cp_campaign.h:93
#define EARLY_UFO_RUSH_INTEREST
Determines the early game period during which DELAY_BETWEEN_MISSION_SPAWNING is halved.
Definition cp_campaign.h:84
void CAP_AddCurrent(base_t *base, baseCapacities_t capacity, int value)
Changes the current (used) capacity on a base.
baseCapacities_t
All possible capacities in base.
Definition cp_capacity.h:27
@ MAX_CAP
Definition cp_capacity.h:37
void CHAR_UpdateData(linkedList_t *updateCharacters)
Transforms the battlescape values to the character.
void CHAR_UpdateStats(const base_t *base, const aircraft_t *aircraft)
Update employees stats after mission.
Header file for character (soldier, alien) related campaign functions.
bool E_DeleteEmployee(Employee *employee)
Removes the employee completely from the game (buildings + global list).
@ EMPL_SOLDIER
Definition cp_employee.h:31
#define E_Foreach(employeeType, var)
bool GEO_IsNight(const vec2_t pos)
Check whether given position is Day or Night.
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.
void GEO_NotifyUFORemoved(const aircraft_t *ufo, bool destroyed)
Notify that a UFO has been removed.
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_UpdateGeoscapeDock(void)
Will add missions and UFOs to the geoscape dock panel.
int GEO_GetCivilianNumberByPosition(const vec2_t pos)
Get number of civilian on a map at given position.
void GEO_NotifyMissionRemoved(const mission_t *mission)
Notify that a mission has been removed.
Header for Geoscape management.
#define GEO_GetMissionAircraft()
Definition cp_geoscape.h:60
#define GEO_IsAircraftSelected(aircraft)
Definition cp_geoscape.h:51
#define GEO_SetSelectedAircraft(aircraft)
Definition cp_geoscape.h:62
#define MapIsWater(color)
Definition cp_geoscape.h:32
installation_t * INS_GetByIDX(int idx)
Get installation by it's index.
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]
messageType_t
Definition cp_messages.h:32
@ MSG_CRASHSITE
Definition cp_messages.h:49
@ MSG_TERRORSITE
Definition cp_messages.h:42
@ MSG_STANDARD
Definition cp_messages.h:35
@ MSG_BASEATTACK
Definition cp_messages.h:43
void CP_BaseAttackMissionIsSuccess(mission_t *mission)
Base attack mission is over and is a success (from an alien point of view): change interest values.
void CP_BaseAttackMissionOnSpawn(void)
Run when the mission is spawned.
void CP_BaseAttackMissionLeave(mission_t *mission)
Base attack mission ends: UFO leave earth.
void CP_BaseAttackMissionDestroyBase(mission_t *mission)
Base attack mission ends: UFO leave earth.
void CP_BaseAttackMissionNextStage(mission_t *mission)
Determine what action should be performed when a Base Attack mission stage ends.
void CP_BaseAttackMissionIsFailure(mission_t *mission)
Base attack mission is over and is a failure (from an alien point of view): change interest values.
Campaign mission headers.
void CP_BuildBaseMissionBaseDestroyed(mission_t *mission)
Alien base has been destroyed: change interest values.
void CP_BuildBaseMissionNextStage(const campaign_t *campaign, mission_t *mission)
Determine what action should be performed when a Build Base mission stage ends.
void CP_BuildBaseMissionOnSpawn(void)
Run when the mission is spawned.
void CP_BuildBaseMissionIsSuccess(mission_t *mission)
Build Base mission is over and is a success (from an alien point of view): change interest values.
void CP_BuildBaseMissionIsFailure(mission_t *mission)
Build Base mission is over and is a failure (from an alien point of view): change interest values.
bool CP_BasemissionIsSubvertingGovernmentMission(const mission_t *mission)
Campaign mission header.
void MIS_InitCallbacks(void)
Init UI callbacks for missions-subsystem.
void MIS_ShutdownCallbacks(void)
Close UI callbacks for missions-subsystem.
header file UI callbacks for missions.
void CP_HarvestMissionNextStage(mission_t *mission)
Determine what action should be performed when a Harvesting mission stage ends.
void CP_HarvestMissionIsFailure(mission_t *mission)
Harvesting mission is over and is a failure: change interest values.
Campaign mission headers.
void CP_InterceptMissionIsFailure(mission_t *mission)
Intercept mission is over and is a failure: change interest values.
void CP_InterceptNextStage(mission_t *mission)
Determine what action should be performed when a Intercept mission stage ends.
void CP_InterceptMissionIsSuccess(mission_t *mission)
Intercept mission is over and is a success: change interest values.
void CP_InterceptGoToInstallation(mission_t *mission)
Set Intercept mission: UFO chooses an installation an flies to it.
void CP_InterceptAircraftMissionSet(mission_t *mission)
Set Intercept mission: UFO looks for new aircraft target.
void CP_InterceptMissionLeave(mission_t *mission, bool destroyed)
Intercept mission ends: UFO leave earth.
Campaign mission headers.
void CP_ReconMissionAerial(mission_t *mission)
Set aerial mission.
void CP_ReconMissionNextStage(mission_t *mission)
Determine what action should be performed when a Recon mission stage ends.
void CP_ReconMissionIsFailure(mission_t *mission)
Recon mission is over and is a failure: change interest values.
void CP_ReconMissionGroundGo(mission_t *mission)
Set ground mission, and go to ground mission pos.
Campaign mission headers.
void CP_RescueNextStage(mission_t *mission)
Determine what action should be performed when a Rescue mission stage ends.
void CP_EndRescueMission(mission_t *mission, aircraft_t *aircraft, bool won)
Actions to be done when rescue mission finished/expired.
Campaign mission headers.
void CP_SupplyMissionIsFailure(mission_t *mission)
Supply mission is over and is a failure (from an alien point of view): change interest values.
void CP_SupplyMissionNextStage(mission_t *mission)
Determine what action should be performed when a Supply mission stage ends.
void CP_SupplyMissionIsSuccess(mission_t *mission)
Supply mission is over and is a success (from an alien point of view): change interest values.
Campaign mission headers.
void CP_TerrorMissionIsFailure(mission_t *mission)
Terror attack mission is over and is a failure: change interest values.
void CP_TerrorMissionOnSpawn(void)
Run when the mission is spawned.
void CP_TerrorMissionNextStage(mission_t *mission)
Determine what action should be performed when a Terror attack mission stage ends.
Campaign mission headers.
void CP_ExecuteMissionTrigger(const mission_t *mission, bool won)
Executes console commands after a mission.
Campaign mission triggers.
void CP_UFOCarrierNextStage(mission_t *mission)
Determine what action should be performed when a UFOCarriering mission stage ends.
Campaign mission headers.
void CP_XVIMissionIsFailure(mission_t *mission)
XVI Spreading mission is over and is a failure: change interest values.
void CP_XVIMissionNextStage(mission_t *mission)
Determine what action should be performed when a XVI Spreading mission stage ends.
void CP_XVIMissionIsSuccess(mission_t *mission)
XVI Spreading mission is over and is a success: change interest values.
Campaign mission header.
bool MIS_SaveXML(xmlNode_t *parent)
Save callback for savegames in XML Format.
void MIS_InitStartup(void)
Init actions for missions-subsystem.
void CP_InitializeSpawningDelay(void)
Initialize spawning delay.
void BATTLE_Start(mission_t *mission, const battleParam_t *battleParameters)
Select the mission type and start the map from mission definition.
static void CP_SetAlienTeamByInterest(mission_t *mission, battleParam_t *battleParameters)
Sets the alien races used for a mission.
static bool CP_IsAlienTeamForCategory(const alienTeamCategory_t *cat, const interestCategory_t missionCat)
Check if an alien team category may be used for a mission category.
static const float MAX_CRASHEDUFO_CONDITION
void CP_MissionNotifyBaseDestroyed(const base_t *base)
Notify that a base has been removed.
static void CP_SetAlienEquipmentByInterest(const mission_t *mission, linkedList_t *equipPack, battleParam_t *battleParameters)
Set alien equipment for a mission (depends on the interest values).
int MIS_GetIdx(const mission_t *mis)
Find idx corresponding to mission.
static bool MIS_IsSpawnedFromGround(const mission_t *mission)
Decides if the mission should be spawned from the ground (without UFO).
void CP_CreateBattleParameters(mission_t *mission, battleParam_t *param, const aircraft_t *aircraft)
Create parameters needed for battle. This is the data that is used for starting the tactical part of ...
int CP_CountMissionOnGeoscape(void)
Count the number of mission active and displayed on geoscape.
static interestCategory_t CP_SelectNewMissionType(void)
Select new mission type.
void CP_MissionNotifyInstallationDestroyed(const installation_t *installation)
Notify missions that an installation has been destroyed.
void MIS_Shutdown(void)
Closing actions for missions-subsystem.
bool CP_CheckMissionLimitedInTime(const mission_t *mission)
Check if mission should end because of limited time.
static void MIS_CreateAlienTeam(mission_t *mission, battleParam_t *battleParam)
Set number of aliens in mission.
mission_t * CP_GetMissionByID(const char *missionId)
Get a mission in ccs.missions by Id.
bool MIS_LoadXML(xmlNode_t *parent)
Load callback for savegames in XML Format.
static const float MIN_CRASHEDUFO_CONDITION
static void CP_SetMissionName(mission_t *mission)
Set mission name.
static void CP_CreateCivilianTeam(const mission_t *mission, battleParam_t *param)
Create civilian team.
void CP_UpdateMissionVisibleOnGeoscape(void)
Update all mission visible on geoscape (in base radar range).
void BATTLE_SetVars(const battleParam_t *battleParameters)
Set some needed cvars from a battle definition.
void CP_MissionEndActions(mission_t *mission, aircraft_t *aircraft, bool won)
Actions to be done after mission finished.
static const char * CP_MissionGetMessage(const mission_t *mission)
Assembles a message that is send to the gamer once the given mission is added to geoscape.
static messageType_t CP_MissionGetMessageLevel(const mission_t *mission)
Decides which message level to take for the given mission.
void CP_SpawnRescueMission(aircraft_t *aircraft, aircraft_t *ufo)
Spawn a new rescue mission for a crashed (phalanx) aircraft.
bool CP_MissionBegin(mission_t *mission)
mission begins: UFO arrive on earth.
const char * MIS_GetModel(const mission_t *mission)
Get mission model that should be shown on the geoscape.
void CP_UFORemoveFromGeoscape(mission_t *mission, bool destroyed)
Removes (temporarily or permanently) a UFO from geoscape: make it land and call notify functions.
static missionDetectionStatus_t CP_CheckMissionVisibleOnGeoscape(const mission_t *mission)
Check if a mission should be visible on geoscape.
void CP_MissionRemove(mission_t *mission)
Removes a mission from mission global array.
static const cmdList_t debugMissionCmds[]
const int MAX_POS_LOOP
void CP_MissionIsOverByUFO(aircraft_t *ufocraft)
Mission is finished because Phalanx team ended it.
static bool CP_MapIsSelectable(const mission_t *mission, const mapDef_t *md, const vec2_t pos)
Check if a map may be selected for mission.
mission_t * CP_CreateNewMission(interestCategory_t category, bool beginNow)
Create a new mission of given category.
bool CP_ChooseMap(mission_t *mission, const vec2_t pos)
Choose a map for given mission.
bool CP_CheckNewMissionDetectedOnGeoscape(void)
Check if mission has been detected by radar.
void CP_MissionEnd(const campaign_t *campaign, mission_t *mission, const battleParam_t *battleParameters, bool won)
Closing actions after fighting a battle.
void CP_MissionRemoveFromGeoscape(mission_t *mission)
Removes a mission from geoscape: make it non visible and call notify functions.
void CP_MissionAddToGeoscape(mission_t *mission, bool force)
Add a mission to geoscape: make it visible and stop time.
void CP_MissionIsOver(mission_t *mission)
Mission is finished because Phalanx team won it.
const char * MIS_GetName(const mission_t *mission)
Returns a short translated name for a mission.
void CP_MissionStageEnd(const campaign_t *campaign, mission_t *mission)
Determine what action should be performed when a mission stage ends.
void CP_SpawnCrashSiteMission(aircraft_t *ufo)
Spawn a new crash site after a UFO has been destroyed.
void CP_UFOProceedMission(const campaign_t *campaign, aircraft_t *ufo)
Make UFO proceed with its mission when the fight with another aircraft is over (and UFO survived).
static bool CP_IsAlienEquipmentSelectable(const mission_t *mission, const equipDef_t *equip, linkedList_t *equipPack)
Check if an alien equipment may be used with a mission.
ufoType_t CP_MissionChooseUFO(const mission_t *mission)
Choose UFO type for a given mission category.
bool CP_CheckNextStageDestination(const campaign_t *campaign, aircraft_t *ufocraft)
Check if a stage mission is over when UFO reached destination.
mission_t * CP_GetMissionByIDSilent(const char *missionId)
Get a mission in ccs.missions by Id without error messages.
missionDetectionStatus_t
possible mission detection status
@ MISDET_ALWAYS_DETECTED
@ MISDET_MAY_BE_DETECTED
@ MISDET_CANT_BE_DETECTED
void CP_MissionDisableTimeLimit(mission_t *mission)
Disable time limit for given mission.
mission_t * MIS_GetByIdx(int id)
Find mission corresponding to idx.
void CP_SpawnNewMissions(void)
Spawn new missions.
Campaign missions headers.
#define MIS_Foreach(var)
iterates through missions
missionStage_t
Definition cp_missions.h:34
@ STAGE_RECON_GROUND
Definition cp_missions.h:40
@ STAGE_RETURN_TO_ORBIT
Definition cp_missions.h:51
@ STAGE_HARVEST
Definition cp_missions.h:49
@ STAGE_MISSION_GOTO
Definition cp_missions.h:39
@ STAGE_SPREAD_XVI
Definition cp_missions.h:46
@ STAGE_BASE_DISCOVERED
Definition cp_missions.h:48
@ STAGE_BUILD_BASE
Definition cp_missions.h:42
@ STAGE_SUPPLY
Definition cp_missions.h:45
@ STAGE_NOT_ACTIVE
Definition cp_missions.h:35
@ STAGE_COME_FROM_ORBIT
Definition cp_missions.h:36
@ STAGE_INTERCEPT
Definition cp_missions.h:47
@ STAGE_SUBVERT_GOV
Definition cp_missions.h:44
@ STAGE_BASE_ATTACK
Definition cp_missions.h:43
@ STAGE_TERROR_MISSION
Definition cp_missions.h:41
@ STAGE_OVER
Definition cp_missions.h:53
@ STAGE_RECON_AIR
Definition cp_missions.h:38
city_t * CITY_GetByPos(vec2_t pos)
Finds a city by it's geoscape coordinates.
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....
void RADAR_NotifyUFORemoved(const aircraft_t *ufo, bool destroyed)
Notify to every radar that the specified ufo has been removed from geoscape.
Definition cp_radar.cpp:213
bool RADAR_CheckRadarSensored(const vec2_t pos)
Check if the specified position is within base radar range.
Definition cp_radar.cpp:377
void CP_GameTimeStop(void)
Stop game time speed.
Definition cp_time.cpp:126
DateTime Date_Random(const DateTime &minFrame, const DateTime &maxFrame)
Return a random relative date which lies between a lower and upper limit.
Definition cp_time.cpp:239
Campaign geoscape time header.
void UFO_SendToDestination(aircraft_t *ufo, const vec2_t dest)
Make the specified UFO go to destination.
Definition cp_ufo.cpp:562
int UFO_GetOneAvailableUFOForMission(const interestCategory_t missionType, bool checkInterest)
Get a suitable UFO for the mission type.
Definition cp_ufo.cpp:217
const char * UFO_GetName(const aircraft_t *ufocraft)
Returns name of the UFO if UFO has been researched.
Definition cp_ufo.cpp:243
void UFO_DetectNewUFO(aircraft_t *ufocraft)
Perform actions when a new UFO is detected.
Definition cp_ufo.cpp:842
aircraft_t * UFO_AddToGeoscape(ufoType_t ufoType, const vec2_t destination, mission_t *mission)
Add a UFO to geoscape.
Definition cp_ufo.cpp:773
void UFO_SetRandomDest(aircraft_t *ufocraft)
Give a random destination to the given UFO, and make him to move there.
Definition cp_ufo.cpp:259
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
void UFO_RemoveFromGeoscape(aircraft_t *ufo)
Remove the specified ufo from geoscape.
Definition cp_ufo.cpp:817
int CP_GetAverageXVIRate(void)
Return the average XVI rate.
Definition cp_xvi.cpp:163
Campaign XVI header.
#define DEBUG_CLIENT
Definition defines.h:59
#define MAX_TEAMS_PER_MISSION
Definition inv_shared.h:618
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
float frand(void)
Return random values between 0 and 1.
Definition mathlib.cpp:506
QGL_EXTERN void(APIENTRY *qglActiveTexture)(GLenum texture)
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.
static const constListEntry_t saveInterestConstants[]
#define SAVE_INTERESTCAT_NAMESPACE
XML tag constants for savegame.
#define SAVE_MISSIONS
#define SAVE_MISSIONS_MISSION_IDX
#define SAVE_MISSIONS_ACTIVE
#define SAVE_MISSIONS_MAPDEF_ID
#define SAVE_MISSIONSTAGE_NAMESPACE
#define SAVE_MISSIONS_FINALDATE
static const constListEntry_t saveMissionConstants[]
#define SAVE_MISSIONS_STARTDATE
#define SAVE_MISSIONS_INSTALLATIONINDEX
#define SAVE_MISSIONS_MISSION
#define SAVE_MISSIONS_CATEGORY
#define SAVE_MISSIONS_STAGE
#define SAVE_MISSIONS_ONGEOSCAPE
#define SAVE_MISSIONS_CRASHED_AIRCRAFT
#define SAVE_MISSIONS_INITIALOVERALLINTEREST
#define SAVE_MISSIONS_POSASSIGNED
#define SAVE_MISSIONS_INITIALINDIVIDUALINTEREST
#define SAVE_MISSIONS_BASEINDEX
#define SAVE_MISSIONS_ONWIN
#define SAVE_MISSIONS_POS
#define SAVE_MISSIONS_ALIENBASEINDEX
#define SAVE_MISSIONS_ONLOSE
#define SAVE_MISSIONS_ID
#define SAVE_MISSIONS_CRASHED
#define UFO_MAX
Definition scripts.h:146
#define UFO_NONE
Definition scripts.h:148
short ufoType_t
Definition scripts.h:145
#define Q_streq(a, b)
Definition shared.h:136
#define OBJZERO(obj)
Definition shared.h:178
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
char const * Q_strstart(char const *str, char const *start)
Matches the start of a string.
Definition shared.cpp:587
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition shared.cpp:410
An aircraft with all it's data.
aircraftSlot_t weapons[MAX_AIRCRAFTSLOT]
struct base_s * homebase
aircraftStatus_t status
struct mission_s * mission
linkedList_t * acTeam
void setUfoType(ufoType_t ufoT)
char name[MAX_VAR]
char * model
ufoType_t getUfoType() const
Alien Base.
alien team category definition
interestCategory_t missionCategories[INTERESTCATEGORY_MAX]
alienTeamGroup_t alienTeamGroups[MAX_ALIEN_GROUP_PER_CATEGORY]
alien team group definition.
const chrTemplate_t * alienChrTemplates[MAX_TEAMS_PER_MISSION]
const teamDef_t * alienTeams[MAX_TEAMS_PER_MISSION]
A base with all it's data.
Definition cp_base.h:84
aircraft_t * aircraftCurrent
Definition cp_base.h:100
int idx
Definition cp_base.h:85
char name[MAX_VAR]
Definition cp_base.h:86
vec3_t pos
Definition cp_base.h:91
equipDef_t storage
Definition cp_base.h:112
alienTeamGroup_t * alienTeamGroup
char alienEquipment[MAX_VAR]
char civTeam[MAX_VAR]
struct mission_s * mission
const char * zoneType
struct nation_s * nation
float minhappiness
Describes a character with all its attributes.
Definition chr_shared.h:388
chrScoreGlobal_t score
Definition chr_shared.h:406
Structure of all stats collected for an actor over time.
Definition chr_shared.h:119
const char * name
Definition cp_nation.h:72
char id[MAX_VAR]
Definition inv_shared.h:606
int minInterest
Definition inv_shared.h:611
int maxInterest
Definition inv_shared.h:612
A installation with all it's data.
int timesAlreadyUsed
Definition q_shared.h:490
linkedList_t * aircraft
Definition q_shared.h:492
char * mapTheme
Definition q_shared.h:464
linkedList_t * ufos
Definition q_shared.h:491
bool storyRelated
Definition q_shared.h:489
char * id
Definition q_shared.h:463
linkedList_t * terrains
Definition q_shared.h:486
char * civTeam
Definition q_shared.h:471
linkedList_t * cultures
Definition q_shared.h:488
int maxAliens
Definition q_shared.h:483
linkedList_t * params
Definition q_shared.h:465
linkedList_t * populations
Definition q_shared.h:487
mission definition
Definition cp_missions.h:86
aircraft_t * ufo
missionResults_t missionResults
mapDef_t * mapDef
Definition cp_missions.h:89
bool onGeoscape
char onwin[256]
bool active
Definition cp_missions.h:90
int initialOverallInterest
missionStage_t stage
Definition cp_missions.h:99
class DateTime startDate
interestCategory_t category
Definition cp_missions.h:98
vec2_t pos
union mission_t::missionData_t data
bool posAssigned
char onlose[256]
class DateTime finalDate
char id[MAX_VAR]
Definition cp_missions.h:88
int initialIndividualInterest
Nation definition.
Definition cp_nation.h:46
const char * name
Definition cp_nation.h:48
const char * id
Definition cp_nation.h:47
vec_t vec2_t[2]
Definition ufotypes.h:38
installation_t * installation
Definition cp_missions.h:94
alienBase_t * alienBase
Definition cp_missions.h:95
#define Vector2Copy(src, dest)
Definition vector.h:52
#define xmlNode_t
Definition xml.h:24