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