1 /*
2 * Copyright (C) 1997-2001 Id Software, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59
17 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 */
20
21 #include "g_local.h"
22
23 game_locals_t game;
24 level_locals_t level;
25 game_import_t gi;
26 game_export_t globals;
27 spawn_temp_t st;
28
29 int sm_meat_index;
30 int snd_fry;
31 int meansOfDeath;
32
33 edict_t *g_edicts;
34
35 cvar_t *deathmatch;
36 cvar_t *coop;
37 cvar_t *dmflags;
38 cvar_t *skill;
39 cvar_t *fraglimit;
40 cvar_t *timelimit;
41 cvar_t *password;
42 cvar_t *spectator_password;
43 cvar_t *needpass;
44 cvar_t *maxclients;
45 cvar_t *maxspectators;
46 cvar_t *maxentities;
47 cvar_t *g_select_empty;
48 cvar_t *dedicated;
49 cvar_t *oldsave;
50
51 cvar_t *filterban;
52
53 cvar_t *sv_maxvelocity;
54 cvar_t *sv_gravity;
55
56 cvar_t *sv_rollspeed;
57 cvar_t *sv_rollangle;
58 cvar_t *gun_x;
59 cvar_t *gun_y;
60 cvar_t *gun_z;
61
62 cvar_t *run_pitch;
63 cvar_t *run_roll;
64 cvar_t *bob_up;
65 cvar_t *bob_pitch;
66 cvar_t *bob_roll;
67
68 cvar_t *sv_cheats;
69
70 cvar_t *flood_msgs;
71 cvar_t *flood_persecond;
72 cvar_t *flood_waitdelay;
73
74 cvar_t *sv_maplist;
75
76 cvar_t *gib_on;
77
78 #ifdef WITH_ACEBOT
79 cvar_t *botchat; /* <-------botchat */
80 cvar_t *botauto_respawn;
81 #endif
82
83 #ifdef GAME_MOD
84 cvar_t *weap_shell;
85 cvar_t *alt_fire_blaster;
86 cvar_t *eject_bullets;
87 cvar_t *eject_bullets_dm_coop;
88 cvar_t *opt_dm_scoreboard;
89 cvar_t *help_min;
90 cvar_t *camoffset;
91 #endif
92
93 void SpawnEntities(char *mapname, char *entities, char *spawnpoint);
94 void ClientThink(edict_t * ent, usercmd_t * cmd);
95 qboolean ClientConnect(edict_t * ent, char *userinfo);
96 void ClientUserinfoChanged(edict_t * ent, char *userinfo);
97 void ClientDisconnect(edict_t * ent);
98 void ClientBegin(edict_t * ent);
99 void ClientCommand(edict_t * ent);
100 void RunEntity (edict_t * ent);
101 void WriteGame (char *filename, qboolean autosave);
102 void ReadGame (char *filename);
103 void WriteLevel(char *filename);
104 void ReadLevel (char *filename);
105 void InitGame (void);
106 void G_RunFrame(void);
107
108
109 /* =================================================================== */
110
111
112 void
ShutdownGame(void)113 ShutdownGame(void)
114 {
115 gi.dprintf("==== ShutdownGame ====\n");
116
117 gi.FreeTags(TAG_LEVEL);
118 gi.FreeTags(TAG_GAME);
119 }
120
121
122 /*
123 * ================= GetGameAPI
124 *
125 * Returns a pointer to the structure with all entry points and global variables
126 * =================
127 */
128 game_export_t *
GetGameAPI(game_import_t * import)129 GetGameAPI(game_import_t * import)
130 {
131 gi = *import;
132
133 globals.apiversion = GAME_API_VERSION;
134 globals.Init = InitGame;
135 globals.Shutdown = ShutdownGame;
136 globals.SpawnEntities = SpawnEntities;
137
138 globals.WriteGame = WriteGame;
139 globals.ReadGame = ReadGame;
140 globals.WriteLevel = WriteLevel;
141 globals.ReadLevel = ReadLevel;
142
143 globals.ClientThink = ClientThink;
144 globals.ClientConnect = ClientConnect;
145 globals.ClientUserinfoChanged = ClientUserinfoChanged;
146 globals.ClientDisconnect = ClientDisconnect;
147 globals.ClientBegin = ClientBegin;
148 globals.ClientCommand = ClientCommand;
149
150 globals.RunFrame = G_RunFrame;
151
152 globals.ServerCommand = ServerCommand;
153
154 globals.edict_size = sizeof(edict_t);
155
156 return &globals;
157 }
158
159 #ifndef GAME_HARD_LINKED
160 /* this is only here so the functions in q_shared.c and q_shwin.c can link */
161 void
Sys_Error(char * error,...)162 Sys_Error(char *error,...)
163 {
164 va_list argptr;
165 char text[1024];
166
167 va_start(argptr, error);
168 vsnprintf(text, sizeof(text) - 1, error, argptr);
169 va_end(argptr);
170
171 gi.error(ERR_FATAL, "%s", text);
172 }
173
174 void
Com_Printf(char * msg,...)175 Com_Printf(char *msg,...)
176 {
177 va_list argptr;
178 char text[1024];
179
180 va_start(argptr, msg);
181 vsnprintf(text, sizeof(text) - 1, msg, argptr);
182 va_end(argptr);
183
184 gi.dprintf("%s", text);
185 }
186
187 #endif
188
189 /* ====================================================================== */
190
191
192 /*
193 * ================= ClientEndServerFrames =================
194 */
195 void
ClientEndServerFrames(void)196 ClientEndServerFrames(void)
197 {
198 int i;
199 edict_t *ent;
200
201 /* calc the player views now that all pushing */
202 /* and damage has been added */
203 for (i = 0; i < maxclients->value; i++) {
204 ent = g_edicts + 1 + i;
205 if (!ent->inuse || !ent->client)
206 continue;
207 ClientEndServerFrame(ent);
208 }
209
210 }
211
212 /*
213 * ================= CreateTargetChangeLevel
214 *
215 * Returns the created target changelevel =================
216 */
217 edict_t *
CreateTargetChangeLevel(char * map)218 CreateTargetChangeLevel(char *map)
219 {
220 edict_t *ent;
221
222 ent = G_Spawn();
223 ent->classname = "target_changelevel";
224 Com_sprintf(level.nextmap, sizeof(level.nextmap), "%s", map);
225 ent->map = level.nextmap;
226 return ent;
227 }
228
229 /*
230 * ================= EndDMLevel
231 *
232 * The timelimit or fraglimit has been exceeded =================
233 */
234 void
EndDMLevel(void)235 EndDMLevel(void)
236 {
237 edict_t *ent;
238 char *s, *t, *f;
239 static const char *seps = " ,\n\r";
240
241 /* stay on same level flag */
242 if ((int)dmflags->value & DF_SAME_LEVEL) {
243 BeginIntermission(CreateTargetChangeLevel(level.mapname));
244 return;
245 }
246 /* see if it's in the map list */
247 if (*sv_maplist->string) {
248 s = strdup(sv_maplist->string);
249 f = NULL;
250 t = strtok(s, seps);
251 while (t != NULL) {
252 if (Q_stricmp(t, level.mapname) == 0) {
253 /* it's in the list, go to the next one */
254 t = strtok(NULL, seps);
255 if (t == NULL) { /* end of list, go to
256 * first one */
257 if (f == NULL) /* there isn't a first
258 * one, same level */
259 BeginIntermission(CreateTargetChangeLevel(level.mapname));
260 else
261 BeginIntermission(CreateTargetChangeLevel(f));
262 } else
263 BeginIntermission(CreateTargetChangeLevel(t));
264 free(s);
265 return;
266 }
267 if (!f)
268 f = t;
269 t = strtok(NULL, seps);
270 }
271 free(s);
272 }
273 if (level.nextmap[0]) /* go to a specific map */
274 BeginIntermission(CreateTargetChangeLevel(level.nextmap));
275 else { /* search for a changelevel */
276 ent = G_Find(NULL, FOFS(classname), "target_changelevel");
277 if (!ent) { /* the map designer didn't include a
278 * changelevel, */
279
280 /*
281 * so create a fake ent that goes back to the same
282 * level
283 */
284 BeginIntermission(CreateTargetChangeLevel(level.mapname));
285 return;
286 }
287 BeginIntermission(ent);
288 }
289 }
290
291
292 /*
293 * ================= CheckNeedPass =================
294 */
295 void
CheckNeedPass(void)296 CheckNeedPass(void)
297 {
298 int need;
299
300 /* if password or spectator_password has changed, update needpass */
301 /* as needed */
302 if (password->modified || spectator_password->modified) {
303 password->modified = spectator_password->modified = false;
304
305 need = 0;
306
307 if (*password->string && Q_stricmp(password->string, "none"))
308 need |= 1;
309 if (*spectator_password->string && Q_stricmp(spectator_password->string, "none"))
310 need |= 2;
311
312 gi.cvar_set("needpass", va("%d", need));
313 }
314 }
315
316 /*
317 * ================= CheckDMRules =================
318 */
319 void
CheckDMRules(void)320 CheckDMRules(void)
321 {
322 int i;
323 gclient_t *cl;
324
325 if (level.intermissiontime)
326 return;
327
328 if (!deathmatch->value)
329 return;
330
331 if (timelimit->value) {
332 if (level.time >= timelimit->value * 60) {
333 #ifdef WITH_ACEBOT
334 safe_bprintf
335 #else
336 gi.bprintf
337 #endif
338 (PRINT_HIGH, "Timelimit hit.\n");
339 EndDMLevel();
340 return;
341 }
342 }
343 if (fraglimit->value) {
344 for (i = 0; i < maxclients->value; i++) {
345 cl = game.clients + i;
346 if (!g_edicts[i + 1].inuse)
347 continue;
348
349 if (cl->resp.score >= fraglimit->value) {
350
351 #ifdef WITH_ACEBOT
352 safe_bprintf
353 #else
354 gi.bprintf
355 #endif
356 (PRINT_HIGH, "Fraglimit hit.\n");
357 EndDMLevel();
358 return;
359 }
360 }
361 }
362 }
363
364
365 /*
366 * ============= ExitLevel =============
367 */
368 void
ExitLevel(void)369 ExitLevel(void)
370 {
371 int i;
372 edict_t *ent;
373 char command [256];
374
375 Com_sprintf(command, sizeof(command), "gamemap \"%s\"\n", level.changemap);
376 gi.AddCommandString(command);
377 level.changemap = NULL;
378 level.exitintermission = 0;
379 level.intermissiontime = 0;
380 ClientEndServerFrames();
381
382 /* clear some things before going to next level */
383 for (i = 0; i < maxclients->value; i++) {
384 ent = g_edicts + 1 + i;
385 if (!ent->inuse)
386 continue;
387 if (ent->health > ent->client->pers.max_health)
388 ent->health = ent->client->pers.max_health;
389 }
390
391 }
392
393 /*
394 * ================ G_RunFrame
395 *
396 * Advances the world by 0.1 seconds ================
397 */
398 void
G_RunFrame(void)399 G_RunFrame(void)
400 {
401 int i;
402 edict_t *ent;
403
404 #ifdef GAME_MOD
405 Blinky_BeginRunFrame();
406 #endif
407
408 level.framenum++;
409 level.time = level.framenum * FRAMETIME;
410
411 /* choose a client for monsters to target this frame */
412 AI_SetSightClient();
413
414 /* exit intermissions */
415
416 if (level.exitintermission) {
417 ExitLevel();
418 return;
419 }
420 //
421 /* treat each object in turn */
422 /* even the world gets a chance to think */
423 //
424 ent = &g_edicts[0];
425 for (i = 0; i < globals.num_edicts; i++, ent++) {
426 if (!ent->inuse)
427 continue;
428
429 level.current_entity = ent;
430
431 VectorCopy(ent->s.origin, ent->s.old_origin);
432
433 /* if the ground entity moved, make sure we are still on it */
434 if ((ent->groundentity) && (ent->groundentity->linkcount != ent->groundentity_linkcount)) {
435 ent->groundentity = NULL;
436 if (!(ent->flags & (FL_SWIM | FL_FLY)) && (ent->svflags & SVF_MONSTER)) {
437 M_CheckGround(ent);
438 }
439 }
440 if (i > 0 && i <= maxclients->value) {
441 ClientBeginServerFrame(ent);
442 #ifdef WITH_ACEBOT
443 /* continue; */
444 #else
445 continue;
446 #endif
447 }
448 G_RunEntity(ent);
449 }
450
451 /* see if it is time to end a deathmatch */
452 CheckDMRules();
453
454 /* see if needpass needs updated */
455 CheckNeedPass();
456
457 /* build the playerstate_t structures for all players */
458 ClientEndServerFrames();
459 }
460