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