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