Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

g_main.c (8283B)


      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 (char *mapname, char *entities, 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 (char *filename, qboolean autosave);
     83 void ReadGame (char *filename);
     84 void WriteLevel (char *filename);
     85 void ReadLevel (char *filename);
     86 void InitGame (void);
     87 void G_RunFrame (void);
     88 
     89 
     90 //===================================================================
     91 
     92 
     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 */
    110 game_export_t *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
    142 void Sys_Error (char *error, ...)
    143 {
    144 	va_list		argptr;
    145 	char		text[1024];
    146 
    147 	va_start (argptr, error);
    148 	vsprintf (text, error, argptr);
    149 	va_end (argptr);
    150 
    151 	gi.error (ERR_FATAL, "%s", text);
    152 }
    153 
    154 void Com_Printf (char *msg, ...)
    155 {
    156 	va_list		argptr;
    157 	char		text[1024];
    158 
    159 	va_start (argptr, msg);
    160 	vsprintf (text, 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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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