UFO: Alien Invasion
Loading...
Searching...
No Matches
cl_game_skirmish.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
26#include "../../cl_shared.h"
27#include "../cl_game.h"
28#include "cl_game_skirmish.h"
29#include "../../ui/ui_data.h"
30
32static const cgame_import_t* cgi;
33
35
36static void GAME_SK_InitMissionBriefing (const char** title, linkedList_t** victoryConditionsMsgIDs, linkedList_t** missionBriefingMsgIDs)
37{
38 const mapDef_t* md = cgi->GAME_GetCurrentSelectedMap();
40 cgi->LIST_AddString(victoryConditionsMsgIDs, md->victoryCondition);
41 }
42 if (Q_strvalid(md->missionBriefing)) {
43 cgi->LIST_AddString(missionBriefingMsgIDs, md->missionBriefing);
44 }
45 if (Q_strvalid(md->description)) {
46 *title = _(md->description);
47 }
48}
49
50static inline const char* GAME_SK_GetRandomMapAssemblyNameForCraft (const char* name)
51{
52 return cgi->Com_GetRandomMapAssemblyNameForCraft(name);
53}
54
60{
61 cgi->Cvar_SetValue("ai_numcivilians", 8);
62 if (md->civTeam != nullptr)
63 cgi->Cvar_Set("ai_civilianteam", "%s", md->civTeam);
64 else
65 cgi->Cvar_Set("ai_civilianteam", "europe");
66
67 cgi->Cvar_Set("sv_hurtaliens", "0");
68
69 /* now store the alien teams in the shared csi struct to let the game dll
70 * have access to this data, too */
71 cgi->csi->numAlienTeams = 0;
72 for (int i = 0; i < cgi->csi->numTeamDefs; i++) {
73 const teamDef_t* td = &cgi->csi->teamDef[i];
74 if (CHRSH_IsTeamDefAlien(td)) {
75 cgi->csi->alienChrTemplates[cgi->csi->numAlienTeams] = nullptr;
76 cgi->csi->alienTeams[cgi->csi->numAlienTeams++] = td;
77 }
78 if (cgi->csi->numAlienTeams >= MAX_TEAMS_PER_MISSION)
79 break;
80 }
81}
82
86static void GAME_SK_Start_f (void)
87{
88 if (cgi->GAME_IsTeamEmpty()) {
89 cgi->GAME_LoadDefaultTeam(false);
90 }
91
92 if (cgi->GAME_IsTeamEmpty()) {
94 const char* ugvTeamDefID = "phalanx_ugv_phoenix";
95 const char* name = cgi->Cvar_GetString("cl_equip");
96 const equipDef_t* ed = cgi->INV_GetEquipmentDefinitionByID(name);
97 const size_t size = cgi->GAME_GetCharacterArraySize();
98 uint32_t maxSoldiers = cgi->Cvar_GetInteger("sv_maxsoldiersperplayer");
99 uint32_t ugvs = cgi->Cvar_GetInteger("cl_ugvs");
100
101 if (maxSoldiers <= 0)
102 maxSoldiers = size;
103
104 ugvs = std::min(ugvs, (uint32_t)(size - maxSoldiers));
105 cgi->Com_Printf("Starting skirmish with %i soldiers and %i ugvs\n", maxSoldiers, ugvs);
106 cgi->GAME_AutoTeam(name, maxSoldiers);
107 for (unsigned int i = 0; i < ugvs; i++)
108 cgi->GAME_AppendTeamMember(i + maxSoldiers, ugvTeamDefID, ed);
109 } else {
110 cgi->Com_Printf("Using already loaded team\n");
111 }
112
113 const mapDef_t* md = cgi->GAME_GetCurrentSelectedMap();
114 if (!md)
115 return;
116
118
119 assert(md->mapTheme);
120
121 cgi->Cbuf_AddText("map %s %s %s;", cgi->Cvar_GetInteger("mn_serverday") ? "day" : "night", md->mapTheme, md->params ? (const char*)cgi->LIST_GetRandom(md->params) : "");
122}
123
124static void GAME_SK_Restart_f (void)
125{
126 cgi->GAME_ReloadMode();
128}
129
133static void GAME_SK_ChangeEquip_f (void)
134{
135 if (cgi->Cmd_Argc() < 2) {
136 cgi->Com_Printf("Usage: %s <cvarname>\n", cgi->Cmd_Argv(0));
137 return;
138 }
139
140 const char* command = cgi->Cmd_Argv(0);
141 const char* cvarName = cgi->Cmd_Argv(1);
142
144 if (Q_streq(command, "sk_prevequip")) {
145 type = BACKWARD;
146 } else if (Q_streq(command, "sk_nextequip")) {
147 type = FORWARD;
148 } else {
149 type = INIT;
150 }
151
152 const equipDef_t* ed = cgi->GAME_ChangeEquip(cgi->cgameType->equipmentList, type, cgi->Cvar_GetString(cvarName));
153
154 char cvarBuf[MAX_VAR];
155 Com_sprintf(cvarBuf, sizeof(cvarBuf), "%sname", cvarName);
156
157 cgi->Cvar_Set(cvarName, "%s", ed->id);
158 cgi->Cvar_Set(cvarBuf, "_%s", ed->name);
159}
160
173static void GAME_SK_Results (dbuffer* msg, int winner, int* numSpawned, int* numAlive, int numKilled[][MAX_TEAMS], int numStunned[][MAX_TEAMS], bool nextmap)
174{
175 if (nextmap)
176 return;
177
178 /* HACK: Change to the main menu now so cgame shutdown won't kill the results popup doing it */
179 cgi->UI_InitStack("main", "");
180
181 if (winner == -1) {
182 cgi->UI_Popup(_("Game Drawn!"), "%s\n", _("The game was a draw!\n\nEnemies escaped."));
183 return;
184 }
185
186 if (winner == 0) {
187 cgi->UI_Popup(_("Game Drawn!"), "%s\n", _("The game was a draw!\n\nNo survivors left on any side."));
188 return;
189 }
190
191 const int team = cgi->GAME_GetCurrentTeam();
192 const int ownFriendlyFire = numKilled[team][team] + numKilled[TEAM_CIVILIAN][team];
193 const int ownLost = numSpawned[team] - numAlive[team] - ownFriendlyFire;
194 const int civFriendlyFire = numKilled[team][TEAM_CIVILIAN] + numKilled[TEAM_CIVILIAN][TEAM_CIVILIAN];
195 const int civLost = numSpawned[TEAM_CIVILIAN] - numAlive[TEAM_CIVILIAN] - civFriendlyFire;
196
197 int enemiesStunned = 0;
198 for (int i = 0; i <= MAX_TEAMS; ++i) {
199 enemiesStunned += numStunned[i][TEAM_ALIEN];
200 }
201 const int enemiesKilled = numSpawned[TEAM_ALIEN] - numAlive[TEAM_ALIEN] - enemiesStunned;
202
203 char resultText[1024];
204 Com_sprintf(resultText, sizeof(resultText),
205 _("\n%i of %i enemies killed, %i stunned, %i survived.\n"
206 "%i of %i team members survived, %i lost in action, %i friendly fire loses.\n"
207 "%i of %i civilians saved, %i civilian loses, %i friendly fire loses."),
208 enemiesKilled, numSpawned[TEAM_ALIEN], enemiesStunned, numAlive[TEAM_ALIEN],
209 numAlive[team], numSpawned[team], ownLost, ownFriendlyFire,
210 numAlive[TEAM_CIVILIAN], numSpawned[TEAM_CIVILIAN], civLost, civFriendlyFire);
211 if (winner == team) {
212 cgi->UI_Popup(_("Congratulations"), "%s\n%s\n", _("You won the game!"), resultText);
213 } else {
214 cgi->UI_Popup(_("Better luck next time"), "%s\n%s\n", _("You've lost the game!"), resultText);
215 }
216
217 /* Ask the game mode to shutdown -- don't execute the shutdown here or we will crash! */
218 cgi->Cbuf_AddText("game_exit");
219}
220
226static inline void GAME_SK_HideDropships (const linkedList_t* dropships)
227{
228 const bool hide = (dropships == nullptr);
229 if (hide) {
230 cgi->UI_ExecuteConfunc("skirmish_hide_dropships true");
231 cgi->Cvar_Set("rm_drop", "%s", "");
232 } else {
233 const char* rma = GAME_SK_GetRandomMapAssemblyNameForCraft((const char*)dropships->data);
234 cgi->Cvar_Set("rm_drop", "%s", rma);
235 cgi->UI_UpdateInvisOptions(cgi->UI_GetOption(OPTION_DROPSHIPS), dropships);
236 cgi->UI_RegisterOption(OPTION_DROPSHIPS, cgi->UI_GetOption(OPTION_DROPSHIPS));
237
238 cgi->UI_ExecuteConfunc("skirmish_hide_dropships false");
239 }
240}
241
247static inline void GAME_SK_HideUFOs (const linkedList_t* ufos)
248{
249 const bool hide = (ufos == nullptr);
250 if (hide) {
251 cgi->UI_ExecuteConfunc("skirmish_hide_ufos true");
252 cgi->Cvar_Set("rm_ufo", "%s", "");
253 } else {
254 const char* rma = GAME_SK_GetRandomMapAssemblyNameForCraft((const char*)ufos->data);
255 cgi->Cvar_Set("rm_ufo", "%s", rma);
256 cgi->UI_UpdateInvisOptions(cgi->UI_GetOption(OPTION_UFOS), ufos);
257 cgi->UI_RegisterOption(OPTION_UFOS, cgi->UI_GetOption(OPTION_UFOS));
258
259 cgi->UI_ExecuteConfunc("skirmish_hide_ufos false");
260 }
261 cgi->Cvar_Set("rm_crashed", "%s", "");
262}
263
264static const mapDef_t* GAME_SK_MapInfo (int step)
265{
266 int i = 0;
267
268 while (!cgi->GAME_GetCurrentSelectedMap()->singleplayer) {
269 i++;
270 cgi->GAME_SwitchCurrentSelectedMap(step ? step : 1);
271 if (i > 100000)
272 cgi->Com_Error(ERR_DROP, "no singleplayer map found");
273 }
274
275 const mapDef_t* md = cgi->GAME_GetCurrentSelectedMap();
276
277 cgi->Cvar_SetValue("ai_singleplayeraliens", md->maxAliens);
278
279 if (md->mapTheme[0] == '.')
280 return nullptr;
281
282 if (md->mapTheme[0] == '+') {
285 } else {
286 GAME_SK_HideUFOs(nullptr);
287 GAME_SK_HideDropships(nullptr);
288 }
289
290 return md;
291}
292
293static void GAME_InitMenuOptions (void)
294{
295 uiNode_t* ufoOptions = nullptr;
296 uiNode_t* aircraftOptions = nullptr;
297
298 const short ufoIdsNum = cgi->Com_GetUFOIdsNum();
299 for (int i = 0; i < ufoIdsNum; i++) {
300 const char* shortName = cgi->Com_UFOTypeToShortName((ufoType_t)i);
301 cgi->UI_AddOption(&ufoOptions, shortName, shortName, GAME_SK_GetRandomMapAssemblyNameForCraft(shortName));
302 }
303 for (int i = 0; i < ufoIdsNum; i++) {
304 const char* shortName = cgi->Com_UFOCrashedTypeToShortName((ufoType_t)i);
305 cgi->UI_AddOption(&ufoOptions, shortName, shortName, GAME_SK_GetRandomMapAssemblyNameForCraft(shortName));
306 }
307 cgi->UI_RegisterOption(OPTION_UFOS, ufoOptions);
308
309 const int maxDropShips = cgi->Com_GetDropShipIdsNum();
310 for (int i = 0; i < maxDropShips; i++) {
311 const char* shortName = cgi->Com_DropShipTypeToShortName((humanAircraftType_t)i);
312 cgi->UI_AddOption(&aircraftOptions, shortName, shortName, GAME_SK_GetRandomMapAssemblyNameForCraft(shortName));
313 }
314 cgi->UI_RegisterOption(OPTION_DROPSHIPS, aircraftOptions);
315}
316
317static const cmdList_t skirmishCmds[] = {
318 {"sk_start", GAME_SK_Start_f, "Start the new skirmish game"},
319 {"sk_prevequip", GAME_SK_ChangeEquip_f, "Previous equipment definition"},
320 {"sk_nextequip", GAME_SK_ChangeEquip_f, "Next equipment definition"},
321 {"sk_initequip", GAME_SK_ChangeEquip_f, "Init equipment definition"},
322 {"game_go", GAME_SK_Restart_f, "Restart the skirmish mission"},
323 {nullptr, nullptr, nullptr}
324};
325static void GAME_SK_InitStartup (void)
326{
327 cgi->Cvar_ForceSet("sv_maxclients", "1");
328 cl_equip = cgi->Cvar_Get("cl_equip", "multiplayer_initial", 0, "Equipment that is used for skirmish mode games");
329
330 cgi->Cmd_TableAddList(skirmishCmds);
331
333}
334
335static void GAME_SK_Shutdown (void)
336{
337 cgi->Cmd_TableRemoveList(skirmishCmds);
338 /* You really don't want this with campaign */
339 cgi->Cvar_ForceSet("g_endlessaliens", "0");
340
341 cgi->UI_ResetData(OPTION_DROPSHIPS);
342 cgi->UI_ResetData(OPTION_UFOS);
343
344 cgi->SV_Shutdown("Quitting server.", false);
345}
346
347#ifndef HARD_LINKED_CGAME
349#else
350const cgame_export_t* GetCGameSkirmishAPI (const cgame_import_t* import)
351#endif
352{
353 static cgame_export_t e;
354
355 OBJZERO(e);
356
357 e.name ="Skirmish mode";
358 e.menu = "skirmish";
359 e.Init = GAME_SK_InitStartup;
360 e.Shutdown = GAME_SK_Shutdown;
362 e.Results = GAME_SK_Results;
363 e.InitMissionBriefing = GAME_SK_InitMissionBriefing;
364
365 cgi = import;
366
367 return &e;
368}
changeEquipType_t
Definition cgame.h:92
@ INIT
Definition cgame.h:95
@ FORWARD
Definition cgame.h:93
@ BACKWARD
Definition cgame.h:94
bool CHRSH_IsTeamDefAlien(const teamDef_t *const td)
Check if a team definition is alien.
Shared game type headers.
#define CGAME_HARD_LINKED_FUNCTIONS
Definition cl_game.h:91
static void GAME_SK_ChangeEquip_f(void)
Changed the given cvar to the next/prev equipment definition.
static void GAME_SK_Results(dbuffer *msg, int winner, int *numSpawned, int *numAlive, int numKilled[][MAX_TEAMS], int numStunned[][MAX_TEAMS], bool nextmap)
After a mission was finished this function is called.
static void GAME_SK_Restart_f(void)
static void GAME_SK_HideDropships(const linkedList_t *dropships)
Hide the dropship selection or show it with the dropship given in the parameter.
static void GAME_SK_Start_f(void)
Starts a new skirmish game.
static void GAME_SK_Shutdown(void)
static const mapDef_t * GAME_SK_MapInfo(int step)
static void GAME_SK_InitStartup(void)
static void GAME_SK_HideUFOs(const linkedList_t *ufos)
Hide the ufo selection or show it with the ufos given in the parameter.
static void GAME_InitMenuOptions(void)
static const cmdList_t skirmishCmds[]
static const char * GAME_SK_GetRandomMapAssemblyNameForCraft(const char *name)
static cvar_t * cl_equip
static void GAME_SK_SetMissionParameters(const mapDef_t *md)
Register some data in the shared client/server structs to ensure that e.g. every known alien race is ...
static CGAME_HARD_LINKED_FUNCTIONS void GAME_SK_InitMissionBriefing(const char **title, linkedList_t **victoryConditionsMsgIDs, linkedList_t **missionBriefingMsgIDs)
const cgame_export_t * GetCGameAPI(const cgame_import_t *import)
Skirmish game type headers.
Share stuff between the different cgame implementations.
#define _(String)
Definition cl_shared.h:44
#define ERR_DROP
Definition common.h:211
const cgame_import_t * cgi
#define MAX_TEAMS
Definition defines.h:98
#define MAX_TEAMS_PER_MISSION
Definition inv_shared.h:618
short humanAircraftType_t
Definition inv_shared.h:31
voidpf void uLong size
Definition ioapi.h:42
#define TEAM_ALIEN
Definition q_shared.h:63
#define TEAM_CIVILIAN
Definition q_shared.h:61
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
short ufoType_t
Definition scripts.h:145
#define Q_strvalid(string)
Definition shared.h:141
#define Q_streq(a, b)
Definition shared.h:136
#define OBJZERO(obj)
Definition shared.h:178
#define MAX_VAR
Definition shared.h:36
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
const mapDef_t *EXPORT * MapInfo(int step)
const char * menu
Definition cgame.h:39
const char * name
Definition cgame.h:38
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition cvar.h:71
const char * name
Definition inv_shared.h:607
char id[MAX_VAR]
Definition inv_shared.h:606
void * data
Definition list.h:31
linkedList_t * aircraft
Definition q_shared.h:492
char * mapTheme
Definition q_shared.h:464
linkedList_t * ufos
Definition q_shared.h:491
char * victoryCondition
Definition q_shared.h:467
char * missionBriefing
Definition q_shared.h:469
char * civTeam
Definition q_shared.h:471
int maxAliens
Definition q_shared.h:483
char * description
Definition q_shared.h:466
linkedList_t * params
Definition q_shared.h:465
Atomic structure used to define most of the UI.
Definition ui_nodes.h:80
Data and interface to share data.
@ OPTION_UFOS
Definition ui_dataids.h:81
@ OPTION_DROPSHIPS
Definition ui_dataids.h:82