1 /**
2 * @file
3 * @brief Main game functions.
4 */
5
6 /*
7 All original material Copyright (C) 2002-2013 UFO: Alien Invasion.
8
9 Original file from Quake 2 v3.21: quake2-2.31/game/g_main.c
10 Copyright (C) 1997-2001 Id Software, Inc.
11
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 as published by the Free Software Foundation; either version 2
15 of the License, or (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20
21 See the GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27 */
28
29 #include "g_local.h"
30 #include "g_ai.h"
31 #include "g_client.h"
32 #include "g_edicts.h"
33 #include "g_match.h"
34 #include "g_spawn.h"
35 #include "g_utils.h"
36
37 game_locals_t game;
38 level_locals_t level;
39 game_import_t gi;
40 game_export_t globals;
41
42 #ifndef HARD_LINKED_GAME
43 cvar_t* sv_maxclients;
44 cvar_t* sv_dedicated;
45 cvar_t* developer;
46 #endif
47
48 cvar_t* logstats;
49 FILE *logstatsfile;
50
51 cvar_t* sv_needpass;
52 cvar_t* sv_maxplayersperteam;
53 cvar_t* sv_maxsoldiersperteam;
54 cvar_t* sv_maxsoldiersperplayer;
55 cvar_t* sv_enablemorale;
56 cvar_t* sv_roundtimelimit;
57 cvar_t* sv_maxentities;
58 cvar_t* sv_filterban;
59 cvar_t* sv_maxteams;
60 cvar_t* sv_ai;
61 cvar_t* sv_teamplay;
62 cvar_t* sv_hurtaliens;
63 cvar_t* sv_shot_origin;
64 static cvar_t* sv_cheats;
65 static cvar_t* sv_send_edicts;
66
67 cvar_t* password;
68
69 cvar_t* ai_alienteam;
70 cvar_t* ai_civilianteam;
71 cvar_t* ai_equipment;
72 cvar_t* ai_singleplayeraliens;
73 cvar_t* ai_numcivilians;
74 cvar_t* ai_multiplayeraliens;
75
76 /* morale cvars */
77 cvar_t* mob_death;
78 cvar_t* mob_wound;
79 cvar_t* mof_watching;
80 cvar_t* mof_teamkill;
81 cvar_t* mof_civilian;
82 cvar_t* mof_enemy;
83 cvar_t* mor_pain;
84
85 /*everyone gets this times morale damage */
86 cvar_t* mor_default;
87
88 /*at this distance the following two get halfed (exponential scale) */
89 cvar_t* mor_distance;
90
91 /*at this distance the following two get halfed (exponential scale) */
92 cvar_t* mor_victim;
93
94 /*at this distance the following two get halfed (exponential scale) */
95 cvar_t* mor_attacker;
96
97 /* how much the morale depends on the size of the damaged team */
98 cvar_t* mon_teamfactor;
99
100 cvar_t* mor_regeneration;
101 cvar_t* mor_shaken;
102 cvar_t* mor_panic;
103 cvar_t* mor_brave;
104
105 cvar_t* m_sanity;
106 cvar_t* m_rage;
107 cvar_t* m_rage_stop;
108 cvar_t* m_panic_stop;
109
110 cvar_t* g_endlessaliens;
111 cvar_t* g_ailua;
112 cvar_t* g_aihumans;
113 cvar_t* g_aidebug;
114 cvar_t* g_drawtraces;
115 cvar_t* g_nodamage;
116 cvar_t* g_notu;
117 cvar_t* g_nospawn;
118 cvar_t* g_actorspeed;
119 cvar_t* g_lastseen;
120 cvar_t* flood_msgs;
121 cvar_t* flood_persecond;
122 cvar_t* flood_waitdelay;
123
124 cvar_t* g_difficulty;
125
126 static const int TAG_INVENTORY = 2389;
127
G_FreeInventory(void * data)128 static void G_FreeInventory (void* data)
129 {
130 G_MemFree(data);
131 }
132
G_AllocInventoryMemory(size_t size)133 static void* G_AllocInventoryMemory (size_t size)
134 {
135 return G_TagMalloc(size, TAG_INVENTORY);
136 }
137
G_FreeAllInventory(void)138 static void G_FreeAllInventory (void)
139 {
140 G_FreeTags(TAG_INVENTORY);
141 }
142
143 static const inventoryImport_t inventoryImport = { G_FreeInventory, G_FreeAllInventory, G_AllocInventoryMemory };
144
145
146 /**
147 * @brief This will be called when the game library is first loaded
148 * @note only happens when a new game/map is started
149 */
G_Init(void)150 static void G_Init (void)
151 {
152 gi.DPrintf("==== InitGame ====\n");
153
154 /* noset vars */
155 sv_dedicated = gi.Cvar_Get("sv_dedicated", "0", CVAR_SERVERINFO | CVAR_NOSET, "Is this a dedicated server?");
156
157 /* latched vars */
158 sv_cheats = gi.Cvar_Get("sv_cheats", "0", CVAR_SERVERINFO | CVAR_LATCH, "Activate cheats");
159 gi.Cvar_Get("gamename", GAMEVERSION, CVAR_SERVERINFO | CVAR_LATCH, nullptr);
160 gi.Cvar_Get("gamedate", __DATE__, CVAR_SERVERINFO | CVAR_LATCH, nullptr);
161 developer = gi.Cvar_Get("developer", "0", 0, "Print out a lot of developer debug messages - useful to track down bugs");
162 logstats = gi.Cvar_Get("logstats", "1", CVAR_ARCHIVE, "Server logfile output for kills");
163
164 /* max. players per team (original quake) */
165 sv_maxplayersperteam = gi.Cvar_Get("sv_maxplayersperteam", "8", CVAR_SERVERINFO | CVAR_LATCH, "How many players (humans) may a team have");
166 /* max. soldiers per team */
167 sv_maxsoldiersperteam = gi.Cvar_Get("sv_maxsoldiersperteam", "4", CVAR_ARCHIVE | CVAR_SERVERINFO, "How many soldiers may one team have");
168 /* max soldiers per player */
169 sv_maxsoldiersperplayer = gi.Cvar_Get("sv_maxsoldiersperplayer", DOUBLEQUOTE(MAX_ACTIVETEAM), CVAR_ARCHIVE | CVAR_SERVERINFO, "How many soldiers one player is able to control in a given team");
170 /* enable moralestates in multiplayer */
171 sv_enablemorale = gi.Cvar_Get("sv_enablemorale", "1", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_LATCH, "Enable morale behaviour for actors");
172 sv_roundtimelimit = gi.Cvar_Get("sv_roundtimelimit", "90", CVAR_ARCHIVE | CVAR_SERVERINFO, "Timelimit in seconds for multiplayer rounds");
173 sv_roundtimelimit->modified = false;
174 sv_maxentities = gi.Cvar_Get("sv_maxentities", "1024", CVAR_LATCH, nullptr);
175
176 sv_maxteams = gi.Cvar_Get("sv_maxteams", "2", CVAR_SERVERINFO, "How many teams for current running map");
177 sv_maxteams->modified = false;
178
179 /* change anytime vars */
180 password = gi.Cvar_Get("password", "", CVAR_USERINFO, nullptr);
181 sv_needpass = gi.Cvar_Get("sv_needpass", "0", CVAR_SERVERINFO, nullptr);
182 sv_filterban = gi.Cvar_Get("sv_filterban", "1", 0, nullptr);
183 sv_ai = gi.Cvar_Get("sv_ai", "1", 0, "Activate or deativate the ai");
184 sv_teamplay = gi.Cvar_Get("sv_teamplay", "0", CVAR_ARCHIVE | CVAR_LATCH | CVAR_SERVERINFO, "Is teamplay activated? see sv_maxclients, sv_maxplayersperteam, sv_maxsoldiersperteam and sv_maxsoldiersperplayer");
185 /* how many connected clients */
186 sv_maxclients = gi.Cvar_Get("sv_maxclients", "1", CVAR_SERVERINFO, "If sv_maxclients is 1 we are in singleplayer - otherwise we are mutliplayer mode (see sv_teamplay)");
187 sv_shot_origin = gi.Cvar_Get("sv_shot_origin", "8", 0, "Assumed distance of muzzle from model");
188 sv_send_edicts = gi.Cvar_Get("sv_send_edicts", "0", CVAR_ARCHIVE | CVAR_CHEAT, "Send server side edicts for client display like triggers");
189 sv_hurtaliens = gi.Cvar_Get("sv_hurtaliens", "0", CVAR_SERVERINFO, "Spawn hurt aliens");
190
191 ai_alienteam = gi.Cvar_Get("ai_alienteam", "ortnok", 0, "Alien team");
192 ai_civilianteam = gi.Cvar_Get("ai_civilianteam", "europe", 0, "Civilian team");
193 /* this cvar is set in singleplayer via campaign definition */
194 ai_equipment = gi.Cvar_Get("ai_equipment", "multiplayer_alien", 0, "Initial equipment definition for aliens");
195 /* aliens in singleplayer (can differ each mission) */
196 ai_singleplayeraliens = gi.Cvar_Get("ai_singleplayeraliens", "30", 0, "How many aliens in this battle (singleplayer)");
197 /* civilians for singleplayer */
198 ai_numcivilians = gi.Cvar_Get("ai_numcivilians", "10", 0, "How many civilians in this battle");
199 /* aliens in multiplayer */
200 ai_multiplayeraliens = gi.Cvar_Get("ai_multiplayeraliens", "8", CVAR_ARCHIVE, "How many (ai controlled) actors in this battle (multiplayer)");
201
202 mob_death = gi.Cvar_Get("mob_death", "10", CVAR_LATCH|CVAR_NOSET, nullptr);
203 mob_wound = gi.Cvar_Get("mob_wound", "0.1", CVAR_LATCH|CVAR_NOSET, nullptr);
204 mof_watching = gi.Cvar_Get("mof_watching", "1.7", CVAR_LATCH|CVAR_NOSET, nullptr);
205 mof_teamkill = gi.Cvar_Get("mof_teamkill", "2.0", CVAR_LATCH|CVAR_NOSET, nullptr);
206 mof_civilian = gi.Cvar_Get("mof_civilian", "0.3", CVAR_LATCH|CVAR_NOSET, nullptr);
207 mof_enemy = gi.Cvar_Get("mof_ememy", "0.5", CVAR_LATCH|CVAR_NOSET, nullptr);
208 mor_pain = gi.Cvar_Get("mof_pain", "3.6", CVAR_LATCH|CVAR_NOSET, nullptr);
209 /* everyone gets this times morale damage */
210 mor_default = gi.Cvar_Get("mor_default", "0.3", CVAR_LATCH|CVAR_NOSET, "Everyone gets this times morale damage");
211 /* at this distance the following two get halved (exponential scale) */
212 mor_distance = gi.Cvar_Get("mor_distance", "120", CVAR_LATCH|CVAR_NOSET, "At this distance the following two get halved (exponential scale)");
213 /* at this distance the following two get halved (exponential scale) */
214 mor_victim = gi.Cvar_Get("mor_victim", "0.7", CVAR_LATCH|CVAR_NOSET, "At this distance the following two get halved (exponential scale)");
215 /* at this distance the following two get halved (exponential scale) */
216 mor_attacker = gi.Cvar_Get("mor_attacker", "0.3", CVAR_LATCH|CVAR_NOSET, "At this distance the following two get halved (exponential scale)");
217 /* how much the morale depends on the size of the damaged team */
218 mon_teamfactor = gi.Cvar_Get("mon_teamfactor", "0.6", CVAR_LATCH|CVAR_NOSET, "How much the morale depends on the size of the damaged team");
219
220 mor_regeneration = gi.Cvar_Get("mor_regeneration", "15", CVAR_LATCH|CVAR_NOSET, nullptr);
221 mor_shaken = gi.Cvar_Get("mor_shaken", "50", CVAR_LATCH|CVAR_NOSET, nullptr);
222 mor_panic = gi.Cvar_Get("mor_panic", "30", CVAR_LATCH|CVAR_NOSET, nullptr);
223 mor_brave = gi.Cvar_Get("mor_brave", "85", CVAR_LATCH|CVAR_NOSET, nullptr);
224
225 m_sanity = gi.Cvar_Get("m_sanity", "1.0", CVAR_LATCH|CVAR_NOSET, nullptr);
226 m_rage = gi.Cvar_Get("m_rage", "0.6", CVAR_LATCH|CVAR_NOSET, nullptr);
227 m_rage_stop = gi.Cvar_Get("m_rage_stop", "2.0", CVAR_LATCH|CVAR_NOSET, nullptr);
228 m_panic_stop = gi.Cvar_Get("m_panic_stop", "1.0", CVAR_LATCH|CVAR_NOSET, nullptr);
229
230 g_endlessaliens = gi.Cvar_Get("g_endlessaliens", "0", CVAR_LATCH|CVAR_SERVERINFO, "Spawn endless aliens");
231 g_ailua = gi.Cvar_Get("g_ailua", "0", 0, "Activate or deactivate the LUA AI");
232 g_aihumans = gi.Cvar_Get("g_aihumans", "0", CVAR_DEVELOPER, "Activate or deactivate the ai for human actors");
233 g_aidebug = gi.Cvar_Get("g_aidebug", "0", CVAR_DEVELOPER|CVAR_CHEAT, "All AI actors are visible");
234 g_drawtraces = gi.Cvar_Get("g_drawtraces", "0", CVAR_DEVELOPER, "All traces will be rendered");
235 g_nodamage = gi.Cvar_Get("g_nodamage", "0", CVAR_DEVELOPER|CVAR_CHEAT, "No damage in developer mode");
236 g_notu = gi.Cvar_Get("g_notu", "0", CVAR_DEVELOPER|CVAR_CHEAT, "No TU costs while performing any action");
237 g_actorspeed = gi.Cvar_Get("g_actorspeed", "1.0", CVAR_ARCHIVE|CVAR_SERVERINFO, "Moving speed of the actor");
238 g_lastseen = gi.Cvar_Get("g_lastseen", "20", CVAR_ARCHIVE|CVAR_SERVERINFO, "Quit the match if no player was seen in this amount of rounds");
239 g_nospawn = gi.Cvar_Get("g_nospawn", "0", CVAR_DEVELOPER|CVAR_CHEAT, "Do not spawn a soldier");
240
241 /* flood control */
242 flood_msgs = gi.Cvar_Get("flood_msgs", "4", 0, nullptr);
243 flood_persecond = gi.Cvar_Get("flood_persecond", "4", 0, nullptr);
244 flood_waitdelay = gi.Cvar_Get("flood_waitdelay", "10", 0, "Delay until someone is unlocked from talking again");
245
246 g_difficulty = gi.Cvar_Get("g_difficulty", "0", CVAR_NOSET, "Singleplayer difficulty level");
247
248 game.sv_maxentities = sv_maxentities->integer;
249 game.sv_maxplayersperteam = sv_maxplayersperteam->integer;
250
251 /* initialize the entity storage */
252 globals.edicts = G_EdictsConstruct();
253 globals.max_edicts = game.sv_maxentities;
254 globals.num_edicts = game.sv_maxplayersperteam;
255
256 /* initialize all players for this game */
257 /* game.sv_maxplayersperteam for human controlled players
258 * + game.sv_maxplayer for ai */
259 game.players = (player_t*)G_TagMalloc(game.sv_maxplayersperteam * 2 * sizeof(game.players[0]), TAG_GAME);
260 globals.players = game.players;
261 globals.maxplayersperteam = game.sv_maxplayersperteam;
262
263 /* init csi and inventory */
264 INVSH_InitCSI(gi.csi);
265 game.i.initInventory("game", gi.csi, &inventoryImport);
266
267 if (logstats->integer)
268 logstatsfile = fopen(va("%s/stats.log", gi.FS_Gamedir()), "a");
269 else
270 logstatsfile = nullptr;
271
272 AI_Init();
273 AIL_Init();
274 }
275
276 /**
277 * @brief Free the tags TAG_LEVEL and TAG_GAME
278 * @sa Mem_FreeTags
279 */
G_Shutdown(void)280 static void G_Shutdown (void)
281 {
282 gi.DPrintf("==== ShutdownGame ====\n");
283
284 AIL_Shutdown();
285
286 if (logstatsfile)
287 fclose(logstatsfile);
288 logstatsfile = nullptr;
289
290 G_FreeTags(TAG_LEVEL);
291 G_FreeTags(TAG_GAME);
292 G_FreeAllInventory();
293
294 Com_Printf("Used inventory slots in game on shutdown: %i\n", game.i.GetUsedSlots());
295 }
296
297 /**
298 * @brief If password has changed, update sv_needpass cvar as needed
299 */
CheckNeedPass(void)300 static void CheckNeedPass (void)
301 {
302 if (password->modified) {
303 password->modified = false;
304
305 const int need = Q_strvalid(password->string) && Q_strcasecmp(password->string, "none") ? 1 : 0;
306 gi.Cvar_Set("sv_needpass", "%i", need);
307 }
308 }
309
G_SendBoundingBoxes(void)310 static void G_SendBoundingBoxes (void)
311 {
312 if (!sv_send_edicts->integer)
313 return;
314
315 Edict* ent = G_EdictsGetFirst(); /* skip the world */
316 while ((ent = G_EdictsGetNextInUse(ent)))
317 G_EventSendEdict(*ent);
318 }
319
320 /**
321 * @sa SV_RunGameFrame
322 * @sa G_MatchEndTrigger
323 * @sa AI_Run
324 * @return true if game reaches its end - false otherwise
325 */
G_RunFrame(void)326 static bool G_RunFrame (void)
327 {
328 level.framenum++;
329 /* server is running at 10 fps */
330 level.time = level.framenum * SERVER_FRAME_SECONDS;
331
332 /* this doesn't belong here, but it works */
333 if (!level.routed) {
334 level.routed = true;
335 G_CompleteRecalcRouting();
336 }
337
338 /* still waiting for other players */
339 if (!G_MatchIsRunning()) {
340 if (sv_maxteams->modified) {
341 /* inform the client */
342 gi.ConfigString(CS_MAXTEAMS, "%i", sv_maxteams->integer);
343 sv_maxteams->modified = false;
344 }
345 }
346
347 if (G_IsMultiPlayer()) {
348 if (sv_roundtimelimit->modified) {
349 /* some played around here - restart the count down */
350 level.roundstartTime = level.time;
351 /* don't allow smaller values here */
352 if (sv_roundtimelimit->integer < 30 && sv_roundtimelimit->integer > 0) {
353 gi.DPrintf("The minimum value for sv_roundtimelimit is 30\n");
354 gi.Cvar_Set("sv_roundtimelimit", "30");
355 }
356 sv_roundtimelimit->modified = false;
357 }
358 G_CheckForceEndRound();
359 }
360
361 /* end this game? */
362 if (G_MatchDoEnd())
363 return true;
364
365 CheckNeedPass();
366
367 /* run ai */
368 AI_Run();
369
370 /* not all teams are spawned or game has already ended */
371 if (G_MatchIsRunning())
372 G_EdictsThink();
373
374 G_SendBoundingBoxes();
375
376 return false;
377 }
378
379 /**
380 * @brief Returns a pointer to the structure with all entry points and global variables
381 */
GetGameAPI(game_import_t * import)382 game_export_t* GetGameAPI (game_import_t* import)
383 {
384 gi = *import;
385 srand(gi.seed);
386
387 globals.apiversion = GAME_API_VERSION;
388 globals.Init = G_Init;
389 globals.Shutdown = G_Shutdown;
390 globals.SpawnEntities = G_SpawnEntities;
391
392 globals.ClientConnect = G_ClientConnect;
393 globals.ClientUserinfoChanged = G_ClientUserinfoChanged;
394 globals.ClientDisconnect = G_ClientDisconnect;
395 globals.ClientBegin = G_ClientBegin;
396 globals.ClientStartMatch = G_ClientStartMatch;
397 globals.ClientCommand = G_ClientCommand;
398 globals.ClientAction = G_ClientAction;
399 globals.ClientEndRound = G_ClientEndRound;
400 globals.ClientTeamInfo = G_ClientTeamInfo;
401 globals.ClientInitActorStates = G_ClientInitActorStates;
402 globals.ClientGetTeamNum = G_ClientGetTeamNum;
403 globals.ClientIsReady = G_ClientIsReady;
404 globals.ClientGetName = G_GetPlayerName;
405 globals.ClientGetActiveTeam = G_GetActiveTeam;
406
407 globals.RunFrame = G_RunFrame;
408
409 globals.ServerCommand = G_ServerCommand;
410
411 globals.edict_size = sizeof(Edict);
412 globals.player_size = sizeof(player_t);
413
414 return &globals;
415 }
416
417 #ifndef HARD_LINKED_GAME
418 /* this is only here so the functions in the shared code can link */
Sys_Error(const char * error,...)419 void Sys_Error (const char* error, ...)
420 {
421 va_list argptr;
422 char text[1024];
423
424 va_start(argptr, error);
425 Q_vsnprintf(text, sizeof(text), error, argptr);
426 va_end(argptr);
427
428 gi.Error("%s", text);
429 }
430
Com_Printf(const char * msg,...)431 void Com_Printf (const char* msg, ...)
432 {
433 va_list argptr;
434 char text[1024];
435
436 va_start(argptr, msg);
437 Q_vsnprintf(text, sizeof(text), msg, argptr);
438 va_end(argptr);
439
440 gi.DPrintf("%s", text);
441 }
442
Com_DPrintf(int level,const char * msg,...)443 void Com_DPrintf (int level, const char* msg, ...)
444 {
445 va_list argptr;
446 char text[1024];
447
448 /* don't confuse non-developers with techie stuff... */
449 if (!developer || developer->integer == 0)
450 return;
451
452 if (!(developer->integer & level))
453 return;
454
455 va_start(argptr, msg);
456 Q_vsnprintf(text, sizeof(text), msg, argptr);
457 va_end(argptr);
458
459 gi.DPrintf("%s", text);
460 }
461 #endif
462