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