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