UFO: Alien Invasion
Loading...
Searching...
No Matches
cp_alienbase.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 "../../cl_shared.h"
26#include "cp_campaign.h"
27#include "cp_alienbase.h"
28#include "cp_geoscape.h"
29#include "cp_missions.h"
30#include "save/save_alienbase.h"
31
32#define MAPDEF_ALIENBASE "alienbase"
33
42{
43 int counter;
44 vec2_t randomPos;
45 float minDistance = 0.0f;
46 const int maxLoopPosition = 6;
47
48 counter = 0;
49 while (counter < maxLoopPosition) {
50 float distance = 0.0f;
51
52 /* Get a random position */
53 CP_GetRandomPosOnGeoscape(randomPos, true);
54
55 /* Alien base must not be too close from phalanx base */
56 if (GEO_PositionCloseToBase(randomPos))
57 continue;
58
59 /* If this is the first alien base, there's no further condition: select this pos and quit */
60 if (AB_Exists()) {
61 Vector2Copy(randomPos, pos);
62 return;
63 }
64
65 /* Calculate minimim distance between THIS position (pos) and all alien bases */
66 AB_Foreach(base) {
67 const float currentDistance = GetDistanceOnGlobe(base->pos, randomPos);
68 if (distance < currentDistance) {
69 distance = currentDistance;
70 }
71 }
72
73 /* If this position is farther than previous ones, select it */
74 if (minDistance < distance) {
75 Vector2Copy(randomPos, pos);
76 minDistance = distance;
77 }
78
79 counter++;
80 }
81 if (counter == maxLoopPosition)
82 Vector2Copy(randomPos, pos);
83}
84
91{
92 alienBase_t base;
93 const float initialStealthValue = 50.0f;
94
95 OBJZERO(base);
96 Vector2Copy(pos, base.pos);
97 base.stealth = initialStealthValue;
98 base.idx = ccs.campaignStats.alienBasesBuilt++;
99
100 return &LIST_Add(&ccs.alienBases, base);
101}
102
108{
109 assert(base);
110
111 cgi->LIST_Remove(&ccs.alienBases, (void*)base);
112
113 /* Alien loose all their interest in supply if there's no base to send the supply */
114 if (!AB_Exists())
115 ccs.interest[INTERESTCATEGORY_SUPPLY] = 0;
116}
117
124{
125 AB_Foreach(base) {
126 if (base->idx == baseIDX)
127 return base;
128 }
129 return nullptr;
130}
131
136{
137 /* Don't spawn more than one mission for a base */
138 MIS_Foreach(mission) {
139 if (mission->category != INTERESTCATEGORY_ALIENBASE)
140 continue;
141 if (mission->data.alienBase == alienBase)
142 return;
143 }
144
146 if (!newMission) {
147 cgi->Com_Printf("CP_SpawnAlienBaseMission: Could not add mission, abort\n");
148 return;
149 }
150
151 newMission->stage = STAGE_BASE_DISCOVERED;
152 newMission->data.alienBase = alienBase;
153
154 newMission->mapDef = cgi->Com_GetMapDefinitionByID(MAPDEF_ALIENBASE);
155 if (!newMission->mapDef)
156 cgi->Com_Error(ERR_FATAL, "Could not find mapdef " MAPDEF_ALIENBASE);
157
158 Vector2Copy(alienBase->pos, newMission->pos);
159 newMission->posAssigned = true;
160
161 /* Alien base stay until it's destroyed */
162 CP_MissionDisableTimeLimit(newMission);
163 /* mission appears on geoscape, player can go there */
164 CP_MissionAddToGeoscape(newMission, false);
165
167}
168
177static void AB_UpdateStealthForOneBase (const aircraft_t* aircraft, alienBase_t* base)
178{
179 float distance;
180 float probability = 0.0001f;
181 const float radarratio = 0.4f;
182 const float decreasingFactor = 5.0f;
183
184 /* base is already discovered */
185 if (base->stealth < 0)
186 return;
187
188 /* aircraft can't find base if it's too far */
189 distance = GetDistanceOnGlobe(aircraft->pos, base->pos);
190 if (distance > aircraft->radar.range)
191 return;
192
193 /* the bigger the base, the higher the probability to find it */
194 probability *= base->supply;
195
196 /* decrease probability if the base is far from aircraft */
197 if (distance > aircraft->radar.range * radarratio)
198 probability /= decreasingFactor;
199
200 /* probability must depend on DETECTION_INTERVAL (in case we change the value) */
201 probability *= DETECTION_INTERVAL;
202
203 base->stealth -= probability;
204
205 /* base discovered ? */
206 if (base->stealth < 0) {
207 base->stealth = -10.0f; /* just to avoid rounding errors */
209 }
210}
211
219{
220 base_t* base = nullptr;
221 while ((base = B_GetNext(base)) != nullptr) {
222 AIR_ForeachFromBase(aircraft, base) {
223 /* Only aircraft on geoscape can detect alien bases */
224 if (!AIR_IsAircraftOnGeoscape(aircraft))
225 continue;
226
227 AB_Foreach(alienBase)
228 AB_UpdateStealthForOneBase(aircraft, alienBase);
229 }
230 }
231}
232
239{
240 const int daysPerWeek = 7;
241 float probability = 1.0f;
242 const float xviLevel = 20.0f;
244
245 /* Stealth is updated only once a week */
246 if (ccs.date.getDateAsDays() % daysPerWeek)
247 return;
248
249 AB_Foreach(base) {
250 const nation_t* nation = GEO_GetNation(base->pos);
251
252 /* If nation is a lot infected, it won't help in finding base (government infected) */
253 if (nation) {
254 const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation);
255 if (stats->xviInfection)
256 probability /= 1.0f + stats->xviInfection / xviLevel;
257 }
258
259 /* the bigger the base, the higher the probability to find it */
260 probability *= base->supply;
261
262 base->stealth -= probability;
263 if (base->stealth < 0) {
264 base->stealth = -10.0f; /* just to avoid rounding errors */
266 }
267 }
268}
269
275{
276 return AB_Exists();
277}
278
284{
285 const int baseCount = AB_GetAlienBaseNumber();
286
287 if (baseCount <= 0) {
288 cgi->Com_Printf("AB_ChooseBaseToSupply: no bases exists (basecount: %d)\n", baseCount);
289 return nullptr;
290 }
291
292 const int selected = rand() % baseCount;
293
294 int i = 0;
295 AB_Foreach(alienBase) {
296 if (i == selected)
297 return alienBase;
298 i++;
299 }
300 return nullptr;
301}
302
308void AB_SupplyBase (alienBase_t* base, bool decreaseStealth)
309{
310 const float decreasedStealthValue = 5.0f;
311
312 assert(base);
313
314 base->supply++;
315 if (decreaseStealth && base->stealth >= 0.0f)
316 base->stealth -= decreasedStealthValue;
317}
318
324{
325 return cgi->LIST_Count(ccs.alienBases);
326}
327
328#ifdef DEBUG
332static void AB_AlienBaseDiscovered_f (void)
333{
334 AB_Foreach(base) {
335 base->stealth = -10.0f;
337 }
338}
339
344static void AB_AlienBaseList_f (void)
345{
346 AB_Foreach(base) {
347 cgi->Com_Printf("Alien Base: %i\n", base->idx);
348 cgi->Com_Printf("...pos: (%f, %f)\n", base->pos[0], base->pos[1]);
349 cgi->Com_Printf("...supply: %i\n", base->supply);
350 if (base->stealth < 0)
351 cgi->Com_Printf("...base discovered\n");
352 else
353 cgi->Com_Printf("...stealth: %f\n", base->stealth);
354 }
355}
356#endif
357
364{
365 int i;
366 xmlNode_t* n, *s;
367
368 n = cgi->XML_GetNode(p, SAVE_ALIENBASE_ALIENBASES);
369 if (!n)
370 return false;
371
372 for (i = 0, s = cgi->XML_GetNode(n, SAVE_ALIENBASE_BASE); s; i++, s = cgi->XML_GetNextNode(s, n, SAVE_ALIENBASE_BASE)) {
373 alienBase_t base;
374
375 base.idx = cgi->XML_GetInt(s, SAVE_ALIENBASE_IDX, -1);
376 if (base.idx < 0) {
377 cgi->Com_Printf("Invalid or no IDX defined for Alienbase %d.\n", i);
378 return false;
379 }
380 if (!cgi->XML_GetPos2(s, SAVE_ALIENBASE_POS, base.pos)) {
381 cgi->Com_Printf("Position is invalid for Alienbase (idx %d)\n", base.idx);
382 return false;
383 }
384 base.supply = cgi->XML_GetInt(s, SAVE_ALIENBASE_SUPPLY, 0);
385 base.stealth = cgi->XML_GetFloat(s, SAVE_ALIENBASE_STEALTH, 0.0);
386 LIST_Add(&ccs.alienBases, base);
387 }
388
389 return true;
390}
391
398{
399 xmlNode_t* n = cgi->XML_AddNode(p, SAVE_ALIENBASE_ALIENBASES);
400
401 AB_Foreach(base) {
402 xmlNode_t* s = cgi->XML_AddNode(n, SAVE_ALIENBASE_BASE);
403 cgi->XML_AddInt(s, SAVE_ALIENBASE_IDX, base->idx);
404 cgi->XML_AddPos2(s, SAVE_ALIENBASE_POS, base->pos);
405 cgi->XML_AddIntValue(s, SAVE_ALIENBASE_SUPPLY, base->supply);
406 cgi->XML_AddFloatValue(s, SAVE_ALIENBASE_STEALTH, base->stealth);
407 }
408
409 return true;
410}
411
413#ifdef DEBUG
414 {"debug_listalienbase", AB_AlienBaseList_f, "Print Alien Bases information to game console"},
415 {"debug_alienbasevisible", AB_AlienBaseDiscovered_f, "Set all alien bases to discovered"},
416#endif
417 {nullptr, nullptr, nullptr}
418};
419
423void AB_InitStartup (void)
424{
425 cgi->Cmd_TableAddList(debugAlienBaseCmds);
426}
427
431void AB_Shutdown (void)
432{
433 cgi->LIST_Delete(&ccs.alienBases);
434
435 cgi->Cmd_TableRemoveList(debugAlienBaseCmds);
436}
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 ERR_FATAL
Definition common.h:210
bool AIR_IsAircraftOnGeoscape(const aircraft_t *aircraft)
Checks whether given aircraft is on geoscape.
#define AIR_ForeachFromBase(var, base)
iterates trough all aircraft from a specific homebase
@ INTERESTCATEGORY_ALIENBASE
@ INTERESTCATEGORY_SUPPLY
bool AB_CheckSupplyMissionPossible(void)
Check if a supply mission is possible.
void AB_SupplyBase(alienBase_t *base, bool decreaseStealth)
Supply a base.
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.
bool AB_SaveXML(xmlNode_t *p)
Save callback for alien base data.
void AB_InitStartup(void)
Init actions for alienbase-subsystem.
void AB_DestroyBase(alienBase_t *base)
Destroy an alien base.
#define MAPDEF_ALIENBASE
void AB_Shutdown(void)
Closing actions for alienbase-subsystem.
static void AB_UpdateStealthForOneBase(const aircraft_t *aircraft, alienBase_t *base)
Update stealth value of one alien base due to one aircraft.
static const cmdList_t debugAlienBaseCmds[]
void AB_UpdateStealthForAllBase(void)
Update stealth value of every base for every aircraft.
alienBase_t * AB_ChooseBaseToSupply(void)
Choose Alien Base that should be supplied.
alienBase_t * AB_BuildBase(const vec2_t pos)
Build a new alien base.
void AB_BaseSearchedByNations(void)
Nations help in searching alien base.
bool AB_LoadXML(xmlNode_t *p)
Load callback for alien base data.
alienBase_t * AB_GetByIDX(int baseIDX)
Get Alien Base per Idx.
#define AB_Exists()
#define AB_Foreach(var)
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition cp_base.cpp:286
const int DETECTION_INTERVAL
delay between actions that must be executed independently of time scale
ccs_t ccs
Header file for single player campaign control.
const cgame_import_t * cgi
void CP_TriggerEvent(campaignTriggerEventType_t type, const void *userdata)
Triggers a campaign event with a special type.
Definition cp_event.cpp:310
@ ALIENBASE_DISCOVERED
Definition cp_event.h:83
nation_t * GEO_GetNation(const vec2_t pos)
Translate nation map color to nation.
base_t * GEO_PositionCloseToBase(const vec2_t pos)
Check if given pos is close to an existing base.
void CP_GetRandomPosOnGeoscape(vec2_t pos, bool noWater)
Determines a random position on geoscape.
Header for Geoscape management.
mission_t * CP_CreateNewMission(interestCategory_t category, bool beginNow)
Create a new mission of given category.
void CP_MissionAddToGeoscape(mission_t *mission, bool force)
Add a mission to geoscape: make it visible and stop time.
void CP_MissionDisableTimeLimit(mission_t *mission)
Disable time limit for given mission.
Campaign missions headers.
#define MIS_Foreach(var)
iterates through missions
@ STAGE_BASE_DISCOVERED
Definition cp_missions.h:48
const nationInfo_t * NAT_GetCurrentMonthInfo(const nation_t *const nation)
Get the current month nation stats.
double GetDistanceOnGlobe(const vec2_t pos1, const vec2_t pos2)
Calculate distance on the geoscape.
Definition mathlib.cpp:171
QGL_EXTERN GLint i
Definition r_gl.h:113
XML tag constants for savegame.
#define SAVE_ALIENBASE_ALIENBASES
#define SAVE_ALIENBASE_IDX
#define SAVE_ALIENBASE_STEALTH
#define SAVE_ALIENBASE_SUPPLY
#define SAVE_ALIENBASE_POS
#define SAVE_ALIENBASE_BASE
#define OBJZERO(obj)
Definition shared.h:178
An aircraft with all it's data.
struct radar_s radar
Alien Base.
A base with all it's data.
Definition cp_base.h:84
mission definition
Definition cp_missions.h:86
mapDef_t * mapDef
Definition cp_missions.h:89
missionStage_t stage
Definition cp_missions.h:99
vec2_t pos
union mission_t::missionData_t data
bool posAssigned
Nation definition.
Definition cp_nation.h:46
Detailed information about the nation relationship (currently per month, but could be used elsewhere)...
Definition cp_nation.h:35
int xviInfection
Definition cp_nation.h:40
vec_t vec2_t[2]
Definition ufotypes.h:38
alienBase_t * alienBase
Definition cp_missions.h:95
#define Vector2Copy(src, dest)
Definition vector.h:52
#define xmlNode_t
Definition xml.h:24