951 lines
21 KiB
C++
951 lines
21 KiB
C++
|
|
#include "g_local.h"
|
|
#include "g_functions.h"
|
|
#include "Q3_Interface.h"
|
|
#include "g_nav.h"
|
|
#include "boltOns.h"
|
|
#include "g_roff.h"
|
|
#include "g_navigator.h"
|
|
#include "anims.h"
|
|
|
|
extern CNavigator navigator;
|
|
|
|
#define STEPSIZE 18
|
|
|
|
level_locals_t level;
|
|
game_import_t gi;
|
|
game_export_t globals;
|
|
|
|
gentity_t *g_entities;
|
|
gentity_t *playerEnt = &g_entities[0];
|
|
|
|
cvar_t *g_speed;
|
|
cvar_t *g_gravity;
|
|
cvar_t *g_sex;
|
|
cvar_t *g_spskill;
|
|
cvar_t *g_cheats;
|
|
cvar_t *g_developer;
|
|
cvar_t *g_timescale;
|
|
cvar_t *g_knockback;
|
|
cvar_t *g_inactivity;
|
|
cvar_t *g_debugMove;
|
|
cvar_t *g_debugDamage;
|
|
cvar_t *g_weaponRespawn;
|
|
cvar_t *g_subtitles;
|
|
cvar_t *g_language;
|
|
cvar_t *g_ICARUSDebug;
|
|
cvar_t *com_buildScript;
|
|
cvar_t *g_skippingcin;
|
|
cvar_t *g_virtualVoyager;
|
|
|
|
qboolean stop_icarus = qfalse;
|
|
|
|
extern char *G_GetLocationForEnt( gentity_t *ent );
|
|
void G_RunFrame (int levelTime);
|
|
void CG_LoadInterface (void);
|
|
void ClearNPCGlobals( void );
|
|
|
|
void ClearPlayerAlertEvents( void );
|
|
#define ALERT_CLEAR_TIME 200
|
|
int eventClearTime = 0;
|
|
|
|
int form_shot_traces = 0;
|
|
int form_updateseg_traces = 0;
|
|
int form_leaderseg_traces = 0;
|
|
int form_closestSP_traces = 0;
|
|
int form_clearpath_traces = 0;
|
|
|
|
extern void NPC_ShowDebugInfo (void);
|
|
extern int ffireForgivenessTimer;
|
|
extern int ffireLevel;
|
|
extern int killPlayerTimer;
|
|
extern int loadBrigTimer;
|
|
extern const int FFIRE_LEVEL_RETALIATION;
|
|
int teamCount[TEAM_NUM_TEAMS];
|
|
int teamLastEnemyTime[TEAM_NUM_TEAMS];
|
|
int teamEnemyCount[TEAM_NUM_TEAMS];
|
|
/*
|
|
================
|
|
G_FindTeams
|
|
|
|
Chain together all entities with a matching team field.
|
|
Entity teams are used for item groups and multi-entity mover groups.
|
|
|
|
All but the first will have the FL_TEAMSLAVE flag set and teammaster field set
|
|
All but the last will have the teamchain field set to the next one
|
|
================
|
|
*/
|
|
void G_FindTeams( void ) {
|
|
gentity_t *e, *e2;
|
|
int i, j;
|
|
int c, c2;
|
|
|
|
c = 0;
|
|
c2 = 0;
|
|
for ( i=1, e=g_entities,i ; i < globals.num_entities ; i++,e++ ){
|
|
if (!e->inuse)
|
|
continue;
|
|
if (!e->team)
|
|
continue;
|
|
if (e->flags & FL_TEAMSLAVE)
|
|
continue;
|
|
e->teammaster = e;
|
|
c++;
|
|
c2++;
|
|
for (j=i+1, e2=e+1 ; j < globals.num_entities ; j++,e2++)
|
|
{
|
|
if (!e2->inuse)
|
|
continue;
|
|
if (!e2->team)
|
|
continue;
|
|
if (e2->flags & FL_TEAMSLAVE)
|
|
continue;
|
|
if (!strcmp(e->team, e2->team))
|
|
{
|
|
c2++;
|
|
e2->teamchain = e->teamchain;
|
|
e->teamchain = e2;
|
|
e2->teammaster = e;
|
|
e2->flags |= FL_TEAMSLAVE;
|
|
|
|
// make sure that targets only point at the master
|
|
if ( e2->targetname ) {
|
|
e->targetname = e2->targetname;
|
|
e2->targetname = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gi.Printf ("%i teams with %i entities\n", c, c2);
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
G_InitCvars
|
|
|
|
============
|
|
*/
|
|
void G_InitCvars( void ) {
|
|
// don't override the cheat state set by the system
|
|
g_cheats = gi.cvar ("sv_cheats", "", 0);
|
|
g_developer = gi.cvar ("developer", "", 0);
|
|
|
|
// noset vars
|
|
gi.cvar( "gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_ROM );
|
|
gi.cvar( "gamedate", __DATE__ , CVAR_ROM );
|
|
g_skippingcin = gi.cvar ("skippingCinematic", "0", CVAR_ROM);
|
|
|
|
// latched vars
|
|
|
|
// change anytime vars
|
|
g_speed = gi.cvar( "g_speed", "250", 0 );
|
|
g_gravity = gi.cvar( "g_gravity", "800", CVAR_USERINFO ); //using userinfo as savegame flag
|
|
g_sex = gi.cvar ("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE );
|
|
g_spskill = gi.cvar ("g_spskill", "0", CVAR_ARCHIVE | CVAR_USERINFO); //using userinfo as savegame flag
|
|
g_knockback = gi.cvar( "g_knockback", "1000", 0 );
|
|
g_inactivity = gi.cvar ("g_inactivity", "0", 0);
|
|
g_debugMove = gi.cvar ("g_debugMove", "0", 0);
|
|
g_debugDamage = gi.cvar ("g_debugDamage", "0", 0);
|
|
g_ICARUSDebug = gi.cvar( "g_ICARUSDebug", "0", 0 );
|
|
g_timescale = gi.cvar( "timescale", "1", 0 );
|
|
g_virtualVoyager = gi.cvar( "cg_virtualvoyager", "0", CVAR_NORESTART );
|
|
|
|
g_subtitles = gi.cvar ("g_subtitles", "2", CVAR_ARCHIVE);
|
|
g_language = gi.cvar ("g_language", "", CVAR_ARCHIVE | CVAR_NORESTART);
|
|
com_buildScript = gi.cvar ("com_buildscript", "0", 0);
|
|
}
|
|
|
|
/*
|
|
============
|
|
InitGame
|
|
|
|
============
|
|
*/
|
|
extern void Q3_SetPrecacheFile (const char *file); //q3_interface
|
|
|
|
// I'm just declaring a global here which I need to get at in NAV_GenerateSquadPaths for deciding if pre-calc'd
|
|
// data is valid, and this saves changing the proto of G_SpawnEntitiesFromString() to include a checksum param which
|
|
// may get changed anyway if a new nav system is ever used. This way saves messing with g_local.h each time -slc
|
|
int giMapChecksum;
|
|
SavedGameJustLoaded_e g_eSavedGameJustLoaded;
|
|
qboolean g_qbLoadTransition = qfalse;
|
|
void InitGame( const char *mapname, const char *spawntarget, int checkSum, const char *entities, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition )
|
|
{
|
|
int i;
|
|
|
|
giMapChecksum = checkSum;
|
|
g_eSavedGameJustLoaded = eSavedGameJustLoaded;
|
|
g_qbLoadTransition = qbLoadTransition;
|
|
|
|
gi.Printf ("------- Game Initialization -------\n");
|
|
gi.Printf ("gamename: %s\n", GAMEVERSION);
|
|
gi.Printf ("gamedate: %s\n", __DATE__);
|
|
|
|
srand( randomSeed );
|
|
|
|
G_InitCvars();
|
|
|
|
G_InitMemory();
|
|
|
|
// set some level globals
|
|
memset( &level, 0, sizeof( level ) );
|
|
level.time = levelTime;
|
|
level.globalTime = globalTime;
|
|
Q_strncpyz( level.mapname, mapname, sizeof(level.mapname) );
|
|
if ( spawntarget != NULL && spawntarget[0] )
|
|
{
|
|
Q_strncpyz( level.spawntarget, spawntarget, sizeof(level.spawntarget) );
|
|
}
|
|
else
|
|
{
|
|
level.spawntarget[0] = 0;
|
|
}
|
|
|
|
|
|
G_InitWorldSession();
|
|
|
|
// initialize all entities for this game
|
|
g_entities = (struct gentity_s *) G_Alloc( MAX_GENTITIES * sizeof(g_entities[0]) );
|
|
memset( g_entities, 0, MAX_GENTITIES * sizeof(g_entities[0]) );
|
|
globals.gentities = g_entities;
|
|
|
|
// initialize all clients for this game
|
|
level.maxclients = 1;
|
|
level.clients = (struct gclient_s *) G_Alloc( level.maxclients * sizeof(level.clients[0]) );
|
|
|
|
// set client fields on player
|
|
g_entities[0].client = level.clients;
|
|
|
|
// always leave room for the max number of clients,
|
|
// even if they aren't all used, so numbers inside that
|
|
// range are NEVER anything but clients
|
|
globals.num_entities = MAX_CLIENTS;
|
|
|
|
//Load bolt-on list
|
|
G_LoadBoltOns();
|
|
|
|
//Sets intial squadpoint data
|
|
G_SquadPathsInit();
|
|
|
|
//Set up NPC init data
|
|
NPC_InitGame();
|
|
|
|
TIMER_Clear();
|
|
|
|
//
|
|
//ICARUS INIT START
|
|
|
|
gi.Printf("------ ICARUS Initialization ------\n");
|
|
gi.Printf("ICARUS version : %1.2f\n", ICARUS_VERSION);
|
|
|
|
Interface_Init( &interface_export );
|
|
ICARUS_Init();
|
|
|
|
gi.Printf ("-----------------------------------\n");
|
|
|
|
//ICARUS INIT END
|
|
//
|
|
|
|
IT_LoadItemParms ();
|
|
IS_LoadInfoItemParms();
|
|
|
|
OBJ_LoadObjectives();
|
|
|
|
OBJ_LoadTactical();
|
|
|
|
ClearRegisteredItems();
|
|
|
|
navCalculatePaths = ( navigator.Load( mapname, checkSum ) == qfalse );
|
|
|
|
// parse the key/value pairs and spawn gentities
|
|
G_SpawnEntitiesFromString( entities );
|
|
|
|
// general initialization
|
|
G_FindTeams();
|
|
|
|
SaveRegisteredItems();
|
|
|
|
gi.Printf ("-----------------------------------\n");
|
|
|
|
//randomize the rand functions
|
|
byte num_calls = (byte)timeGetTime();
|
|
|
|
for(i = 0; i < (int)num_calls; i++)
|
|
{
|
|
rand();
|
|
}
|
|
|
|
//Calculate all paths
|
|
if ( navCalculatePaths )
|
|
{
|
|
NAV_CalculatePaths( mapname, checkSum );
|
|
|
|
navigator.CalculatePaths();
|
|
|
|
if ( navigator.Save( mapname, checkSum ) == qfalse )
|
|
{
|
|
gi.Printf("Unable to save navigations data for map \"%s\" (checksum:%d)\n", mapname, checkSum );
|
|
}
|
|
}
|
|
|
|
//Precache the auto-built dialogue .pre file
|
|
if (strrchr(mapname,'/')) {
|
|
Q3_SetPrecacheFile (va("%s/behaved",strrchr(mapname,'/')));
|
|
} else {
|
|
Q3_SetPrecacheFile (va("%s/behaved",mapname));
|
|
}
|
|
|
|
//Precache the designer-made extras .pre file
|
|
if (strrchr(mapname,'/')) {
|
|
Q3_SetPrecacheFile (va("%s/extra",strrchr(mapname,'/')));
|
|
} else {
|
|
Q3_SetPrecacheFile (va("%s/extra",mapname));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
ShutdownGame
|
|
=================
|
|
*/
|
|
void ShutdownGame( void ) {
|
|
gi.Printf ("==== ShutdownGame ====\n");
|
|
|
|
gi.Printf ("... ICARUS_Shutdown\n");
|
|
ICARUS_Shutdown (); //Shut ICARUS down
|
|
|
|
gi.Printf ("... Reference Tags Cleared\n");
|
|
TAG_Init(); //Clear the reference tags
|
|
|
|
gi.Printf ("... Navigation Data Cleared\n");
|
|
NAV_Shutdown();
|
|
|
|
// write all the client session data so we can get it back
|
|
G_WriteSessionData();
|
|
}
|
|
|
|
|
|
|
|
//===================================================================
|
|
|
|
static void G_Cvar_Create( const char *var_name, const char *var_value, int flags ) {
|
|
gi.cvar( var_name, var_value, flags );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
GetGameAPI
|
|
|
|
Returns a pointer to the structure with all entry points
|
|
and global variables
|
|
=================
|
|
*/
|
|
game_export_t *GetGameAPI( game_import_t *import ) {
|
|
gameinfo_import_t gameinfo_import;
|
|
|
|
gi = *import;
|
|
|
|
globals.apiversion = GAME_API_VERSION;
|
|
globals.Init = InitGame;
|
|
globals.Shutdown = ShutdownGame;
|
|
|
|
globals.WriteLevel = WriteLevel;
|
|
globals.ReadLevel = ReadLevel;
|
|
globals.GameAllowedToSaveHere = GameAllowedToSaveHere;
|
|
|
|
globals.ClientThink = ClientThink;
|
|
globals.ClientConnect = ClientConnect;
|
|
globals.ClientUserinfoChanged = ClientUserinfoChanged;
|
|
globals.ClientDisconnect = ClientDisconnect;
|
|
globals.ClientBegin = ClientBegin;
|
|
globals.ClientCommand = ClientCommand;
|
|
|
|
globals.RunFrame = G_RunFrame;
|
|
|
|
globals.ConsoleCommand = ConsoleCommand;
|
|
|
|
globals.gentitySize = sizeof(gentity_t);
|
|
|
|
gameinfo_import.FS_FOpenFile = gi.FS_FOpenFile;
|
|
gameinfo_import.FS_Read = gi.FS_Read;
|
|
gameinfo_import.FS_FCloseFile = gi.FS_FCloseFile;
|
|
gameinfo_import.Cvar_Set = gi.cvar_set;
|
|
gameinfo_import.Cvar_VariableStringBuffer = gi.Cvar_VariableStringBuffer;
|
|
gameinfo_import.Cvar_Create = G_Cvar_Create;
|
|
GI_Init( &gameinfo_import );
|
|
|
|
return &globals;
|
|
}
|
|
|
|
void QDECL G_Error( const char *fmt, ... ) {
|
|
va_list argptr;
|
|
char text[1024];
|
|
|
|
va_start (argptr, fmt);
|
|
vsprintf (text, fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
gi.Error( ERR_DROP, "%s", text);
|
|
}
|
|
|
|
#ifndef GAME_HARD_LINKED
|
|
// this is only here so the functions in q_shared.c and bg_*.c can link
|
|
|
|
/*
|
|
-------------------------
|
|
Com_Error
|
|
-------------------------
|
|
*/
|
|
|
|
void Com_Error ( int level, const char *error, ... ) {
|
|
va_list argptr;
|
|
char text[1024];
|
|
|
|
va_start (argptr, error);
|
|
vsprintf (text, error, argptr);
|
|
va_end (argptr);
|
|
|
|
gi.Error( level, "%s", text);
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
Com_Printf
|
|
-------------------------
|
|
*/
|
|
|
|
void Com_Printf( const char *msg, ... ) {
|
|
va_list argptr;
|
|
char text[1024];
|
|
|
|
va_start (argptr, msg);
|
|
vsprintf (text, msg, argptr);
|
|
va_end (argptr);
|
|
|
|
gi.Printf ("%s", text);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
========================================================================
|
|
|
|
MAP CHANGING
|
|
|
|
========================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
========================================================================
|
|
|
|
FUNCTIONS CALLED EVERY FRAME
|
|
|
|
========================================================================
|
|
*/
|
|
|
|
void G_CheckTasksCompleted (gentity_t *ent)
|
|
{
|
|
if ( Q3_TaskIDPending( ent, TID_CHAN_VOICE ) )
|
|
{
|
|
if ( !gi.S_Override[ent->s.number] )
|
|
{//not playing a voice sound
|
|
//return task_complete
|
|
Q3_TaskIDComplete( ent, TID_CHAN_VOICE );
|
|
}
|
|
}
|
|
|
|
if ( Q3_TaskIDPending( ent, TID_LOCATION ) )
|
|
{
|
|
char *currentLoc = G_GetLocationForEnt( ent );
|
|
|
|
if ( currentLoc && currentLoc[0] && Q_stricmp( ent->message, currentLoc ) == 0 )
|
|
{//we're in the desired location
|
|
Q3_TaskIDComplete( ent, TID_LOCATION );
|
|
}
|
|
//FIXME: else see if were in other trigger_locations?
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
G_RunThink
|
|
|
|
Runs thinking code for this frame if necessary
|
|
=============
|
|
*/
|
|
void G_RunThink (gentity_t *ent)
|
|
{
|
|
float thinktime;
|
|
|
|
/*
|
|
if ( ent->NPC == NULL )
|
|
{
|
|
if ( ent->taskManager && !stop_icarus )
|
|
{
|
|
ent->taskManager->Update( );
|
|
}
|
|
}
|
|
*/
|
|
|
|
thinktime = ent->nextthink;
|
|
if ( thinktime <= 0 )
|
|
{
|
|
goto runicarus;
|
|
}
|
|
|
|
if ( thinktime > level.time )
|
|
{
|
|
goto runicarus;
|
|
}
|
|
|
|
ent->nextthink = 0;
|
|
if ( ent->e_ThinkFunc == thinkF_NULL ) // actually you don't need this if I check for it in the next function -slc
|
|
{
|
|
//gi.Error ( "NULL ent->think");
|
|
goto runicarus;
|
|
}
|
|
|
|
GEntity_ThinkFunc( ent ); // ent->think (ent);
|
|
|
|
runicarus:
|
|
if ( ent->inuse ) // GEntity_ThinkFunc( ent ) can have freed up this ent if it was a type flier_child (stasis1 crash)
|
|
{
|
|
if ( ent->NPC == NULL )
|
|
{
|
|
if ( ent->taskManager && !stop_icarus )
|
|
{
|
|
ent->taskManager->Update( );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
G_Animate
|
|
-------------------------
|
|
*/
|
|
|
|
void G_Animate ( gentity_t *self )
|
|
{
|
|
if ( self->s.frame == self->endFrame )
|
|
{
|
|
if ( self->svFlags & SVF_ANIMATING )
|
|
{
|
|
if ( self->loopAnim )
|
|
{
|
|
self->s.frame = self->startFrame;
|
|
}
|
|
else
|
|
{
|
|
self->svFlags &= ~SVF_ANIMATING;
|
|
}
|
|
|
|
//Finished sequence - FIXME: only do this once even on looping anims?
|
|
Q3_TaskIDComplete( self, TID_ANIM_BOTH );
|
|
}
|
|
return;
|
|
}
|
|
|
|
self->svFlags |= SVF_ANIMATING;
|
|
|
|
if ( self->startFrame < self->endFrame )
|
|
{
|
|
if ( self->s.frame < self->startFrame || self->s.frame > self->endFrame )
|
|
{
|
|
self->s.frame = self->startFrame;
|
|
}
|
|
else
|
|
{
|
|
self->s.frame++;
|
|
}
|
|
}
|
|
else if ( self->startFrame > self->endFrame )
|
|
{
|
|
if ( self->s.frame > self->startFrame || self->s.frame < self->endFrame )
|
|
{
|
|
self->s.frame = self->startFrame;
|
|
}
|
|
else
|
|
{
|
|
self->s.frame--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self->s.frame = self->endFrame;
|
|
}
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
G_AnimateBoltOn
|
|
-------------------------
|
|
*/
|
|
|
|
void G_AnimateBoltOn ( boltOnInfo_t *boltOn )
|
|
{
|
|
if ( boltOn->frame == boltOn->endFrame )
|
|
{
|
|
if ( boltOn->loopAnim )
|
|
{
|
|
boltOn->frame = boltOn->startFrame;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ( boltOn->startFrame < boltOn->endFrame )
|
|
{
|
|
if ( boltOn->frame < boltOn->startFrame || boltOn->frame > boltOn->endFrame )
|
|
{
|
|
boltOn->frame = boltOn->startFrame;
|
|
}
|
|
else
|
|
{
|
|
boltOn->frame++;
|
|
}
|
|
}
|
|
else if ( boltOn->startFrame > boltOn->endFrame )
|
|
{
|
|
if ( boltOn->frame > boltOn->startFrame || boltOn->frame < boltOn->endFrame )
|
|
{
|
|
boltOn->frame = boltOn->startFrame;
|
|
}
|
|
else
|
|
{
|
|
boltOn->frame--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
boltOn->frame = boltOn->endFrame;
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
G_AnimateBoltOns
|
|
-------------------------
|
|
*/
|
|
}
|
|
void G_AnimateBoltOns (gentity_t *self)
|
|
{
|
|
//Go through all my boltOns and animate them if needbe
|
|
if ( !self->client )
|
|
{
|
|
if ( self->boltOn.index >= 0 && self->boltOn.index < numBoltOns )
|
|
{
|
|
G_AnimateBoltOn( &self->boltOn );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( int i = 0; i < MAX_BOLT_ONS; i++ )
|
|
{
|
|
if ( self->client->renderInfo.boltOns[i].index >= 0 && self->client->renderInfo.boltOns[i].index < numBoltOns )
|
|
{
|
|
G_AnimateBoltOn( &self->client->renderInfo.boltOns[i] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
ResetTeamCounters
|
|
-------------------------
|
|
*/
|
|
|
|
void ResetTeamCounters( void )
|
|
{
|
|
//clear team enemy counters
|
|
for ( int team = TEAM_FREE; team < TEAM_NUM_TEAMS; team++ )
|
|
{
|
|
teamEnemyCount[team] = 0;
|
|
teamCount[team] = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
UpdateTeamCounters
|
|
-------------------------
|
|
*/
|
|
|
|
void UpdateTeamCounters( gentity_t *ent )
|
|
{
|
|
if ( !ent->NPC )
|
|
{
|
|
return;
|
|
}
|
|
if ( !ent->client )
|
|
{
|
|
return;
|
|
}
|
|
if ( ent->health <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
if ( (ent->s.eFlags&EF_NODRAW) )
|
|
{
|
|
return;
|
|
}
|
|
if ( ent->client->playerTeam == TEAM_FREE )
|
|
{
|
|
return;
|
|
}
|
|
//this is an NPC who is alive and visible and is on a specific team
|
|
|
|
teamCount[ent->client->playerTeam]++;
|
|
if ( !ent->enemy )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//ent has an enemy
|
|
if ( !ent->enemy->client )
|
|
{//enemy is a normal ent
|
|
if ( ent->noDamageTeam == ent->client->playerTeam )
|
|
{//it's on my team, don't count it as an enemy
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{//enemy is another NPC/player
|
|
if ( ent->enemy->client->playerTeam == ent->client->playerTeam)
|
|
{//enemy is on the same team, don't count it as an enemy
|
|
return;
|
|
}
|
|
}
|
|
|
|
//ent's enemy is not on the same team
|
|
teamLastEnemyTime[ent->client->playerTeam] = level.time;
|
|
teamEnemyCount[ent->client->playerTeam]++;
|
|
}
|
|
|
|
extern void NPC_SetAnim(gentity_t *ent,int type,int anim,int priority);
|
|
extern void G_MakeTeamVulnerable( void );
|
|
void G_CheckEndLevelTimers( gentity_t *ent )
|
|
{
|
|
if ( killPlayerTimer && level.time > killPlayerTimer )
|
|
{
|
|
killPlayerTimer = 0;
|
|
ent->health = 0;
|
|
if ( ent->client && ent->client->ps.stats[STAT_HEALTH] > 0 )
|
|
{
|
|
//simulate death
|
|
ent->client->ps.stats[STAT_HEALTH] = 0;
|
|
//debounce respawn time
|
|
ent->client->respawnTime = level.time + 2000;
|
|
//play the "what have I done?!" anim
|
|
NPC_SetAnim( ent, SETANIM_BOTH, BOTH_GUILT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
|
/*
|
|
NPC_SetAnim( ent, SETANIM_TORSO, BOTH_SIT2TO1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
|
NPC_SetAnim( ent, SETANIM_LEGS, LEGS_KNEELDOWN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
|
*/
|
|
ent->client->ps.torsoAnimTimer = -1;
|
|
ent->client->ps.legsAnimTimer = -1;
|
|
//look at yourself
|
|
ent->client->ps.stats[STAT_DEAD_YAW] = ent->client->ps.viewangles[YAW]+180;
|
|
//stop all scripts
|
|
if (Q_stricmpn(level.mapname,"_holo",5)) {
|
|
stop_icarus = qtrue;
|
|
}
|
|
//make the team killable
|
|
G_MakeTeamVulnerable();
|
|
}
|
|
}
|
|
if ( loadBrigTimer && level.time > loadBrigTimer )
|
|
{
|
|
gentity_t *brigent = NULL;
|
|
|
|
loadBrigTimer = 0;
|
|
|
|
while( (brigent = G_Find(brigent, FOFS(classname), "target_level_change" )) != NULL )
|
|
{
|
|
if ( Q_stricmp("_brig", brigent->message) == 0 )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( brigent )
|
|
{
|
|
GEntity_UseFunc( brigent, ent, ent );
|
|
}
|
|
else
|
|
{//somehow it was lost, do a manual load
|
|
gi.SendConsoleCommand( "wait;maptransition _brig\n" );
|
|
}
|
|
|
|
}
|
|
}
|
|
/*
|
|
================
|
|
G_RunFrame
|
|
|
|
Advances the non-player objects in the world
|
|
================
|
|
*/
|
|
void G_RunFrame( int levelTime ) {
|
|
int i;
|
|
gentity_t *ent;
|
|
int msec;
|
|
|
|
level.framenum++;
|
|
level.previousTime = level.time;
|
|
level.time = levelTime;
|
|
msec = level.time - level.previousTime;
|
|
|
|
ResetTeamCounters();
|
|
|
|
//remember last waypoint, clear current one
|
|
for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++)
|
|
{
|
|
if ( ent->waypoint != WAYPOINT_NONE )
|
|
{
|
|
ent->lastWaypoint = ent->waypoint;
|
|
ent->waypoint = WAYPOINT_NONE;
|
|
}
|
|
}
|
|
|
|
//Look to clear out old events
|
|
if ( eventClearTime < level.time )
|
|
{
|
|
ClearPlayerAlertEvents();
|
|
eventClearTime = level.time + ALERT_CLEAR_TIME;
|
|
}
|
|
|
|
//Run the frame for all entities
|
|
for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++)
|
|
{
|
|
if ( !ent->inuse )
|
|
continue;
|
|
|
|
// clear events that are too old
|
|
if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) {
|
|
if ( ent->s.event ) {
|
|
ent->s.event = 0; // &= EV_EVENT_BITS;
|
|
if ( ent->client ) {
|
|
ent->client->ps.externalEvent = 0;
|
|
}
|
|
}
|
|
if ( ent->freeAfterEvent ) {
|
|
// tempEntities or dropped items completely go away after their event
|
|
G_FreeEntity( ent );
|
|
continue;
|
|
} else if ( ent->unlinkAfterEvent ) {
|
|
// items that will respawn will hide themselves after their pickup event
|
|
ent->unlinkAfterEvent = qfalse;
|
|
gi.unlinkentity( ent );
|
|
}
|
|
}
|
|
|
|
// temporary entities don't think
|
|
if ( ent->freeAfterEvent )
|
|
continue;
|
|
|
|
G_CheckTasksCompleted(ent);
|
|
|
|
G_Roff( ent );
|
|
|
|
if( !ent->client )
|
|
{
|
|
if ( !(ent->svFlags & SVF_SELF_ANIMATING) )
|
|
{//FIXME: make sure this is done only for models with frames?
|
|
//Or just flag as animating?
|
|
if ( ent->s.eFlags & EF_ANIM_ONCE )
|
|
{
|
|
ent->s.frame++;
|
|
}
|
|
else if ( !(ent->s.eFlags & EF_ANIM_ALLFAST) )
|
|
{
|
|
G_Animate( ent );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
G_AnimateBoltOns( ent );
|
|
}
|
|
|
|
if ( ent->s.eType == ET_MISSILE )
|
|
{
|
|
G_RunMissile( ent );
|
|
continue;
|
|
}
|
|
|
|
if ( ent->s.eType == ET_ITEM )
|
|
{
|
|
G_RunItem( ent );
|
|
continue;
|
|
}
|
|
|
|
if ( ent->s.eType == ET_MOVER )
|
|
{
|
|
G_RunMover( ent );
|
|
continue;
|
|
}
|
|
|
|
//The player
|
|
if ( i == 0 )
|
|
{
|
|
G_CheckEndLevelTimers( ent );
|
|
//Recalculate the nearest waypoint for the coming NPC updates
|
|
NAV_FindPlayerWaypoint();
|
|
|
|
if( ent->taskManager && !stop_icarus )
|
|
{
|
|
ent->taskManager->Update();
|
|
}
|
|
|
|
continue; // players are ucmd driven
|
|
}
|
|
|
|
G_RunThink( ent ); // be aware that ent may be free after returning from here, at least one func frees them
|
|
ClearNPCGlobals(); // but these 2 funcs are ok
|
|
UpdateTeamCounters( ent ); // to call anyway on a freed ent.
|
|
}
|
|
|
|
// perform final fixups on the player
|
|
ent = &g_entities[0];
|
|
if ( ent->inuse ) {
|
|
ClientEndFrame( ent );
|
|
}
|
|
|
|
//DEBUG STUFF
|
|
NAV_ShowDebugInfo();
|
|
NPC_ShowDebugInfo();
|
|
|
|
//handle the ffire counter
|
|
if ( ffireLevel < FFIRE_LEVEL_RETALIATION )
|
|
{//if we haven't reached the retaliation point, clear the counter
|
|
if ( level.time - teamLastEnemyTime[TEAM_STARFLEET] < 10000 ||//teammates have had an enemy in the last 10 seconds
|
|
level.time - ffireForgivenessTimer > 120000 )//Haven't shot your teammates in the past 2 minutes
|
|
{
|
|
//reset friendly fire counter
|
|
ffireLevel = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
extern qboolean player_locked;
|
|
|
|
void G_LoadSave_WriteMiscData(void)
|
|
{
|
|
gi.AppendToSaveGame('LCKD', &player_locked, sizeof(player_locked));
|
|
}
|
|
|
|
|
|
|
|
void G_LoadSave_ReadMiscData(void)
|
|
{
|
|
gi.ReadFromSaveGame('LCKD', &player_locked, sizeof(player_locked));
|
|
}
|
|
|