#include "g_local.h" #include "voice_bitch.h" #include "voice_punk.h" game_locals_t game; level_locals_t level; game_import_t gi; game_export_t globals; spawn_temp_t st; int sm_meat_index; int snd_fry; int meansOfDeath; edict_t *g_edicts; cast_memory_t *g_cast_memory; cast_group_t *g_cast_groups; int num_object_bounds=0; object_bounds_t *g_objbnds[MAX_OBJECT_BOUNDS]; cvar_t *deathmatch; // RAFAEL // cvar_t *marines; cvar_t *coop; cvar_t *dmflags; cvar_t *skill; cvar_t *fraglimit; cvar_t *timelimit; cvar_t *cashlimit; cvar_t *password; cvar_t *maxclients; cvar_t *maxentities; cvar_t *g_select_empty; cvar_t *dedicated; cvar_t *maxrate; cvar_t *filterban; cvar_t *sv_maxvelocity; cvar_t *sv_gravity; cvar_t *sv_rollspeed; cvar_t *sv_rollangle; cvar_t *gun_x; cvar_t *gun_y; cvar_t *gun_z; cvar_t *run_pitch; cvar_t *run_roll; cvar_t *bob_up; cvar_t *bob_pitch; cvar_t *bob_roll; cvar_t *sv_cheats; cvar_t *flood_msgs; cvar_t *flood_persecond; cvar_t *flood_waitdelay; // Ridah, new cvar's cvar_t *developer; cvar_t *ai_debug_memory; cvar_t *g_vehicle_test; cvar_t *dm_locational_damage; cvar_t *showlights; cvar_t *r_directional_lighting; cvar_t *cl_captions; cvar_t *sv_runscale; // 2.0 = double speed, 0.0 = zero movement cvar_t *burn_enabled; cvar_t *burn_size; cvar_t *burn_intensity; cvar_t *burn_r; cvar_t *burn_g; cvar_t *burn_b; cvar_t *timescale; cvar_t *teamplay; cvar_t *g_cashspawndelay; cvar_t *cl_parental_lock; cvar_t *cl_parental_override; cvar_t *dm_realmode; cvar_t *g_mapcycle_file; // Ridah, done. void SpawnEntities (char *mapname, char *entities, char *spawnpoint); void ClientThink (edict_t *ent, usercmd_t *cmd); qboolean ClientConnect (edict_t *ent, char *userinfo); void ClientUserinfoChanged (edict_t *ent, char *userinfo); void ClientDisconnect (edict_t *ent); void ClientBegin (edict_t *ent); void ClientCommand (edict_t *ent); void RunEntity (edict_t *ent); void WriteGame (char *filename, qboolean autosave); void ReadGame (char *filename); void WriteLevel (char *filename); void ReadLevel (char *filename); void InitGame (void); void G_RunFrame (void); // JOSEPH 23-OCT-98 void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject); //=================================================================== int gameinc=0; void ShutdownGame (void) { gi.dprintf ("==== ShutdownGame ====\n"); // BEGIN: Xatrix/Ridah/Navigator/21-mar-1998 NAV_PurgeActiveNodes (level.node_data); // END: Xatrix/Ridah/Navigator/21-mar-1998 // Ridah, clear the lightpaint buffers if (num_lpbuf > 0) { int i; for (i=0; ivalue ; i++) { ent = g_edicts + 1 + i; if (!ent->inuse || !ent->client) continue; ClientEndServerFrame (ent); } } /* ================= MapCycleNext Uses maps.lst and teammaps.lst to cycle the maps during deathmatch ================= */ char *MapCycleNext( char *map ) { char *basevars[] = {"basedir", "cddir", NULL}; // consol variables that point to possible file locations cvar_t *game_dir, *base_dir; char filename[MAX_QPATH], dir[MAX_QPATH]; FILE *f; static char nextmap[MAX_QPATH]; char firstmap[MAX_QPATH]; char travmap[MAX_QPATH]; qboolean matched = false; int i; char ch; qboolean eof = false; game_dir = gi.cvar("game", "", 0); // dir, eg: .\gamedir\routes for (i=0; basevars[i]; i++) { base_dir = gi.cvar(basevars[i], ".", 0); strcpy( dir, base_dir->string); if (dir[strlen(dir)-1] != DIR_SLASH[0]) strcat( dir, DIR_SLASH); if (strlen(game_dir->string) == 0) strcat( dir, "main"); else strcat( dir, game_dir->string); // filename, eg: .\gamedir\maps.lst strcpy( filename, dir); strcat( filename, DIR_SLASH); if (g_mapcycle_file->string && strlen(g_mapcycle_file->string) > 0) strcat( filename, g_mapcycle_file->string); else if (!teamplay->value) strcat( filename, "maps.lst"); else strcat( filename, "teammaps.lst"); // try and open the file for reading f = fopen ( filename, "rb"); if (f) break; // we have a valid file } if (!f) // no valid file found return NULL; // read in the first map fscanf( f, "%s", firstmap ); strcpy( travmap, firstmap ); ch = 0; while (ch!='\n' && !feof(f)) fscanf(f, "%c", &ch); do { eof = feof(f); if (!Q_stricmp( travmap, level.mapname )) { matched = true; } if (!eof) { fscanf( f, "%s", travmap ); ch = 0; while (ch!='\n' && !feof(f)) fscanf(f, "%c", &ch); } if (matched) { if (strcmp(travmap, level.mapname) != 0 && strlen(travmap) > 1) { // take this map strcpy( nextmap, travmap ); } else { // use the firstmap strcpy( nextmap, firstmap ); } fclose(f); return nextmap; } } while (!eof); fclose(f); // no match, so return nothing return NULL; } /* ================= EndDMLevel The timelimit or fraglimit has been exceeded ================= */ void EndDMLevel (void) { edict_t *ent; char *nextmap; // stay on same level flag if ((int)dmflags->value & DF_SAME_LEVEL) { ent = G_Spawn (); ent->classname = "target_changelevel"; ent->map = level.mapname; goto done; } if (nextmap = MapCycleNext( level.mapname )) { ent = G_Spawn (); ent->classname = "target_changelevel"; ent->map = nextmap; goto done; } if (level.nextmap[0]) { // go to a specific map ent = G_Spawn (); ent->classname = "target_changelevel"; ent->map = level.nextmap; } else { // search for a changeleve ent = G_Find (NULL, FOFS(classname), "target_changelevel"); if (!ent) { // the map designer didn't include a changelevel, // so create a fake ent that goes back to the same level ent = G_Spawn (); ent->classname = "target_changelevel"; ent->map = level.mapname; } } done: if (ent && ent->map) gi.dprintf("DM changelevel: %s (time: %i secs)\n", ent->map, (int)level.time ); // Ridah, play a random music clip gi.WriteByte( svc_stufftext ); gi.WriteString( va("play world/cypress%i.wav", 2+(rand()%4)) ); gi.multicast (vec3_origin, MULTICAST_ALL); BeginIntermission (ent); } /* ================= CheckDMRules ================= */ void CheckDMRules (void) { int i; gclient_t *cl; if (level.intermissiontime) return; if (!deathmatch->value) return; if (timelimit->value) { if (level.time >= timelimit->value*60) { gi.bprintf (PRINT_HIGH, "Timelimit hit.\n"); EndDMLevel (); return; } } if (fraglimit->value) { for (i=0 ; ivalue ; i++) { cl = game.clients + i; if (!g_edicts[i+1].inuse) continue; if (cl->resp.score >= fraglimit->value) { gi.bprintf (PRINT_HIGH, "Fraglimit hit.\n"); EndDMLevel (); return; } } } if (cashlimit->value) { if ((team_cash[1] >= cashlimit->value) || (team_cash[2] >= cashlimit->value)) { gi.bprintf (PRINT_HIGH, "Cashlimit hit.\n"); EndDMLevel (); return; } } } /* ============= ExitLevel ============= */ void ExitLevel (void) { int i; edict_t *ent; char command [256]; Com_sprintf (command, sizeof(command), "gamemap \"%s\"\n", level.changemap); gi.AddCommandString (command); level.changemap = NULL; level.exitintermission = 0; level.intermissiontime = 0; ClientEndServerFrames (); // RAFAEL level.cut_scene_time = 0; level.speaktime = 0; // JOSEPH 25-FEB-99 level.cut_scene_end_count = 0; level.fadeendtime = 0; level.totalfade = 0; // END JOSEPH // JOSEPH 13-JUN-99 level.helpchange = 0; // END JOSEPH if (level.bar_lvl) { extern void Cmd_HolsterBar_f (edict_t *ent); edict_t *player; player = &g_edicts[1]; level.bar_lvl = false; Cmd_HolsterBar_f (player); } // level.pawn_time = 0; // level.pawn_exit = false; // clear some things before going to next level for (i=0 ; ivalue ; i++) { ent = g_edicts + 1 + i; if (!ent->inuse) continue; if (ent->health > ent->client->pers.max_health) ent->health = ent->client->pers.max_health; } // BEGIN: Xatrix/Ridah/19-apr-1998 // make sure Nav data isn't carried over to next level NAV_PurgeActiveNodes(level.node_data); level.node_data = NULL; // BEGIN: Xatrix/Ridah/19-apr-1998 } /* ================ G_RunFrame Advances the world by 0.1 seconds ================ */ extern int bbox_cnt; extern edict_t *mdx_bbox[]; void AI_ProcessCombat (void); void G_RunFrame (void) { int i; edict_t *ent; level.framenum++; level.time = level.framenum*FRAMETIME; // exit intermissions if (level.exitintermission) { ExitLevel (); return; } // do character sighting/memory stuff if ((maxclients->value > 1) && !(deathmatch->value)) { // coop server, do more checking here if (dedicated->value) AI_UpdateCharacterMemories( 256 ); } // Process Generic Combat AI layer AI_ProcessCombat (); // // treat each object in turn // even the world gets a chance to think // ent = &g_edicts[0]; for (i=0 ; iinuse) continue; level.current_entity = ent; VectorCopy (ent->s.origin, ent->s.old_origin); if (ent->svflags & SVF_MONSTER || ent->client) { if (ent->waterlevel > 1) { ent->onfiretime = 0; } // On fire if (ent->onfiretime < 0) { ent->onfiretime++; } else if (ent->onfiretime > 0) { vec3_t point, org, dir; int i,j; float dist; // Deathmatch flames done on client-side if ((!deathmatch->value /*|| ent->onfiretime == 1*/) && (deathmatch->value || !ent->client)) { VectorSubtract( g_edicts[1].s.origin, ent->s.origin, dir ); dist = VectorNormalize( dir ); // Ridah, spawn flames at each body part MDX_HitCheck( ent, world, world, vec3_origin, vec3_origin, vec3_origin, 0, 0, 0, 0, vec3_origin ); for (i = 0; i < bbox_cnt; i++) { // don't draw so many if the client is up close if (dist < 256) { if (random() > dist/256) continue; } VectorAdd( mdx_bbox[i]->s.origin, dir, org ); if (!deathmatch->value) { for (j=0; j<2; j++) { point[2] = (org[2] + ((rand()%18) - 6) + 6); point[1] = (org[1] + ((rand()%10) - 5)); point[0] = (org[0] + ((rand()%10) - 5)); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_SFXFIREGO); gi.WritePosition (point); if (ent->onfiretime == 1) gi.WriteByte (1.2 * 10.0); else gi.WriteByte (0.6 * 10.0); gi.multicast (point, MULTICAST_PVS); } } // just do one smoke cloud if ((ent->onfiretime == 1) && (rand()%2)) { point[2] = (org[2] + 20);// + ((rand()&31) - 16) + 20); point[1] = (org[1]);// + ((rand()%14) - 7)); point[0] = (org[0]);// + ((rand()%14) - 7)); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_SFXSMOKE); gi.WritePosition (point); // gi.WriteDir (ent->s.angles); gi.WriteByte (16 + (rand()%24)); gi.WriteByte (0); gi.multicast (point, MULTICAST_PVS); } } } if (!ent->deadflag) { edict_t *trav=NULL; float damage=1; if (!deathmatch->value) damage;// *= 3; else damage;// *= 2; T_Damage( ent, ent->onfireent, ent->onfireent, vec3_origin, ent->s.origin, vec3_origin, damage, 0, DAMAGE_NO_KNOCKBACK, MOD_FLAMETHROWER ); // make sure they are in the "catch_fire" motion if (!deathmatch->value && (ent->health > 0) && ent->cast_info.catch_fire) { ent->cast_info.catch_fire( ent, ent->onfireent ); } } ent->onfiretime--; if (ent->onfiretime <= 0) { ent->onfireent = NULL; ent->onfiretime = 0; } // JOSEPH 3-JUN-99 if (ent->health > 0 && ent->onfiretime == 0) { ent->s.model_parts[PART_GUN].invisible_objects = 0; ent->s.model_parts[PART_GUN2].invisible_objects = 0; } else { ent->s.model_parts[PART_GUN].invisible_objects = (1<<0 | 1<<1); ent->s.model_parts[PART_GUN2].invisible_objects = (1<<0 | 1<<1); } // END JOSEPH if (ent->health > 0) { // use voice tables for this? // gi.dprintf( "SOUND TODO: ARRRGGGHH!!! (on fire)\n" ); } else // dead { if (ent->onfiretime > 20) ent->onfiretime = 20; if (ent->onfiretime == 1) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BURN_TO_A_CRISP); gi.WritePosition (ent->s.origin); gi.multicast (ent->s.origin, MULTICAST_PVS); } } } } if (ent->svflags & SVF_MONSTER) { // float alpha; // Blood trail // JOSEPH 24-JAN-99 if ((!ent->deadflag) && (ent->health < ent->max_health) && (!(ent->svflags & SVF_PROP))/*&& ((ent->healtimer&3) == 2)*/) // END JOSEPH { vec3_t stop; vec3_t start; trace_t trace; VectorCopy(ent->s.origin, start); start[0] += ((rand()&15)-8); start[1] += ((rand()&15)-8); VectorCopy(start, stop); stop[2] -= 16*16; trace = gi.trace (start, NULL, NULL, stop, ent, MASK_DEADSOLID); if (trace.fraction < 1) { float rnd; rnd = (0.2 + 1.5*random()); SurfaceSpriteEffect(SFX_SPRITE_SURF_BLOOD1, (byte)(rnd * SFX_BLOOD_WIDTH), (byte)(rnd * SFX_BLOOD_HEIGHT), trace.ent, trace.endpos, trace.plane.normal); } } // Heal thy self (healspeed -1 = no heal, healspeed 0 = 1, healspeed >1 = value) if ((!ent->deadflag) && (ent->health < ent->max_health) && (ent->healspeed >= 0) && (!ent->leader || ent->cast_group != 1 || (((int)timescale->value) == 1)) // Ridah, added this or they can set timescale = 100 and followers will restore full health in second && (ent->healtimer++ > 30)) // END JOSEPH { int i, i2, baseskin; ent->healtimer = 0; if (!ent->healspeed) ent->health += 1; else ent->health += ent->healspeed; if (ent->health > ent->max_health) ent->health = ent->max_health; for (i = 0; i < MAX_MODEL_PARTS; i++) { for (i2 = 0; i2 < MAX_MODELPART_OBJECTS; i2++) { baseskin = ent->s.model_parts[i].baseskin; if (ent->s.model_parts[i].skinnum[i2] > baseskin) { if (ent->health > (ent->max_health * 0.75)) { ent->s.model_parts[i].skinnum[i2] = baseskin; } else if (ent->health > (ent->max_health * 0.5)) { if (cl_parental_lock->value && !cl_parental_override->value) ent->s.model_parts[i].skinnum[i2] = baseskin; else ent->s.model_parts[i].skinnum[i2] = baseskin + 1; } } } } } } // END JOSEPH // if the ground entity moved, make sure we are still on it if ((ent->groundentity) && (ent->groundentity->linkcount != ent->groundentity_linkcount)) { ent->groundentity = NULL; if ( !(ent->flags & (FL_SWIM|FL_FLY)) && (ent->svflags & SVF_MONSTER) ) { M_CheckGround (ent); } } if (i > 0 && i <= maxclients->value) { ClientBeginServerFrame (ent); } else { G_RunEntity (ent); // Ridah, fast walking speed if ( (ent->cast_info.aiflags & AI_FASTWALK) && (ent->svflags & SVF_MONSTER) && (ent->cast_info.currentmove) // && (ent->cast_info.currentmove->frame->aifunc == ai_run) && (ent->cast_info.currentmove->frame->dist < 20) && (!ent->enemy)) { G_RunEntity (ent); // JOSEPH 12-MAR-99 if (ent->think) ent->think(ent); // END JOSEPH } } // Ridah, update lights if using directional lighting if (!(r_directional_lighting->value) && !deathmatch->value) { if (ent->s.renderfx2 & RF2_DIR_LIGHTS) { VectorSet( ent->s.last_lighting_update_pos, -9999, -9999, -9999 ); } } else if (((ent->s.renderfx2 & RF2_DIR_LIGHTS) || (ent->client) || deathmatch->value)) { if (!level.num_light_sources) // no lights to source from, so default back to no dir lighting { ent->s.renderfx2 &= ~RF2_DIR_LIGHTS; } else { if (ent->client) ent->s.renderfx2 |= RF2_DIR_LIGHTS; // if single player, only calculate if it's visible to our player if ( (!VectorCompare(ent->s.last_lighting_update_pos, ent->s.origin)) && ( (ent->client && !deathmatch->value) || ( (VectorDistance( ent->s.origin, ent->s.last_lighting_update_pos ) > (deathmatch->value ? 128 : 64)) && ( (deathmatch->value) || (level.cut_scene_time) || ( (gi.inPVS( g_edicts[1].s.origin, ent->s.origin)) && (infront( &g_edicts[1], ent ) )))))) { UpdateDirLights( ent ); VectorCopy( ent->s.origin, ent->s.last_lighting_update_pos ); } else if (showlights->value && gi.inPVS( g_edicts[1].s.origin, ent->s.origin)) { UpdateDirLights( ent ); } } } } // see if it is time to end a deathmatch CheckDMRules (); // build the playerstate_t structures for all players ClientEndServerFrames (); }