File: game\g_main.c

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