10419 lines
281 KiB
C++
10419 lines
281 KiB
C++
|
// Copyright (C) 2007 Id Software, Inc.
|
||
|
//
|
||
|
|
||
|
#include "precompiled.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
|
||
|
#define new DEBUG_NEW
|
||
|
#undef THIS_FILE
|
||
|
static char THIS_FILE[] = __FILE__;
|
||
|
#endif
|
||
|
|
||
|
#if !defined( _XENON ) && !defined( MONOLITHIC )
|
||
|
idCVar* idCVar::staticVars;
|
||
|
#endif
|
||
|
|
||
|
#include "../framework/Licensee.h"
|
||
|
#include "../framework/LogitechLCDSystem.h"
|
||
|
|
||
|
#include "../bse/BSEInterface.h"
|
||
|
#include "../bse/BSE_Envelope.h"
|
||
|
#include "../bse/BSE_SpawnDomains.h"
|
||
|
#include "../bse/BSE_Particle.h"
|
||
|
#include "../bse/BSE.h"
|
||
|
|
||
|
#include "roles/Inventory.h"
|
||
|
#include "roles/FireTeams.h"
|
||
|
#include "roles/ObjectiveManager.h"
|
||
|
#include "roles/Tasks.h"
|
||
|
#include "roles/WayPointManager.h"
|
||
|
#include "structures/TeamManager.h"
|
||
|
#include "rules/GameRules.h"
|
||
|
#include "Player.h"
|
||
|
#include "../framework/BuildVersion.h"
|
||
|
#include "WorldSpawn.h"
|
||
|
#include "Misc.h"
|
||
|
#include "Camera.h"
|
||
|
#include "client/ClientEffect.h"
|
||
|
#include "client/ClientMoveable.h"
|
||
|
#include "gamesys/SysCmds.h"
|
||
|
#include "Trigger.h"
|
||
|
#include "structures/DeployMask.h"
|
||
|
#include "Projectile.h"
|
||
|
#include "vehicles/VehicleSuspension.h"
|
||
|
#include "vehicles/VehicleView.h"
|
||
|
#include "vehicles/Pathing.h"
|
||
|
#include "vehicles/Transport.h"
|
||
|
#include "vehicles/VehicleWeapon.h"
|
||
|
#include "vehicles/SoundControl.h"
|
||
|
#include "../framework/declManager.h"
|
||
|
#include "../framework/KeyInput.h"
|
||
|
#include "../decllib/DeclSurfaceType.h"
|
||
|
#include "structures/DeployRequest.h"
|
||
|
#include "CommandMapInfo.h"
|
||
|
#include "demos/DemoManager.h"
|
||
|
#include "anim/Anim_FrameCommands.h"
|
||
|
#include "rules/UserGroup.h"
|
||
|
#include "rules/AdminSystem.h"
|
||
|
#include "rules/VoteManager.h"
|
||
|
#include "Atmosphere.h"
|
||
|
#include "misc/Door.h"
|
||
|
#include "Light.h"
|
||
|
#include "Sound.h"
|
||
|
#include "guis/UserInterfaceManager.h"
|
||
|
#include "guis/UserInterfaceLocal.h"
|
||
|
#include "guis/GuiSurface.h"
|
||
|
#include "../decllib/declTypeHolder.h"
|
||
|
#include "./effects/Wakes.h"
|
||
|
#include "./effects/TireTread.h"
|
||
|
#include "./effects/FootPrints.h"
|
||
|
#include "ContentMask.h"
|
||
|
#include "proficiency/StatsTracker.h"
|
||
|
// Gordon: FIXME: Fix the case of this directory
|
||
|
#include "Waypoints/LocationMarker.h"
|
||
|
|
||
|
#include "script/Script_Helper.h"
|
||
|
#include "script/Script_Program.h"
|
||
|
#include "script/ScriptEntityHelpers.h"
|
||
|
#include "script/Script_ScriptObject.h"
|
||
|
#include "script/Script_DLL.h"
|
||
|
|
||
|
#include "../framework/AdManager.h"
|
||
|
#include "../framework/GraphManager.h"
|
||
|
|
||
|
#include "../idlib/PropertiesImpl.h"
|
||
|
|
||
|
#include "../renderer/Image.h"
|
||
|
#include "../renderer/DeviceContext.h"
|
||
|
#include "../decllib/declRenderBinding.h"
|
||
|
|
||
|
#ifdef _XENON
|
||
|
#include "../xenon/live/LiveManager.h"
|
||
|
#include "../xenon/live/LiveService.h"
|
||
|
#else
|
||
|
#include "../sdnet/SDNet.h"
|
||
|
#include "../sdnet/SDNetUser.h"
|
||
|
#include "../sdnet/SDNetProfile.h"
|
||
|
#endif
|
||
|
|
||
|
#include "botai/BotThread.h"
|
||
|
#include "botai/BotThreadData.h"
|
||
|
#include "botai/Bot.h"
|
||
|
|
||
|
#include "misc/ProfileHelper.h"
|
||
|
#include "AntiLag.h"
|
||
|
|
||
|
#if !defined( _XENON ) && !defined( MONOLITHIC )
|
||
|
idSys * sys = NULL;
|
||
|
idCommon * common = NULL;
|
||
|
idCmdSystem * cmdSystem = NULL;
|
||
|
idCVarSystem * cvarSystem = NULL;
|
||
|
idFileSystem * fileSystem = NULL;
|
||
|
idNetworkSystem * networkSystem = NULL;
|
||
|
idRenderSystem * renderSystem = NULL;
|
||
|
sdDeviceContext * deviceContext = NULL;
|
||
|
idSoundSystem * soundSystem = NULL;
|
||
|
idRenderModelManager * renderModelManager = NULL;
|
||
|
idDeclManager * declManager = NULL;
|
||
|
idCollisionModelManager * collisionModelManager = NULL;
|
||
|
idAASFileManager* AASFileManager = NULL;
|
||
|
sdNetService* networkService = NULL;
|
||
|
sdAdManager* adManager = NULL;
|
||
|
sdKeyInputManager* keyInputManager = NULL;
|
||
|
sdNotificationSystem* notificationSystem = NULL;
|
||
|
sdGraphManager* graphManager = NULL;
|
||
|
#else
|
||
|
extern sdNotificationSystem* notificationSystem;
|
||
|
#endif
|
||
|
|
||
|
#if !defined( _XENON )
|
||
|
idRenderWorld * gameRenderWorld = NULL;
|
||
|
idSoundWorld * gameSoundWorld = NULL;
|
||
|
#endif
|
||
|
|
||
|
idList< surfaceProperties_t > idGameLocal::surfaceTypes;
|
||
|
|
||
|
#if !defined( _XENON ) && !defined( MONOLITHIC )
|
||
|
rvBSEManager * bse = NULL;
|
||
|
|
||
|
idCVar com_forceGenericSIMD( "com_forceGenericSIMD", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "force generic platform independent SIMD" );
|
||
|
idCVar com_timescale( "timescale", "1", CVAR_FLOAT | CVAR_SYSTEM | CVAR_NETWORKSYNC, "scales the time", 0.1f, 10.0f );
|
||
|
idCVar com_timeServer( "com_timeServer", "0", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "" );
|
||
|
#else
|
||
|
extern idCVar com_forceGenericSIMD;
|
||
|
#endif
|
||
|
#ifndef MONOLITHIC
|
||
|
idCVar bse_simple( "bse_simple", "0", CVAR_BOOL | CVAR_ARCHIVE, "simple versions of effects" );
|
||
|
#endif
|
||
|
|
||
|
static gameExport_t gameExport;
|
||
|
|
||
|
// global animation lib
|
||
|
idAnimManager animationLib;
|
||
|
|
||
|
// the rest of the engine will only reference the "game" variable, while all local aspects stay hidden
|
||
|
idGameLocal gameLocal;
|
||
|
|
||
|
#if !defined( MONOLITHIC )
|
||
|
idGame * game = &gameLocal; // statically pointed at an idGameLocal
|
||
|
#else
|
||
|
idGame * game; // statically pointed at an idGameLocal
|
||
|
#endif
|
||
|
|
||
|
#ifdef MONOLITHIC
|
||
|
#include "monolithic_dependencies.h"
|
||
|
|
||
|
volatile void ForceLink( void ) {
|
||
|
#include "monolithic_types.h"
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
GetGameAPI
|
||
|
============
|
||
|
*/
|
||
|
#if __GNUC__ >= 4
|
||
|
#pragma GCC visibility push(default)
|
||
|
#endif
|
||
|
extern "C" gameExport_t *GetGameAPI( gameImport_t *import ) {
|
||
|
|
||
|
if ( import->version == GAME_API_VERSION ) {
|
||
|
|
||
|
// set interface pointers used by the game
|
||
|
sys = import->sys;
|
||
|
common = import->common;
|
||
|
cmdSystem = import->cmdSystem;
|
||
|
cvarSystem = import->cvarSystem;
|
||
|
fileSystem = import->fileSystem;
|
||
|
networkSystem = import->networkSystem;
|
||
|
renderSystem = import->renderSystem;
|
||
|
deviceContext = import->deviceContext;
|
||
|
soundSystem = import->soundSystem;
|
||
|
renderModelManager = import->renderModelManager;
|
||
|
declManager = import->declManager;
|
||
|
collisionModelManager = import->collisionModelManager;
|
||
|
bse = import->bse;
|
||
|
#ifndef _XENON
|
||
|
networkService = import->networkService;
|
||
|
#endif
|
||
|
adManager = import->adManager;
|
||
|
keyInputManager = import->keyInputManager;
|
||
|
AASFileManager = import->AASFileManager;
|
||
|
notificationSystem = import->notificationSystem;
|
||
|
graphManager = import->graphManager;
|
||
|
|
||
|
#if !defined( _XENON ) && !defined( MONOLITHIC )
|
||
|
idDict::SetGlobalPools( import->globalKeys, import->globalValues );
|
||
|
idStr::SetStringAllocator( import->stringAllocator );
|
||
|
idWStr::SetStringAllocator( import->wideStringAllocator );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// set interface pointers used by idLib
|
||
|
idLib::sys = sys;
|
||
|
idLib::common = common;
|
||
|
idLib::cvarSystem = cvarSystem;
|
||
|
idLib::fileSystem = fileSystem;
|
||
|
|
||
|
#if defined( _XENON ) || defined( MONOLITHIC )
|
||
|
game = &gameLocal;
|
||
|
#endif
|
||
|
|
||
|
// setup export interface
|
||
|
gameExport.version = GAME_API_VERSION;
|
||
|
gameExport.game = game;
|
||
|
gameExport.gameEdit = gameEdit;
|
||
|
|
||
|
return &gameExport;
|
||
|
}
|
||
|
#if __GNUC__ >= 4
|
||
|
#pragma GCC visibility pop
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
sdPlayZone
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPlayZone::sdPlayZone
|
||
|
================
|
||
|
*/
|
||
|
sdPlayZone::sdPlayZone( const idDict& info, const idBounds& bounds ) {
|
||
|
_bounds = bounds;
|
||
|
_size = _bounds.GetMaxs() - _bounds.GetMins();
|
||
|
_priority = info.GetInt( "priority", "0" );
|
||
|
_flags = 0;
|
||
|
_target = info.GetString( "target" );
|
||
|
const char* titleName = info.GetString( "title" );
|
||
|
|
||
|
if( titleName[ 0 ] != '\0' ) {
|
||
|
_title = declHolder.declLocStrType.LocalFind( titleName, true );
|
||
|
}
|
||
|
if ( info.GetBool( "zone_commandmap" ) ) {
|
||
|
_flags |= PZF_COMMANDMAP;
|
||
|
}
|
||
|
if ( info.GetBool( "zone_playzone" ) ) {
|
||
|
_flags |= PZF_PLAYZONE;
|
||
|
}
|
||
|
if ( info.GetBool( "zone_deployment" ) ) {
|
||
|
_flags |= PZF_DEPLOYMENT;
|
||
|
}
|
||
|
if ( info.GetBool( "zone_paths" ) ) {
|
||
|
_flags |= PZF_PATH;
|
||
|
}
|
||
|
if ( info.GetBool( "zone_heightmap" ) ) {
|
||
|
_flags |= PZF_HEIGHTMAP;
|
||
|
}
|
||
|
if ( info.GetBool( "zone_vehicle_route" ) ) {
|
||
|
_flags |= PZF_VEHICLEROUTE;
|
||
|
}
|
||
|
if ( info.GetBool( "zone_world" ) ) {
|
||
|
_flags |= PZF_WORLD;
|
||
|
}
|
||
|
if ( info.GetBool( "zone_choosable" ) ) {
|
||
|
_flags |= PZF_CHOOSABLE;
|
||
|
}
|
||
|
|
||
|
_commandmapMaterial = gameLocal.declMaterialType[ info.GetString( "mtr_commandmap" ) ];
|
||
|
|
||
|
const char* heightMapName = info.GetString( "hm_heightmap" );
|
||
|
if ( *heightMapName != '\0' ) {
|
||
|
_heightMap.Init( heightMapName, bounds );
|
||
|
}
|
||
|
|
||
|
const idKeyValue* kv;
|
||
|
|
||
|
kv = NULL;
|
||
|
while ( kv = info.MatchPrefix( "path_", kv ) ) {
|
||
|
const idStr& key = kv->GetKey();
|
||
|
|
||
|
const sdDeclVehiclePath* path = gameLocal.declVehiclePathType[ kv->GetValue().c_str() ];
|
||
|
if ( !path ) {
|
||
|
gameLocal.Warning( "Invalid Path '%s' on Playzone '%s'", kv->GetValue().c_str(), info.GetString( "name" ) );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
playzonePath_t& p = _paths.Alloc();
|
||
|
p.first = key.Right( key.Length() - 5 );
|
||
|
p.second = new sdVehiclePathGrid( path, bounds );
|
||
|
}
|
||
|
|
||
|
kv = NULL;
|
||
|
while ( kv = info.MatchPrefix( "dm_", kv ) ) {
|
||
|
qhandle_t handle = gameLocal.GetDeploymentMask( kv->GetKey() );
|
||
|
if ( _masks.Num() <= handle ) {
|
||
|
_masks.SetNum( handle + 1 );
|
||
|
}
|
||
|
_masks[ handle ].Init( kv->GetValue(), sdBounds2D( _bounds ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPlayZone::GetMask
|
||
|
================
|
||
|
*/
|
||
|
const sdDeployMaskInstance* sdPlayZone::GetMask( qhandle_t handle ) const {
|
||
|
if ( handle < 0 || handle >= _masks.Num() ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if ( !_masks[ handle ].IsValid() ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return &_masks[ handle ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPlayZone::SaveMasks
|
||
|
================
|
||
|
*/
|
||
|
void sdPlayZone::SaveMasks( void ) {
|
||
|
for ( int i = 0; i < _masks.Num(); i++ ) {
|
||
|
sdDeployMaskInstance& mask = _masks[ i ];
|
||
|
if ( !mask.IsValid() ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
mask.WriteTGA();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPlayZone::GetPath
|
||
|
================
|
||
|
*/
|
||
|
sdVehiclePathGrid* sdPlayZone::GetPath( const char* name ) const {
|
||
|
for ( int i = 0; i < _paths.Num(); i++ ) {
|
||
|
if ( _paths[ i ].first.Icmp( name ) != 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
return _paths[ i ].second;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdPlayZone::sdPlayZone
|
||
|
================
|
||
|
*/
|
||
|
sdPlayZone::~sdPlayZone( void ) {
|
||
|
for ( int i = 0; i < _paths.Num(); i++ ) {
|
||
|
delete _paths[ i ].second;
|
||
|
}
|
||
|
_paths.Clear();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
sdLoggedTrace
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
extern const idEventDef EV_GetTraceBody;
|
||
|
extern const idEventDef EV_GetTraceFraction;
|
||
|
extern const idEventDef EV_GetTraceEndPos;
|
||
|
extern const idEventDef EV_GetTracePoint;
|
||
|
extern const idEventDef EV_GetTraceNormal;
|
||
|
extern const idEventDef EV_GetTraceEntity;
|
||
|
extern const idEventDef EV_GetTraceSurfaceFlags;
|
||
|
extern const idEventDef EV_GetTraceSurfaceType;
|
||
|
extern const idEventDef EV_GetTraceSurfaceColor;
|
||
|
extern const idEventDef EV_GetTraceJoint;
|
||
|
|
||
|
CLASS_DECLARATION( idClass, sdLoggedTrace )
|
||
|
EVENT( EV_GetTraceFraction, sdLoggedTrace::Event_GetTraceFraction )
|
||
|
EVENT( EV_GetTraceEndPos, sdLoggedTrace::Event_GetTraceEndPos )
|
||
|
EVENT( EV_GetTracePoint, sdLoggedTrace::Event_GetTracePoint )
|
||
|
EVENT( EV_GetTraceNormal, sdLoggedTrace::Event_GetTraceNormal )
|
||
|
EVENT( EV_GetTraceEntity, sdLoggedTrace::Event_GetTraceEntity )
|
||
|
EVENT( EV_GetTraceSurfaceFlags, sdLoggedTrace::Event_GetTraceSurfaceFlags )
|
||
|
EVENT( EV_GetTraceSurfaceType, sdLoggedTrace::Event_GetTraceSurfaceType )
|
||
|
EVENT( EV_GetTraceSurfaceColor, sdLoggedTrace::Event_GetTraceSurfaceColor )
|
||
|
EVENT( EV_GetTraceJoint, sdLoggedTrace::Event_GetTraceJoint )
|
||
|
EVENT( EV_GetTraceBody, sdLoggedTrace::Event_GetTraceBody )
|
||
|
END_CLASS
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::sdLoggedTrace
|
||
|
================
|
||
|
*/
|
||
|
sdLoggedTrace::sdLoggedTrace( void ) {
|
||
|
memset( &trace, 0, sizeof( trace ) );
|
||
|
object = gameLocal.program->AllocScriptObject( this, gameLocal.program->GetDefaultType() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::~sdLoggedTrace
|
||
|
================
|
||
|
*/
|
||
|
sdLoggedTrace::~sdLoggedTrace( void ) {
|
||
|
gameLocal.program->FreeScriptObject( object );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::Init
|
||
|
================
|
||
|
*/
|
||
|
void sdLoggedTrace::Init( int _index, const trace_t& _trace ) {
|
||
|
index = _index;
|
||
|
trace = _trace;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::Event_GetTraceFraction
|
||
|
================
|
||
|
*/
|
||
|
void sdLoggedTrace::Event_GetTraceFraction( void ) {
|
||
|
sdProgram::ReturnFloat( trace.fraction );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::Event_GetTraceEndPos
|
||
|
================
|
||
|
*/
|
||
|
void sdLoggedTrace::Event_GetTraceEndPos( void ) {
|
||
|
sdProgram::ReturnVector( trace.endpos );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::Event_GetTracePoint
|
||
|
================
|
||
|
*/
|
||
|
void sdLoggedTrace::Event_GetTracePoint( void ) {
|
||
|
sdProgram::ReturnVector( trace.c.point );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::Event_GetTraceNormal
|
||
|
================
|
||
|
*/
|
||
|
void sdLoggedTrace::Event_GetTraceNormal( void ) {
|
||
|
if ( trace.fraction < 1.0f ) {
|
||
|
sdProgram::ReturnVector( trace.c.normal );
|
||
|
} else {
|
||
|
sdProgram::ReturnVector( vec3_origin );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::Event_GetTraceEntity
|
||
|
================
|
||
|
*/
|
||
|
void sdLoggedTrace::Event_GetTraceEntity( void ) {
|
||
|
if ( trace.fraction < 1.0f ) {
|
||
|
sdProgram::ReturnEntity( gameLocal.entities[ trace.c.entityNum ] );
|
||
|
} else {
|
||
|
sdProgram::ReturnEntity( NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::Event_GetTraceJoint
|
||
|
================
|
||
|
*/
|
||
|
void sdLoggedTrace::Event_GetTraceJoint( void ) {
|
||
|
if ( trace.fraction < 1.0f && trace.c.id < 0 ) {
|
||
|
idEntity* ent = gameLocal.entities[ trace.c.entityNum ];
|
||
|
if ( ent->GetAnimator() != NULL ) {
|
||
|
sdProgram::ReturnString( ent->GetAnimator()->GetJointName( CLIPMODEL_ID_TO_JOINT_HANDLE( trace.c.id ) ) );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
sdProgram::ReturnString( "" );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::Event_GetTraceBody
|
||
|
================
|
||
|
*/
|
||
|
void sdLoggedTrace::Event_GetTraceBody( void ) {
|
||
|
if ( trace.fraction < 1.0f && trace.c.id < 0 ) {
|
||
|
idAFEntity_Base *af = static_cast<idAFEntity_Base *>( gameLocal.entities[ trace.c.entityNum ] );
|
||
|
if ( af && af->IsType( idAFEntity_Base::Type ) && af->IsActiveAF() ) {
|
||
|
int bodyId = af->BodyForClipModelId( trace.c.id );
|
||
|
idAFBody *body = af->GetAFPhysics()->GetBody( bodyId );
|
||
|
if ( body ) {
|
||
|
sdProgram::ReturnString( body->GetName() );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
sdProgram::ReturnString( "" );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::Event_GetTraceSurfaceFlags
|
||
|
================
|
||
|
*/
|
||
|
void sdLoggedTrace::Event_GetTraceSurfaceFlags( void ) {
|
||
|
if ( trace.fraction < 1.0f && trace.c.material != NULL ) {
|
||
|
sdProgram::ReturnInteger( trace.c.material->GetSurfaceFlags() );
|
||
|
return;
|
||
|
}
|
||
|
sdProgram::ReturnInteger( 0 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::Event_GetTraceSurfaceType
|
||
|
================
|
||
|
*/
|
||
|
void sdLoggedTrace::Event_GetTraceSurfaceType( void ) {
|
||
|
if ( trace.fraction < 1.0f && trace.c.surfaceType ) {
|
||
|
sdProgram::ReturnString( trace.c.surfaceType->GetName() );
|
||
|
} else {
|
||
|
sdProgram::ReturnString( "" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdLoggedTrace::Event_GetTraceSurfaceColor
|
||
|
================
|
||
|
*/
|
||
|
void sdLoggedTrace::Event_GetTraceSurfaceColor( void ) {
|
||
|
sdProgram::ReturnVector( trace.c.surfaceColor );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
sdEntityCollection
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
sdEntityCollection::Remove
|
||
|
================
|
||
|
*/
|
||
|
void sdEntityCollection::Remove( idEntity* entity ) {
|
||
|
if ( !Contains( entity ) ) {
|
||
|
return;
|
||
|
}
|
||
|
for ( int i = 0; i < list.Num(); i++ ) {
|
||
|
if ( list[ i ].GetEntity() == entity ) {
|
||
|
list.RemoveIndex( i );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
mask.Clear( entity->entityNumber );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
sdEntityDebugInfo
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
sdEntityDebugInfo::sdEntityDebugInfo
|
||
|
============
|
||
|
*/
|
||
|
sdEntityDebugInfo::sdEntityDebugInfo( void ) {
|
||
|
lastOperationTime = 0;
|
||
|
lastOperation = OP_NONE;
|
||
|
type = NULL;
|
||
|
name = "";
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
sdEntityDebugInfo::OnCreate
|
||
|
============
|
||
|
*/
|
||
|
void sdEntityDebugInfo::OnCreate( idEntity* self ) {
|
||
|
lastOperationTime = gameLocal.time;
|
||
|
lastOperation = OP_CREATE;
|
||
|
type = self->GetType();
|
||
|
name = self->GetName();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
sdEntityDebugInfo::OnDestroy
|
||
|
============
|
||
|
*/
|
||
|
void sdEntityDebugInfo::OnDestroy( idEntity* self ) {
|
||
|
lastOperationTime = gameLocal.time;
|
||
|
lastOperation = OP_DESTROY;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
sdEntityDebugInfo::PrintStatus
|
||
|
============
|
||
|
*/
|
||
|
void sdEntityDebugInfo::PrintStatus( int index ) {
|
||
|
gameLocal.Printf( "Current Time: %i\n", gameLocal.time );
|
||
|
gameLocal.Printf( "Entity Number: %i\n", index );
|
||
|
|
||
|
switch ( lastOperation ) {
|
||
|
case OP_NONE:
|
||
|
gameLocal.Printf( "No Information Found\n" );
|
||
|
break;
|
||
|
|
||
|
case OP_CREATE:
|
||
|
gameLocal.Printf( "Created At: %i\n", lastOperationTime );
|
||
|
gameLocal.Printf( "Type: %s\n", type->classname );
|
||
|
gameLocal.Printf( "Name: %s\n", name.c_str() );
|
||
|
break;
|
||
|
|
||
|
case OP_DESTROY:
|
||
|
gameLocal.Printf( "Destroyed At: %i\n", lastOperationTime );
|
||
|
gameLocal.Printf( "Type: %s\n", type->classname );
|
||
|
gameLocal.Printf( "Name: %s\n", name.c_str() );
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
idGameLocal
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
idCVar idGameLocal::g_cacheDictionaryMedia( "g_cacheDictionaryMedia", "1", CVAR_GAME | CVAR_BOOL | CVAR_NOCHEAT | CVAR_RANKLOCKED, "Precache all media from entity dictionaries" );
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::idGameLocal
|
||
|
============
|
||
|
*/
|
||
|
idGameLocal::idGameLocal( void ) :
|
||
|
systemNotifications( 1 ) {
|
||
|
rulesStateObject.Init( this );
|
||
|
|
||
|
proficiencyLog = NULL;
|
||
|
networkLog = NULL;
|
||
|
objectiveLog = NULL;
|
||
|
#if !defined( SD_DEMO_BUILD )
|
||
|
clientStatsRequestTask = NULL;
|
||
|
#endif /* !SD_DEMO_BUILD */
|
||
|
mapMetaDataList = NULL;
|
||
|
campaignMetaDataList = NULL;
|
||
|
|
||
|
memset( deployRequests, 0, sizeof( deployRequests ) );
|
||
|
|
||
|
com_unlockFPS = NULL;
|
||
|
|
||
|
Clear();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::Clear
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::Clear( void ) {
|
||
|
int i;
|
||
|
|
||
|
msec = USERCMD_MSEC;
|
||
|
|
||
|
ClearDeployRequests();
|
||
|
|
||
|
serverInfo.Clear();
|
||
|
numClients = 0;
|
||
|
memset( usercmds, 0, sizeof( usercmds ) );
|
||
|
memset( clientConnected, 0, sizeof( clientConnected ) );
|
||
|
memset( clientRanks, 0, sizeof( clientRanks ) );
|
||
|
#if !defined( SD_DEMO_BUILD )
|
||
|
clientStatsRequestIndex = -1;
|
||
|
clientStatsRequestsPending = false;
|
||
|
#endif /* !SD_DEMO_BUILD */
|
||
|
entities.Memset( 0 );
|
||
|
memset( spawnIds, -1, sizeof( spawnIds ) );
|
||
|
firstFreeIndex = 0;
|
||
|
numEntities = 0;
|
||
|
spawnedEntities.Clear();
|
||
|
activeEntities.Clear();
|
||
|
activeNetworkEntities.Clear();
|
||
|
networkedEntities.Clear();
|
||
|
nonNetworkedEntities.Clear();
|
||
|
changedEntities.Clear();
|
||
|
interpolateEntities.Clear();
|
||
|
sortPushers = false;
|
||
|
sortTeamMasters = false;
|
||
|
random.SetSeed( 0 );
|
||
|
world = NULL;
|
||
|
frameCommandThread = NULL;
|
||
|
testmodel = NULL;
|
||
|
clip.Shutdown();
|
||
|
pvs.Shutdown();
|
||
|
editEntities = NULL;
|
||
|
entityHash.Clear( 1024, MAX_GENTITIES );
|
||
|
framenum = 0;
|
||
|
previousTime = 0;
|
||
|
time = 0;
|
||
|
timeOffset = 0;
|
||
|
localViewChangedTime = 0;
|
||
|
nextTeamBalanceCheckTime = 0;
|
||
|
damageLogFile = NULL;
|
||
|
debugLogFile = NULL;
|
||
|
|
||
|
insideExecuteMapChange = false;
|
||
|
|
||
|
doingMapRestart = false;
|
||
|
reloadingSameMap = true;
|
||
|
mapFileName.Clear();
|
||
|
mapFile = NULL;
|
||
|
|
||
|
botMapFile = NULL;
|
||
|
|
||
|
spawnCount = INITIAL_SPAWN_COUNT;
|
||
|
camera = NULL;
|
||
|
spawnArgs.Clear();
|
||
|
gravity.Set( 0, 0, -1 );
|
||
|
gamestate = GAMESTATE_UNINITIALIZED;
|
||
|
rules = NULL;
|
||
|
SetSnapShotPlayer( NULL );
|
||
|
SetSnapShotClient( NULL );
|
||
|
|
||
|
numEntityDefBits = 0;
|
||
|
numDamageDeclBits = 0;
|
||
|
numInvItemBits = 0;
|
||
|
numSkinDeclBits = 0;
|
||
|
numDeployObjectBits = 0;
|
||
|
numPlayerClassBits = 0;
|
||
|
numClientIndexBits = 0;
|
||
|
|
||
|
localClientNum = ASYNC_DEMO_CLIENT_INDEX;
|
||
|
isServer = false;
|
||
|
isClient = false;
|
||
|
isRepeater = false;
|
||
|
serverIsRepeater = false;
|
||
|
snapShotClientIsRepeater = false;
|
||
|
repeaterClientFollowIndex = -1;
|
||
|
realClientTime = 0;
|
||
|
isNewFrame = true;
|
||
|
isPaused = false;
|
||
|
|
||
|
globalMaterial = NULL;
|
||
|
newInfo.Clear();
|
||
|
|
||
|
entityNetEventQueue.Clear();
|
||
|
savedEntityNetEventQueue.Clear();
|
||
|
|
||
|
loggedTraces.SetNum( MAX_LOGGED_TRACES );
|
||
|
for ( i = 0; i < MAX_LOGGED_TRACES; i++ ) {
|
||
|
loggedTraces[ i ] = NULL;
|
||
|
}
|
||
|
|
||
|
loggedDecals.SetNum( MAX_LOGGED_DECALS );
|
||
|
for ( i = 0; i < MAX_LOGGED_DECALS; i++ ) {
|
||
|
loggedDecals[ i ] = NULL;
|
||
|
}
|
||
|
|
||
|
clientSpawnedEntities.Clear();
|
||
|
memset( clientEntities, 0, sizeof( clientEntities ) );
|
||
|
for (int i=0; i<MAX_CENTITIES; i++) {
|
||
|
clientSpawnIds[i] = -INITIAL_SPAWN_COUNT;
|
||
|
}
|
||
|
|
||
|
uiMainMenuHandle.Release();
|
||
|
uiLevelLoadHandle.Release();
|
||
|
pureWaitHandle.Release();
|
||
|
uiSystemUIHandle.Release();
|
||
|
|
||
|
mapMetaData = NULL;
|
||
|
mapInfo = NULL;
|
||
|
|
||
|
for ( int i = 0; i < MAX_BATTLESENSE_RANKS; i++ ) {
|
||
|
battleSenseBonus[ i ] = NULL;
|
||
|
}
|
||
|
|
||
|
lagoMaterial = NULL;
|
||
|
|
||
|
memset( &unlock, 0, sizeof( unlock ) );
|
||
|
unlock.minAngles.Set( -180.0f, -180.0f, -180.0f );
|
||
|
unlock.maxAngles.Set( 180.0f, 180.0f, 180.0f );
|
||
|
|
||
|
nextBotPopulationCheck = 0;
|
||
|
|
||
|
useSimpleEffect = bse_simple.GetBool();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idGameLocal::GetBotFPS
|
||
|
===============
|
||
|
*/
|
||
|
int idGameLocal::GetBotFPS( void ) const {
|
||
|
return botThreadData.GetBotFPS();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idGameLocal::GetRandomBotName
|
||
|
===============
|
||
|
*/
|
||
|
bool idGameLocal::GetRandomBotName( int clientNum, idStr& botName ) {
|
||
|
bool foundName = botThreadData.FindRandomBotName( clientNum, botName );
|
||
|
return foundName;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idGameLocal::GetBotDebugInfo
|
||
|
===============
|
||
|
*/
|
||
|
botDebugInfo_t idGameLocal::GetBotDebugInfo( int clientNum ) {
|
||
|
if ( !botThreadData.ClientIsValid( clientNum ) ) { //mal: someone goofed and try to pass an invalid client.
|
||
|
botDebugInfo_t botInfo;
|
||
|
memset( &botInfo, 0, sizeof( botInfo ) );
|
||
|
return botInfo;
|
||
|
}
|
||
|
return botThreadData.GetBotDebugInfo( clientNum );
|
||
|
}
|
||
|
|
||
|
#if defined( SD_PUBLIC_BUILD )
|
||
|
idCVar g_useCompiledScript( "g_useCompiledScript", "1", CVAR_GAME | CVAR_BOOL | CVAR_ARCHIVE, "enable/disable native compiled scripts" );
|
||
|
#else
|
||
|
idCVar g_useCompiledScript( "g_useCompiledScript", "0", CVAR_GAME | CVAR_BOOL | CVAR_ARCHIVE, "enable/disable native compiled scripts" );
|
||
|
#endif // SD_PUBLIC_BUILD
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::LoadScript
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::LoadScript( void ) {
|
||
|
if ( mapInfo == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// before scripts are loaded, shut down old threads, etc
|
||
|
sdTeamManager::GetInstance().OnScriptChange();
|
||
|
if ( rules ) {
|
||
|
rules->OnScriptChange();
|
||
|
}
|
||
|
sdObjectiveManager::GetInstance().OnScriptChange();
|
||
|
sdTaskManager::GetInstance().Init();
|
||
|
sdTaskManager::GetInstance().OnScriptChange();
|
||
|
|
||
|
if ( program != NULL && program->IsValid() ) {
|
||
|
const sdProgram::sdFunction* func = program->FindFunction( "game_shutdown" );
|
||
|
if ( func && frameCommandThread ) {
|
||
|
frameCommandThread->CallFunction( func );
|
||
|
if ( !frameCommandThread->Execute() ) {
|
||
|
Error( "idGameLocal::LoadScript Shutdown Cannot be Blocking" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( frameCommandThread != NULL ) {
|
||
|
gameLocal.program->FreeThread( frameCommandThread );
|
||
|
frameCommandThread = NULL;
|
||
|
}
|
||
|
|
||
|
delete program;
|
||
|
program = NULL;
|
||
|
|
||
|
if ( g_useCompiledScript.GetBool() ) {
|
||
|
program = new sdDLLProgram();
|
||
|
if ( !program->Init() ) {
|
||
|
delete program;
|
||
|
program = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( program == NULL ) {
|
||
|
program = new idProgram();
|
||
|
program->Init();
|
||
|
}
|
||
|
|
||
|
UpdateLevelLoadScreen( common->LocalizeText( "guis/mainmenu/loading/compiling_scripts" ).c_str() );
|
||
|
|
||
|
InitScriptForMap();
|
||
|
|
||
|
// after scripts are loaded, load new objects
|
||
|
sdTeamManager::GetInstance().OnNewScriptLoad();
|
||
|
if ( rules ) {
|
||
|
rules->OnNewScriptLoad();
|
||
|
}
|
||
|
sdObjectiveManager::GetInstance().OnNewScriptLoad();
|
||
|
sdTaskManager::GetInstance().OnNewScriptLoad();
|
||
|
}
|
||
|
|
||
|
// crash here so we can try to get a stack trace
|
||
|
void WinPureCallHandler( void ) {
|
||
|
*( int* )( 0x00000000 ) = 7;
|
||
|
}
|
||
|
|
||
|
class idCVarCallback_TeamForceBalanceChanged_BotControl : public idCVarCallback {
|
||
|
public:
|
||
|
~idCVarCallback_TeamForceBalanceChanged_BotControl() {
|
||
|
si_teamForceBalance.UnRegisterCallback( this );
|
||
|
}
|
||
|
|
||
|
void Register( void ) {
|
||
|
si_teamForceBalance.RegisterCallback( this );
|
||
|
}
|
||
|
|
||
|
virtual void OnChanged( void ) {
|
||
|
if ( !gameLocal.isClient && !networkSystem->IsRankedServer() ) {
|
||
|
if( si_teamForceBalance.GetBool() ) {
|
||
|
bot_uiNumStrogg.SetInteger( -1 );
|
||
|
bot_uiNumGDF.SetInteger( -1 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} g_updateTeamForceBalance_BotControl;
|
||
|
|
||
|
class idCVarCallback_BotControl : public idCVarCallback {
|
||
|
public:
|
||
|
idCVarCallback_BotControl( idCVar& watch ) {
|
||
|
_watch = &watch;
|
||
|
}
|
||
|
|
||
|
~idCVarCallback_BotControl() {
|
||
|
_watch->UnRegisterCallback( this );
|
||
|
}
|
||
|
|
||
|
void Register( void ) {
|
||
|
_watch->RegisterCallback( this );
|
||
|
}
|
||
|
|
||
|
virtual void OnChanged( void ) {
|
||
|
if ( !gameLocal.isClient ) {
|
||
|
if( _watch->GetInteger() != -1 && !networkSystem->IsRankedServer() ) {
|
||
|
si_teamForceBalance.SetBool( false );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idCVar* _watch;
|
||
|
} g_updateNumGDF_BotControl( bot_uiNumGDF ),
|
||
|
g_updateNumStrogg_BotControl( bot_uiNumStrogg );
|
||
|
|
||
|
class idCVarCallback_ServerInfoDirectSet : public idCVarCallback {
|
||
|
public:
|
||
|
idCVarCallback_ServerInfoDirectSet( idCVar& watch ) {
|
||
|
_watch = &watch;
|
||
|
}
|
||
|
|
||
|
~idCVarCallback_ServerInfoDirectSet() {
|
||
|
_watch->UnRegisterCallback( this );
|
||
|
}
|
||
|
|
||
|
void Register( void ) {
|
||
|
_watch->RegisterCallback( this );
|
||
|
}
|
||
|
|
||
|
virtual void OnChanged( void ) {
|
||
|
if ( !gameLocal.isClient ) {
|
||
|
sys->SetServerInfo( _watch->GetName(), _watch->GetString() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idCVar* _watch;
|
||
|
} g_updateTimeLimit( si_timeLimit ),
|
||
|
g_updateMaxPlayer( si_maxPlayers ),
|
||
|
g_updatePrivateClients( si_privateClients ),
|
||
|
g_updateTeamForceBalance( si_teamForceBalance ),
|
||
|
g_updateAdminStart( si_adminStart ),
|
||
|
g_updateMinPlayers( si_minPlayers ),
|
||
|
g_updateReadyPercent( si_readyPercent ),
|
||
|
g_updateDisableVoting( si_disableVoting ),
|
||
|
g_updateServerName( si_name ),
|
||
|
g_updateNoProficiency( si_noProficiency ),
|
||
|
g_updateTeamDamage( si_teamDamage ),
|
||
|
g_updateNeedPass( si_needPass ),
|
||
|
g_updateDisableGlobalChat( si_disableGlobalChat ),
|
||
|
g_updateGameReviewReadyWait( si_gameReviewReadyWait );
|
||
|
|
||
|
|
||
|
class idCVarCallback_CommandMapZoom : public idCVarCallback {
|
||
|
public:
|
||
|
idCVarCallback_CommandMapZoom( idCVar& watch ) {
|
||
|
_watch = &watch;
|
||
|
}
|
||
|
|
||
|
~idCVarCallback_CommandMapZoom() {
|
||
|
_watch->UnRegisterCallback( this );
|
||
|
}
|
||
|
|
||
|
void Register( void ) {
|
||
|
_watch->RegisterCallback( this );
|
||
|
}
|
||
|
|
||
|
virtual void OnChanged( void ) {
|
||
|
idPlayer::SetupCommandMapZoom();
|
||
|
}
|
||
|
|
||
|
idCVar* _watch;
|
||
|
} g_commandMapZoomCvarCallback( g_commandMapZoom );
|
||
|
|
||
|
class idCVarCallback_BotMinClients : public idCVarCallback {
|
||
|
public:
|
||
|
idCVarCallback_BotMinClients( idCVar& watch ) {
|
||
|
_watch = &watch;
|
||
|
}
|
||
|
|
||
|
~idCVarCallback_BotMinClients() {
|
||
|
_watch->UnRegisterCallback( this );
|
||
|
}
|
||
|
|
||
|
void Register( void ) {
|
||
|
_watch->RegisterCallback( this );
|
||
|
}
|
||
|
|
||
|
virtual void OnChanged( void ) {
|
||
|
if ( bot_minClients.GetInteger() > bot_minClientsMax.GetInteger() ) {
|
||
|
bot_minClients.SetInteger( bot_minClientsMax.GetInteger() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idCVar* _watch;
|
||
|
} bot_minClientsCvarCallback( bot_minClients ),
|
||
|
bot_minClientsMaxCvarCallback( bot_minClientsMax );
|
||
|
|
||
|
/*void ParseNetworkLog( void ) {
|
||
|
idFile* file2 = fileSystem->OpenFileWrite( "network_dump.log" );
|
||
|
|
||
|
idLexer parser( "network.log" );
|
||
|
|
||
|
while ( true ) {
|
||
|
if ( parser.EndOfFile() ) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
idStr str;
|
||
|
parser.ParseCompleteLine( str );
|
||
|
|
||
|
if ( str.Cmpn( "Entity Broadcast: ", 18 ) != 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
file2->WriteFloatString( "%s", str.c_str() );
|
||
|
}
|
||
|
|
||
|
fileSystem->CloseFile( file2 );
|
||
|
}
|
||
|
|
||
|
void ParseNetworkLog( void ) {
|
||
|
idFile* file2 = fileSystem->OpenFileWrite( "network_dump.log" );
|
||
|
|
||
|
idLexer parser( "network_dump.csv" );
|
||
|
|
||
|
struct entryData_t {
|
||
|
int totalSize;
|
||
|
int count;
|
||
|
int type;
|
||
|
};
|
||
|
|
||
|
struct typeData_t {
|
||
|
idList< entryData_t > entries;
|
||
|
};
|
||
|
|
||
|
idHashMap< typeData_t* > types;
|
||
|
|
||
|
while ( true ) {
|
||
|
if ( parser.EndOfFile() ) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
idToken name;
|
||
|
parser.ReadToken( &name );
|
||
|
|
||
|
parser.ExpectTokenString( "," );
|
||
|
|
||
|
idToken valueString;
|
||
|
parser.ReadToken( &valueString );
|
||
|
|
||
|
parser.ExpectTokenString( "," );
|
||
|
|
||
|
idToken sizeString;
|
||
|
parser.ReadToken( &sizeString );
|
||
|
|
||
|
parser.SkipRestOfLine();
|
||
|
|
||
|
typeData_t** dataPtr;
|
||
|
typeData_t* data;
|
||
|
if ( types.Get( name.c_str(), &dataPtr ) ) {
|
||
|
data = *dataPtr;
|
||
|
} else {
|
||
|
data = new typeData_t;
|
||
|
types.Set( name.c_str(), data );
|
||
|
}
|
||
|
|
||
|
int value = valueString.GetIntValue();
|
||
|
int size = sizeString.GetIntValue();
|
||
|
|
||
|
int i;
|
||
|
for ( i = 0; i < data->entries.Num(); i++ ) {
|
||
|
if ( data->entries[ i ].type == value ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ( i == data->entries.Num() ) {
|
||
|
entryData_t& newEntry = data->entries.Alloc();
|
||
|
newEntry.type = value;
|
||
|
newEntry.count = 0;
|
||
|
newEntry.totalSize = 0;
|
||
|
}
|
||
|
|
||
|
data->entries[ i ].count++;
|
||
|
data->entries[ i ].totalSize += size;
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < types.Num(); i++ ) {
|
||
|
typeData_t** dataPtr = types.GetIndex( i );
|
||
|
assert( dataPtr != NULL );
|
||
|
typeData_t* data = *dataPtr;
|
||
|
|
||
|
for ( int j = 0; j < data->entries.Num(); j++ ) {
|
||
|
file2->WriteFloatString( "%s,%d,%d,%d\n", types.GetKey( i ).c_str(), data->entries[ j ].type, data->entries[ j ].count, data->entries[ j ].totalSize );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fileSystem->CloseFile( file2 );
|
||
|
}*/
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::LoadLifeStatsData
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::LoadLifeStatsData( void ) {
|
||
|
const sdDeclStringMap* lifeStataData = declStringMapType[ "life_stats_data" ];
|
||
|
if ( lifeStataData == NULL ) {
|
||
|
gameLocal.Warning( "No Life Stats Data Found" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const idDict& info = lifeStataData->GetDict();
|
||
|
|
||
|
const idKeyValue* kv = NULL;
|
||
|
while ( ( kv = info.MatchPrefix( "life_stat_", kv ) ) != NULL ) {
|
||
|
const char* statName = kv->GetValue().c_str();
|
||
|
if ( *statName == '\0' ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const char* textName = info.GetString( va( "text_%s", kv->GetKey().c_str() ) );
|
||
|
const sdDeclLocStr* text = declHolder.declLocStrType[ textName ];
|
||
|
if ( text == NULL ) {
|
||
|
gameLocal.Warning( "sdDeclPlayerClass::ReadFromDict Bad Text '%s' for Life Stat %s", textName, kv->GetKey().c_str() );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
sdStringBuilder_Heap textNameLong( va( "%s_long", textName ) );
|
||
|
const sdDeclLocStr* textLong = declHolder.declLocStrType[ textNameLong.c_str() ];
|
||
|
if ( textLong == NULL ) {
|
||
|
gameLocal.Warning( "sdDeclPlayerClass::ReadFromDict Bad Long Text '%s' for Life Stat %s", textNameLong.c_str(), kv->GetKey().c_str() );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
lifeStat_t& stat = lifeStats.Alloc();
|
||
|
stat.stat = statName;
|
||
|
stat.text = text;
|
||
|
stat.textLong = textLong;
|
||
|
stat.isTimeBased = info.GetBool( va( "timebased_%s", kv->GetKey().c_str() ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::Init
|
||
|
|
||
|
initialize the game object, only happens once at startup, not each level load
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::Init( void ) {
|
||
|
#ifdef _WIN32
|
||
|
_set_purecall_handler( WinPureCallHandler );
|
||
|
#endif // _WIN32
|
||
|
|
||
|
// initialize idLib
|
||
|
idLib::Init();
|
||
|
|
||
|
// register static cvars declared in the game
|
||
|
idCVar::RegisterStaticVars();
|
||
|
|
||
|
g_updateTimeLimit.Register();
|
||
|
g_updateMaxPlayer.Register();
|
||
|
g_updatePrivateClients.Register();
|
||
|
g_updateTeamForceBalance.Register();
|
||
|
g_updateAdminStart.Register();
|
||
|
g_updateDisableVoting.Register();
|
||
|
g_updateMinPlayers.Register();
|
||
|
g_updateReadyPercent.Register();
|
||
|
g_updateServerName.Register();
|
||
|
g_updateNoProficiency.Register();
|
||
|
g_updateTeamDamage.Register();
|
||
|
g_updateNeedPass.Register();
|
||
|
g_updateGameReviewReadyWait.Register();
|
||
|
g_updateDisableGlobalChat.Register();
|
||
|
|
||
|
g_updateTeamForceBalance_BotControl.Register();
|
||
|
g_updateNumGDF_BotControl.Register();
|
||
|
g_updateNumStrogg_BotControl.Register();
|
||
|
|
||
|
bot_minClientsCvarCallback.Register();
|
||
|
bot_minClientsMaxCvarCallback.Register();
|
||
|
|
||
|
idWeapon::RegisterCVarCallback();
|
||
|
sdDeclAOR::InitCVars();
|
||
|
|
||
|
sdAnimFrameCommand::Init();
|
||
|
|
||
|
sdStatsTracker::Init();
|
||
|
|
||
|
sdStatsTracker& statsTracker = sdGlobalStatsTracker::GetInstance();
|
||
|
|
||
|
totalShotsFiredStat = statsTracker.GetStat( statsTracker.AllocStat( "total_shots_fired", sdNetStatKeyValue::SVT_INT ) );
|
||
|
totalShotsHitStat = statsTracker.GetStat( statsTracker.AllocStat( "total_shots_hit", sdNetStatKeyValue::SVT_INT ) );
|
||
|
|
||
|
// initialize processor specific SIMD
|
||
|
idSIMD::InitProcessor( "game", com_forceGenericSIMD.GetBool() );
|
||
|
|
||
|
Printf( "--------- Initializing Game ----------\n" );
|
||
|
Printf( "gamename: %s\n", GAME_VERSION );
|
||
|
Printf( "gamedate: %s\n", __DATE__ );
|
||
|
|
||
|
idStr versionString = "LOADING GAME:";
|
||
|
common->PrintLoadingMessage( versionString + " REGISTERING DECLARATIONS" );
|
||
|
|
||
|
// register game specific decl types
|
||
|
declManager->RegisterDeclType( &declModelDefType );
|
||
|
declManager->RegisterDeclType( &declExportDefType );
|
||
|
declManager->RegisterDeclType( &declVehicleScriptDefType );
|
||
|
declManager->RegisterDeclType( &declAmmoTypeType );
|
||
|
declManager->RegisterDeclType( &declInvSlotType );
|
||
|
declManager->RegisterDeclType( &declInvItemTypeType );
|
||
|
declManager->RegisterDeclType( &declInvItemType );
|
||
|
declManager->RegisterDeclType( &declItemPackageType );
|
||
|
declManager->RegisterDeclType( &declStringMapType );
|
||
|
declManager->RegisterDeclType( &declDamageType );
|
||
|
declManager->RegisterDeclType( &declDamageFilterType );
|
||
|
declManager->RegisterDeclType( &declCampaignType );
|
||
|
declManager->RegisterDeclType( &declQuickChatType );
|
||
|
declManager->RegisterDeclType( &declMapInfoType );
|
||
|
declManager->RegisterDeclType( &declToolTipType );
|
||
|
declManager->RegisterDeclType( &declTargetInfoType );
|
||
|
declManager->RegisterDeclType( &declProficiencyTypeType );
|
||
|
declManager->RegisterDeclType( &declProficiencyItemType );
|
||
|
declManager->RegisterDeclType( &declRankType );
|
||
|
declManager->RegisterDeclType( &declDeployableObjectType );
|
||
|
declManager->RegisterDeclType( &declDeployableZoneType );
|
||
|
declManager->RegisterDeclType( &declPlayerClassType );
|
||
|
declManager->RegisterDeclType( &declGUIType );
|
||
|
declManager->RegisterDeclType( &declTeamInfoType );
|
||
|
declManager->RegisterDeclType( &declPlayerTaskType );
|
||
|
declManager->RegisterDeclType( &declRequirementType );
|
||
|
declManager->RegisterDeclType( &declGUIThemeType );
|
||
|
declManager->RegisterDeclType( &declVehiclePathType );
|
||
|
declManager->RegisterDeclType( &declKeyBindingType );
|
||
|
declManager->RegisterDeclType( &declRadialMenuType );
|
||
|
declManager->RegisterDeclType( &declAORType );
|
||
|
declManager->RegisterDeclType( &declRatingType );
|
||
|
declManager->RegisterDeclType( &declHeightMapType );
|
||
|
declManager->RegisterDeclType( &declDeployMaskType );
|
||
|
|
||
|
idDeclTypeInterface* declInterface = declManager->GetDeclType( declSurfaceTypeIdentifier );
|
||
|
declInterface->RegisterPostParse( idGameLocal::SurfaceTypePostParse );
|
||
|
|
||
|
declTableType.Init( declTableIdentifier );
|
||
|
declMaterialType.Init( declMaterialIdentifier );
|
||
|
declSkinType.Init( declSkinIdentifier );
|
||
|
declSoundShaderType.Init( declSoundShaderIdentifier );
|
||
|
declEntityDefType.Init( declEntityDefIdentifier );
|
||
|
declAFType.Init( declAFIdentifier );
|
||
|
declEffectsType.Init( declEffectsIdentifier );
|
||
|
declAtmosphereType.Init( declAtmosphereIdentifier );
|
||
|
declStuffTypeType.Init( declStuffTypeIdentifier );
|
||
|
declDecalType.Init( declDecalIdentifier );
|
||
|
declSurfaceTypeType.Init( declSurfaceTypeIdentifier );
|
||
|
|
||
|
sdRequirementCheck::InitFactory();
|
||
|
sdDeclItemPackage::InitConsumables();
|
||
|
|
||
|
common->PrintLoadingMessage( versionString + " SCANNING DECLARATION FOLDERS" );
|
||
|
|
||
|
// register game specific decl folders
|
||
|
declManager->RegisterDeclFolder( "def", ".def" );
|
||
|
declManager->RegisterDeclFolder( "af", ".af" );
|
||
|
declManager->RegisterDeclFolder( "vehicles", ".vscript" );
|
||
|
declManager->RegisterDeclFolder( "quickchat", ".qc" );
|
||
|
declManager->RegisterDeclFolder( "mapinfo", ".txt" );
|
||
|
declManager->RegisterDeclFolder( "mapinfo", ".md" );
|
||
|
declManager->RegisterDeclFolder( "effects", ".effect" );
|
||
|
declManager->RegisterDeclFolder( "guis", ".gui" );
|
||
|
declManager->RegisterDeclFolder( "guis", ".guitheme" );
|
||
|
declManager->RegisterDeclFolder( "bindings", ".binding" );
|
||
|
declManager->RegisterDeclFolder( "decals", ".decal" );
|
||
|
declManager->RegisterDeclFolder( "menus", ".radialmenu" );
|
||
|
|
||
|
mapMetaDataList = fileSystem->ListAddonMetaData( "mapMetaData" );
|
||
|
campaignMetaDataList = fileSystem->ListAddonMetaData( "campaignMetaData" );
|
||
|
|
||
|
declManager->FinishedRegistering();
|
||
|
|
||
|
cmdSystem->AddCommand( "listModelDefs", idListGameDecls_f< DECLTYPE_MODEL >, CMD_FL_SYSTEM | CMD_FL_GAME, "lists model defs" );
|
||
|
cmdSystem->AddCommand( "listVehicleScripts", idListGameDecls_f< DECLTYPE_VEHICLESCRIPT >, CMD_FL_SYSTEM | CMD_FL_GAME, "lists vehicle scripts" );
|
||
|
cmdSystem->AddCommand( "listGUIThemes", idListGameDecls_f< DECLTYPE_GUITHEME >, CMD_FL_SYSTEM | CMD_FL_GAME, "lists GUI themes" );
|
||
|
cmdSystem->AddCommand( "listClientEntities", idGameLocal::ListClientEntities_f, CMD_FL_SYSTEM | CMD_FL_GAME, "lists Client Entities" );
|
||
|
|
||
|
cmdSystem->AddCommand( "printModelDefs", idPrintGameDecls_f< DECLTYPE_MODEL >, CMD_FL_SYSTEM | CMD_FL_GAME, "prints a model def", idArgCompletionGameDecl_f< DECLTYPE_MODEL > );
|
||
|
cmdSystem->AddCommand( "printVehicleScripts", idPrintGameDecls_f< DECLTYPE_VEHICLESCRIPT >, CMD_FL_SYSTEM | CMD_FL_GAME, "prints a vehicle script", idArgCompletionGameDecl_f< DECLTYPE_VEHICLESCRIPT > );
|
||
|
|
||
|
cmdSystem->AddCommand( "testGUI", idGameLocal::TestGUI_f, CMD_FL_SYSTEM | CMD_FL_GAME, "Replace the main menu with a test gui.", idArgCompletionGameDecl_f< DECLTYPE_GUI > );
|
||
|
|
||
|
sdDeclGUI::InitDefines();
|
||
|
|
||
|
globalProperties.Init();
|
||
|
limboProperties.Init();
|
||
|
localPlayerProperties.Init();
|
||
|
|
||
|
sdInventory::BuildSlotBankLookup();
|
||
|
|
||
|
Clear();
|
||
|
|
||
|
LoadLifeStatsData();
|
||
|
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
proficiencyTables[ i ].Init( i );
|
||
|
proficiencyTables[ i ].Clear( true );
|
||
|
}
|
||
|
|
||
|
common->PrintLoadingMessage( versionString + " INIT CLASSES AND EVENTS" );
|
||
|
|
||
|
for ( int i = 0; i < MAX_BATTLESENSE_RANKS; i++ ) {
|
||
|
battleSenseBonus[ i ] = gameLocal.declProficiencyItemType[ va( "pro_battlesense_rank%i", i + 1 ) ];
|
||
|
}
|
||
|
|
||
|
clip.AllocThread();
|
||
|
|
||
|
idEvent::Init();
|
||
|
idClass::InitClasses();
|
||
|
|
||
|
InitConsoleCommands();
|
||
|
|
||
|
#ifdef _XENON
|
||
|
liveManager->Initialize();
|
||
|
#else
|
||
|
sdnet.Init();
|
||
|
updateManager.Init();
|
||
|
#endif
|
||
|
|
||
|
reservedClientSlots.Clear();
|
||
|
|
||
|
sdDemoManager::GetInstance().Init();
|
||
|
|
||
|
common->PrintLoadingMessage( versionString + " INIT UI" );
|
||
|
|
||
|
uiManager->Init();
|
||
|
|
||
|
common->PrintLoadingMessage( versionString + " GAMEPLAY SYSTEMS" );
|
||
|
|
||
|
sdRequirementManager::GetInstance().Init();
|
||
|
sdProficiencyManager::GetInstance().Init();
|
||
|
sdUserGroupManager::GetInstance().Init();
|
||
|
sdAdminSystem::GetInstance().Init();
|
||
|
sdVoteManager::GetInstance().Init();
|
||
|
|
||
|
common->PrintLoadingMessage( versionString + " INIT TEAMS" );
|
||
|
sdTeamManager::GetInstance().Init();
|
||
|
sdFireTeamManager::GetInstance().Init();
|
||
|
gameLocal.aorManager.Init();
|
||
|
|
||
|
sdCommandMapInfoManager::GetInstance().Init();
|
||
|
|
||
|
sdVehicleSuspension::Startup();
|
||
|
sdVehicleView::Startup();
|
||
|
sdScriptedEntityHelper::Startup();
|
||
|
sdVehicleSoundControlBase::Startup();
|
||
|
|
||
|
mapLoadCount = 0;
|
||
|
playerSpawnTime = 0;
|
||
|
|
||
|
OnInputInit();
|
||
|
|
||
|
gamestate = GAMESTATE_NOMAP;
|
||
|
|
||
|
currentPartialLoadTeamAssets = NULL;
|
||
|
|
||
|
if ( !networkSystem->IsDedicated() ) {
|
||
|
common->PrintLoadingMessage( versionString + " LOAD UI" );
|
||
|
|
||
|
lagoMaterial = declHolder.declMaterialType.LocalFind( LAGO_MATERIAL, false );
|
||
|
|
||
|
uiScopes.Clear();
|
||
|
uiScopes.Append( guiScope_t( "player", &localPlayerProperties ) );
|
||
|
uiScopes.Append( guiScope_t( "globals", &globalProperties ) );
|
||
|
uiScopes.Append( guiScope_t( "limbo", &limboProperties ) );
|
||
|
uiScopes.Append( guiScope_t( "demo", &sdDemoManager::GetInstance().GetProperties() ) );
|
||
|
uiScopes.Append( guiScope_t( "admin", &sdAdminSystem::GetInstance().GetProperties() ) );
|
||
|
#ifndef _XENON
|
||
|
uiScopes.Append( guiScope_t( "sdnet", &sdnet.GetProperties() ) );
|
||
|
uiScopes.Append( guiScope_t( "updates", &updateManager ) );
|
||
|
#endif
|
||
|
sdDemoManager::GetInstance().InitGUIs();
|
||
|
|
||
|
uiManager->RegisterListEnumerationCallback( "demoList", CreateDemoList );
|
||
|
uiManager->RegisterListEnumerationCallback( "lifeStatsList", CreateLifeStatsList );
|
||
|
uiManager->RegisterListEnumerationCallback( "predictedUpgradesList",CreatePredictedUpgradesList );
|
||
|
uiManager->RegisterListEnumerationCallback( "reviewUpgradesList", CreateUpgradesReviewList );
|
||
|
uiManager->RegisterListEnumerationCallback( "modList", CreateModList );
|
||
|
uiManager->RegisterListEnumerationCallback( "crosshairs", CreateCrosshairList );
|
||
|
uiManager->RegisterListEnumerationCallback( "keyBindings", CreateKeyBindingList );
|
||
|
uiManager->RegisterListEnumerationCallback( "vehiclePlayerList", CreateVehiclePlayerList );
|
||
|
uiManager->RegisterListEnumerationCallback( "activeTaskList", CreateActiveTaskList );
|
||
|
uiManager->RegisterListEnumerationCallback( "fireTeamList", CreateFireTeamList );
|
||
|
uiManager->RegisterListEnumerationCallback( "inventoryList", CreateInventoryList );
|
||
|
uiManager->RegisterListEnumerationCallback( "scoreboardList", CreateScoreboardList ); // input: string name of team to fill; return: average ping, average XP, number of active players
|
||
|
uiManager->RegisterListEnumerationCallback( "playerAdminList", CreatePlayerAdminList );
|
||
|
uiManager->RegisterListEnumerationCallback( "userGroupList", CreateUserGroupList );
|
||
|
uiManager->RegisterListEnumerationCallback( "serverConfigList", CreateServerConfigList );
|
||
|
uiManager->RegisterListEnumerationCallback( "videoModeList", CreateVideoModeList );
|
||
|
uiManager->RegisterListEnumerationCallback( "campaignList", CreateCampaignList );
|
||
|
uiManager->RegisterListEnumerationCallback( "mapList", CreateMapList );
|
||
|
uiManager->RegisterListEnumerationCallback( "weaponSwitchList", CreateWeaponSwitchList );
|
||
|
uiManager->RegisterListEnumerationCallback( "callVoteList", sdVoteManagerLocal::CreateCallVoteList );
|
||
|
uiManager->RegisterListEnumerationCallback( "callVoteListOptions", sdVoteManagerLocal::CreateCallVoteOptionList );
|
||
|
uiManager->RegisterListEnumerationCallback( "colors", CreateColorList );
|
||
|
uiManager->RegisterListEnumerationCallback( "spawnLocations", CreateSpawnLocationList );
|
||
|
uiManager->RegisterListEnumerationCallback( "availableMSAA", CreateMSAAList );
|
||
|
uiManager->RegisterListEnumerationCallback( "soundOutput", CreateSoundPlaybackList );
|
||
|
uiManager->RegisterListEnumerationCallback( "soundInput", CreateSoundCaptureList );
|
||
|
|
||
|
|
||
|
sdDeclGUI::AddDefine( va( "MSG_OK %i", MSG_OK ) );
|
||
|
sdDeclGUI::AddDefine( va( "MSG_OKCANCEL %i", MSG_OKCANCEL ) );
|
||
|
sdDeclGUI::AddDefine( va( "MSG_YESNO %i", MSG_YESNO ) );
|
||
|
sdDeclGUI::AddDefine( va( "MSG_DOWNLOAD_YESNO %i", MSG_DOWNLOAD_YESNO ) );
|
||
|
sdDeclGUI::AddDefine( va( "MSG_NEED_PASSWORD %i", MSG_NEED_PASSWORD ) );
|
||
|
sdDeclGUI::AddDefine( va( "MSG_NEED_AUTH %i", MSG_NEED_AUTH ) );
|
||
|
sdDeclGUI::AddDefine( va( "MSG_ABORT %i", MSG_ABORT ) );
|
||
|
|
||
|
sdDeclGUI::AddDefine( va( "CHAT_MODE_MESSAGE %i", sdGameRules::CHAT_MODE_MESSAGE ) );
|
||
|
sdDeclGUI::AddDefine( va( "CHAT_MODE_OBITUARY %i", sdGameRules::CHAT_MODE_OBITUARY ) );
|
||
|
|
||
|
|
||
|
uiMainMenuHandle = LoadUserInterface( "mainmenu", false, true );
|
||
|
uiSystemUIHandle = LoadUserInterface( "system", false, true );
|
||
|
pureWaitHandle = LoadUserInterface( "purewait", false, true );
|
||
|
|
||
|
if( sdUserInterfaceLocal* systemUI = GetUserInterface( uiSystemUIHandle ) ) {
|
||
|
systemUI->Activate();
|
||
|
}
|
||
|
|
||
|
LoadMainMenuPartialMedia( true );
|
||
|
}
|
||
|
|
||
|
g_commandMapZoomCvarCallback.Register();
|
||
|
|
||
|
com_unlockFPS = cvarSystem->Find( "com_unlockFPS" );
|
||
|
|
||
|
// Ensure dynamic items are precached
|
||
|
TouchMedia();
|
||
|
|
||
|
sdProficiencyManagerLocal::ReadRankInfo( rankInfo );
|
||
|
|
||
|
// cache rules
|
||
|
for ( int i = 0; i < idClass::GetNumTypes(); i++ ) {
|
||
|
idTypeInfo* type = idClass::GetType( i );
|
||
|
if ( !sdGameRules::IsRuleType( *type ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
rulesCache.Set( type->classname, type->CreateInstance()->Cast< sdGameRules >() );
|
||
|
}
|
||
|
|
||
|
isAutorecording = false;
|
||
|
hasTakenScoreShot = false;
|
||
|
|
||
|
Printf( "game initialized.\n" );
|
||
|
Printf( "--------------------------------------\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::TouchMedia
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::TouchMedia() {
|
||
|
// touch all crosshair defs
|
||
|
int num = declStringMapType.Num();
|
||
|
for( int i = 0; i < num; i++ ) {
|
||
|
const sdDeclStringMap* decl = gameLocal.declStringMapType.LocalFindByIndex( i, false );
|
||
|
if( idStr::Icmpn( decl->GetName(), "crosshairs", 10 ) != 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
// ensure we're parsed
|
||
|
gameLocal.declStringMapType.LocalFindByIndex( i, true );
|
||
|
|
||
|
sdDeclGUI::CacheMaterialDictionary( "GameLocal::Init", decl->GetDict() );
|
||
|
}
|
||
|
|
||
|
// ensure levelshots are copied
|
||
|
sdAddonMetaDataList* metaData = fileSystem->ListAddonMetaData( "mapMetaData" );
|
||
|
for( int i = 0; i < metaData->GetNumMetaData(); i++ ) {
|
||
|
const idDict& data = metaData->GetMetaData( i );
|
||
|
if( !data.GetBool( "show_in_browser" ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const char* imageName = data.GetString( "server_shot_thumb", "levelshots/thumbs/generic.tga" );
|
||
|
sdFilePtr f( fileSystem->OpenFileRead( imageName, true ) );
|
||
|
if( !f.IsValid() ) {
|
||
|
Warning( "Could not touch '%s'", imageName );
|
||
|
}
|
||
|
}
|
||
|
fileSystem->FreeAddonMetaDataList( metaData );
|
||
|
metaData = NULL;
|
||
|
|
||
|
for( int i = 0; i < gameLocal.campaignMetaDataList->GetNumMetaData(); i++ ) {
|
||
|
const idDict& data = gameLocal.campaignMetaDataList->GetMetaData( i );
|
||
|
if( !data.GetBool( "show_in_browser" ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
const idDict& dict = gameLocal.campaignMetaDataList->GetMetaData( i );
|
||
|
const char* imageName = data.GetString( "server_shot_thumb", "levelshots/campaigns/thumbs/custom.tga" );
|
||
|
sdFilePtr f( fileSystem->OpenFileRead( imageName, true ) );
|
||
|
if( !f.IsValid() ) {
|
||
|
Warning( "Could not touch '%s'", imageName );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ParseNetworkLog();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::RegisterLoggedTrace
|
||
|
================
|
||
|
*/
|
||
|
sdLoggedTrace* idGameLocal::RegisterLoggedTrace( const trace_t& trace ) {
|
||
|
for ( int i = 0; i < MAX_LOGGED_TRACES; i++ ) {
|
||
|
if ( !loggedTraces[ i ] ) {
|
||
|
loggedTraces[ i ] = new sdLoggedTrace();
|
||
|
loggedTraces[ i ]->Init( i, trace );
|
||
|
return loggedTraces[ i ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gameLocal.Warning( "idGameLocal::RegisterLoggedTrace No Free Traces" );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::FreeLoggedTrace
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::FreeLoggedTrace( sdLoggedTrace* trace ) {
|
||
|
if ( !trace ) {
|
||
|
gameLocal.Warning( "idGameLocal::FreeLoggedTrace NULL trace" );
|
||
|
assert( false );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int index = trace->GetIndex();
|
||
|
delete trace;
|
||
|
loggedTraces[ index ] = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::RegisterLoggedDecal
|
||
|
================
|
||
|
*/
|
||
|
int idGameLocal::RegisterLoggedDecal( const idMaterial* material ) {
|
||
|
if ( networkSystem->IsDedicated() ) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int i;
|
||
|
for ( i = 0; i < MAX_LOGGED_DECALS; i++ ) {
|
||
|
if ( !loggedDecals[ i ] ) {
|
||
|
gameDecalInfo_t* info = new gameDecalInfo_t;
|
||
|
loggedDecals[ i ] = info;
|
||
|
|
||
|
// init
|
||
|
memset( &info->renderEntity, 0, sizeof( info->renderEntity ));
|
||
|
info->renderEntity.hModel = gameRenderWorld->CreateDecalModel();
|
||
|
info->renderEntity.customShader = material;
|
||
|
info->renderEntity.spawnID = -1;
|
||
|
info->renderEntity.axis.Identity();
|
||
|
info->renderEntity.origin.Zero();
|
||
|
info->renderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
|
||
|
info->renderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0f;
|
||
|
info->renderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
|
||
|
info->renderEntity.shaderParms[ SHADERPARM_ALPHA ] = 1.0f;
|
||
|
|
||
|
info->renderEntityHandle = gameRenderWorld->AddEntityDef( &info->renderEntity );
|
||
|
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetLoggedDecal
|
||
|
================
|
||
|
*/
|
||
|
gameDecalInfo_t* idGameLocal::GetLoggedDecal( int index ) {
|
||
|
if ( index < 0 || index >= MAX_LOGGED_DECALS ) {
|
||
|
gameLocal.Warning( "idGameLocal::GetLoggedDecal : index '%d' out of bounds", index );
|
||
|
assert( false );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return loggedDecals[ index ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::ResetLoggedDecal
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::ResetLoggedDecal( int index ) {
|
||
|
if ( index < 0 || index >= MAX_LOGGED_DECALS ) {
|
||
|
gameLocal.Warning( "idGameLocal::ResetLoggedDecal : index '%d' out of bounds", index );
|
||
|
assert( false );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
gameDecalInfo_t* info = loggedDecals[ index ];
|
||
|
if( !info->renderEntity.hModel || !info->renderEntity.hModel->Surface( 0 )) {
|
||
|
assert( 0 );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
gameRenderWorld->ResetDecalModel( info->renderEntity.hModel );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::FreeLoggedDecal
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::FreeLoggedDecal( int index ) {
|
||
|
if ( index < 0 || index >= MAX_LOGGED_DECALS ) {
|
||
|
gameLocal.Warning( "idGameLocal::FreeLoggedDecal : index '%d' out of bounds", index );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
gameDecalInfo_t* info = loggedDecals[ index ];
|
||
|
assert( info );
|
||
|
if( !info ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if( info->renderEntity.hModel != NULL ) {
|
||
|
renderModelManager->FreeModel( info->renderEntity.hModel );
|
||
|
}
|
||
|
|
||
|
if ( gameRenderWorld ) {
|
||
|
gameRenderWorld->FreeEntityDef( info->renderEntityHandle );
|
||
|
}
|
||
|
|
||
|
delete info;
|
||
|
loggedDecals[ index ] = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::ClientsOnSameTeam
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::ClientsOnSameTeam( int clientNum1, int clientNum2, voiceMode_t voiceMode ) {
|
||
|
idPlayer* player1 = GetClient( clientNum1 );
|
||
|
idPlayer* player2 = GetClient( clientNum2 );
|
||
|
|
||
|
if ( player1 == NULL || player2 == NULL ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Gordon: Client 1 is the one talking, so only check that mask
|
||
|
if ( clientMuteMask[ clientNum1 ].Get( clientNum2 ) != 0 ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
switch ( voiceMode ) {
|
||
|
case VO_TEAM:
|
||
|
if ( !player2->userInfo.voipReceiveTeam ) {
|
||
|
return false;
|
||
|
}
|
||
|
return player1->IsTeam( player2->GetTeam() );
|
||
|
case VO_FIRETEAM: {
|
||
|
if ( !player2->userInfo.voipReceiveFireTeam ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( !player1->IsTeam( player2->GetTeam() ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
sdFireTeam* f1 = gameLocal.rules->GetPlayerFireTeam( clientNum1 );
|
||
|
sdFireTeam* f2 = gameLocal.rules->GetPlayerFireTeam( clientNum2 );
|
||
|
if ( f1 != NULL || f2 != NULL ) {
|
||
|
return f1 == f2;
|
||
|
}
|
||
|
|
||
|
taskHandle_t t1 = player1->GetActiveTaskHandle();
|
||
|
taskHandle_t t2 = player2->GetActiveTaskHandle();
|
||
|
if ( t1.IsValid() || t2.IsValid() ) {
|
||
|
return t1 == t2;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
case VO_GLOBAL: {
|
||
|
if ( !player2->userInfo.voipReceiveGlobal ) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::AllowClientAudio
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::AllowClientAudio( int clientNum, voiceMode_t voiceTeam ) {
|
||
|
idPlayer* player = GetClient( clientNum );
|
||
|
if ( !player ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.rules->MuteStatus( player ) & MF_AUDIO ) {
|
||
|
const sdUserGroup& userGroup = sdUserGroupManager::GetInstance().GetGroup( player->GetUserGroup() );
|
||
|
if ( !userGroup.HasPermission( PF_NO_MUTE_VOIP ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( voiceTeam == VO_GLOBAL ) {
|
||
|
if ( g_disableGlobalAudio.GetBool() ) {
|
||
|
const sdUserGroup& userGroup = sdUserGroupManager::GetInstance().GetGroup( player->GetUserGroup() );
|
||
|
if ( !userGroup.HasPermission( PF_NO_MUTE_VOIP ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::SetRules
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetRules( idTypeInfo* type ) {
|
||
|
delete rules;
|
||
|
rules = type->CreateInstance()->Cast< sdGameRules >();
|
||
|
if ( rules == NULL ) {
|
||
|
Error( "idGameLocal::SetRules '%s' is not a valid rule type", type->classname );
|
||
|
}
|
||
|
|
||
|
if ( program && program->IsValid() ) {
|
||
|
rules->OnNewScriptLoad();
|
||
|
}
|
||
|
|
||
|
if ( isServer ) {
|
||
|
sdReliableServerMessage msg( GAME_RELIABLE_SMESSAGE_RULES_DATA );
|
||
|
msg.WriteLong( sdGameRules::EVENT_CREATE );
|
||
|
msg.WriteLong( type->typeNum );
|
||
|
msg.Send( sdReliableMessageClientInfoAll() );
|
||
|
}
|
||
|
|
||
|
ResetGameState( ENSM_RULES );
|
||
|
|
||
|
// multiplayer commands
|
||
|
rules->InitConsoleCommands();
|
||
|
|
||
|
if( !networkSystem->IsDedicated() ) {
|
||
|
UpdateCampaignStats( false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::KeyMove
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::KeyMove( char forward, char right, char up, usercmd_t& cmd ) {
|
||
|
idPlayer* player = GetLocalPlayer();
|
||
|
if ( !player ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return player->KeyMove( forward, right, up, cmd );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::ControllerMove
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::ControllerMove( bool doGameCallback, const int numControllers, const int* controllerNumbers, const float** controllerAxis, idVec3& viewAngles, usercmd_t& cmd ) {
|
||
|
idPlayer* player = GetLocalPlayer();
|
||
|
if ( !player ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sdHudModule* module = gameLocal.localPlayerProperties.GetActiveHudModule();
|
||
|
if ( module != NULL && module->InhibitControllerMovement() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
player->ControllerMove( doGameCallback, numControllers, controllerNumbers, controllerAxis, viewAngles, cmd );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::MouseMove
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::MouseMove( const idVec3& vecAngleBase, idVec3& vecAngleDelta ) {
|
||
|
if ( IsPaused() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idPlayer* player = GetLocalPlayer();
|
||
|
if ( !player ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idAngles angleBase( vecAngleBase[ PITCH ], vecAngleBase[ YAW ], 0.0f );
|
||
|
idAngles angleDelta( vecAngleDelta[ PITCH ], vecAngleDelta[ YAW ], 0.0f );
|
||
|
player->MouseMove( angleBase, angleDelta );
|
||
|
vecAngleDelta[ PITCH ] = angleDelta.pitch;
|
||
|
vecAngleDelta[ YAW ] = angleDelta.yaw;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::GetSensitivity
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::GetSensitivity( float& scaleX, float& scaleY ) {
|
||
|
if ( IsPaused() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
idPlayer* player = GetLocalPlayer();
|
||
|
if ( !player ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return player->GetSensitivity( scaleX, scaleY );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::Shutdown
|
||
|
|
||
|
shut down the entire game
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::Shutdown( void ) {
|
||
|
|
||
|
if ( common == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Printf( "------------ Game Shutdown -----------\n" );
|
||
|
|
||
|
rulesCache.DeleteValues();
|
||
|
rulesCache.Clear();
|
||
|
|
||
|
if ( damageLogFile != NULL ) {
|
||
|
fileSystem->CloseFile( damageLogFile );
|
||
|
damageLogFile = NULL;
|
||
|
}
|
||
|
|
||
|
if ( debugLogFile != NULL ) {
|
||
|
fileSystem->CloseFile( debugLogFile );
|
||
|
debugLogFile = NULL;
|
||
|
}
|
||
|
|
||
|
if ( proficiencyLog != NULL ) {
|
||
|
fileSystem->CloseFile( proficiencyLog );
|
||
|
proficiencyLog = NULL;
|
||
|
}
|
||
|
|
||
|
if ( networkLog != NULL ) {
|
||
|
fileSystem->CloseFile( networkLog );
|
||
|
networkLog = NULL;
|
||
|
}
|
||
|
|
||
|
if ( objectiveLog != NULL ) {
|
||
|
fileSystem->CloseFile( objectiveLog );
|
||
|
objectiveLog = NULL;
|
||
|
}
|
||
|
|
||
|
if ( rules ) {
|
||
|
rules->Shutdown();
|
||
|
}
|
||
|
|
||
|
MapShutdown();
|
||
|
|
||
|
idWeapon::UnRegisterCVarCallback();
|
||
|
sdDeclAOR::ShutdownCVars();
|
||
|
|
||
|
sdRequirementCheck::ShutdownFactory();
|
||
|
sdDeclItemPackage::ShutdownConsumables();
|
||
|
sdDeclGUI::ClearDefines();
|
||
|
|
||
|
idDeclTypeInterface* declInterface = declManager->GetDeclType( declSurfaceTypeIdentifier );
|
||
|
declInterface->UnregisterPostParse( idGameLocal::SurfaceTypePostParse );
|
||
|
|
||
|
fileSystem->FreeAddonMetaDataList( mapMetaDataList );
|
||
|
mapMetaDataList = NULL;
|
||
|
|
||
|
fileSystem->FreeAddonMetaDataList( campaignMetaDataList );
|
||
|
campaignMetaDataList = NULL;
|
||
|
|
||
|
// unregister game specific decl types
|
||
|
declManager->UnregisterDeclType( &declModelDefType );
|
||
|
declManager->UnregisterDeclType( &declExportDefType );
|
||
|
declManager->UnregisterDeclType( &declVehicleScriptDefType );
|
||
|
declManager->UnregisterDeclType( &declAmmoTypeType );
|
||
|
declManager->UnregisterDeclType( &declInvSlotType );
|
||
|
declManager->UnregisterDeclType( &declInvItemTypeType );
|
||
|
declManager->UnregisterDeclType( &declInvItemType );
|
||
|
declManager->UnregisterDeclType( &declItemPackageType );
|
||
|
declManager->UnregisterDeclType( &declStringMapType );
|
||
|
declManager->UnregisterDeclType( &declDamageType );
|
||
|
declManager->UnregisterDeclType( &declDamageFilterType );
|
||
|
declManager->UnregisterDeclType( &declCampaignType );
|
||
|
declManager->UnregisterDeclType( &declQuickChatType );
|
||
|
declManager->UnregisterDeclType( &declMapInfoType );
|
||
|
declManager->UnregisterDeclType( &declToolTipType );
|
||
|
declManager->UnregisterDeclType( &declTargetInfoType );
|
||
|
declManager->UnregisterDeclType( &declProficiencyTypeType );
|
||
|
declManager->UnregisterDeclType( &declProficiencyItemType );
|
||
|
declManager->UnregisterDeclType( &declRankType );
|
||
|
declManager->UnregisterDeclType( &declDeployableObjectType );
|
||
|
declManager->UnregisterDeclType( &declDeployableZoneType );
|
||
|
declManager->UnregisterDeclType( &declPlayerClassType );
|
||
|
declManager->UnregisterDeclType( &declGUIType );
|
||
|
declManager->UnregisterDeclType( &declTeamInfoType );
|
||
|
declManager->UnregisterDeclType( &declPlayerTaskType );
|
||
|
declManager->UnregisterDeclType( &declRequirementType );
|
||
|
declManager->UnregisterDeclType( &declGUIThemeType );
|
||
|
declManager->UnregisterDeclType( &declVehiclePathType );
|
||
|
declManager->UnregisterDeclType( &declKeyBindingType );
|
||
|
declManager->UnregisterDeclType( &declRadialMenuType );
|
||
|
declManager->UnregisterDeclType( &declAORType );
|
||
|
declManager->UnregisterDeclType( &declRatingType );
|
||
|
declManager->UnregisterDeclType( &declHeightMapType );
|
||
|
declManager->UnregisterDeclType( &declDeployMaskType );
|
||
|
|
||
|
// register game specific decl folders
|
||
|
declManager->UnregisterDeclFolder( "def", ".def" );
|
||
|
declManager->UnregisterDeclFolder( "af", ".af" );
|
||
|
declManager->UnregisterDeclFolder( "atmosphere", ".climate" );
|
||
|
declManager->UnregisterDeclFolder( "atmosphere", ".forecast" );
|
||
|
declManager->UnregisterDeclFolder( "vehicles", ".vscript" );
|
||
|
declManager->UnregisterDeclFolder( "quickchat", ".qc" );
|
||
|
declManager->UnregisterDeclFolder( "mapinfo", ".txt" );
|
||
|
declManager->UnregisterDeclFolder( "mapinfo", ".md" );
|
||
|
declManager->UnregisterDeclFolder( "effects", ".effect" );
|
||
|
declManager->UnregisterDeclFolder( "guis", ".gui" );
|
||
|
declManager->UnregisterDeclFolder( "guis", ".guitheme" );
|
||
|
declManager->UnregisterDeclFolder( "bindings", ".binding" );
|
||
|
declManager->UnregisterDeclFolder( "decals", ".decal" );
|
||
|
declManager->UnregisterDeclFolder( "menus", ".radialmenu" );
|
||
|
|
||
|
#if defined( ID_ALLOW_TOOLS )
|
||
|
// shutdown the model exporter
|
||
|
idModelExport::Shutdown();
|
||
|
#endif /* ID_ALLOW_TOOLS */
|
||
|
|
||
|
sdRequirementManager::DestroyInstance();
|
||
|
sdProficiencyManager::DestroyInstance();
|
||
|
sdTeamManager::DestroyInstance();
|
||
|
|
||
|
int i;
|
||
|
for ( i = 0; i < loggedTraces.Num(); i++ ) {
|
||
|
if ( loggedTraces[ i ] ) {
|
||
|
FreeLoggedTrace( loggedTraces[ i ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ( i = 0; i < loggedDecals.Num(); i++ ) {
|
||
|
if ( loggedDecals[ i ] ) {
|
||
|
FreeLoggedDecal( i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete program;
|
||
|
program = NULL;
|
||
|
|
||
|
ClearPlayZones();
|
||
|
|
||
|
entityCollections.DeleteContents( true );
|
||
|
entityCollectionsHash.Clear();
|
||
|
|
||
|
sdAnimFrameCommand::Shutdown();
|
||
|
|
||
|
sdGlobalStatsTracker::DestroyInstance();
|
||
|
|
||
|
idEvent::Shutdown();
|
||
|
|
||
|
idClass::ShutdownClasses();
|
||
|
|
||
|
// clear list with forces
|
||
|
idForce::ClearForceList();
|
||
|
|
||
|
// delete the .map file
|
||
|
delete mapFile;
|
||
|
mapFile = NULL;
|
||
|
|
||
|
//mal: delete the bot entities
|
||
|
delete botMapFile;
|
||
|
botMapFile = NULL;
|
||
|
|
||
|
ShutdownConsoleCommands();
|
||
|
|
||
|
clip.FreeThread();
|
||
|
|
||
|
// free memory allocated by class objects
|
||
|
Clear();
|
||
|
|
||
|
// shut down the animation manager
|
||
|
animationLib.Shutdown();
|
||
|
|
||
|
playZoneAreas.DeleteContents( true );
|
||
|
|
||
|
delete rules;
|
||
|
rules = NULL;
|
||
|
|
||
|
uiManager->Clear( true );
|
||
|
uiManager->Shutdown();
|
||
|
|
||
|
sdCommandMapInfoManager::GetInstance().Shutdown();
|
||
|
sdCommandMapInfoManager::DestroyInstance();
|
||
|
|
||
|
sdVehicleSuspension::Shutdown();
|
||
|
sdVehicleView::Shutdown();
|
||
|
sdScriptedEntityHelper::Shutdown();
|
||
|
sdVehicleSoundControlBase::Shutdown();
|
||
|
|
||
|
#ifdef _XENON
|
||
|
liveManager->Shutdown();
|
||
|
#else
|
||
|
sdnet.Shutdown();
|
||
|
updateManager.Shutdown();
|
||
|
#endif
|
||
|
|
||
|
localPlayerProperties.Shutdown();
|
||
|
globalProperties.Shutdown();
|
||
|
limboProperties.Shutdown();
|
||
|
|
||
|
sdDemoManager::GetInstance().Shutdown();
|
||
|
sdDemoManager::DestroyInstance();
|
||
|
|
||
|
Printf( "--------------------------------------\n" );
|
||
|
|
||
|
// remove auto-completion function pointers pointing into this DLL
|
||
|
cvarSystem->RemoveFlaggedAutoCompletion( CVAR_GAME );
|
||
|
|
||
|
// enable leak test
|
||
|
Mem_EnableLeakTest( "game" );
|
||
|
|
||
|
// shutdown idLib
|
||
|
#if !defined( MONOLITHIC )
|
||
|
idLib::ShutDown();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::Printf
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::Printf( const char *fmt, ... ) const {
|
||
|
va_list argptr;
|
||
|
char text[MAX_STRING_CHARS];
|
||
|
|
||
|
va_start( argptr, fmt );
|
||
|
idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
|
||
|
va_end( argptr );
|
||
|
|
||
|
common->Printf( "%s", text );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::DPrintf
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::DPrintf( const char *fmt, ... ) const {
|
||
|
va_list argptr;
|
||
|
char text[MAX_STRING_CHARS];
|
||
|
|
||
|
if ( !IsDeveloper() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
va_start( argptr, fmt );
|
||
|
idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
|
||
|
va_end( argptr );
|
||
|
|
||
|
common->DPrintf( "%s", text );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::Warning
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::Warning( const char *fmt, ... ) const {
|
||
|
va_list argptr;
|
||
|
char text[MAX_STRING_CHARS];
|
||
|
|
||
|
va_start( argptr, fmt );
|
||
|
idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
|
||
|
va_end( argptr );
|
||
|
|
||
|
sdProgramThread* thread = NULL;
|
||
|
if ( gameLocal.program != NULL ) {
|
||
|
thread = gameLocal.program->GetCurrentThread();
|
||
|
}
|
||
|
|
||
|
if ( thread != NULL ) {
|
||
|
thread->Warning( "%s", text );
|
||
|
} else {
|
||
|
common->Warning( "%s", text );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::DWarning
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::DWarning( const char *fmt, ... ) const {
|
||
|
va_list argptr;
|
||
|
char text[MAX_STRING_CHARS];
|
||
|
|
||
|
if ( !IsDeveloper() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
va_start( argptr, fmt );
|
||
|
idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
|
||
|
va_end( argptr );
|
||
|
|
||
|
sdProgramThread* thread = NULL;
|
||
|
if ( gameLocal.program != NULL ) {
|
||
|
thread = gameLocal.program->GetCurrentThread();
|
||
|
}
|
||
|
|
||
|
if ( thread != NULL ) {
|
||
|
thread->Warning( "%s", text );
|
||
|
} else {
|
||
|
common->DWarning( "%s", text );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::Error
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::Error( const char *fmt, ... ) const {
|
||
|
va_list argptr;
|
||
|
char text[MAX_STRING_CHARS];
|
||
|
|
||
|
va_start( argptr, fmt );
|
||
|
idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
|
||
|
va_end( argptr );
|
||
|
|
||
|
assert( false );
|
||
|
|
||
|
if ( program != NULL ) {
|
||
|
if ( program->OnError( text ) ) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
common->Error( "%s", text );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
gameError
|
||
|
===============
|
||
|
*/
|
||
|
void gameError( const char *fmt, ... ) {
|
||
|
va_list argptr;
|
||
|
char text[MAX_STRING_CHARS];
|
||
|
|
||
|
va_start( argptr, fmt );
|
||
|
idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
|
||
|
va_end( argptr );
|
||
|
|
||
|
gameLocal.Error( "%s", text );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::ToggleNoclipMode
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::ToggleNoclipMode( idPlayer* player ) const {
|
||
|
if ( !player ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !gameLocal.CheatsOk( false ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const wchar_t* msg;
|
||
|
if ( player->GetNoClip() ) {
|
||
|
player->SetNoClip( false );
|
||
|
msg = L"noclip OFF";
|
||
|
} else {
|
||
|
player->SetNoClip( true );
|
||
|
msg = L"noclip ON";
|
||
|
}
|
||
|
|
||
|
player->SendUnLocalisedMessage( msg );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::ToggleGodMode
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::ToggleGodMode( idPlayer* player ) const {
|
||
|
if ( !player ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !gameLocal.CheatsOk( false ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const wchar_t* msg;
|
||
|
if ( player->GetGodMode() ) {
|
||
|
player->SetGodMode( false );
|
||
|
msg = L"godmode OFF";
|
||
|
} else {
|
||
|
player->SetGodMode( true );
|
||
|
msg = L"godmode ON";
|
||
|
}
|
||
|
|
||
|
player->SendUnLocalisedMessage( msg );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::NetworkSpawn
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::NetworkSpawn( const char* classname, idPlayer* player ) {
|
||
|
if ( !player ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !gameLocal.CheatsOk( false ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
float yaw;
|
||
|
idVec3 org;
|
||
|
idDict dict;
|
||
|
|
||
|
yaw = player->viewAngles.yaw;
|
||
|
|
||
|
dict.Set( "classname", classname );
|
||
|
dict.SetFloat( "angle", yaw + 180 );
|
||
|
|
||
|
org = player->GetPhysics()->GetOrigin() + idAngles( 0, yaw, 0 ).ToForward() * 80 + idVec3( 0, 0, 1 );
|
||
|
dict.SetVector( "origin", org );
|
||
|
|
||
|
gameLocal.SpawnEntityDef( dict, true );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::UserInfoChanged
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::UserInfoChanged( int clientNum ) {
|
||
|
idPlayer* player = GetClient( clientNum );
|
||
|
if ( player != NULL ) {
|
||
|
player->UserInfoChanged();
|
||
|
rules->EnterGame( player );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::CleanName
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::CleanName( idStr& name ) {
|
||
|
name.RemoveColors();
|
||
|
name.StripTrailingWhiteSpace();
|
||
|
}
|
||
|
|
||
|
#define USERINFO_GET_NEW_NAME \
|
||
|
baseName = _userInfo.GetString( "ui_name" ); \
|
||
|
name = baseName; \
|
||
|
CleanName( name );
|
||
|
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::ValidateUserInfo
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::ValidateUserInfo( int clientNum, idDict& _userInfo ) {
|
||
|
bool modifiedInfo = false;
|
||
|
|
||
|
idStr baseName;
|
||
|
idStr name;
|
||
|
|
||
|
USERINFO_GET_NEW_NAME
|
||
|
|
||
|
// don't let numeric nicknames, it can be exploited to go around kick and ban commands from the server
|
||
|
if ( baseName.IsNumeric() ) {
|
||
|
_userInfo.Set( "ui_name", va( "%s_", baseName.c_str() ) );
|
||
|
modifiedInfo = true;
|
||
|
|
||
|
USERINFO_GET_NEW_NAME
|
||
|
}
|
||
|
|
||
|
sdNetClientId clientId;
|
||
|
networkSystem->ServerGetClientNetId( clientNum, clientId );
|
||
|
|
||
|
bool demonwareAccountName = false;
|
||
|
if ( clientId.IsValid() ) {
|
||
|
demonwareAccountName = true;
|
||
|
}
|
||
|
|
||
|
// don't allow dupe nicknames - unless its a bot, in which case he'll change HIS name to defer to the human.
|
||
|
for ( int i = 0; i < numClients; i++ ) {
|
||
|
if ( i == clientNum ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
idPlayer* player = GetClient( i );
|
||
|
if ( player ) {
|
||
|
idStr cleanBaseName = player->userInfo.baseName;
|
||
|
idGameLocal::CleanName( cleanBaseName );
|
||
|
if ( !idStr::Icmp( name, cleanBaseName ) ) {
|
||
|
if ( player->IsType( idBot::Type ) ) {
|
||
|
botThreadData.ChangeBotName( player );
|
||
|
i = -1; //rescan. There shouldn't be other bots with this name, but COULD be another human player.
|
||
|
} else if ( !demonwareAccountName ) {
|
||
|
_userInfo.Set( "ui_name", va( "%s_", baseName.c_str() ) );
|
||
|
modifiedInfo = true;
|
||
|
|
||
|
USERINFO_GET_NEW_NAME
|
||
|
|
||
|
i = -1; // rescan
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// allow users to see bots
|
||
|
idPlayer* p = GetClient( clientNum );
|
||
|
if ( p != NULL ) {
|
||
|
bool isBot = p->IsType( idBot::Type );
|
||
|
if ( _userInfo.GetBool( "ui_bot" ) != isBot ) {
|
||
|
_userInfo.SetBool( "ui_bot", isBot );
|
||
|
modifiedInfo = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return modifiedInfo;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::SetServerInfo
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetServerInfo( const idDict& _serverInfo ) {
|
||
|
serverInfo = _serverInfo;
|
||
|
UpdateServerInfoFlags();
|
||
|
ParseServerInfo();
|
||
|
#ifdef SD_SUPPORT_REPEATER
|
||
|
UpdateRepeaterInfo();
|
||
|
#endif // SD_SUPPORT_REPEATER
|
||
|
|
||
|
sdnet.UpdateGameSession( true, false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::ParseServerInfo
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::ParseServerInfo( void ) {
|
||
|
serverInfoData.timeLimit = Max( 0, MINS2MS( serverInfo.GetFloat( "si_timeLimit" ) ) );
|
||
|
serverInfoData.adminStart = serverInfo.GetBool( "si_adminStart" );
|
||
|
serverInfoData.noProficiency = serverInfo.GetBool( "si_noProficiency" );
|
||
|
serverInfoData.votingDisabled = serverInfo.GetBool( "si_disableVoting" );
|
||
|
serverInfoData.minPlayers = serverInfo.GetInt( "si_minPlayers" );
|
||
|
serverInfoData.readyPercent = serverInfo.GetInt( "si_readyPercent" );
|
||
|
serverInfoData.gameReviewReadyWait = serverInfo.GetBool( "si_gameReviewReadyWait" );
|
||
|
|
||
|
localPlayerProperties.OnServerInfoChanged();
|
||
|
|
||
|
UpdateCampaignStats( false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::PushChangedEntity
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::PushChangedEntity( idEntity* ent ) {
|
||
|
changedEntities.Alloc() = ent;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::LoadMap
|
||
|
|
||
|
Initializes all map variables common to both save games and spawned games.
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::LoadMap( const char* mapName, int randSeed, int startTime ) {
|
||
|
|
||
|
doingMapRestart = false;
|
||
|
|
||
|
if ( !reloadingSameMap ) {
|
||
|
useSimpleEffect = bse_simple.GetBool();
|
||
|
}
|
||
|
|
||
|
// regrab map/campaign meta data
|
||
|
fileSystem->FreeAddonMetaDataList( mapMetaDataList );
|
||
|
fileSystem->FreeAddonMetaDataList( campaignMetaDataList );
|
||
|
|
||
|
mapMetaDataList = fileSystem->ListAddonMetaData( "mapMetaData" );
|
||
|
campaignMetaDataList = fileSystem->ListAddonMetaData( "campaignMetaData" );
|
||
|
|
||
|
SetupMapMetaData( mapName );
|
||
|
|
||
|
gameLocal.declEntityDefType[ "player_edf" ];
|
||
|
gameLocal.declEntityDefType[ "bot_edf" ];
|
||
|
|
||
|
if ( !reloadingSameMap ) {
|
||
|
OnNewMapLoad( mapName );
|
||
|
} else {
|
||
|
if ( program == NULL || !program->IsValid() ) {
|
||
|
LoadScript();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char* zoneName = mapMetaData->GetString( "massive_zone_name" );
|
||
|
if ( !*zoneName ) {
|
||
|
zoneName = mapName;
|
||
|
}
|
||
|
adManager->SetAdZone( zoneName );
|
||
|
|
||
|
// Gordon: not precaching this entry automatically on purpose, as it'll get hit outside of level load otherwise
|
||
|
mapSkinPool = gameLocal.declStringMapType[ mapMetaData->GetString( "climate_skins", "climate_skins_temperate" ) ];
|
||
|
if ( mapSkinPool != NULL ) {
|
||
|
gameLocal.CacheDictionaryMedia( mapSkinPool->GetDict() );
|
||
|
}
|
||
|
|
||
|
// clear the sound system
|
||
|
// jrad - don't do this, since we need to play during level loads
|
||
|
//gameSoundWorld->ClearAllSoundEmitters();
|
||
|
|
||
|
localPlayerProperties.CloseActiveHudModules();
|
||
|
|
||
|
if ( !reloadingSameMap || mapFile == NULL || mapFile->NeedsReload() ) {
|
||
|
delete mapFile;
|
||
|
mapFile = new idMapFile;
|
||
|
if ( !mapFile->Parse( ( idStr( mapName ) + "." ) + ENTITY_FILE_EXT ) ) {
|
||
|
delete mapFile;
|
||
|
mapFile = NULL;
|
||
|
Error( "Couldn't load %s", mapName );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !reloadingSameMap || botMapFile == NULL || botMapFile->NeedsReload() ) {
|
||
|
delete botMapFile;
|
||
|
botMapFile = new idMapFile;
|
||
|
if ( !botMapFile->ParseBotEntities( ( idStr( mapName ) + "." ) + BOT_ENTITY_FILE_EXT ) ) {
|
||
|
delete botMapFile;
|
||
|
botMapFile = NULL;
|
||
|
Warning( "Couldn't load bot entities for %s", mapName );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mapFileName = mapName;
|
||
|
|
||
|
if ( mapFile != NULL && gameLocal.isServer ) {
|
||
|
gameLocal.Printf( "----------- Loading Map AAS ------------\n" );
|
||
|
botThreadData.LoadMap( mapName, randSeed );
|
||
|
botThreadData.InitAAS( mapFile ); //mal: load the AAS for this map, if the mapfile was loaded.
|
||
|
}
|
||
|
|
||
|
// load the collision map
|
||
|
collisionModelManager->LoadMap( mapName, false );
|
||
|
|
||
|
numClients = 0;
|
||
|
|
||
|
// initialize all entities for this game
|
||
|
entities.Memset( 0 );
|
||
|
memset( usercmds, 0, sizeof( usercmds ) );
|
||
|
memset( spawnIds, -1, sizeof( spawnIds ) );
|
||
|
spawnCount = INITIAL_SPAWN_COUNT;
|
||
|
|
||
|
spawnedEntities.Clear();
|
||
|
activeEntities.Clear();
|
||
|
activeNetworkEntities.Clear();
|
||
|
changedEntities.Clear();
|
||
|
sortTeamMasters = false;
|
||
|
sortPushers = false;
|
||
|
|
||
|
clientSpawnedEntities.Clear ();
|
||
|
for (int i=0; i<MAX_CENTITIES; i++) {
|
||
|
clientSpawnIds[i] = -INITIAL_SPAWN_COUNT;
|
||
|
}
|
||
|
memset ( clientEntities, 0, sizeof(clientEntities) );
|
||
|
firstFreeClientIndex = 0;
|
||
|
|
||
|
globalMaterial = NULL;
|
||
|
|
||
|
// 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
|
||
|
numEntities = MAX_CLIENTS;
|
||
|
firstFreeIndex = MAX_CLIENTS;
|
||
|
|
||
|
// reset the random number generator.
|
||
|
random.SetSeed( randSeed );
|
||
|
|
||
|
camera = NULL;
|
||
|
world = NULL;
|
||
|
testmodel = NULL;
|
||
|
//testFx = NULL;
|
||
|
|
||
|
this->startTime = startTime;
|
||
|
previousTime = startTime;
|
||
|
time = startTime;
|
||
|
timeOffset = 0;
|
||
|
nextBotPopulationCheck = startTime;
|
||
|
|
||
|
framenum = 0;
|
||
|
|
||
|
if ( !editEntities ) {
|
||
|
editEntities = new idEditEntities;
|
||
|
}
|
||
|
|
||
|
gravity.Set( 0, 0, -g_gravity.GetFloat() );
|
||
|
|
||
|
spawnArgs.Clear();
|
||
|
|
||
|
clip.Init();
|
||
|
pvs.Init();
|
||
|
aorManager.OnMapLoad();
|
||
|
sdAntiLagManager::GetInstance().OnMapLoad();
|
||
|
sdCommandMapInfoManager::GetInstance().Clear();
|
||
|
|
||
|
if ( !reloadingSameMap ) {
|
||
|
mapFile->RemovePrimitiveData(); // FIXME: Arnout/Jared: deprecated - we should remove this
|
||
|
}
|
||
|
|
||
|
CloseMessageBox(); // jrad - close any error/connecting message boxes in the main menu now that we're in
|
||
|
|
||
|
if( sdUserInterfaceScope* scope = gameLocal.globalProperties.GetSubScope( "game" ) ) {
|
||
|
if( sdProperties::sdProperty* property = scope->GetProperty( "isRunning", sdProperties::PT_FLOAT )) {
|
||
|
*property->value.floatValue = GameState() > GAMESTATE_NOMAP ? 1.0f : 0.0f;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::OnLocalMapRestart
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::OnLocalMapRestart( void ) {
|
||
|
rules->OnLocalMapRestart();
|
||
|
sdObjectiveManager::GetInstance().OnLocalMapRestart();
|
||
|
|
||
|
hasTakenScoreShot = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::LocalMapRestart
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::LocalMapRestart( void ) {
|
||
|
doingMapRestart = true;
|
||
|
|
||
|
renderSystem->LockThreads();
|
||
|
|
||
|
if ( gameLocal.isServer ) {
|
||
|
sdReliableServerMessage msg( GAME_RELIABLE_SMESSAGE_MAP_RESTART );
|
||
|
msg.Send( sdReliableMessageClientInfoAll() );
|
||
|
}
|
||
|
OnLocalMapRestart();
|
||
|
|
||
|
botThread->StopThread();
|
||
|
|
||
|
gamestate = GAMESTATE_SHUTDOWN;
|
||
|
|
||
|
idStr currentSpawnPoints[ MAX_CLIENTS ];
|
||
|
for ( int i = 0; i < numClients; i++ ) {
|
||
|
idPlayer* player = GetClient( i );
|
||
|
if ( player == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
idEntity* spawn = player->GetSpawnPoint();
|
||
|
if ( spawn != NULL ) {
|
||
|
currentSpawnPoints[ i ] = spawn->GetName();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MapClear( false );
|
||
|
|
||
|
gamestate = GAMESTATE_STARTUP;
|
||
|
|
||
|
botThreadData.Clear( true );
|
||
|
|
||
|
if ( mapFile != NULL && gameLocal.isServer ) {
|
||
|
botThreadData.LoadMap( mapFile->GetName(), sys->Milliseconds() );
|
||
|
botThreadData.InitAAS( mapFile ); //mal: load the AAS for this map, if the mapfile was loaded.
|
||
|
}
|
||
|
|
||
|
OnPreMapStart();
|
||
|
SpawnMapEntities();
|
||
|
OnMapStart();
|
||
|
|
||
|
if ( botMapFile != NULL ) {
|
||
|
botThreadData.LoadActions( botMapFile ); //mal: load any bot actions for this map, if the bot entities were loaded.
|
||
|
|
||
|
if ( gameLocal.isServer ) {
|
||
|
botThreadData.LoadRoutes( botMapFile ); //mal: load any bot routes for this map, if the bot entities were loaded.
|
||
|
idStr navName = botMapFile->GetName();
|
||
|
navName.SetFileExtension( "nav" );
|
||
|
botThreadData.botVehicleNodes.LoadNodes( navName );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// setup the client entities again
|
||
|
for ( int i = 0; i < numClients; i++ ) {
|
||
|
idPlayer* player = GetClient( i );
|
||
|
if ( player == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
player->ClearDeathSound();
|
||
|
player->Killed( NULL, NULL, 0, vec3_zero, -1, NULL );
|
||
|
player->ServerForceRespawn( true );
|
||
|
|
||
|
if ( currentSpawnPoints[ i ].Length() ) {
|
||
|
idEntity* spawn = gameLocal.FindEntity( currentSpawnPoints[ i ] );
|
||
|
if ( spawn != NULL ) {
|
||
|
player->SetSpawnPoint( spawn );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gamestate = GAMESTATE_ACTIVE;
|
||
|
|
||
|
botThreadData.Init();
|
||
|
botThreadData.LoadBotNames();
|
||
|
botThreadData.ResetClientsInfo();
|
||
|
botThreadData.ResetBotsInfo();
|
||
|
|
||
|
botThread->StartThread();
|
||
|
renderSystem->UnlockThreads();
|
||
|
|
||
|
doingMapRestart = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::MapRestart
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::MapRestart( void ) {
|
||
|
if ( isClient ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
LocalMapRestart();
|
||
|
rules->MapRestart();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::MapRestart_f
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::MapRestart_f( const idCmdArgs &args ) {
|
||
|
if ( !gameLocal.isServer ) {
|
||
|
gameLocal.Printf( "server is not running - use spawnServer\n" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
gameLocal.MapRestart();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::NextMap_f
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::NextMap_f( const idCmdArgs &args ) {
|
||
|
gameLocal.NextMap();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::StartDemos_f
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::StartDemos_f( const idCmdArgs &args ) {
|
||
|
if ( gameLocal.isClient ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.isServer ) {
|
||
|
sdReliableServerMessage msg( GAME_RELIABLE_SMESSAGE_RECORD_DEMO );
|
||
|
msg.Send( sdReliableMessageClientInfoAll() );
|
||
|
}
|
||
|
|
||
|
gameLocal.StartRecordingDemo();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::GetRulesType
|
||
|
===================
|
||
|
*/
|
||
|
idTypeInfo* idGameLocal::GetRulesType( bool errorOnFail ) {
|
||
|
const char* ruleset;
|
||
|
if ( networkSystem->IsRankedServer() ) {
|
||
|
ruleset = "sdGameRulesCampaign";
|
||
|
} else {
|
||
|
ruleset = si_rules.GetString();
|
||
|
}
|
||
|
|
||
|
idTypeInfo* type = idClass::GetClass( ruleset );
|
||
|
if ( type == NULL || !sdGameRules::IsRuleType( *type ) ) {
|
||
|
if ( errorOnFail ) {
|
||
|
gameLocal.Error( "idGameLocal::GetRulesType Invalid Rule Type '%s'", ruleset );
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
return type;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::MakeRules
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::MakeRules( void ) {
|
||
|
if ( networkSystem->IsRankedServer() ) {
|
||
|
si_rules.SetString( "sdGameRulesCampaign" );
|
||
|
}
|
||
|
|
||
|
idTypeInfo* type = GetRulesType( true );
|
||
|
if ( rules != NULL ) {
|
||
|
if ( rules->GetType() == type ) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool playerStatesStored = false;
|
||
|
sdGameRules::playerStateList_t playerStates;
|
||
|
if ( gameLocal.rules != NULL ) {
|
||
|
playerStatesStored = true;
|
||
|
gameLocal.rules->SavePlayerStates( playerStates );
|
||
|
}
|
||
|
|
||
|
SetRules( type );
|
||
|
|
||
|
if ( playerStatesStored ) {
|
||
|
gameLocal.rules->RestorePlayerStates( playerStates );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::OnPreMapStart
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::OnPreMapStart( void ) {
|
||
|
localPlayerProperties.ShutdownGUIs();
|
||
|
PurgeMainMenuPartialMedia();
|
||
|
|
||
|
playzoneMask = gameLocal.GetDeploymentMask( "dm_playzone" );
|
||
|
|
||
|
idDoor::OnNewMapLoad();
|
||
|
idLight::OnNewMapLoad();
|
||
|
idSound::OnNewMapLoad();
|
||
|
sdLocationMarker::OnNewMapLoad();
|
||
|
|
||
|
ClearDeployRequests();
|
||
|
ClearPlayZones();
|
||
|
ClearTargetTimers();
|
||
|
envDefs.Clear();
|
||
|
|
||
|
sdTaskManager::GetInstance().Init();
|
||
|
sdObjectiveManager::GetInstance().Init();
|
||
|
sdWayPointManager::GetInstance().Init();
|
||
|
sdWakeManager::GetInstance().Init();
|
||
|
tireTreadManager->Init();
|
||
|
footPrintManager->Init();
|
||
|
playerView.Init();
|
||
|
|
||
|
sdDemoManager::GetInstance().StartDemo(); // FIXME: move to EndLevelLoad?
|
||
|
|
||
|
spawnSpots.Clear();
|
||
|
playZoneAreas.DeleteContents( true );
|
||
|
|
||
|
int numAreas = gameRenderWorld->NumAreas();
|
||
|
playZoneAreas.SetNum( numAreas );
|
||
|
playZoneAreaNames.SetNum( numAreas );
|
||
|
for ( int i = 0; i < numAreas; i++ ) {
|
||
|
playZoneAreas[ i ] = NULL;
|
||
|
playZoneAreaNames[ i ] = "";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::OnNewMapLoad
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::OnNewMapLoad( const char* mapName ) {
|
||
|
UpdateLevelLoadScreen( common->LocalizeText( "guis/mainmenu/loading/mapdata" ).c_str() );
|
||
|
|
||
|
sdTeamManager::GetInstance().OnNewMapLoad();
|
||
|
|
||
|
SetupMapMetaData( mapName );
|
||
|
|
||
|
CacheDictionaryMedia( *mapMetaData );
|
||
|
|
||
|
mapInfo = declMapInfoType.LocalFind( mapMetaData->GetString( "mapinfo", "default" ) );
|
||
|
gameLocal.aorManager.Setup();
|
||
|
|
||
|
// touch all the quickchat decls (they won't all get touched by the radial menu, since some are dynamic)
|
||
|
for ( int i = 0; i < declQuickChatType.Num(); i++ ) {
|
||
|
const idDecl* decl = declQuickChatType.FindByIndex( i );
|
||
|
}
|
||
|
|
||
|
UpdateLevelLoadScreen( common->LocalizeText( "guis/mainmenu/loading/compiling_scripts" ).c_str() );
|
||
|
LoadScript();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetupMapMetaData
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetupMapMetaData( const char* mapName ) {
|
||
|
assert( mapMetaDataList != NULL );
|
||
|
|
||
|
idStr strippedFileName = mapName;
|
||
|
strippedFileName.StripFileExtension();
|
||
|
mapMetaData = mapMetaDataList->FindMetaData( strippedFileName.c_str(), &defaultMetaData );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::OnMapStart
|
||
|
===================
|
||
|
*/
|
||
|
idCVar g_logDebugText( "g_logDebugText", "0", CVAR_BOOL | CVAR_GAME, "" );
|
||
|
|
||
|
void idGameLocal::OnMapStart( void ) {
|
||
|
sdObjectiveManager::GetInstance().OnMapStart();
|
||
|
sdTeamManager::GetInstance().OnMapStart();
|
||
|
sdTaskManager::GetInstance().OnMapStart();
|
||
|
|
||
|
sdLocationMarker::OnMapStart();
|
||
|
LinkPlayZoneAreas();
|
||
|
|
||
|
localPlayerProperties.InitGUIs();
|
||
|
|
||
|
// free up any unused animations
|
||
|
animationLib.FlushUnusedAnims();
|
||
|
|
||
|
playerSpawnTime = time + 500; // Gordon: There is a slight delay to let entities settle down, have their teams set, etc
|
||
|
|
||
|
// open and set up the debug logging file
|
||
|
if ( !isClient && g_logDebugText.GetBool() ) {
|
||
|
if ( debugLogFile != NULL ) {
|
||
|
fileSystem->CloseFile( debugLogFile );
|
||
|
debugLogFile = NULL;
|
||
|
}
|
||
|
idStr logFileName( "debugLog" );
|
||
|
|
||
|
sysTime_t time;
|
||
|
sys->RealTime( &time );
|
||
|
|
||
|
logFileName += va( "_%d%02d%02d_%02d%02d%02d_", 1900 + time.tm_year, 1 + time.tm_mon, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec );
|
||
|
idStr mapName = mapFileName;
|
||
|
mapFileName.ExtractFileBase( mapName );
|
||
|
logFileName += mapName;
|
||
|
logFileName += ".txt";
|
||
|
|
||
|
debugLogFile = fileSystem->OpenFileWrite( logFileName.c_str() );
|
||
|
if ( debugLogFile != NULL ) {
|
||
|
debugLogFile->Printf( "time, entityNumber, text\n" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::InitFromNewMap
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::InitFromNewMap( const char* mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randSeed, int startTime, bool isUserChange ) {
|
||
|
mapLoadCount++;
|
||
|
|
||
|
botThread->StopThread();
|
||
|
|
||
|
this->isServer = isServer;
|
||
|
this->isClient = isClient;
|
||
|
localViewChangedTime = 0;
|
||
|
nextTeamBalanceCheckTime = 0;
|
||
|
|
||
|
if ( !gameLocal.isClient ) {
|
||
|
SetPaused( false );
|
||
|
}
|
||
|
|
||
|
playerView.ClearEffects();
|
||
|
playerView.ClearRepeaterView();
|
||
|
repeaterClientFollowIndex = -1;
|
||
|
|
||
|
if ( net_serverDownload.GetInteger() == 3 ) {
|
||
|
#if !defined( SD_PUBLIC_TOOLS )
|
||
|
networkSystem->HTTPEnable( this->isServer || this->isRepeater );
|
||
|
#endif // !SD_PUBLIC_TOOLS
|
||
|
}
|
||
|
|
||
|
GetMDFExportTargets(); // Gordon: force this to be touched during level loads so that builds get it, etc
|
||
|
|
||
|
#ifndef _XENON
|
||
|
if ( isServer && sdnet.NeedsGameSession() ) {
|
||
|
if ( networkService->GetState() == sdNetService::SS_INITIALIZED ) {
|
||
|
sdnet.Connect();
|
||
|
} else if ( networkService->GetState() == sdNetService::SS_ONLINE ) {
|
||
|
if ( networkService->GetDedicatedServerState() == sdNetService::DS_OFFLINE ) {
|
||
|
sdnet.SignInDedicated();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
Printf( "----------- Game Map Init ------------\n" );
|
||
|
|
||
|
gamestate = GAMESTATE_STARTUP;
|
||
|
gameRenderWorld = renderWorld;
|
||
|
gameSoundWorld = soundWorld;
|
||
|
|
||
|
sdVoteManager::GetInstance().FreeVotes();
|
||
|
|
||
|
InitAsyncNetwork();
|
||
|
|
||
|
if ( g_execMapConfigs.GetBool() ) {
|
||
|
idStr configName = mapName;
|
||
|
configName.SetFileExtension( "cfg" );
|
||
|
if ( fileSystem->FindFile( configName.c_str() ) == FIND_NO ) {
|
||
|
configName = "defaultmap.cfg";
|
||
|
}
|
||
|
idStr oldRules = si_rules.GetString();
|
||
|
cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "exec '%s'", configName.c_str() ) );
|
||
|
if ( oldRules.Cmp( si_rules.GetString() ) != 0 ) {
|
||
|
gameLocal.Warning( "Changing ruleset in the map config is not a good idea, ruleset has been reset." );
|
||
|
si_rules.SetString( oldRules.c_str() );
|
||
|
}
|
||
|
sys->FlushServerInfo(); // Gordon: we may have changed timelimit or something
|
||
|
}
|
||
|
|
||
|
if ( rules != NULL ) {
|
||
|
rules->NewMap( isUserChange );
|
||
|
}
|
||
|
|
||
|
UpdateCampaignStats( true );
|
||
|
UpdateLevelLoadScreen( common->LocalizeText( "guis/mainmenu/loading/loading_map" ).c_str() );
|
||
|
|
||
|
botThreadData.Clear( true );
|
||
|
|
||
|
LoadMap( mapName, randSeed, startTime );
|
||
|
|
||
|
OnPreMapStart();
|
||
|
SpawnMapEntities();
|
||
|
OnMapStart();
|
||
|
|
||
|
if ( botMapFile != NULL ) {
|
||
|
gameLocal.Printf( "----------- Loading Map Bot Actions ------------\n" );
|
||
|
botThreadData.LoadActions( botMapFile ); //mal: load any bot actions for this map, if the bot entities were loaded.
|
||
|
|
||
|
if ( gameLocal.isServer ) {
|
||
|
botThreadData.LoadRoutes( botMapFile ); //mal: load any bot routes for this map, if the bot entities were loaded.
|
||
|
|
||
|
idStr navName = botMapFile->GetName();
|
||
|
navName.SetFileExtension( "nav" );
|
||
|
botThreadData.botVehicleNodes.LoadNodes( navName );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
botThreadData.Init();
|
||
|
botThreadData.LoadBotNames();
|
||
|
botThreadData.LoadTrainingBotNames();
|
||
|
|
||
|
gamestate = GAMESTATE_ACTIVE;
|
||
|
|
||
|
botThread->StartThread();
|
||
|
|
||
|
hasTakenScoreShot = false;
|
||
|
|
||
|
Printf( "--------------------------------------\n" );
|
||
|
|
||
|
guidFile.RemoveOldEntries();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::MapClear
|
||
|
===========
|
||
|
*/
|
||
|
void idGameLocal::MapClear( bool clearClients ) {
|
||
|
idLight::OnMapClear();
|
||
|
idSound::OnMapClear();
|
||
|
idDoor::OnMapClear();
|
||
|
sdLocationMarker::OnMapClear( clearClients );
|
||
|
|
||
|
if ( gameRenderWorld ) {
|
||
|
gameRenderWorld->ClearDecals();
|
||
|
}
|
||
|
|
||
|
sdWakeManager::GetInstance().Deinit();
|
||
|
tireTreadManager->Deinit();
|
||
|
footPrintManager->Deinit();
|
||
|
|
||
|
int checkCount = 1000;
|
||
|
bool listEmpty = false;
|
||
|
while ( !listEmpty ) {
|
||
|
checkCount--;
|
||
|
if ( checkCount == 0 ) {
|
||
|
gameLocal.Error( "idGameLocal::MapClear Failed to remove all entities, loading a new map will likely cause the game to crash" );
|
||
|
break;
|
||
|
}
|
||
|
listEmpty = true;
|
||
|
for( int i = ( clearClients ? 0 : MAX_CLIENTS ); i < MAX_GENTITIES; i++ ) {
|
||
|
if ( !entities[ i ] ) {
|
||
|
continue;
|
||
|
}
|
||
|
listEmpty = false;
|
||
|
|
||
|
entities[ i ]->ProcessEvent( &EV_Remove );
|
||
|
|
||
|
// ~idEntity is in charge of setting the pointer to NULL
|
||
|
// it will also clear pending events for this entity
|
||
|
assert( !entities[ i ] );
|
||
|
spawnIds[ i ] = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
entityHash.Clear( 1024, MAX_GENTITIES );
|
||
|
|
||
|
if ( !clearClients ) {
|
||
|
// add back the hashes of the clients
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
if ( !entities[ i ] ) {
|
||
|
continue;
|
||
|
}
|
||
|
entityHash.Add( entityHash.GenerateKey( entities[ i ]->name.c_str(), true ), i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int entityStart = MAX_CLIENTS;
|
||
|
if ( clearClients ) {
|
||
|
entityStart = 0;
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < entityCollections.Num(); i++ ) {
|
||
|
for ( int j = entityStart; j < MAX_GENTITIES; j++ ) {
|
||
|
if ( entityCollections[ i ]->Contains( j ) ) {
|
||
|
assert( false );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( editEntities ) {
|
||
|
delete editEntities;
|
||
|
editEntities = NULL;
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < MAX_CENTITIES; i++ ) {
|
||
|
delete clientEntities[ i ];
|
||
|
assert( !clientEntities[ i ] );
|
||
|
clientSpawnIds[ i ] = -INITIAL_SPAWN_COUNT;
|
||
|
}
|
||
|
|
||
|
if ( debugLogFile != NULL ) {
|
||
|
fileSystem->CloseFile( debugLogFile );
|
||
|
debugLogFile = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::MapShutdown
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::MapShutdown( void ) {
|
||
|
if ( gamestate == GAMESTATE_NOMAP ) {
|
||
|
return;
|
||
|
}
|
||
|
assert( gamestate != GAMESTATE_STARTUP );
|
||
|
|
||
|
if( rules != NULL ) {
|
||
|
rules->SetWinner( NULL );
|
||
|
}
|
||
|
|
||
|
Printf( "--------- Game Map Shutdown ----------\n" );
|
||
|
|
||
|
gamestate = GAMESTATE_SHUTDOWN;
|
||
|
|
||
|
botThread->StopThread();
|
||
|
botThreadData.Clear( true );
|
||
|
|
||
|
#if !defined( SD_DEMO_BUILD )
|
||
|
FreeClientStatsRequestTask();
|
||
|
clientStatsRequestIndex = -1;
|
||
|
#endif /* !SD_DEMO_BUILD */
|
||
|
|
||
|
if ( gameRenderWorld != NULL ) {
|
||
|
// clear any debug lines, text, and polygons
|
||
|
gameRenderWorld->DebugClearLines( 0 );
|
||
|
gameRenderWorld->DebugClearPolygons( 0 );
|
||
|
|
||
|
gameRenderWorld->ClearDecals();
|
||
|
sdEffect::FreeDeadEffects();
|
||
|
}
|
||
|
|
||
|
|
||
|
sdWakeManager::GetInstance().Deinit();
|
||
|
tireTreadManager->Deinit();
|
||
|
footPrintManager->Deinit();
|
||
|
|
||
|
adManager->SetAdZone( "" );
|
||
|
|
||
|
sdDemoManager::GetInstance().EndDemo();
|
||
|
|
||
|
MapClear( true );
|
||
|
|
||
|
if ( gameRenderWorld != NULL ) {
|
||
|
gameRenderWorld->FreeStoppedEffectDefs();
|
||
|
}
|
||
|
|
||
|
renderSystem->SyncRenderSystem();
|
||
|
|
||
|
if ( gameSoundWorld != NULL ) {
|
||
|
gameSoundWorld->PlayShaderDirectly( NULL, SND_PLAYER_TOOLTIP );
|
||
|
gameSoundWorld->FadeSoundClasses( 0, 0, 0.5f );
|
||
|
gameSoundWorld->PlaceListener( playerView.GetCurrentView().vieworg, playerView.GetCurrentView().viewaxis, -1, time );
|
||
|
}
|
||
|
|
||
|
sdObjectiveManager::GetInstance().OnMapShutdown();
|
||
|
sdTaskManager::GetInstance().OnMapShutdown();
|
||
|
sdAntiLagManager::GetInstance().OnMapShutdown();
|
||
|
|
||
|
pvs.Shutdown();
|
||
|
|
||
|
clip.Shutdown();
|
||
|
|
||
|
traceModelCache.ClearTraceModelCache();
|
||
|
|
||
|
// free the collision map
|
||
|
collisionModelManager->PurgeModels();
|
||
|
|
||
|
ShutdownAsyncNetwork();
|
||
|
|
||
|
mapFileName.Clear();
|
||
|
|
||
|
gameRenderWorld = NULL;
|
||
|
gameSoundWorld = NULL;
|
||
|
|
||
|
if( !networkSystem->IsDedicated() ) {
|
||
|
localPlayerProperties.ShutdownGUIs();
|
||
|
}
|
||
|
|
||
|
gamestate = GAMESTATE_NOMAP;
|
||
|
|
||
|
if ( gameLocal.program != NULL ) {
|
||
|
gameLocal.program->PruneThreads();
|
||
|
}
|
||
|
|
||
|
LoadMainMenuPartialMedia( false );
|
||
|
PurgeAndLoadTeamAssets( NULL );
|
||
|
|
||
|
mapMetaData = NULL;
|
||
|
|
||
|
if( sdUserInterfaceScope* scope = gameLocal.globalProperties.GetSubScope( "game" ) ) {
|
||
|
if( sdProperties::sdProperty* property = scope->GetProperty( "isRunning", sdProperties::PT_FLOAT )) {
|
||
|
*property->value.floatValue = GameState() > GAMESTATE_NOMAP ? 1.0f : 0.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sdProfileHelperManager::GetInstance().StopAll();
|
||
|
|
||
|
Printf( "--------------------------------------\n" );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::DumpOggSounds
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::DumpOggSounds() {
|
||
|
int i, j, k, size;
|
||
|
#if 0
|
||
|
int totalSize;
|
||
|
idFile *file, *delFile;
|
||
|
#endif
|
||
|
idStrList oggSounds, highQualOggSounds, localizedVOSounds;
|
||
|
const idSoundShader *soundShader;
|
||
|
const soundShaderParms_t *parms;
|
||
|
idStr soundName;
|
||
|
|
||
|
for ( i = 0; i < declHolder.declSoundShaderType.Num(); i++ ) {
|
||
|
soundShader = declHolder.FindSoundShaderByIndex( i, false );
|
||
|
parms = soundShader->GetParms();
|
||
|
|
||
|
if ( soundShader->EverReferenced() && soundShader->GetState() != DS_DEFAULTED ) {
|
||
|
|
||
|
const_cast<idSoundShader *>(soundShader)->EnsureNotPurged();
|
||
|
|
||
|
for ( j = 0; j < soundShader->GetNumSounds(); j++ ) {
|
||
|
soundName = soundShader->GetSound( j );
|
||
|
soundName.BackSlashesToSlashes();
|
||
|
|
||
|
// don't OGG sounds that cause a shake because that would
|
||
|
// cause continuous seeking on the OGG file which is expensive
|
||
|
if ( parms->shakes != 0.0f ) {
|
||
|
shakeSounds.AddUnique( soundName );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// use higher quality sounds for VO
|
||
|
/*if ( soundName.Find( "sounds/vo/", false ) != idStr::INVALID_POSITION ) {
|
||
|
highQualOggSounds.AddUnique( soundName );
|
||
|
continue;
|
||
|
}*/
|
||
|
|
||
|
if ( soundName.Find( "localization/english/sounds/", false ) != idStr::INVALID_POSITION ) {
|
||
|
// use a separate list for localized VO
|
||
|
localizedVOSounds.AddUnique( soundName );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !soundShader->IsOGGCompressed() ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
for ( k = 0; k < shakeSounds.Num(); k++ ) {
|
||
|
if ( shakeSounds[ k ].IcmpPath( soundName ) == 0 ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ( k < shakeSounds.Num() ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
oggSounds.AddUnique( soundName );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idFile* pyFile = fileSystem->OpenFileWrite( "makeogg.py" );
|
||
|
|
||
|
// write generic code
|
||
|
pyFile->WriteFloatString( "#!/usr/bin/env python\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( "import os\n" );
|
||
|
pyFile->WriteFloatString( "import sys\n" );
|
||
|
pyFile->WriteFloatString( "import subprocess\n" );
|
||
|
pyFile->WriteFloatString( "import time\n" );
|
||
|
pyFile->WriteFloatString( "import traceback\n" );
|
||
|
pyFile->WriteFloatString( "from optparse import OptionParser\n" );
|
||
|
pyFile->WriteFloatString( "from threading import Thread, Lock\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( "def SoundFile( sound, language ):\n" );
|
||
|
pyFile->WriteFloatString( " sourceFile = sound\n" );
|
||
|
pyFile->WriteFloatString( " if language is not None:\n" );
|
||
|
pyFile->WriteFloatString( " sourceFile = sourceFile.replace( '$LANGUAGE$', language )\n" );
|
||
|
pyFile->WriteFloatString( " return sourceFile\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( "class OggEnc2Thread( Thread ):\n" );
|
||
|
pyFile->WriteFloatString( " def __init__( self, encodeSounds, language=None ):\n" );
|
||
|
pyFile->WriteFloatString( " Thread.__init__( self )\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " self.encodeSounds = encodeSounds\n" );
|
||
|
pyFile->WriteFloatString( " self.language = language\n" );
|
||
|
pyFile->WriteFloatString( " self.stop = False\n" );
|
||
|
pyFile->WriteFloatString( " self.error = False\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " def Stop( self ):\n" );
|
||
|
pyFile->WriteFloatString( " if self.isAlive():\n" );
|
||
|
pyFile->WriteFloatString( " self.stop = True\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " def OnCompleted( self ):\n" );
|
||
|
pyFile->WriteFloatString( " return\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " def run( self ):\n" );
|
||
|
pyFile->WriteFloatString( " self.stop = False\n" );
|
||
|
pyFile->WriteFloatString( " self.error = False\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " for sound in self.encodeSounds:\n" );
|
||
|
pyFile->WriteFloatString( " if self.stop:\n" );
|
||
|
pyFile->WriteFloatString( " return\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " sourceFile = SoundFile( sound[0], self.language )\n" );
|
||
|
pyFile->WriteFloatString( " destFile = SoundFile( sound[1], self.language )\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " try:\n" );
|
||
|
pyFile->WriteFloatString( " if not self.EncodeSound( sourceFile, destFile, sound[2] ):\n" );
|
||
|
pyFile->WriteFloatString( " self.error = True\n" );
|
||
|
pyFile->WriteFloatString( " return\n" );
|
||
|
pyFile->WriteFloatString( " except:\n" );
|
||
|
pyFile->WriteFloatString( " sys.stdout.write( traceback.format_exc() )\n" );
|
||
|
pyFile->WriteFloatString( " return\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " def OggEnc2( self, sourceFile, destFile, quality ):\n" );
|
||
|
pyFile->WriteFloatString( " returnCode = 1\n" );
|
||
|
pyFile->WriteFloatString( " stdoutLines = []\n" );
|
||
|
pyFile->WriteFloatString( " stderrLines = []\n" );
|
||
|
pyFile->WriteFloatString( " error = ''\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " # error if the source file doesn't exist\n" );
|
||
|
pyFile->WriteFloatString( " if not os.path.exists( sourceFile ):\n" );
|
||
|
pyFile->WriteFloatString( " sys.stdout.write( 'OggEnc2 failed: %%s (%%i)\\\\n' %% ( 'source file missing', 1 ) )\n" );
|
||
|
pyFile->WriteFloatString( " return False\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " try:\n" );
|
||
|
pyFile->WriteFloatString( " p = subprocess.Popen( 'oggenc2 -Q -q %%d -o \"%%s\" \"%%s\"' %% ( quality, destFile, sourceFile ), stdout=subprocess.PIPE, stderr=subprocess.PIPE )\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " stdoutLines = p.stdout.readlines()\n" );
|
||
|
pyFile->WriteFloatString( " stderrLines = []#p.stderr.readlines()\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " returnCode = p.wait()\n" );
|
||
|
pyFile->WriteFloatString( " except OSError, e:\n" );
|
||
|
pyFile->WriteFloatString( " error = e\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " if not os.path.exists( destFile ):\n" );
|
||
|
pyFile->WriteFloatString( " error = 'output file missing'\n" );
|
||
|
pyFile->WriteFloatString( " returnCode = 1\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " sys.stdout.writelines( stdoutLines )\n" );
|
||
|
pyFile->WriteFloatString( " sys.stderr.writelines( stderrLines )\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " if returnCode == 0:\n" );
|
||
|
pyFile->WriteFloatString( " return True\n" );
|
||
|
pyFile->WriteFloatString( " else:\n" );
|
||
|
pyFile->WriteFloatString( " sys.stdout.write( 'OggEnc2 failed: %%s (%%i)\\\\n' %% ( error, returnCode ) )\n" );
|
||
|
pyFile->WriteFloatString( " return False\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " def EncodeSound( self, sourceFile, destFile, quality ):\n" );
|
||
|
pyFile->WriteFloatString( " sourceFile = os.path.normpath( sourceFile )\n" );
|
||
|
pyFile->WriteFloatString( " destFile = os.path.normpath( destFile )\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " # skip already existing files\n" );
|
||
|
pyFile->WriteFloatString( " if os.path.exists( destFile ):\n" );
|
||
|
pyFile->WriteFloatString( " return True\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " sys.stdout.write( 'Encoding to %%s\\\\n' %% destFile )\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " destPath = os.path.dirname( destFile )\n" );
|
||
|
pyFile->WriteFloatString( " if not os.path.exists( destPath ):\n" );
|
||
|
pyFile->WriteFloatString( " sys.stdout.write( 'Output directory \\\\'%%s\\\\' does not exist\\\\n' %% destPath )\n" );
|
||
|
pyFile->WriteFloatString( " return False\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " if not self.OggEnc2( sourceFile, destFile, quality ):\n" );
|
||
|
pyFile->WriteFloatString( " return False\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " return True\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( "def CreateDirectories( encodeSounds, language=None ):\n" );
|
||
|
pyFile->WriteFloatString( " for sound in encodeSounds:\n" );
|
||
|
pyFile->WriteFloatString( " destFile = os.path.normpath( SoundFile( sound[1], language ) )\n" );
|
||
|
pyFile->WriteFloatString( " destPath = os.path.dirname( destFile )\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " if not os.path.exists( destPath ):\n" );
|
||
|
pyFile->WriteFloatString( " try:\n" );
|
||
|
pyFile->WriteFloatString( " os.makedirs( destPath )\n" );
|
||
|
pyFile->WriteFloatString( " except os.error, e:\n" );
|
||
|
pyFile->WriteFloatString( " sys.stdout.write( 'Directory creation failed: %%s (%%i)\\\\n' %% ( e.strerror, e.errno ) )\n" );
|
||
|
pyFile->WriteFloatString( " return False\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " return True\n\n" );
|
||
|
|
||
|
cpuInfo_t cpuInfo;
|
||
|
sys->GetCPUInfo( cpuInfo );
|
||
|
|
||
|
pyFile->WriteFloatString( "cfg_number_of_hardware_threads = %d\n\n", cpuInfo.physicalNum );
|
||
|
|
||
|
pyFile->WriteFloatString( "def RunThreads( threadClass, encodeSounds, language=None ):\n" );
|
||
|
pyFile->WriteFloatString( " threads = []\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " offset = 0\n" );
|
||
|
pyFile->WriteFloatString( " step = len( encodeSounds ) / cfg_number_of_hardware_threads\n" );
|
||
|
pyFile->WriteFloatString( " for core in xrange( 0, cfg_number_of_hardware_threads ):\n" );
|
||
|
pyFile->WriteFloatString( " if core == cfg_number_of_hardware_threads - 1:\n" );
|
||
|
pyFile->WriteFloatString( " start = offset\n" );
|
||
|
pyFile->WriteFloatString( " end = len( encodeSounds )\n" );
|
||
|
pyFile->WriteFloatString( " else:\n" );
|
||
|
pyFile->WriteFloatString( " start = offset\n" );
|
||
|
pyFile->WriteFloatString( " end = offset + step\n" );
|
||
|
pyFile->WriteFloatString( " offset = end\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " threads.append( threadClass( encodeSounds[start:end], language ) )\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " for thread in threads:\n" );
|
||
|
pyFile->WriteFloatString( " thread.start()\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " error = False\n" );
|
||
|
pyFile->WriteFloatString( " while True: \n" );
|
||
|
pyFile->WriteFloatString( " time.sleep( 1 )\n" );
|
||
|
pyFile->WriteFloatString( " for thread in threads:\n" );
|
||
|
pyFile->WriteFloatString( " error |= thread.error\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " if error:\n" );
|
||
|
pyFile->WriteFloatString( " for thread in threads:\n" );
|
||
|
pyFile->WriteFloatString( " if thread.isAlive():\n" );
|
||
|
pyFile->WriteFloatString( " thread.Stop()\n" );
|
||
|
pyFile->WriteFloatString( " thread.join()\n" );
|
||
|
pyFile->WriteFloatString( " return 1\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " terminate = True\n" );
|
||
|
pyFile->WriteFloatString( " for thread in threads:\n" );
|
||
|
pyFile->WriteFloatString( " terminate &= not thread.isAlive()\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " if terminate:\n" );
|
||
|
pyFile->WriteFloatString( " for thread in threads:\n" );
|
||
|
pyFile->WriteFloatString( " thread.OnCompleted()\n" );
|
||
|
pyFile->WriteFloatString( " return 0\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( "def vararg_strings( option, opt_str, value, parser ):\n" );
|
||
|
pyFile->WriteFloatString( " assert value is None\n" );
|
||
|
pyFile->WriteFloatString( " found = 0\n" );
|
||
|
pyFile->WriteFloatString( " value = []\n" );
|
||
|
pyFile->WriteFloatString( " rargs = parser.rargs\n" );
|
||
|
pyFile->WriteFloatString( " while rargs:\n" );
|
||
|
pyFile->WriteFloatString( " arg = rargs[0]\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " # Stop if we hit an arg like \"--foo\", \"-a\", \"-fx\", \"--file=f\", etc\n" );
|
||
|
pyFile->WriteFloatString( " if ( ( arg[:2] == '--' and len( arg ) > 2 ) or\n" );
|
||
|
pyFile->WriteFloatString( " ( arg[:1] == '-' and len( arg ) > 1 and arg[1] != '-' ) ):\n" );
|
||
|
pyFile->WriteFloatString( " break\n" );
|
||
|
pyFile->WriteFloatString( " else:\n" );
|
||
|
pyFile->WriteFloatString( " value.append( arg )\n" );
|
||
|
pyFile->WriteFloatString( " del rargs[0]\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " setattr( parser.values, option.dest, value )\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " found += 1\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " if found == 0:\n" );
|
||
|
pyFile->WriteFloatString( " raise optparse.OptionValueError( '%%s option requires an argument' %% opt_str )\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( "def main():\n" );
|
||
|
pyFile->WriteFloatString( " encodeSounds = [\n" );
|
||
|
/*
|
||
|
highQualOggSounds
|
||
|
*/
|
||
|
for ( i = 0; i < highQualOggSounds.Num(); i++ ) {
|
||
|
size = fileSystem->ReadFile( highQualOggSounds[i], NULL, NULL );
|
||
|
|
||
|
idStr oggFileName = highQualOggSounds[i];
|
||
|
oggFileName.SetFileExtension( ".ogg" );
|
||
|
|
||
|
idStr outFile = va( "%s" PREGENERATED_BASEDIR "/ogg/%s", fileSystem->RelativePathToOSPath( "", "fs_savepath" ), oggFileName.c_str() );
|
||
|
idStr inFile = fileSystem->RelativePathToOSPath( highQualOggSounds[i].c_str(), "fs_basepath" );
|
||
|
|
||
|
outFile.BackSlashesToSlashes();
|
||
|
inFile.BackSlashesToSlashes();
|
||
|
|
||
|
pyFile->WriteFloatString( " [ '%s', '%s', 4 ],\n", inFile.c_str(), outFile.c_str() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
oggSounds
|
||
|
*/
|
||
|
for ( i = 0; i < oggSounds.Num(); i++ ) {
|
||
|
size = fileSystem->ReadFile( oggSounds[i], NULL, NULL );
|
||
|
|
||
|
idStr oggFileName = oggSounds[i];
|
||
|
oggFileName.SetFileExtension( ".ogg" );
|
||
|
|
||
|
idStr outFile = va( "%s" PREGENERATED_BASEDIR "/ogg/%s", fileSystem->RelativePathToOSPath( "", "fs_savepath" ), oggFileName.c_str() );
|
||
|
idStr inFile = fileSystem->RelativePathToOSPath( oggSounds[i].c_str(), "fs_basepath" );
|
||
|
|
||
|
outFile.BackSlashesToSlashes();
|
||
|
inFile.BackSlashesToSlashes();
|
||
|
|
||
|
pyFile->WriteFloatString( " [ '%s', '%s', 0 ],\n", inFile.c_str(), outFile.c_str() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
end
|
||
|
*/
|
||
|
pyFile->WriteFloatString( " ]\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " localizedEncodeSounds = [\n" );
|
||
|
/*
|
||
|
localizedVOSounds
|
||
|
*/
|
||
|
for ( i = 0; i < localizedVOSounds.Num(); i++ ) {
|
||
|
size = fileSystem->ReadFile( localizedVOSounds[i], NULL, NULL );
|
||
|
|
||
|
idStr oggFileName = localizedVOSounds[i];
|
||
|
oggFileName.SetFileExtension( ".ogg" );
|
||
|
|
||
|
idStr outFile = va( "%s" PREGENERATED_BASEDIR "/ogg/%s", fileSystem->RelativePathToOSPath( "", "fs_savepath" ), oggFileName.c_str() );
|
||
|
idStr inFile = fileSystem->RelativePathToOSPath( localizedVOSounds[i].c_str(), "fs_cdpath" );
|
||
|
|
||
|
outFile.BackSlashesToSlashes();
|
||
|
inFile.BackSlashesToSlashes();
|
||
|
|
||
|
outFile.Replace( "/localization/english/sounds/", "/localization/$LANGUAGE$/sounds/" );
|
||
|
inFile.Replace( "/localization/english/sounds/", "/localization/$LANGUAGE$/sounds/" );
|
||
|
|
||
|
pyFile->WriteFloatString( " [ '%s', '%s', 4 ],\n", inFile.c_str(), outFile.c_str() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
end
|
||
|
*/
|
||
|
pyFile->WriteFloatString( " ]\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " parser = OptionParser( 'usage: %%prog [options]' )\n" );
|
||
|
pyFile->WriteFloatString( " parser.add_option( '-l', '--languages', dest='languages', help='VO Languages to encode', action='callback', callback=vararg_strings )\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " ( options, args ) = parser.parse_args()\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " if not CreateDirectories( encodeSounds ):\n" );
|
||
|
pyFile->WriteFloatString( " return 1\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " status = RunThreads( OggEnc2Thread, encodeSounds )\n" );
|
||
|
pyFile->WriteFloatString( " if status != 0:\n" );
|
||
|
pyFile->WriteFloatString( " return status\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " if options.languages is not None:\n" );
|
||
|
pyFile->WriteFloatString( " for language in options.languages:\n" );
|
||
|
pyFile->WriteFloatString( " if not CreateDirectories( localizedEncodeSounds, language ):\n" );
|
||
|
pyFile->WriteFloatString( " return 1\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " status = RunThreads( OggEnc2Thread, localizedEncodeSounds, language )\n" );
|
||
|
pyFile->WriteFloatString( " if status != 0:\n" );
|
||
|
pyFile->WriteFloatString( " return status\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( " return 0\n\n" );
|
||
|
|
||
|
pyFile->WriteFloatString( "if __name__ == '__main__':\n" );
|
||
|
pyFile->WriteFloatString( " sys.exit( main() )\n" );
|
||
|
|
||
|
fileSystem->CloseFile( pyFile );
|
||
|
|
||
|
shakeSounds.Clear();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::GetShakeSounds
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::GetShakeSounds( const idDict& dict ) {
|
||
|
const idSoundShader* soundShader;
|
||
|
const char* soundShaderName;
|
||
|
idStr soundName;
|
||
|
|
||
|
soundShaderName = dict.GetString( "s_shader" );
|
||
|
if ( soundShaderName != '\0' && dict.GetFloat( "s_shakes" ) != 0.0f ) {
|
||
|
soundShader = declHolder.declSoundShaderType.LocalFind( soundShaderName );
|
||
|
|
||
|
for ( int i = 0; i < soundShader->GetNumSounds(); i++ ) {
|
||
|
soundName = soundShader->GetSound( i );
|
||
|
soundName.BackSlashesToSlashes();
|
||
|
|
||
|
shakeSounds.AddUnique( soundName );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::FinishBuild
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::FinishBuild( void ) {
|
||
|
if ( !cvarSystem->GetCVarBool( "com_makingBuild") ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
DumpOggSounds();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::CacheDictionaryMedia
|
||
|
|
||
|
This is called after parsing an EntityDef and for each entity spawnArgs before
|
||
|
merging the entitydef. It could be done post-merge, but that would
|
||
|
avoid the fast pre-cache check associated with each entityDef
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::CacheDictionaryMedia( const idDict& dict ) {
|
||
|
if ( doingMapRestart ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !g_cacheDictionaryMedia.GetBool() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( cvarSystem->GetCVarBool( "com_makingBuild" ) ) {
|
||
|
GetShakeSounds( dict );
|
||
|
}
|
||
|
static int counter = 0;
|
||
|
if( ( ( counter & 15 ) == 0 ) && !networkSystem->IsDedicated() ) {
|
||
|
common->PacifierUpdate();
|
||
|
}
|
||
|
|
||
|
declManager->CacheFromDict( dict );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::InitScriptForMap
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::InitScriptForMap( void ) {
|
||
|
// create a thread to run frame commands on
|
||
|
if ( !frameCommandThread ) {
|
||
|
frameCommandThread = gameLocal.program->CreateThread();
|
||
|
frameCommandThread->ManualDelete();
|
||
|
frameCommandThread->SetName( "frameCommands" );
|
||
|
}
|
||
|
|
||
|
// run the main game script function (not the level specific main)
|
||
|
const sdProgram::sdFunction* func = program->FindFunction( SCRIPT_DEFAULTFUNC );
|
||
|
if ( func != NULL ) {
|
||
|
frameCommandThread->CallFunction( func );
|
||
|
if ( !frameCommandThread->Execute() ) {
|
||
|
frameCommandThread->EndThread();
|
||
|
gameLocal.Error( "idGameLocal::InitScriptForMap Startup Function May Not be Blocking" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::SpawnPlayer
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SpawnPlayer( int clientNum, bool isBot ) {
|
||
|
// they can connect
|
||
|
Printf( "SpawnPlayer: %i\n", clientNum );
|
||
|
|
||
|
idDict args;
|
||
|
args.SetInt( "spawn_entnum", clientNum );
|
||
|
|
||
|
if ( isBot ) {
|
||
|
args.Set( "classname", "bot_edf" );
|
||
|
botThreadData.SetupBotInfo( clientNum );
|
||
|
} else {
|
||
|
args.Set( "classname", "player_edf" );
|
||
|
botThreadData.GetGameWorldState()->clientInfo[ clientNum ].isBot = false;
|
||
|
}
|
||
|
|
||
|
botThreadData.InitClientInfo( clientNum, true, false );
|
||
|
|
||
|
if ( !SpawnEntityDef( args, true ) ) {
|
||
|
Error( "Failed to spawn player as '%s'", args.GetString( "classname" ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetClientByName
|
||
|
================
|
||
|
*/
|
||
|
idPlayer* idGameLocal::GetClientByName( const char *name ) const {
|
||
|
idStr temp( name );
|
||
|
gameLocal.CleanName( temp );
|
||
|
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
idPlayer* player = GetClient( i );
|
||
|
if ( !player ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( temp.Icmp( player->userInfo.cleanName ) == 0 ) {
|
||
|
return player;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::IsLocalViewPlayer
|
||
|
================
|
||
|
*/
|
||
|
bool idGameLocal::IsLocalViewPlayer( const idEntity* player ) const {
|
||
|
return player == GetLocalViewPlayer();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetLocalViewPlayer
|
||
|
================
|
||
|
*/
|
||
|
idPlayer* idGameLocal::GetLocalViewPlayer( void ) const {
|
||
|
return GetActiveViewer();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::DoClientSideStuff
|
||
|
================
|
||
|
*/
|
||
|
bool idGameLocal::DoClientSideStuff() const {
|
||
|
return !networkSystem->IsDedicated();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::OnLocalViewPlayerChanged
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::OnLocalViewPlayerChanged( void ) {
|
||
|
UpdatePlayerShadows();
|
||
|
sdObjectiveManager::GetInstance().OnLocalViewPlayerChanged();
|
||
|
localViewChangedTime = realClientTime;
|
||
|
playerView.ClearEffects();
|
||
|
|
||
|
ResetTeamAssets();
|
||
|
|
||
|
idPlayer* player = GetLocalViewPlayer();
|
||
|
if ( player != NULL ) {
|
||
|
player->ClearDamageDealt();
|
||
|
localPlayerProperties.SetActiveCamera( player->GetRemoteCamera() );
|
||
|
localPlayerProperties.SetActivePlayer( player );
|
||
|
localPlayerProperties.SetActiveWeapon( player->GetWeapon() );
|
||
|
} else {
|
||
|
localPlayerProperties.SetActiveCamera( NULL );
|
||
|
localPlayerProperties.SetActivePlayer( NULL );
|
||
|
localPlayerProperties.SetActiveWeapon( NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::OnLocalViewPlayerChanged
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::OnLocalViewPlayerTeamChanged( void ) {
|
||
|
sdObjectiveManager::GetInstance().OnLocalViewPlayerTeamChanged();
|
||
|
ResetTeamAssets();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::ResetTeamAssets
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::ResetTeamAssets( void ) {
|
||
|
idPlayer* player = GetLocalViewPlayer();
|
||
|
if ( player == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sdTeamInfo* team = player->GetGameTeam();
|
||
|
if ( team == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
PurgeAndLoadTeamAssets( declStringMapType[ team->GetDict().GetString( "partial_load" ) ] );
|
||
|
}
|
||
|
|
||
|
int SortPlayersByXP( const idPlayer* a, const idPlayer* b ) {
|
||
|
return ( int )( a->GetProficiencyTable().GetXP() - b->GetProficiencyTable().GetXP() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::FindUnbalancedTeam
|
||
|
================
|
||
|
*/
|
||
|
sdTeamInfo* idGameLocal::FindUnbalancedTeam( sdTeamInfo** lowest ) {
|
||
|
const int TEAM_BALANCE_CHECK_THRESHOLD = 2;
|
||
|
|
||
|
int numTeams = sdTeamManager::GetInstance().GetNumTeams();
|
||
|
|
||
|
size_t arraySize = sizeof( int ) * numTeams;
|
||
|
int* teamPlayerCounts = ( int* )_alloca( arraySize );
|
||
|
memset( teamPlayerCounts, 0, arraySize );
|
||
|
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
idPlayer* player = GetClient( i );
|
||
|
if ( player == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
sdTeamInfo* team = player->GetGameTeam();
|
||
|
if ( team == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
teamPlayerCounts[ team->GetIndex() ]++;
|
||
|
}
|
||
|
|
||
|
int highestTeam = -1;
|
||
|
int lowestTeam = -1;
|
||
|
for ( int i = 0; i < numTeams; i++ ) {
|
||
|
if ( highestTeam == -1 || ( teamPlayerCounts[ i ] > teamPlayerCounts[ highestTeam ] ) ) {
|
||
|
highestTeam = i;
|
||
|
}
|
||
|
|
||
|
if ( lowestTeam == -1 || ( teamPlayerCounts[ i ] < teamPlayerCounts[ lowestTeam ] ) ) {
|
||
|
lowestTeam = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ( teamPlayerCounts[ highestTeam ] - teamPlayerCounts[ lowestTeam ] ) < TEAM_BALANCE_CHECK_THRESHOLD ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if ( lowest != NULL ) {
|
||
|
*lowest = &sdTeamManager::GetInstance().GetTeamByIndex( lowestTeam );
|
||
|
}
|
||
|
return &sdTeamManager::GetInstance().GetTeamByIndex( highestTeam );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::CheckTeamBalance
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::CheckTeamBalance( void ) {
|
||
|
bool valid = si_teamForceBalance.GetBool() && gameLocal.rules->GetState() == sdGameRules::GS_GAMEON && g_smartTeamBalance.GetBool() && g_smartTeamBalanceReward.GetInteger() > 0;
|
||
|
|
||
|
class sdBalanceTeamSwitchFinalizer : public sdVoteFinalizer {
|
||
|
public:
|
||
|
sdBalanceTeamSwitchFinalizer( idPlayer* player, sdTeamInfo* team ) {
|
||
|
this->player = player;
|
||
|
this->team = player->GetGameTeam();
|
||
|
this->switchTeam = team;
|
||
|
}
|
||
|
|
||
|
void OnVoteCompleted( bool passed ) const {
|
||
|
if ( !passed ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sdTeamInfo* switchTeam;
|
||
|
sdTeamInfo* team = gameLocal.FindUnbalancedTeam( &switchTeam );
|
||
|
if ( !CheckValid( team, switchTeam ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idPlayer* player = this->player;
|
||
|
player->GiveClassProficiency( g_smartTeamBalanceReward.GetInteger(), "helped team balance" );
|
||
|
gameLocal.rules->SetClientTeam( player, switchTeam->GetIndex() + 1, true, "" );
|
||
|
}
|
||
|
|
||
|
bool CheckValid( sdTeamInfo* team, sdTeamInfo* switchTeam ) const {
|
||
|
idPlayer* player = this->player;
|
||
|
if ( this->team != team || this->switchTeam != switchTeam || player == NULL || player->GetGameTeam() != this->team ) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
idEntityPtr< idPlayer > player;
|
||
|
sdTeamInfo* team;
|
||
|
sdTeamInfo* switchTeam;
|
||
|
};
|
||
|
|
||
|
sdTeamInfo* switchTeam;
|
||
|
sdTeamInfo* team = FindUnbalancedTeam( &switchTeam );
|
||
|
|
||
|
sdPlayerVote* vote = sdVoteManager::GetInstance().FindVote( VI_SWITCH_TEAM );
|
||
|
if ( vote != NULL ) {
|
||
|
bool cancel = false;
|
||
|
|
||
|
sdBalanceTeamSwitchFinalizer* finalizer = ( sdBalanceTeamSwitchFinalizer* )vote->GetFinalizer();
|
||
|
if ( valid && finalizer->CheckValid( team, switchTeam ) ) {
|
||
|
return;
|
||
|
}
|
||
|
sdVoteManager::GetInstance().CancelVote( vote );
|
||
|
}
|
||
|
|
||
|
if ( !valid || team == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.time < nextTeamBalanceCheckTime ) {
|
||
|
return;
|
||
|
}
|
||
|
nextTeamBalanceCheckTime = gameLocal.time + SEC2MS( 20 );
|
||
|
|
||
|
idStaticList< idPlayer*, MAX_CLIENTS > sortedPlayers;
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
idPlayer* player = GetClient( i );
|
||
|
if ( player == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( player->GetGameTeam() != team ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( player->userInfo.isBot ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( player->GetNextTeamBalanceSwitchTime() > gameLocal.time ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
sortedPlayers.Append( player );
|
||
|
}
|
||
|
|
||
|
if ( sortedPlayers.Num() == 0 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sortedPlayers.Sort( SortPlayersByXP );
|
||
|
|
||
|
int index = ( int )idMath::Floor( sortedPlayers.Num() * 0.33f ); // Gordon: Aim for someone who has a decent amount of XP, but not right at the top, as they are less likely to switch
|
||
|
|
||
|
vote = sdVoteManager::GetInstance().AllocVote();
|
||
|
if ( vote != NULL ) {
|
||
|
idPlayer* player = sortedPlayers[ index ];
|
||
|
player->SetNextTeamBalanceSwitchTime( gameLocal.time + MINS2MS( 2 ) );
|
||
|
|
||
|
vote->DisableFinishMessage();
|
||
|
vote->MakePrivateVote( player );
|
||
|
vote->Tag( VI_SWITCH_TEAM, player );
|
||
|
vote->SetText( gameLocal.declToolTipType[ "unbalanced_teams_switch" ] );
|
||
|
vote->AddTextParm( switchTeam->GetTitle() );
|
||
|
vote->AddTextParm( va( L"%d", g_smartTeamBalanceReward.GetInteger() ) );
|
||
|
vote->SetFinalizer( new sdBalanceTeamSwitchFinalizer( player, switchTeam ) );
|
||
|
vote->Start();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::UpdateGravity
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::UpdateGravity( void ) {
|
||
|
if ( g_gravity.IsModified() ) {
|
||
|
if ( g_gravity.GetFloat() == 0.0f ) {
|
||
|
g_gravity.SetFloat( 1.0f );
|
||
|
}
|
||
|
gravity.Set( 0, 0, -g_gravity.GetFloat() );
|
||
|
g_gravity.ClearModified();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetGravity
|
||
|
================
|
||
|
*/
|
||
|
const idVec3 &idGameLocal::GetGravity( void ) const {
|
||
|
return gravity;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::SortActiveEntityList
|
||
|
|
||
|
Sorts the active entity list such that pushing entities come first,
|
||
|
actors come next and physics team slaves appear after their master.
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::SortActiveEntityList( void ) {
|
||
|
idEntity* nextEnt;
|
||
|
|
||
|
// if the active entity list needs to be reordered to place physics team masters at the front
|
||
|
if ( sortTeamMasters ) {
|
||
|
for ( idEntity* ent = activeEntities.Next(); ent != NULL; ent = nextEnt ) {
|
||
|
nextEnt = ent->activeNode.Next();
|
||
|
|
||
|
idEntity* master = ent->GetTeamMaster();
|
||
|
if ( master && master == ent ) {
|
||
|
ent->activeNode.AddToFront( activeEntities );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if the active entity list needs to be reordered to place pushers at the front
|
||
|
if ( sortPushers ) {
|
||
|
for ( idEntity* ent = activeEntities.Next(); ent != NULL; ent = nextEnt ) {
|
||
|
nextEnt = ent->activeNode.Next();
|
||
|
|
||
|
idEntity* master = ent->GetTeamMaster();
|
||
|
if ( !master || master == ent ) {
|
||
|
// check if there is an actor on the team
|
||
|
idEntity* part;
|
||
|
for ( part = ent; part != NULL; part = part->GetNextTeamEntity() ) {
|
||
|
if ( part->GetPhysics()->IsType( idPhysics_Actor::Type ) ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// if there is an actor on the team
|
||
|
if ( part ) {
|
||
|
ent->activeNode.AddToFront( activeEntities );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sortTeamMasters = false;
|
||
|
sortPushers = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idGameLocal::ControlBotPopulation
|
||
|
===============
|
||
|
*/
|
||
|
void idGameLocal::ControlBotPopulation( void ) {
|
||
|
|
||
|
if( !gameLocal.isServer ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( networkSystem->IsRankedServer() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( gamestate != GAMESTATE_ACTIVE ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int minBots = bot_minClients.GetInteger();
|
||
|
|
||
|
// tick the bot population logic
|
||
|
if ( bot_enable.GetBool() && bot_minClients.GetInteger() >= 0 && time > nextBotPopulationCheck ) {
|
||
|
int iClient;
|
||
|
int numClients = 0, numBots = 0;
|
||
|
for ( iClient = 0; iClient < MAX_CLIENTS; iClient++ ) {
|
||
|
if ( entities[iClient] != NULL ) {
|
||
|
numClients++;
|
||
|
if ( entities[iClient]->IsType( idBot::Type ) ) {
|
||
|
numBots++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int numGDF = botThreadData.GetNumClientsOnTeam( GDF );
|
||
|
int numStrogg = botThreadData.GetNumClientsOnTeam( STROGG );
|
||
|
int limitStrogg = bot_uiNumStrogg.GetInteger();
|
||
|
int limitGDF = bot_uiNumGDF.GetInteger();
|
||
|
int numStroggOverLimit = ( limitStrogg < 0 ) ? 0 : botThreadData.GetNumBotsOnTeam( STROGG ) - limitStrogg;
|
||
|
int numGDFOverLimit = ( limitGDF < 0 ) ? 0 : botThreadData.GetNumBotsOnTeam( GDF ) - limitGDF;
|
||
|
|
||
|
if ( ( numClients > bot_minClients.GetInteger() || ( numStroggOverLimit > 0 ) || ( numGDFOverLimit > 0 ) ) && numBots > 0 ) {
|
||
|
playerTeamTypes_t team;
|
||
|
|
||
|
if ( ( numStroggOverLimit <= 0 && numGDFOverLimit <= 0 && numStrogg > numGDF ) || ( numStroggOverLimit > 0 ) ) { //try to keep the teams even.
|
||
|
team = STROGG;
|
||
|
} else if ( ( numStroggOverLimit <= 0 && numGDFOverLimit <= 0 && numGDF > numStrogg ) || ( numGDFOverLimit > 0 ) ) {
|
||
|
team = GDF;
|
||
|
} else {
|
||
|
team = ( random.RandomInt( 100 ) > 50 ) ? GDF : STROGG;
|
||
|
}
|
||
|
|
||
|
if ( team == STROGG ) {
|
||
|
numBots = numStrogg;
|
||
|
} else {
|
||
|
numBots = numGDF;
|
||
|
}
|
||
|
|
||
|
// randomize a bot to be kicked
|
||
|
numBots = random.RandomInt( numBots );
|
||
|
for ( iClient = 0; iClient < MAX_CLIENTS; iClient++ ) {
|
||
|
if ( entities[iClient] != NULL ) {
|
||
|
if ( entities[iClient]->IsType( idBot::Type ) ) {
|
||
|
if ( botThreadData.GetGameWorldState()->clientInfo[ iClient ].team != team ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( numBots == 0 ) {
|
||
|
// kick it!
|
||
|
networkSystem->ServerKickClient( iClient, "", false );
|
||
|
break;
|
||
|
}
|
||
|
numBots--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else if ( numClients < bot_minClients.GetInteger() && ( ( ( limitGDF < 0 ) || numGDF < limitGDF ) || ( ( limitStrogg < 0 ) || ( numStrogg < limitStrogg ) ) ) ) {
|
||
|
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "admin addbot\n" );
|
||
|
} else if ( numClients == minBots && limitGDF == limitStrogg && numGDF != numStrogg && ( numGDF >= numStrogg + 2 || numStrogg >= numGDF + 2 ) ) { //mal: do some team balancing on the server when the numbers have settled, unless player wants uneven teams.
|
||
|
playerTeamTypes_t team;
|
||
|
|
||
|
if ( numGDF > numStrogg ) {
|
||
|
team = GDF;
|
||
|
} else {
|
||
|
team = STROGG;
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
if ( entities[ i ] != NULL ) {
|
||
|
if ( entities[ i ]->IsType( idBot::Type ) ) {
|
||
|
if ( botThreadData.GetGameWorldState()->clientInfo[ i ].team != team ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
networkSystem->ServerKickClient( i, "", false );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// next poll
|
||
|
nextBotPopulationCheck = time + 3000;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef GAME_FPU_EXCEPTIONS
|
||
|
extern idCVar g_fpuExceptions;
|
||
|
idCVar g_fpuExceptions( "g_fpuExceptions", "0", CVAR_BOOL | CVAR_GAME | CVAR_NOCHEAT, "enables fpu exception catching" );
|
||
|
#endif // GAME_FPU_EXCEPTIONS
|
||
|
|
||
|
idCVar g_debugStatsSetup( "g_debugStatsSetup", "0", CVAR_GAME | CVAR_BOOL | CVAR_NOCHEAT, "prints any problems with calculating a client rank when they connect to a server" );
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::RunFrame
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::RunFrame( const usercmd_t *clientCmds, int elapsedTime ) {
|
||
|
idPlayer* localPlayer = GetLocalPlayer();
|
||
|
if ( localPlayer != NULL ) {
|
||
|
unlock.canUnlockFrames = localPlayer->IsFPSUnlock();
|
||
|
} else {
|
||
|
unlock.canUnlockFrames = false;
|
||
|
}
|
||
|
|
||
|
unlock.unlockedDraw = false;
|
||
|
interpolateEntities.Clear();
|
||
|
|
||
|
|
||
|
|
||
|
idEntity* ent;
|
||
|
idTimer timer_think, timer_events, timer_singlethink;
|
||
|
idPlayer* player;
|
||
|
const renderView_t* view;
|
||
|
|
||
|
#ifdef GAME_FPU_EXCEPTIONS
|
||
|
if ( g_fpuExceptions.GetBool() ) {
|
||
|
sys->FPU_EnableExceptions( FPU_EXCEPTION_DENORMALIZED_OPERAND );
|
||
|
} else {
|
||
|
sys->FPU_EnableExceptions( 0 );
|
||
|
}
|
||
|
#endif // GAME_FPU_EXCEPTIONS
|
||
|
|
||
|
assert( !isClient );
|
||
|
|
||
|
player = GetLocalPlayer();
|
||
|
|
||
|
isNewFrame = true;
|
||
|
|
||
|
// update demo state
|
||
|
sdDemoManagerLocal& demoManager = sdDemoManager::GetInstance();
|
||
|
demoManager.RunDemoFrame( NULL );
|
||
|
|
||
|
{
|
||
|
|
||
|
// update the game time
|
||
|
framenum++;
|
||
|
|
||
|
if ( IsPaused() ) {
|
||
|
msec = 0;
|
||
|
previousTime = time;
|
||
|
timeOffset += elapsedTime;
|
||
|
time += 0;
|
||
|
} else {
|
||
|
msec = elapsedTime;
|
||
|
previousTime = time;
|
||
|
timeOffset += 0;
|
||
|
time += elapsedTime;
|
||
|
}
|
||
|
realClientTime = ToGuiTime( time );
|
||
|
|
||
|
#ifdef GAME_DLL
|
||
|
// allow changing SIMD usage on the fly
|
||
|
if ( com_forceGenericSIMD.IsModified() ) {
|
||
|
idSIMD::InitProcessor( "game", com_forceGenericSIMD.GetBool() );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef CLIP_DEBUG_EXTREME
|
||
|
clip.UpdateTraceLogging();
|
||
|
#endif
|
||
|
sdProfileHelperManager::GetInstance().Update();
|
||
|
|
||
|
// make sure the random number counter is used each frame so random events
|
||
|
// are influenced by the player's actions
|
||
|
random.RandomInt();
|
||
|
|
||
|
#ifndef _XENON
|
||
|
UpdateGameSession();
|
||
|
#endif // _XENON
|
||
|
|
||
|
if ( player ) {
|
||
|
// update the renderview so that any gui videos play from the right frame
|
||
|
view = player->GetRenderView();
|
||
|
if ( view ) {
|
||
|
gameRenderWorld->SetRenderView( view );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// clear any debug lines from a previous frame
|
||
|
gameRenderWorld->DebugClearLines( time );
|
||
|
|
||
|
// clear any debug polygons from a previous frame
|
||
|
gameRenderWorld->DebugClearPolygons( time );
|
||
|
|
||
|
// set the user commands for this frame
|
||
|
memcpy( usercmds, clientCmds, numClients * sizeof( usercmds[ 0 ] ) );
|
||
|
|
||
|
UpdateDeploymentRequests();
|
||
|
|
||
|
// update our gravity vector if needed.
|
||
|
UpdateGravity();
|
||
|
|
||
|
bse->StartFrame();
|
||
|
|
||
|
timer_think.Clear();
|
||
|
timer_think.Start();
|
||
|
|
||
|
// update some of the world state for the bots
|
||
|
botThreadData.UpdateState();
|
||
|
|
||
|
// set latest bot output as current for the game
|
||
|
botThreadData.SetCurrentGameOutputState();
|
||
|
|
||
|
idEntity::ClearEntityCaches();
|
||
|
|
||
|
//mal: generate bot user cmds
|
||
|
for( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
idPlayer* botPlayer = GetClient( i );
|
||
|
|
||
|
if ( botPlayer == NULL || botPlayer->userInfo.isBot == false ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
idBot* bot = botPlayer->Cast< idBot >();
|
||
|
|
||
|
if ( bot == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
bot->Bot_InputToUserCmd();
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < changedEntities.Num(); i++ ) {
|
||
|
idEntity* ent = changedEntities[ i ];
|
||
|
if ( !ent ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !ent->thinkFlags || ent->NoThink() ) {
|
||
|
ent->activeNode.Remove();
|
||
|
} else {
|
||
|
if ( !ent->activeNode.InList() ) {
|
||
|
ent->activeNode.AddToEnd( gameLocal.activeEntities );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
changedEntities.SetNum( 0, false );
|
||
|
|
||
|
// sort the active entity list
|
||
|
SortActiveEntityList();
|
||
|
|
||
|
int num = 0;
|
||
|
for ( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
|
||
|
sdProfileHelper_ScopeTimer( "EntityThink", ent->spawnArgs.GetString( "classname" ), g_profileEntityThink.GetBool() );
|
||
|
ent->Think();
|
||
|
num++;
|
||
|
}
|
||
|
|
||
|
idPlayer* viewPlayer = GetLocalViewPlayer();
|
||
|
if ( viewPlayer != NULL ) {
|
||
|
playerView.UpdateProxyView( viewPlayer, false );
|
||
|
}
|
||
|
|
||
|
for( ent = postThinkEntities.Next(); ent; ent = ent->GetPostThinkNode()->Next() ) {
|
||
|
ent->PostThink();
|
||
|
}
|
||
|
|
||
|
timer_think.Stop();
|
||
|
timer_events.Clear();
|
||
|
timer_events.Start();
|
||
|
|
||
|
rvClientEntity* cent;
|
||
|
for ( cent = clientSpawnedEntities.Next(); cent != NULL; cent = cent->spawnNode.Next() ) {
|
||
|
cent->Think();
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.localClientNum != ASYNC_DEMO_CLIENT_INDEX ) {
|
||
|
localPlayerProperties.UpdateHudModules();
|
||
|
}
|
||
|
|
||
|
// Some will remove themselves
|
||
|
sdEffect::UpdateDeadEffects();
|
||
|
|
||
|
if ( viewPlayer != NULL ) {
|
||
|
playerView.CalculateShake( viewPlayer );
|
||
|
|
||
|
// fps unlock: record view position
|
||
|
int index = framenum & 1;
|
||
|
unlock.originlog[ index ] = viewPlayer->GetRenderView()->vieworg;
|
||
|
}
|
||
|
|
||
|
// service any pending events
|
||
|
idEvent::ServiceEvents();
|
||
|
|
||
|
timer_events.Stop();
|
||
|
|
||
|
sdTeamManager::GetInstance().Think();
|
||
|
sdTaskManager::GetInstance().Think();
|
||
|
sdFireTeamManager::GetInstance().Think();
|
||
|
sdObjectiveManager::GetInstance().Think();
|
||
|
sdWayPointManager::GetInstance().Think();
|
||
|
sdVoteManager::GetInstance().Think();
|
||
|
sdWakeManager::GetInstance().Think();
|
||
|
if ( !gameLocal.IsPaused() ) {
|
||
|
sdAntiLagManager::GetInstance().Think();
|
||
|
}
|
||
|
tireTreadManager->Think();
|
||
|
footPrintManager->Think();
|
||
|
sdGlobalStatsTracker::GetInstance().UpdateStatsRequests();
|
||
|
|
||
|
ControlBotPopulation();
|
||
|
botThreadData.CheckBotClassSpread();
|
||
|
|
||
|
// set latest world state as current for the bots
|
||
|
botThreadData.SetCurrentGameWorldState();
|
||
|
|
||
|
bse->EndFrame();
|
||
|
|
||
|
UpdateLoggedDecals();
|
||
|
|
||
|
// Run multiplayer game rules
|
||
|
rules->Run();
|
||
|
|
||
|
CheckTeamBalance();
|
||
|
|
||
|
// decrease deleted clip model reference counts
|
||
|
clip.ThreadDeleteClipModels();
|
||
|
|
||
|
// delete clip models without references for real
|
||
|
clip.ActuallyDeleteClipModels();
|
||
|
|
||
|
// display how long it took to calculate the current game frame
|
||
|
if ( g_frametime.GetBool() ) {
|
||
|
Printf( "game %d: all:%.1f th:%.1f ev:%.1f %d ents \n",
|
||
|
time, timer_think.Milliseconds() + timer_events.Milliseconds(),
|
||
|
timer_think.Milliseconds(), timer_events.Milliseconds(), num );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UpdateServerRankStats();
|
||
|
|
||
|
#ifdef GAME_FPU_EXCEPTIONS
|
||
|
sys->FPU_EnableExceptions( 0 );
|
||
|
#endif // GAME_FPU_EXCEPTIONS
|
||
|
|
||
|
demoManager.EndDemoFrame();
|
||
|
|
||
|
// show any debug info for this frame
|
||
|
RunDebugInfo();
|
||
|
D_DrawDebugLines();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::UpdateServerRankStats
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::UpdateServerRankStats( void ) {
|
||
|
if ( !clientStatsRequestsPending ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !gameLocal.isServer ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#if !defined( SD_DEMO_BUILD )
|
||
|
if ( clientStatsRequestIndex != -1 ) {
|
||
|
if ( !clientConnected[ clientStatsRequestIndex ] ) {
|
||
|
FreeClientStatsRequestTask();
|
||
|
} else {
|
||
|
assert( clientStatsRequestTask != NULL );
|
||
|
|
||
|
sdNetTask::taskStatus_e taskStatus = clientStatsRequestTask->GetState();
|
||
|
if ( taskStatus == sdNetTask::TS_DONE ) {
|
||
|
FinishClientStatsRequest();
|
||
|
FreeClientStatsRequestTask();
|
||
|
SetupFixedClientRank( clientStatsRequestIndex );
|
||
|
} else {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( clientStatsRequestIndex == -1 ) {
|
||
|
clientStatsRequestIndex = 0;
|
||
|
}
|
||
|
#endif /* !SD_DEMO_BUILD */
|
||
|
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
#if !defined( SD_DEMO_BUILD )
|
||
|
int clientIndex = ( clientStatsRequestIndex + i ) % MAX_CLIENTS;
|
||
|
#else
|
||
|
int clientIndex = i;
|
||
|
#endif // SD_DEMO_BUILD
|
||
|
if ( !clientConnected[ clientIndex ] ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( clientRanks[ clientIndex ].calculated ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.GetLocalPlayer() != NULL ) {
|
||
|
if ( clientIndex == localClientNum ) {
|
||
|
sdNetUser* activeUser = networkService->GetActiveUser();
|
||
|
if ( activeUser != NULL ) {
|
||
|
sdStatsTracker::ReadLocalUserStats( activeUser, clientStatsList[ clientIndex ] );
|
||
|
CreateClientStatsHash( clientIndex );
|
||
|
}
|
||
|
}
|
||
|
clientRanks[ clientIndex ].calculated = true;
|
||
|
} else {
|
||
|
#if !defined( SD_DEMO_BUILD )
|
||
|
if ( StartClientStatsRequest( clientIndex ) ) {
|
||
|
clientStatsRequestIndex = clientIndex;
|
||
|
return;
|
||
|
} else {
|
||
|
if ( g_debugStatsSetup.GetBool() ) {
|
||
|
gameLocal.Printf( "Failed To Start Stats Request Task for client %d\n", clientIndex );
|
||
|
}
|
||
|
clientRanks[ clientIndex ].calculated = true;
|
||
|
clientRanks[ clientIndex ].rank = -1;
|
||
|
}
|
||
|
#else
|
||
|
clientRanks[ clientIndex ].calculated = true;
|
||
|
#endif /* !SD_DEMO_BUILD */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
clientStatsRequestsPending = false;
|
||
|
#if !defined( SD_DEMO_BUILD )
|
||
|
clientStatsRequestIndex = -1;
|
||
|
#endif /* !SD_DEMO_BUILD */
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::CreateClientStatsHash
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::CreateClientStatsHash( int clientIndex ) {
|
||
|
sdNetStatKeyValList& statsList = clientStatsList[ clientIndex ];
|
||
|
idHashIndex& hashIndex = clientStatsHash[ clientIndex ];
|
||
|
hashIndex.Clear();
|
||
|
|
||
|
for ( int i = 0; i < statsList.Num(); i++ ) {
|
||
|
int hashKey = hashIndex.GenerateKey( statsList[ i ].key->c_str(), false );
|
||
|
hashIndex.Add( hashKey, i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if !defined( SD_DEMO_BUILD )
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::FreeClientStatsRequestTask
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::FreeClientStatsRequestTask( void ) {
|
||
|
if ( clientStatsRequestTask == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
networkService->FreeTask( clientStatsRequestTask );
|
||
|
clientStatsRequestTask = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::FinishClientStatsRequest
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::FinishClientStatsRequest( void ) {
|
||
|
assert( clientStatsRequestTask != NULL && clientStatsRequestIndex != -1 );
|
||
|
|
||
|
clientRanks[ clientStatsRequestIndex ].calculated = true;
|
||
|
|
||
|
sdNetErrorCode_e errorCode = clientStatsRequestTask->GetErrorCode();
|
||
|
if ( errorCode != SDNET_NO_ERROR ) {
|
||
|
if ( g_debugStatsSetup.GetBool() ) {
|
||
|
gameLocal.Printf( "Stats Request Task Failed for client %d\n", clientStatsRequestIndex );
|
||
|
}
|
||
|
clientRanks[ clientStatsRequestIndex ].rank = -1;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CreateClientStatsHash( clientStatsRequestIndex );
|
||
|
|
||
|
rankInfo.CreateData( clientStatsHash[ clientStatsRequestIndex ], clientStatsList[ clientStatsRequestIndex ], rankScratchInfo );
|
||
|
|
||
|
int rank = rankScratchInfo.completeTasks;
|
||
|
if ( rank >= gameLocal.declRankType.Num() ) {
|
||
|
rank = gameLocal.declRankType.Num() - 1;
|
||
|
}
|
||
|
clientRanks[ clientStatsRequestIndex ].rank = rank;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::StartClientStatsRequest
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::StartClientStatsRequest( int clientIndex ) {
|
||
|
sdNetClientId clientId;
|
||
|
networkSystem->ServerGetClientNetId( clientIndex, clientId );
|
||
|
if ( clientId.IsValid() ) {
|
||
|
clientStatsHash[ clientIndex ].Clear();
|
||
|
clientStatsRequestTask = networkService->GetStatsManager().ReadDictionary( clientId, clientStatsList[ clientIndex ] );
|
||
|
}
|
||
|
|
||
|
return clientStatsRequestTask != NULL;
|
||
|
}
|
||
|
|
||
|
#endif /* !SD_DEMO_BUILD */
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetGlobalStatsValueMax
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::GetGlobalStatsValueMax( int clientIndex, const char* name, sdPlayerStatEntry::statValue_t& value ) {
|
||
|
sdNetStatKeyValList& statsList = clientStatsList[ clientIndex ];
|
||
|
idHashIndex& hashIndex = clientStatsHash[ clientIndex ];
|
||
|
|
||
|
int key = hashIndex.GenerateKey( name, false );
|
||
|
for ( int index = hashIndex.GetFirst( key ); index != hashIndex.NULL_INDEX; index = hashIndex.GetNext( index ) ) {
|
||
|
if ( idStr::Icmp( statsList[ index ].key->c_str(), name ) != 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
switch ( statsList[ index ].type ) {
|
||
|
case sdNetStatKeyValue::SVT_INT:
|
||
|
case sdNetStatKeyValue::SVT_INT_MAX:
|
||
|
if ( statsList[ index ].val.i > value.GetInt() ) {
|
||
|
value = sdPlayerStatEntry::statValue_t( statsList[ index ].val.i );
|
||
|
}
|
||
|
break;
|
||
|
case sdNetStatKeyValue::SVT_FLOAT:
|
||
|
case sdNetStatKeyValue::SVT_FLOAT_MAX:
|
||
|
if ( statsList[ index ].val.f > value.GetFloat() ) {
|
||
|
value = sdPlayerStatEntry::statValue_t( statsList[ index ].val.f );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetupFixedClientRank
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetupFixedClientRank( int clientIndex ) {
|
||
|
if ( gameLocal.isClient ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idPlayer* player = gameLocal.GetClient( clientIndex );
|
||
|
if ( player == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( clientRanks[ clientIndex ].rank == -1 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const sdDeclRank* rank = FindRankForLevel( clientRanks[ clientIndex ].rank );
|
||
|
if ( rank != NULL ) {
|
||
|
player->GetProficiencyTable().SetFixedRank( rank );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::FindRankForLevel
|
||
|
============
|
||
|
*/
|
||
|
const sdDeclRank* idGameLocal::FindRankForLevel( int rankLevel ) {
|
||
|
int highestRankLevel = -1;
|
||
|
const sdDeclRank* highestRank = NULL;
|
||
|
const sdDeclRank* rank = NULL;
|
||
|
for ( int i = 0; i < gameLocal.declRankType.Num(); i++ ) {
|
||
|
const sdDeclRank* testRank = gameLocal.declRankType[ i ];
|
||
|
int level = gameLocal.declRankType[ i ]->GetLevel();
|
||
|
if ( rankLevel == level ) {
|
||
|
rank = testRank;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( level > highestRankLevel ) {
|
||
|
highestRankLevel = level;
|
||
|
highestRank = testRank;
|
||
|
}
|
||
|
}
|
||
|
if ( rank == NULL ) {
|
||
|
return highestRank;
|
||
|
}
|
||
|
return rank;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::UpdateLoggedDecals
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::UpdateLoggedDecals( void ) {
|
||
|
for( int i = 0; i < MAX_LOGGED_DECALS; i++ ) {
|
||
|
if( loggedDecals[ i ] == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
gameDecalInfo_t* info = loggedDecals[ i ];
|
||
|
info->renderEntity.flags.forceUpdate = true;
|
||
|
gameRenderWorld->UpdateEntityDef( info->renderEntityHandle, &info->renderEntity );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
======================================================================
|
||
|
|
||
|
Game view drawing
|
||
|
|
||
|
======================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
idGameLocal::CalcFov
|
||
|
|
||
|
Calculates the horizontal and vertical field of view based on a horizontal field of view and custom aspect ratio
|
||
|
====================
|
||
|
*/
|
||
|
void idGameLocal::CalcFov( float base_fov, float &fov_x, float &fov_y, const int width, const int height, const float correctYAspect ) const {
|
||
|
float x;
|
||
|
float y;
|
||
|
float ratio_x;
|
||
|
float ratio_y;
|
||
|
|
||
|
// first, calculate the vertical fov based on a 640x480 view
|
||
|
x = width / idMath::Tan( base_fov / 360.0f * idMath::PI );
|
||
|
if ( correctYAspect > 0.f ) {
|
||
|
x /= correctYAspect;
|
||
|
}
|
||
|
y = idMath::ATan( height, x );
|
||
|
fov_y = y * 360.0f / idMath::PI;
|
||
|
|
||
|
switch( r_aspectRatio.GetInteger() ) {
|
||
|
default:
|
||
|
case 0:
|
||
|
// 4:3
|
||
|
fov_x = base_fov;
|
||
|
return;
|
||
|
case 1:
|
||
|
// 16:9
|
||
|
ratio_x = 16.0f;
|
||
|
ratio_y = 9.0f;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
// 16:10
|
||
|
ratio_x = 16.0f;
|
||
|
ratio_y = 10.0f;
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
// 5:4
|
||
|
ratio_x = 5.0f;
|
||
|
ratio_y = 4.0f;
|
||
|
break;
|
||
|
|
||
|
case -1:
|
||
|
// custom
|
||
|
ratio_x = r_customAspectRatioH.GetFloat();
|
||
|
ratio_y = r_customAspectRatioV.GetFloat();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
y = ratio_y / idMath::Tan( fov_y / 360.0f * idMath::PI );
|
||
|
if ( correctYAspect > 0.f ) {
|
||
|
y *= (height/(float)width) * (SCREEN_WIDTH / (float)SCREEN_HEIGHT);
|
||
|
}
|
||
|
fov_x = idMath::ATan( ratio_x, y ) * 360.0f / idMath::PI;
|
||
|
|
||
|
if ( fov_x < base_fov && correctYAspect == 0.0f ) {
|
||
|
fov_x = base_fov;
|
||
|
x = ratio_x / idMath::Tan( fov_x / 360.0f * idMath::PI );
|
||
|
fov_y = idMath::ATan( ratio_y, x ) * 360.0f / idMath::PI;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idCVar g_testSpectator( "g_testSpectator", "-1", CVAR_GAME | CVAR_INTEGER, "" );
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetActiveViewer
|
||
|
================
|
||
|
*/
|
||
|
idPlayer* idGameLocal::GetActiveViewer( void ) const {
|
||
|
sdDemoManagerLocal& manager = sdDemoManager::GetInstance();
|
||
|
if ( manager.InPlayBack() ) {
|
||
|
if ( manager.NoClip() ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
if ( g_testSpectator.GetInteger() >= 0 && g_testSpectator.GetInteger() < MAX_CLIENTS ) {
|
||
|
idPlayer* viewPlayer = GetClient( g_testSpectator.GetInteger() );
|
||
|
if ( viewPlayer != NULL ) {
|
||
|
return viewPlayer;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idPlayer* viewPlayer = NULL;
|
||
|
if ( gameLocal.serverIsRepeater ) {
|
||
|
int followClient = GetRepeaterFollowClientIndex();
|
||
|
if ( followClient != -1 ) {
|
||
|
viewPlayer = GetClient( followClient );
|
||
|
}
|
||
|
} else if ( localClientNum != ASYNC_DEMO_CLIENT_INDEX ) {
|
||
|
viewPlayer = GetClient( localClientNum );
|
||
|
}
|
||
|
|
||
|
if ( viewPlayer != NULL ) {
|
||
|
if ( viewPlayer->IsSpectating() ) {
|
||
|
idPlayer* spectator = viewPlayer->GetSpectateClient();
|
||
|
if ( spectator ) {
|
||
|
viewPlayer = spectator;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return viewPlayer;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::Draw
|
||
|
|
||
|
makes rendering and sound system calls
|
||
|
================
|
||
|
*/
|
||
|
bool idGameLocal::Draw( void ) {
|
||
|
return gameLocal.playerView.RenderPlayerView( GetActiveViewer() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::Draw2D
|
||
|
================
|
||
|
*/
|
||
|
bool idGameLocal::Draw2D( void ) {
|
||
|
return gameLocal.playerView.RenderPlayerView2D( GetActiveViewer() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idGameLocal::ClientUpdateView
|
||
|
===============
|
||
|
*/
|
||
|
void idGameLocal::ClientUpdateView( const usercmd_t &cmd, int timeLeft ) {
|
||
|
bool updateSight = false;
|
||
|
|
||
|
unlock.unlockedDraw = true;
|
||
|
|
||
|
if ( !unlock.canUnlockFrames ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idPlayer *p = GetActiveViewer();
|
||
|
if ( p == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( p->IsBeingBriefed() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Aspyr's mac code indicated the possibility of interpFraction being 0 causing divide by zero in the angle unlock
|
||
|
// this will catch it, still I'd like to confirm this can happen at all
|
||
|
if ( timeLeft == USERCMD_MSEC ) {
|
||
|
common->Warning( "idGameLocal::ClientUpdateView: timeLeft == USERCMD_MSEC abort" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int effectiveTime = time + ( USERCMD_MSEC - timeLeft );
|
||
|
gameRenderWorld->DebugClearLines( effectiveTime );
|
||
|
gameRenderWorld->DebugClearPolygons( effectiveTime );
|
||
|
|
||
|
// store reference positions and orientations at the beginning of a cycle of unlock frames
|
||
|
if ( framenum != unlock.lastFullDrawFrame ) {
|
||
|
unlock.refAngles = p->renderView.viewaxis.ToAngles();
|
||
|
|
||
|
if ( g_unlock_updateViewpos.GetBool() && g_unlock_viewStyle.GetInteger() == 1 ) {
|
||
|
// when view is interpolated, grab n - 1 as the reference view origin
|
||
|
unlock.viewOrigin = unlock.originlog[ ( framenum + 1 ) & 1 ];
|
||
|
} else {
|
||
|
unlock.viewOrigin = p->renderView.vieworg;
|
||
|
}
|
||
|
|
||
|
if ( p->weapon != NULL ) {
|
||
|
// NOTE: when interpolating, the origin here is already n-1 due to the adjustment done in RenderPlayerView
|
||
|
unlock.weaponOrigin = p->weapon->GetRenderEntity()->origin;
|
||
|
unlock.weaponAxis = p->weapon->GetRenderEntity()->axis;
|
||
|
|
||
|
// look for a bound GUI and store it's original position
|
||
|
// only ever expecting one, if there's more the reference position can be stored in sdGuiSurface directly
|
||
|
bool gotOne = false;
|
||
|
for ( rvClientEntity* cent = p->weapon->clientEntities.Next(); cent != NULL; cent = cent->bindNode.Next() ) {
|
||
|
sdGuiSurface* guiSurface = cent->Cast< sdGuiSurface >();
|
||
|
if ( guiSurface != NULL ) {
|
||
|
if ( gotOne ) {
|
||
|
common->Warning( "Multiple GUIs bound to player weapon in ClientUpdateView" );
|
||
|
}
|
||
|
guiSurface->GetRenderable().GetPosition( unlock.weaponGUIOrigin, unlock.weaponGUIAxis );
|
||
|
gotOne = true; // safe check single GUI
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unlock.doWeapon = true;
|
||
|
} else {
|
||
|
unlock.doWeapon = false;
|
||
|
}
|
||
|
|
||
|
unlock.lastFullDrawFrame = framenum;
|
||
|
}
|
||
|
|
||
|
float interpFraction = float( USERCMD_MSEC - timeLeft ) / float( USERCMD_MSEC );
|
||
|
|
||
|
idAngles currentAngles = unlock.refAngles;
|
||
|
|
||
|
// Gordon: FIXME: This doesn't take all clamps/rate limits into account
|
||
|
if ( g_unlock_updateAngles.GetBool() && !IsPaused() ) {
|
||
|
// apply the latest angles to update the view axis
|
||
|
for ( int i = 0; i < 3; i++ ) {
|
||
|
currentAngles[i] = unlock.refAngles[i] + SHORT2ANGLE( cmd.angles[i] ) - SHORT2ANGLE( p->usercmd.angles[i] );
|
||
|
}
|
||
|
|
||
|
// HACK: let the player clamp the mouse move
|
||
|
idAngles delta = ( currentAngles - unlock.refAngles ).Normalize180();
|
||
|
delta /= interpFraction;
|
||
|
p->MouseMove( unlock.refAngles, delta );
|
||
|
delta *= interpFraction;
|
||
|
currentAngles = unlock.refAngles + delta;
|
||
|
|
||
|
// clamp to the limits
|
||
|
currentAngles.Normalize180();
|
||
|
currentAngles.Clamp( unlock.minAngles, unlock.maxAngles );
|
||
|
|
||
|
p->renderView.viewaxis = currentAngles.ToMat3();
|
||
|
updateSight = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
idVec3 originDelta = vec3_origin;
|
||
|
if ( g_unlock_updateViewpos.GetBool() ) {
|
||
|
switch ( g_unlock_viewStyle.GetInteger() ) {
|
||
|
case 0: {
|
||
|
// extrapolate last speed, ignoring any newer data
|
||
|
idVec3 current = unlock.originlog[ framenum & 1 ];
|
||
|
idVec3 prev = unlock.originlog[ ( framenum + 1 ) & 1 ];
|
||
|
originDelta = ( current - prev ) * interpFraction;
|
||
|
p->renderView.vieworg = current + originDelta;
|
||
|
break;
|
||
|
}
|
||
|
case 1: {
|
||
|
// interpolate between two last positions
|
||
|
// lags behind of one frame (32ms), but no misprediction hitches
|
||
|
idVec3 current = unlock.originlog[ framenum & 1 ];
|
||
|
idVec3 prev = unlock.originlog[ ( framenum + 1 ) & 1 ];
|
||
|
originDelta = ( current - prev ) * interpFraction;
|
||
|
p->renderView.vieworg = prev + originDelta;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
updateSight = true;
|
||
|
}
|
||
|
|
||
|
// sight model for the various scopes needs to be updated
|
||
|
// conveniently uses the same origin as the view, so all computation is already done
|
||
|
if ( updateSight ) {
|
||
|
sdClientAnimated *sight = p->GetSight();
|
||
|
if ( sight != NULL ) {
|
||
|
float fovx, fovy;
|
||
|
gameLocal.CalcFov( p->GetSightFOV(), fovx, fovy );
|
||
|
sight->GetRenderEntity()->weaponDepthHackFOV_x = fovx;
|
||
|
sight->GetRenderEntity()->weaponDepthHackFOV_y = fovy;
|
||
|
|
||
|
sight->SetOrigin( p->renderView.vieworg );
|
||
|
sight->SetAxis( p->renderView.viewaxis );
|
||
|
sight->Present();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// update the origin and axis of the weapon - tied to either position or angles update
|
||
|
if ( unlock.doWeapon && ( g_unlock_updateViewpos.GetBool() || g_unlock_updateAngles.GetBool() ) ) {
|
||
|
// compute the transformation matrix for the angle offset
|
||
|
// get (cmd - ucmd) with angle clamping factored in
|
||
|
idAngles a = currentAngles - unlock.refAngles;
|
||
|
a.Normalize180();
|
||
|
// get the transformation in world axis base
|
||
|
idMat3 world;
|
||
|
idAngles::YawToMat3( unlock.refAngles.yaw, world );
|
||
|
idMat3 t = world.Transpose() * a.ToMat3() * world;
|
||
|
|
||
|
idVec3 updatedOrigin = unlock.weaponOrigin - unlock.viewOrigin;
|
||
|
updatedOrigin *= t;
|
||
|
updatedOrigin += unlock.viewOrigin;
|
||
|
idMat3 updatedAxis = unlock.weaponAxis * t;
|
||
|
|
||
|
p->weapon->GetRenderEntity()->origin = updatedOrigin + originDelta;
|
||
|
p->weapon->GetRenderEntity()->axis = updatedAxis;
|
||
|
|
||
|
idVec3 oldOrigin = p->weapon->GetRenderEntity()->origin;
|
||
|
|
||
|
p->weapon->BecomeActive( TH_UPDATEVISUALS );
|
||
|
p->weapon->Present();
|
||
|
|
||
|
// update bound GUIs as well
|
||
|
for ( rvClientEntity* cent = p->weapon->clientEntities.Next(); cent != NULL; cent = cent->bindNode.Next() ) {
|
||
|
sdGuiSurface* guiSurface;
|
||
|
rvClientEffect* effect;
|
||
|
if ( ( guiSurface = cent->Cast< sdGuiSurface >() ) != NULL ) {
|
||
|
// same adjustment work as the weapon model
|
||
|
updatedOrigin = unlock.weaponGUIOrigin - unlock.viewOrigin;
|
||
|
updatedOrigin *= t;
|
||
|
updatedOrigin += unlock.viewOrigin;
|
||
|
updatedAxis = unlock.weaponGUIAxis * t;
|
||
|
|
||
|
guiSurface->GetRenderable().SetPosition( updatedOrigin + originDelta, updatedAxis );
|
||
|
} else if ( ( effect = cent->Cast< rvClientEffect >() ) != NULL ) {
|
||
|
effect->ClientUpdateView();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( g_unlock_interpolateMoving.GetBool() ) {
|
||
|
// walk entities that need interpolation and present them to the renderer with updated origins
|
||
|
idEntity *e;
|
||
|
for ( e = interpolateEntities.Next(); e != NULL; e = e->interpolateNode.Next() ) {
|
||
|
if ( e == p ) { // not on self (which you don't see in 1stp anyway so shouldn't matter)
|
||
|
continue;
|
||
|
}
|
||
|
idVec3 A = e->interpolateHistory[ ( gameLocal.framenum + 1 ) & 1 ];
|
||
|
idVec3 B = e->interpolateHistory[ gameLocal.framenum & 1 ];
|
||
|
float ratio = interpFraction;
|
||
|
e->GetRenderEntity()->origin = ( 1.0f - ratio ) * A + ratio * B;
|
||
|
e->BecomeActive( TH_UPDATEVISUALS );
|
||
|
e->Present();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetLevelMap
|
||
|
|
||
|
should only be used for in-game level editing
|
||
|
================
|
||
|
*/
|
||
|
idMapFile *idGameLocal::GetLevelMap( void ) {
|
||
|
if ( mapFile && mapFile->HasPrimitiveData()) {
|
||
|
return mapFile;
|
||
|
}
|
||
|
if ( !mapFileName.Length() ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if ( mapFile ) {
|
||
|
delete mapFile;
|
||
|
}
|
||
|
|
||
|
mapFile = new idMapFile;
|
||
|
if ( !mapFile->Parse( mapFileName ) ) {
|
||
|
delete mapFile;
|
||
|
mapFile = NULL;
|
||
|
}
|
||
|
|
||
|
return mapFile;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetMapName
|
||
|
================
|
||
|
*/
|
||
|
const char *idGameLocal::GetMapName( void ) const {
|
||
|
return mapFileName.c_str();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::CallFrameCommand
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::CallFrameCommand( const sdProgram::sdFunction *frameCommand ) {
|
||
|
frameCommandThread->CallFunction( frameCommand );
|
||
|
if ( !frameCommandThread->Execute() ) {
|
||
|
gameLocal.Error( "idGameLocal::CallFrameCommand '%s' Cannot be Blocking", frameCommand->GetName() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::CallFrameCommand
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::CallFrameCommand( idScriptObject* object, const sdProgram::sdFunction *frameCommand ) {
|
||
|
frameCommandThread->CallFunction( object, frameCommand );
|
||
|
if ( !frameCommandThread->Execute() ) {
|
||
|
gameLocal.Error( "idGameLocal::CallFrameCommand '%s' Cannot be Blocking", frameCommand->GetName() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::CallObjectFrameCommand
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::CallObjectFrameCommand( idScriptObject* object, const char *frameCommand, bool allowError ) {
|
||
|
if ( !object ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const sdProgram::sdFunction* func = object->GetFunction( frameCommand );
|
||
|
|
||
|
if ( !func ) {
|
||
|
if ( allowError ) {
|
||
|
Error( "Unknown function '%s' called for frame command on entity '%s'", frameCommand, object->GetTypeName() );
|
||
|
}
|
||
|
} else {
|
||
|
frameCommandThread->CallFunction( object, func );
|
||
|
if ( !frameCommandThread->Execute() ) {
|
||
|
gameLocal.Error( "idGameLocal::CallFrameCommand '%s' Cannot be Blocking", frameCommand );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::ShowTargets
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::ShowTargets( void ) {
|
||
|
idMat3 axis = GetLocalPlayer()->viewAngles.ToMat3();
|
||
|
idVec3 up = axis[ 2 ] * 5.0f;
|
||
|
const idVec3 &viewPos = GetLocalPlayer()->GetPhysics()->GetOrigin();
|
||
|
idBounds viewTextBounds( viewPos );
|
||
|
idBounds viewBounds( viewPos );
|
||
|
idBounds box( idVec3( -4.0f, -4.0f, -4.0f ), idVec3( 4.0f, 4.0f, 4.0f ) );
|
||
|
idEntity *ent;
|
||
|
idEntity *target;
|
||
|
int i;
|
||
|
idBounds totalBounds;
|
||
|
|
||
|
viewTextBounds.ExpandSelf( 128.0f );
|
||
|
viewBounds.ExpandSelf( 512.0f );
|
||
|
for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
totalBounds = ent->GetPhysics()->GetAbsBounds();
|
||
|
for( i = 0; i < ent->targets.Num(); i++ ) {
|
||
|
target = ent->targets[ i ].GetEntity();
|
||
|
if ( target ) {
|
||
|
totalBounds.AddBounds( target->GetPhysics()->GetAbsBounds() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !viewBounds.IntersectsBounds( totalBounds ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
float dist;
|
||
|
idVec3 dir = totalBounds.GetCenter() - viewPos;
|
||
|
dir.NormalizeFast();
|
||
|
totalBounds.RayIntersection( viewPos, dir, dist );
|
||
|
float frac = ( 512.0f - dist ) / 512.0f;
|
||
|
if ( frac < 0.0f ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
gameRenderWorld->DebugBounds( ( ent->IsHidden() ? colorLtGrey : colorOrange ) * frac, ent->GetPhysics()->GetAbsBounds() );
|
||
|
if ( viewTextBounds.IntersectsBounds( ent->GetPhysics()->GetAbsBounds() ) ) {
|
||
|
idVec3 center = ent->GetPhysics()->GetAbsBounds().GetCenter();
|
||
|
gameRenderWorld->DrawText( ent->name.c_str(), center - up, 0.1f, colorWhite * frac, axis, 1 );
|
||
|
gameRenderWorld->DrawText( ent->GetEntityDefName(), center, 0.1f, colorWhite * frac, axis, 1 );
|
||
|
gameRenderWorld->DrawText( va( "#%d", ent->entityNumber ), center + up, 0.1f, colorWhite * frac, axis, 1 );
|
||
|
}
|
||
|
|
||
|
for( i = 0; i < ent->targets.Num(); i++ ) {
|
||
|
target = ent->targets[ i ].GetEntity();
|
||
|
if ( target ) {
|
||
|
gameRenderWorld->DebugArrow( colorYellow * frac, ent->GetPhysics()->GetAbsBounds().GetCenter(), target->GetPhysics()->GetOrigin(), 10, 0 );
|
||
|
gameRenderWorld->DebugBounds( colorGreen * frac, box, target->GetPhysics()->GetOrigin() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idCVar g_showEntityInfoPrint( "g_showEntityInfoPrint", "0", CVAR_BOOL, "" );
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::RunDebugInfo
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::RunDebugInfo( void ) {
|
||
|
idEntity *ent;
|
||
|
|
||
|
idPlayer* player = GetLocalPlayer();
|
||
|
if ( !player ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
renderView_t* view = &player->renderView;
|
||
|
|
||
|
const idVec3& origin = view->vieworg;
|
||
|
|
||
|
if ( g_showEntityInfo.GetBool() ) {
|
||
|
idMat3 axis = player->viewAngles.ToMat3();
|
||
|
idVec3 up = axis[ 2 ] * 5.0f;
|
||
|
idBounds viewTextBounds( origin );
|
||
|
idBounds viewBounds( origin );
|
||
|
|
||
|
viewTextBounds.ExpandSelf( 128.0f );
|
||
|
viewBounds.ExpandSelf( g_maxShowDistance.GetFloat() );
|
||
|
for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
// don't draw the worldspawn
|
||
|
if ( ent == world ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// skip if the entity is very far away
|
||
|
if ( !viewBounds.IntersectsBounds( ent->GetPhysics()->GetAbsBounds() ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const idBounds &entBounds = ent->GetPhysics()->GetAbsBounds();
|
||
|
int contents = ent->GetPhysics()->GetContents();
|
||
|
if ( contents & CONTENTS_BODY || contents & CONTENTS_SLIDEMOVER ) {
|
||
|
gameRenderWorld->DebugBounds( colorCyan, entBounds );
|
||
|
} else if ( contents & CONTENTS_TRIGGER ) {
|
||
|
gameRenderWorld->DebugBounds( colorOrange, entBounds );
|
||
|
} else if ( contents & CONTENTS_SOLID ) {
|
||
|
gameRenderWorld->DebugBounds( colorGreen, entBounds );
|
||
|
} else {
|
||
|
if ( !entBounds.GetVolume() ) {
|
||
|
gameRenderWorld->DebugBounds( colorMdGrey, entBounds.Expand( 8.0f ) );
|
||
|
} else {
|
||
|
gameRenderWorld->DebugBounds( colorMdGrey, entBounds );
|
||
|
}
|
||
|
}
|
||
|
if ( viewTextBounds.IntersectsBounds( entBounds ) ) {
|
||
|
gameRenderWorld->DrawText( ent->name.c_str(), entBounds.GetCenter() - up, 0.1f, colorWhite, axis, 1 );
|
||
|
gameRenderWorld->DrawText( ent->GetType()->classname, entBounds.GetCenter(), 0.1f, colorWhite, axis, 1 );
|
||
|
gameRenderWorld->DrawText( va( "#%d", ent->entityNumber ), entBounds.GetCenter() + up, 0.1f, colorWhite, axis, 1 );
|
||
|
}
|
||
|
|
||
|
if ( g_showEntityInfoPrint.GetBool() ) {
|
||
|
gameLocal.Printf( "%s\n", ent->name.c_str() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g_showEntityInfoPrint.SetBool( false );
|
||
|
}
|
||
|
|
||
|
// debug tool to draw bounding boxes around active entities
|
||
|
if ( g_showActiveEntities.GetBool() ) {
|
||
|
for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
|
||
|
idBounds b = ent->GetPhysics()->GetBounds();
|
||
|
if ( b.GetVolume() <= 0 ) {
|
||
|
b[0][0] = b[0][1] = b[0][2] = -8;
|
||
|
b[1][0] = b[1][1] = b[1][2] = 8;
|
||
|
}
|
||
|
gameRenderWorld->DebugBounds( colorGreen, b, ent->GetPhysics()->GetOrigin(), ent->GetPhysics()->GetAxis() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( g_showTargets.GetBool() ) {
|
||
|
ShowTargets();
|
||
|
}
|
||
|
|
||
|
if ( g_showTriggers.GetBool() ) {
|
||
|
idTrigger::DrawDebugInfo();
|
||
|
}
|
||
|
|
||
|
if ( g_editEntityMode.GetBool() && editEntities ) {
|
||
|
editEntities->DisplayEntities();
|
||
|
}
|
||
|
|
||
|
if ( g_showCollisionWorld.GetInteger() ) {
|
||
|
gameLocal.clip.DrawWorld( g_maxShowDistance.GetFloat() );
|
||
|
}
|
||
|
|
||
|
if ( g_showCollisionModels.GetBool() ) {
|
||
|
clip.DrawClipModels( player->renderView.vieworg, player->renderView.viewaxis, g_maxShowDistance.GetFloat(), pm_thirdPerson.GetBool() ? NULL : player );
|
||
|
}
|
||
|
|
||
|
if ( g_showRenderModelBounds.GetBool() ) {
|
||
|
for ( idEntity* ent = gameLocal.spawnedEntities.Next(); ent; ent = ent->spawnNode.Next() ) {
|
||
|
const renderEntity_t* renderEnt = ent->GetRenderEntity();
|
||
|
if ( !renderEnt ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
gameRenderWorld->DebugBounds( colorRed, renderEnt->bounds, renderEnt->origin, renderEnt->axis );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( g_showClipSectors.GetBool() ) {
|
||
|
clip.DrawClipSectors();
|
||
|
}
|
||
|
|
||
|
if ( g_showAreaClipSectors.GetFloat() ) {
|
||
|
clip.DrawAreaClipSectors( g_showAreaClipSectors.GetFloat() );
|
||
|
}
|
||
|
|
||
|
if ( g_showCollisionTraces.GetBool() ) {
|
||
|
clip.PrintStatistics();
|
||
|
}
|
||
|
|
||
|
if ( g_showPVS.GetInteger() ) {
|
||
|
pvs.DrawPVS( origin, ( g_showPVS.GetInteger() == 2 ) ? PVS_ALL_PORTALS_OPEN : PVS_NORMAL );
|
||
|
}
|
||
|
|
||
|
if ( *g_debugMask.GetString() ) {
|
||
|
qhandle_t handle = gameLocal.GetDeploymentMask( g_debugMask.GetString() );
|
||
|
if ( handle != -1 ) {
|
||
|
DebugDeploymentMask( handle );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( g_showActiveDeployZones.GetBool() ) {
|
||
|
sdInstanceCollector< sdDeployZone > deployZones( true );
|
||
|
for ( int i = 0; i < deployZones.Num(); i++ ) {
|
||
|
idPhysics* physics = deployZones[ i ]->GetPhysics();
|
||
|
if ( deployZones[ i ]->IsActive() ) {
|
||
|
physics->GetClipModel()->Draw( physics->GetOrigin(), physics->GetAxis() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( g_debugLocations.GetBool() ) {
|
||
|
sdLocationMarker::DebugDraw( player->renderView.vieworg );
|
||
|
}
|
||
|
|
||
|
// collision map debug output
|
||
|
collisionModelManager->DebugOutput( player->renderView.vieworg, player->renderView.viewaxis );
|
||
|
|
||
|
botThreadData.DebugOutput();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
idGameLocal::CheatsOk
|
||
|
==================
|
||
|
*/
|
||
|
bool idGameLocal::CheatsOk( bool requirePlayer ) {
|
||
|
idPlayer *player;
|
||
|
|
||
|
if ( networkSystem->IsActive() ) {
|
||
|
if ( !cvarSystem->GetCVarBool( "net_allowCheats" ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
} else {
|
||
|
if ( !IsDeveloper() ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
player = GetLocalPlayer();
|
||
|
if ( !requirePlayer || ( player && ( player->GetHealth() > 0 ) ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
Printf( "You must be alive to use this command.\n" );
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::RegisterEntity
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::RegisterEntity( idEntity *ent ) {
|
||
|
int spawn_entnum;
|
||
|
|
||
|
if ( !spawnArgs.GetInt( "spawn_entnum", "0", spawn_entnum ) ) {
|
||
|
while( entities[firstFreeIndex] && firstFreeIndex < ENTITYNUM_MAX_NORMAL ) {
|
||
|
firstFreeIndex++;
|
||
|
}
|
||
|
if ( firstFreeIndex >= ENTITYNUM_MAX_NORMAL ) {
|
||
|
Error( "no free entities" );
|
||
|
}
|
||
|
spawn_entnum = firstFreeIndex++;
|
||
|
}
|
||
|
|
||
|
if ( spawn_entnum >= MAX_GENTITIES ) {
|
||
|
Error( "idGameLocal::RegisterEntity: spawn count overflow" );
|
||
|
}
|
||
|
|
||
|
if ( entities[ spawn_entnum ] != NULL ) {
|
||
|
Printf( "Index: %d\n", spawn_entnum );
|
||
|
Printf( "Old Entity: '%s'\n", entities[ spawn_entnum ]->GetName() );
|
||
|
Printf( "New Entity: '%s'\n", ent->GetName() );
|
||
|
Error( "idGameLocal::RegisterEntity: entity spawned in existing entity slot" );
|
||
|
}
|
||
|
|
||
|
entities[ spawn_entnum ] = ent;
|
||
|
spawnIds[ spawn_entnum ] = spawnCount++;
|
||
|
ent->entityNumber = spawn_entnum;
|
||
|
|
||
|
int id = GetSpawnId( ent );
|
||
|
if( id == NETWORKEVENT_RULES_ID || id == NETWORKEVENT_OBJECTIVE_ID ) {
|
||
|
Error( "Spawn ID equal to event magic number!" );
|
||
|
}
|
||
|
|
||
|
ent->spawnNode.AddToEnd( spawnedEntities );
|
||
|
ent->spawnArgs.TransferKeyValues( spawnArgs );
|
||
|
|
||
|
const idKeyValue* kv = NULL;
|
||
|
while ( kv = ent->spawnArgs.MatchPrefix( "collection", kv ) ) {
|
||
|
const char* collectionName = kv->GetValue();
|
||
|
sdEntityCollection* collection = GetEntityCollection( collectionName, true );
|
||
|
collection->Add( ent );
|
||
|
}
|
||
|
|
||
|
if ( spawn_entnum != ENTITYNUM_WORLD && spawn_entnum > numEntities ) {
|
||
|
numEntities = spawn_entnum;
|
||
|
}
|
||
|
|
||
|
ent->ForceNetworkUpdate();
|
||
|
|
||
|
entityDebugInfo[ spawn_entnum ].OnCreate( ent );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::ForceNetworkUpdate
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::ForceNetworkUpdate( idEntity *ent ) {
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
clientNetworkInfo[ i ].lastMarker[ ent->entityNumber ] = -1;
|
||
|
}
|
||
|
demoClientNetworkInfo.lastMarker[ ent->entityNumber ] = -1;
|
||
|
|
||
|
#ifdef SD_SUPPORT_REPEATER
|
||
|
repeaterClientNetworkInfo.lastMarker[ ent->entityNumber ] = -1;
|
||
|
for ( int i = 0; i < repeaterNetworkInfo.Num(); i++ ) {
|
||
|
if ( repeaterNetworkInfo[ i ] == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
( *repeaterNetworkInfo[ i ] ).lastMarker[ ent->entityNumber ] = -1;
|
||
|
}
|
||
|
#endif // SD_SUPPORT_REPEATER
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::UnregisterEntity
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::UnregisterEntity( idEntity *ent ) {
|
||
|
assert( ent );
|
||
|
|
||
|
entityDebugInfo[ ent->entityNumber ].OnDestroy( ent );
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
sdCommandMapInfoManager::GetInstance().OnEntityDeleted( ent );
|
||
|
#endif // _DEBUG
|
||
|
|
||
|
FreeEntityNetworkEvents( ent, -1 );
|
||
|
|
||
|
if ( editEntities ) {
|
||
|
editEntities->RemoveSelectedEntity( ent );
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < entityCollections.Num(); i++ ) {
|
||
|
entityCollections[ i ]->Remove( ent );
|
||
|
}
|
||
|
|
||
|
if ( ( ent->entityNumber != ENTITYNUM_NONE ) && ( entities[ ent->entityNumber ] == ent ) ) {
|
||
|
ent->spawnNode.Remove();
|
||
|
ent->networkNode.Remove();
|
||
|
entities[ ent->entityNumber ] = NULL;
|
||
|
spawnIds[ ent->entityNumber ] = -1;
|
||
|
if ( ent->entityNumber >= MAX_CLIENTS && ent->entityNumber < firstFreeIndex ) {
|
||
|
firstFreeIndex = ent->entityNumber;
|
||
|
}
|
||
|
ent->entityNumber = ENTITYNUM_NONE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::SpawnEntityType
|
||
|
================
|
||
|
*/
|
||
|
idEntity *idGameLocal::SpawnEntityType( const idTypeInfo &classdef, bool callPostMapSpawn, const idDict *args ) {
|
||
|
idClass *obj;
|
||
|
|
||
|
if ( !classdef.IsType( idEntity::Type ) ) {
|
||
|
Error( "Attempted to spawn non-entity class '%s'", classdef.classname );
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
if ( args ) {
|
||
|
spawnArgs = *args;
|
||
|
} else {
|
||
|
spawnArgs.Clear();
|
||
|
}
|
||
|
obj = classdef.CreateInstance();
|
||
|
obj->CallSpawn();
|
||
|
}
|
||
|
|
||
|
catch( idAllocError & ) {
|
||
|
obj = NULL;
|
||
|
}
|
||
|
|
||
|
idEntity* ent = obj->Cast< idEntity >();
|
||
|
if ( ent ) {
|
||
|
if ( ent->StartSynced() ) {
|
||
|
ent->SetNetworkSynced( true );
|
||
|
}
|
||
|
|
||
|
if ( callPostMapSpawn ) {
|
||
|
ent->PostMapSpawn();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
spawnArgs.Clear();
|
||
|
|
||
|
return ent;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::CallSpawnFuncs
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::CallSpawnFuncs( idEntity* entity, const idDict *args ) {
|
||
|
if ( args ) {
|
||
|
spawnArgs = *args;
|
||
|
} else {
|
||
|
spawnArgs.Clear();
|
||
|
}
|
||
|
entity->CallSpawn();
|
||
|
spawnArgs.Clear();
|
||
|
|
||
|
if ( entity->StartSynced() ) {
|
||
|
entity->SetNetworkSynced( true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::CreateEntityType
|
||
|
================
|
||
|
*/
|
||
|
idEntity *idGameLocal::CreateEntityType( const idTypeInfo &classdef ) {
|
||
|
if ( !classdef.IsType( idEntity::Type ) ) {
|
||
|
Error( "Attempted to spawn non-entity class '%s'", classdef.classname );
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
return reinterpret_cast< idEntity* >( classdef.CreateInstance() );
|
||
|
}
|
||
|
catch( idAllocError & ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::FindEntityDefDict
|
||
|
===================
|
||
|
*/
|
||
|
const idDict* idGameLocal::FindEntityDefDict( const char *name, bool makeDefault ) const {
|
||
|
const idDeclEntityDef* def = declHolder.declEntityDefType.LocalFind( name, makeDefault );
|
||
|
if ( !def ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
return &def->dict;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::SpawnEntityDef
|
||
|
|
||
|
Finds the spawn function for the entity and calls it,
|
||
|
returning false if not found
|
||
|
===================
|
||
|
*/
|
||
|
bool idGameLocal::SpawnEntityDef( const idDict &args, bool callPostMapSpawn, idEntity **ent, int entityNum, int mapSpawnId ) {
|
||
|
if ( ent ) {
|
||
|
*ent = NULL;
|
||
|
}
|
||
|
|
||
|
spawnArgs = args;
|
||
|
|
||
|
idStr error;
|
||
|
const char* name;
|
||
|
if ( spawnArgs.GetString( "name", "", &name ) ) {
|
||
|
sprintf( error, " on '%s'", name );
|
||
|
}
|
||
|
|
||
|
const char* classname = spawnArgs.GetString( "classname" );
|
||
|
|
||
|
const idDeclEntityDef *def = declHolder.declEntityDefType.LocalFind( classname, false );
|
||
|
if ( !def ) {
|
||
|
Warning( "Unknown classname '%s'%s.", classname, error.c_str() );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
spawnArgs.SetDefaults( &def->dict );
|
||
|
if ( entityNum != -1 ) {
|
||
|
if ( InhibitEntitySpawn( spawnArgs ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
spawnArgs.SetInt( "spawn_entnum", entityNum );
|
||
|
|
||
|
// precache any media specified in the map entity
|
||
|
CacheDictionaryMedia( spawnArgs );
|
||
|
}
|
||
|
|
||
|
// check if we should spawn a class object
|
||
|
const char* spawn = spawnArgs.GetString( "spawnclass" );
|
||
|
if ( !*spawn ) {
|
||
|
Warning( "%s doesn't include a spawnclass %s.", classname, error.c_str() );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
idTypeInfo* cls = idClass::GetClass( spawn );
|
||
|
if ( cls == NULL ) {
|
||
|
Warning( "Could not spawn '%s'. Class '%s' not found%s.", classname, spawn, error.c_str() );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( cls->InhibitSpawn( args ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
idClass* obj = cls->CreateInstance();
|
||
|
if ( obj == NULL ) {
|
||
|
Warning( "Could not spawn '%s'. Instance could not be created%s.", classname, error.c_str() );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// setting the mapSpawnId on the dict so it is available in the Spawn function
|
||
|
if ( mapSpawnId != -1 ) {
|
||
|
spawnArgs.SetInt( "spawn_mapSpawnId", mapSpawnId );
|
||
|
}
|
||
|
|
||
|
obj->CallSpawn();
|
||
|
|
||
|
idEntity* e = obj->Cast< idEntity >();
|
||
|
if ( ent != NULL ) {
|
||
|
*ent = e;
|
||
|
}
|
||
|
|
||
|
if ( e != NULL ) {
|
||
|
e->mapSpawnId = mapSpawnId;
|
||
|
|
||
|
if ( e->StartSynced() ) {
|
||
|
e->SetNetworkSynced( true );
|
||
|
}
|
||
|
|
||
|
if ( callPostMapSpawn ) {
|
||
|
e->PostMapSpawn();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::SpawnClientEntityDef
|
||
|
|
||
|
Finds the spawn function for the entity and calls it,
|
||
|
returning false if not found
|
||
|
===================
|
||
|
*/
|
||
|
bool idGameLocal::SpawnClientEntityDef( const idDict &args, rvClientEntity **ent, int mapSpawnId ) {
|
||
|
if ( !gameLocal.DoClientSideStuff() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( ent ) {
|
||
|
*ent = NULL;
|
||
|
}
|
||
|
|
||
|
spawnArgs = args;
|
||
|
|
||
|
idStr error;
|
||
|
const char* name;
|
||
|
if ( spawnArgs.GetString( "name", "", &name ) ) {
|
||
|
sprintf( error, " on '%s'", name);
|
||
|
}
|
||
|
|
||
|
const char* classname = spawnArgs.GetString( "classname" );
|
||
|
|
||
|
const idDeclEntityDef *def = declHolder.declEntityDefType.LocalFind( classname, false );
|
||
|
if ( !def ) {
|
||
|
Warning( "Unknown classname '%s'%s.", classname, error.c_str() );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
spawnArgs.SetDefaults( &def->dict );
|
||
|
if ( mapSpawnId != -1 ) {
|
||
|
if ( InhibitEntitySpawn( spawnArgs ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
spawnArgs.SetInt( "spawn_entnum", mapSpawnId );
|
||
|
|
||
|
// precache any media specified in the map entity
|
||
|
CacheDictionaryMedia( spawnArgs );
|
||
|
}
|
||
|
|
||
|
// check if we should spawn a class object
|
||
|
const char* spawn = spawnArgs.GetString( "spawnclass" );
|
||
|
if ( !*spawn ) {
|
||
|
Warning( "%s doesn't include a spawnclass %s.", classname, error.c_str() );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
idTypeInfo* cls = idClass::GetClass( spawn );
|
||
|
if ( !cls ) {
|
||
|
Warning( "Could not spawn '%s'. Class '%s' not found%s.", classname, spawn, error.c_str() );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( cls->InhibitSpawn( args ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
idClass* obj = cls->CreateInstance();
|
||
|
if ( !obj ) {
|
||
|
Warning( "Could not spawn '%s'. Instance could not be created%s.", classname, error.c_str() );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// setting the mapSpawnId on the dict so it is available in the Spawn function
|
||
|
if ( mapSpawnId != -1 ) {
|
||
|
spawnArgs.SetInt( "spawn_mapSpawnId", mapSpawnId );
|
||
|
}
|
||
|
|
||
|
obj->CallSpawn();
|
||
|
|
||
|
rvClientEntity* e = obj->Cast< rvClientEntity >();
|
||
|
if ( ent ) {
|
||
|
*ent = e;
|
||
|
}
|
||
|
|
||
|
// If it's scripted make sure we also initialize the script object...
|
||
|
// this is a bit backward but it's because the basic rvClientEntity doesn't have spawnargs
|
||
|
sdClientScriptEntity *sc = obj->Cast< sdClientScriptEntity >();
|
||
|
if ( sc != NULL ) {
|
||
|
const char* scriptClass = spawnArgs.GetString( "scriptobject" );
|
||
|
if ( !*scriptClass ) {
|
||
|
Warning( "Could not spawn '%s'. No script object specified.", classname );
|
||
|
return false;
|
||
|
}
|
||
|
sc->CreateByName( &spawnArgs, scriptClass );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::InhibitEntitySpawn
|
||
|
================
|
||
|
*/
|
||
|
bool idGameLocal::InhibitEntitySpawn( idDict &spawnArgs ) {
|
||
|
// allow game rules to inhibit an entity's spawn
|
||
|
sdGameRules* tempRules = GetRulesInstance( serverInfo.GetString( "si_rules" ) );
|
||
|
if ( tempRules->InhibitEntitySpawn( spawnArgs ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
idGameLocal::GameState
|
||
|
|
||
|
Used to allow entities to know if they're being spawned during the initial spawn.
|
||
|
==============
|
||
|
*/
|
||
|
gameState_t idGameLocal::GameState( void ) const {
|
||
|
return gamestate;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
idGameLocal::SpawnMapEntities
|
||
|
|
||
|
Parses textual entity definitions out of an entstring and spawns gentities.
|
||
|
==============
|
||
|
*/
|
||
|
void idGameLocal::SpawnMapEntities( void ) {
|
||
|
Printf( "Spawning entities\n" );
|
||
|
|
||
|
if ( mapFile == NULL ) {
|
||
|
Printf( "No mapfile present\n" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int numMapEntities = mapFile->GetNumEntities();
|
||
|
if ( numMapEntities == 0 ) {
|
||
|
Error( "...no entities" );
|
||
|
}
|
||
|
|
||
|
int num = 0;
|
||
|
int inhibit = 0;
|
||
|
|
||
|
insideExecuteMapChange = true;
|
||
|
|
||
|
bool showUpdate = !networkSystem->IsDedicated();
|
||
|
idWStrList locArgs( 1 );
|
||
|
locArgs.SetNum( 1 );
|
||
|
|
||
|
locArgs[ 0 ] = L"0";
|
||
|
UpdateLevelLoadScreen( common->LocalizeText( "guis/mainmenu/loading/spawning_entities", locArgs ).c_str() );
|
||
|
|
||
|
for ( int i = 0; i < numMapEntities; i++ ) {
|
||
|
idMapEntity* mapEnt = mapFile->GetEntity( i );
|
||
|
|
||
|
int entNum = i == 0 ? ENTITYNUM_WORLD : ( MAX_CLIENTS + i - 1 );
|
||
|
|
||
|
idEntity* ent;
|
||
|
if ( SpawnEntityDef( mapEnt->epairs, false, &ent, entNum, i ) ) {
|
||
|
num++;
|
||
|
} else {
|
||
|
if ( i == 0 ) {
|
||
|
insideExecuteMapChange = false;
|
||
|
Error( "Problem spawning world entity" );
|
||
|
}
|
||
|
inhibit++;
|
||
|
}
|
||
|
if( showUpdate && i % 25 == 0 ) {
|
||
|
locArgs[ 0 ] = va( L"%0.f", ( static_cast< float >( i ) / numMapEntities ) * 100.0f );
|
||
|
|
||
|
UpdateLevelLoadScreen( common->LocalizeText( "guis/mainmenu/loading/spawning_entities", locArgs ).c_str() );
|
||
|
common->PacifierUpdate();
|
||
|
}
|
||
|
}
|
||
|
// lock at 100%
|
||
|
locArgs[ 0 ] = L"100";
|
||
|
UpdateLevelLoadScreen( common->LocalizeText( "guis/mainmenu/loading/spawning_entities", locArgs ).c_str() );
|
||
|
|
||
|
insideExecuteMapChange = false;
|
||
|
|
||
|
for ( idEntity* ent = spawnedEntities.Next(); ent; ent = ent->spawnNode.Next() ) {
|
||
|
ent->PostMapSpawn();
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.isClient ) {
|
||
|
// Remove any synced entities, as we won't get deletion events for ones from the map
|
||
|
idEntity* nextNetEnt;
|
||
|
for ( idEntity* netEnt = networkedEntities.Next(); netEnt; netEnt = nextNetEnt ) {
|
||
|
nextNetEnt = netEnt->networkNode.Next();
|
||
|
delete netEnt;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idDict args;
|
||
|
args.SetInt( "spawn_entnum", ENTITYNUM_CLIENT );
|
||
|
if ( !SpawnEntityType( rvClientPhysics::Type, false, &args ) || !entities[ ENTITYNUM_CLIENT ] ) {
|
||
|
Error( "Problem spawning client physics entity" );
|
||
|
}
|
||
|
|
||
|
Printf( "...%i entities spawned, %i inhibited\n\n", num, inhibit );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::AddEntityToHash
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::AddEntityToHash( const char *name, idEntity *ent ) {
|
||
|
if ( FindEntity( name ) ) {
|
||
|
Error( "Multiple entities named '%s'", name );
|
||
|
}
|
||
|
entityHash.Add( entityHash.GenerateKey( name, true ), ent->entityNumber );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::RemoveEntityFromHash
|
||
|
================
|
||
|
*/
|
||
|
bool idGameLocal::RemoveEntityFromHash( const char *name, idEntity *ent ) {
|
||
|
int hash, i;
|
||
|
|
||
|
hash = entityHash.GenerateKey( name, true );
|
||
|
for ( i = entityHash.GetFirst( hash ); i != -1; i = entityHash.GetNext( i ) ) {
|
||
|
if ( entities[i] && entities[i] == ent && entities[i]->name.Icmp( name ) == 0 ) {
|
||
|
entityHash.Remove( hash, i );
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetTargets
|
||
|
================
|
||
|
*/
|
||
|
int idGameLocal::GetTargets( const idDict &args, idList< idEntityPtr<idEntity> > &list, const char *ref ) const {
|
||
|
int i, num, refLength;
|
||
|
const idKeyValue *arg;
|
||
|
idEntity *ent;
|
||
|
|
||
|
list.Clear();
|
||
|
|
||
|
refLength = idStr::Length( ref );
|
||
|
num = args.GetNumKeyVals();
|
||
|
for( i = 0; i < num; i++ ) {
|
||
|
|
||
|
arg = args.GetKeyVal( i );
|
||
|
if ( arg->GetKey().Icmpn( ref, refLength ) == 0 ) {
|
||
|
|
||
|
ent = FindEntity( arg->GetValue() );
|
||
|
if ( ent ) {
|
||
|
idEntityPtr<idEntity> &entityPtr = list.Alloc();
|
||
|
entityPtr = ent;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return list.Num();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
idGameLocal::GetTraceEntity
|
||
|
|
||
|
returns the master entity of a trace. for example, if the trace entity is the player's head, it will return the player.
|
||
|
=============
|
||
|
*/
|
||
|
idEntity *idGameLocal::GetTraceEntity( const trace_t &trace ) const {
|
||
|
if ( trace.fraction == 1.0f ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return entities[ trace.c.entityNum ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
idGameLocal::ArgCompletion_EntityName
|
||
|
|
||
|
Argument completion for entity names
|
||
|
=============
|
||
|
*/
|
||
|
void idGameLocal::ArgCompletion_EntityName( const idCmdArgs &args, void(*callback)( const char *s ) ) {
|
||
|
int i;
|
||
|
|
||
|
for( i = 0; i < gameLocal.numEntities; i++ ) {
|
||
|
if ( gameLocal.entities[ i ] ) {
|
||
|
callback( va( "%s %s", args.Argv( 0 ), gameLocal.entities[ i ]->name.c_str() ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
idGameLocal::FindEntity
|
||
|
|
||
|
Returns the entity whose name matches the specified string.
|
||
|
=============
|
||
|
*/
|
||
|
idEntity *idGameLocal::FindEntity( const char *name ) const {
|
||
|
int hash, i;
|
||
|
|
||
|
hash = entityHash.GenerateKey( name, true );
|
||
|
for ( i = entityHash.GetFirst( hash ); i != -1; i = entityHash.GetNext( i ) ) {
|
||
|
if ( entities[i] && entities[i]->name.Icmp( name ) == 0 ) {
|
||
|
return entities[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
idGameLocal::FindClassTypeT
|
||
|
=============
|
||
|
*/
|
||
|
template< typename T >
|
||
|
T *idGameLocal::FindClassTypeT( idEntity *from ) const {
|
||
|
idEntity *ent;
|
||
|
|
||
|
if ( !from ) {
|
||
|
ent = spawnedEntities.Next();
|
||
|
} else {
|
||
|
ent = from->spawnNode.Next();
|
||
|
}
|
||
|
|
||
|
for ( ; ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
if ( ent->IsType( T::Type ) ) {
|
||
|
return reinterpret_cast< T* >( ent );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
idGameLocal::FindEntityByType
|
||
|
=============
|
||
|
*/
|
||
|
idEntity *idGameLocal::FindEntityByType( idEntity *from, const idDeclEntityDef* type ) const {
|
||
|
idEntity* ent = from ? from->spawnNode.Next() : spawnedEntities.Next();
|
||
|
|
||
|
for ( ; ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
if ( ent->entityDefNumber == type->Index() ) {
|
||
|
return ent;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
idGameLocal::FindClassType
|
||
|
=============
|
||
|
*/
|
||
|
idEntity *idGameLocal::FindClassType( idEntity *from, const idTypeInfo& type, idEntity *ignore ) const {
|
||
|
idEntity* ent = from ? from->spawnNode.Next() : spawnedEntities.Next();
|
||
|
|
||
|
for ( ; ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
if ( ent->IsType( type ) && ent != ignore ) {
|
||
|
return ent;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
idGameLocal::FindClassTypeReverse
|
||
|
=============
|
||
|
*/
|
||
|
idEntity *idGameLocal::FindClassTypeReverse( idEntity *from, const idTypeInfo& type, idEntity *ignore ) const {
|
||
|
idEntity* ent;
|
||
|
|
||
|
if ( from ) {
|
||
|
ent = from->spawnNode.Prev();
|
||
|
} else {
|
||
|
idEntity* lastEnt;
|
||
|
|
||
|
lastEnt = ent = spawnedEntities.Next();
|
||
|
for ( ; ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
lastEnt = ent;
|
||
|
}
|
||
|
ent = lastEnt;
|
||
|
}
|
||
|
|
||
|
for ( ; ent != NULL; ent = ent->spawnNode.Prev() ) {
|
||
|
if ( ent->IsType( type ) && ent != ignore ) {
|
||
|
return ent;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
idGameLocal::FindClassTypeInRadius
|
||
|
=============
|
||
|
*/
|
||
|
idEntity *idGameLocal::FindClassTypeInRadius( const idVec3& org, float radius, idEntity *from, const idTypeInfo& type, idEntity *ignore ) const {
|
||
|
idEntity *ent;
|
||
|
|
||
|
if ( !from ) {
|
||
|
ent = spawnedEntities.Next();
|
||
|
} else {
|
||
|
ent = from->spawnNode.Next();
|
||
|
}
|
||
|
|
||
|
float rSqr = Square( radius );
|
||
|
idVec3 dist;
|
||
|
for ( ; ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
assert( ent );
|
||
|
if ( ent->IsType( type ) && ent != ignore ) {
|
||
|
idPhysics* physics = ent->GetPhysics();
|
||
|
if( !physics ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
dist = physics->GetOrigin() - org;
|
||
|
if( dist.LengthSqr() > rSqr ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
return ent;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
idGameLocal::FindTraceEntity
|
||
|
|
||
|
Searches all active entities for the closest ( to start ) match that intersects
|
||
|
the line start,end
|
||
|
=============
|
||
|
*/
|
||
|
idEntity *idGameLocal::FindTraceEntity( idVec3 start, idVec3 end, const idTypeInfo &c, const idEntity *skip ) const {
|
||
|
idEntity *ent;
|
||
|
idEntity *bestEnt;
|
||
|
float scale;
|
||
|
float bestScale;
|
||
|
idBounds b;
|
||
|
|
||
|
bestEnt = NULL;
|
||
|
bestScale = 1.0f;
|
||
|
for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
if ( ent->IsType( c ) && ent != skip ) {
|
||
|
b = ent->GetPhysics()->GetAbsBounds().Expand( 16 );
|
||
|
if ( b.RayIntersection( start, end-start, scale ) ) {
|
||
|
if ( scale >= 0.0f && scale < bestScale ) {
|
||
|
bestEnt = ent;
|
||
|
bestScale = scale;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bestEnt;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::EntitiesWithinRadius
|
||
|
================
|
||
|
*/
|
||
|
int idGameLocal::EntitiesWithinRadius( const idVec3 org, float radius, idEntity **entityList, int maxCount ) const {
|
||
|
idEntity *ent;
|
||
|
idBounds bo( org );
|
||
|
int entCount = 0;
|
||
|
|
||
|
bo.ExpandSelf( radius );
|
||
|
for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
if ( ent->GetPhysics()->GetAbsBounds().IntersectsBounds( bo ) ) {
|
||
|
entityList[ entCount++ ] = ent;
|
||
|
if( entCount >= maxCount ) {
|
||
|
return entCount;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return entCount;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
idGameLocal::KillBox
|
||
|
|
||
|
Kills all entities that would touch the proposed new positioning of ent. The ent itself will not being killed.
|
||
|
Checks if player entities are in the teleporter, and marks them to die at teleport exit instead of immediately.
|
||
|
If catch_teleport, this only marks teleport players for death on exit
|
||
|
=================
|
||
|
*/
|
||
|
void idGameLocal::KillBox( idEntity *ent ) {
|
||
|
int i;
|
||
|
int num;
|
||
|
idEntity* hit;
|
||
|
const idClipModel* cm;
|
||
|
const idClipModel* clipModels[ MAX_GENTITIES ];
|
||
|
idPhysics* phys;
|
||
|
|
||
|
phys = ent->GetPhysics();
|
||
|
if ( !phys->GetNumClipModels() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
num = clip.ClipModelsTouchingBounds( CLIP_DEBUG_PARMS phys->GetAbsBounds(), phys->GetClipMask(), clipModels, MAX_GENTITIES, ent );
|
||
|
for ( i = 0; i < num; i++ ) {
|
||
|
cm = clipModels[ i ];
|
||
|
|
||
|
// don't check render entities
|
||
|
if ( cm->IsRenderModel() ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
hit = cm->GetEntity();
|
||
|
if ( !hit->fl.takedamage ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !phys->ClipContents( cm ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// nail it
|
||
|
hit->Damage( ent, ent, vec3_origin, DAMAGE_FOR_NAME( "damage_telefrag" ), 1.0f, NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::RadiusDamage
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::RadiusDamage( const idVec3 &origin, idEntity *inflictor, idEntity *attacker, idEntity *ignoreDamage, idEntity *ignorePush, const sdDeclDamage* damageDecl, float dmgPower, float radiusScale ) {
|
||
|
if ( damageDecl == NULL ) {
|
||
|
gameLocal.Error( "idGameLocal::RadiusDamage NULL Damage" );
|
||
|
}
|
||
|
|
||
|
float radius = Max( 1.f, damageDecl->GetRadius() * radiusScale );
|
||
|
|
||
|
if ( !gameLocal.isClient ) {
|
||
|
float dist, damageScale;
|
||
|
idEntity* ent;
|
||
|
idEntity* entityList[ MAX_GENTITIES ];
|
||
|
idBounds entityBounds[ MAX_GENTITIES ];
|
||
|
int numListedEntities;
|
||
|
idBounds bounds;
|
||
|
idVec3 v, damagePoint, dir;
|
||
|
int i;
|
||
|
|
||
|
assert( damageDecl );
|
||
|
|
||
|
if ( !damageDecl ) {
|
||
|
Warning( "idGameLocal::RadiusDamage NULL damage parm" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
bounds = idBounds( origin ).Expand( radius );
|
||
|
|
||
|
// get all entities touching the bounds
|
||
|
numListedEntities = clip.EntitiesTouchingBounds( bounds, -1, entityList, MAX_GENTITIES );
|
||
|
|
||
|
// Gordon: Store the bounds as they were when we started, as the damage could cause shifts, due to players being knocked out of vehicles
|
||
|
for ( int e = 0; e < numListedEntities; e++ ) {
|
||
|
entityBounds[ e ] = entityList[ e ]->GetPhysics()->GetAbsBounds();
|
||
|
}
|
||
|
|
||
|
for ( int pass = 0; pass < 2; pass++ ) {
|
||
|
// apply damage to the entities
|
||
|
for ( int e = 0; e < numListedEntities; e++ ) {
|
||
|
ent = entityList[ e ];
|
||
|
assert( ent );
|
||
|
|
||
|
if ( pass == 0 ) {
|
||
|
if ( ent->IsType( idPlayer::Type ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
} else {
|
||
|
if ( !ent->IsType( idPlayer::Type ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !ent->fl.takedamage || ent == ignoreDamage ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const idBounds& absBounds = entityBounds[ e ];
|
||
|
|
||
|
// find the distance from the edge of the bounding box
|
||
|
for ( i = 0; i < 3; i++ ) {
|
||
|
if ( origin[ i ] < absBounds[ 0 ][ i ] ) {
|
||
|
v[ i ] = absBounds[ 0 ][ i ] - origin[ i ];
|
||
|
} else if ( origin[ i ] > absBounds[ 1 ][ i ] ) {
|
||
|
v[ i ] = origin[ i ] - absBounds[ 1 ][ i ];
|
||
|
} else {
|
||
|
v[ i ] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dist = v.Length();
|
||
|
if ( dist >= radius ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
trace_t tr;
|
||
|
if ( damageDecl->GetNoTrace() || ent->CanDamage( origin, damagePoint, MASK_EXPLOSIONSOLID, ignoreDamage, &tr ) ) {
|
||
|
// get the damage scale
|
||
|
damageScale = dmgPower * ( 1.0f - dist / radius );
|
||
|
dir = ent->GetPhysics()->GetOrigin() - inflictor->GetPhysics()->GetOrigin();
|
||
|
dir.Normalize();
|
||
|
ent->Damage( inflictor, attacker, dir, damageDecl, damageScale, &tr );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// push physics objects
|
||
|
RadiusPush( origin, radius, damageDecl, dmgPower, attacker, ignorePush, 0, true );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
idGameLocal::RadiusPush
|
||
|
==============
|
||
|
*/
|
||
|
void idGameLocal::RadiusPush( const idVec3 &origin, float radius, const sdDeclDamage* damageDecl, float pushScale, const idEntity *inflictor, const idEntity *ignore, int flags, bool saveEvent ) {
|
||
|
if ( isClient && saveEvent ) {
|
||
|
new sdPhysicsEvent_RadiusPush( physicsEvents, origin, radius, damageDecl, pushScale, inflictor, ignore, flags );
|
||
|
}
|
||
|
|
||
|
idEntity* entityList[ MAX_GENTITIES ];
|
||
|
|
||
|
idVec3 dir;
|
||
|
dir.Set( 0.0f, 0.0f, 1.0f );
|
||
|
|
||
|
idBounds bounds = idBounds( origin ).Expand( radius );
|
||
|
|
||
|
// get all the entities touching the bounds
|
||
|
int numListedEntities = clip.EntitiesTouchingBounds( bounds, -1, entityList, MAX_GENTITIES );
|
||
|
|
||
|
// apply impact to all the entities
|
||
|
for ( int i = 0; i < numListedEntities; i++ ) {
|
||
|
idEntity* ent = entityList[ i ];
|
||
|
if ( ent == ignore ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !ent->DoRadiusPush() ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( flags & RP_GROUNDONLY ) {
|
||
|
if ( !ent->GetPhysics()->HasGroundContacts() ) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
modelTrace_t result;
|
||
|
idVec3 otherOrigin = ent->GetPhysics()->GetAbsBounds().GetCenter();
|
||
|
if ( gameRenderWorld->FastWorldTrace( result, origin, otherOrigin ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
ent->ApplyRadiusPush( origin, otherOrigin, damageDecl, pushScale, radius );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
idGameLocal::RadiusPushClipModel
|
||
|
==============
|
||
|
*/
|
||
|
void idGameLocal::RadiusPushClipModel( const idVec3 &origin, const float push, const idClipModel *clipModel ) {
|
||
|
int i, j;
|
||
|
float dot, dist, area;
|
||
|
const idTraceModel *trm;
|
||
|
const traceModelPoly_t *poly;
|
||
|
idFixedWinding w;
|
||
|
idVec3 v, localOrigin, center, impulse;
|
||
|
|
||
|
trm = clipModel->GetTraceModel();
|
||
|
if ( !trm || 1 ) {
|
||
|
impulse = clipModel->GetAbsBounds().GetCenter() - origin;
|
||
|
impulse.Normalize();
|
||
|
impulse.z += 1.0f;
|
||
|
clipModel->GetEntity()->AddForce( world, clipModel->GetId(), clipModel->GetOrigin(), push * impulse );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
localOrigin = ( origin - clipModel->GetOrigin() ) * clipModel->GetAxis().Transpose();
|
||
|
for ( i = 0; i < trm->numPolys; i++ ) {
|
||
|
poly = &trm->polys[i];
|
||
|
|
||
|
center.Zero();
|
||
|
for ( j = 0; j < poly->numEdges; j++ ) {
|
||
|
v = trm->verts[ trm->edges[ abs(poly->edges[j]) ].v[ INTSIGNBITSET( poly->edges[j] ) ] ];
|
||
|
center += v;
|
||
|
v -= localOrigin;
|
||
|
v.NormalizeFast(); // project point on a unit sphere
|
||
|
w.AddPoint( v );
|
||
|
}
|
||
|
center /= poly->numEdges;
|
||
|
v = center - localOrigin;
|
||
|
dist = v.NormalizeFast();
|
||
|
dot = v * poly->normal;
|
||
|
if ( dot > 0.0f ) {
|
||
|
continue;
|
||
|
}
|
||
|
area = w.GetArea();
|
||
|
// impulse in polygon normal direction
|
||
|
impulse = poly->normal * clipModel->GetAxis();
|
||
|
// always push up for nicer effect
|
||
|
impulse.z -= 1.0f;
|
||
|
// scale impulse based on visible surface area and polygon angle
|
||
|
impulse *= push * ( dot * area * ( 1.0f / ( 4.0f * idMath::PI ) ) );
|
||
|
// scale away distance for nicer effect
|
||
|
impulse *= ( dist * 2.0f );
|
||
|
// impulse is applied to the center of the polygon
|
||
|
center = clipModel->GetOrigin() + center * clipModel->GetAxis();
|
||
|
|
||
|
clipModel->GetEntity()->AddForce( world, clipModel->GetId(), center, impulse );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
idGameLocal::RadiusPushEntities
|
||
|
==============
|
||
|
*/
|
||
|
void idGameLocal::RadiusPushEntities( const idVec3& origin, float force, float radius ) {
|
||
|
float rSquared = Square( radius );
|
||
|
|
||
|
const int MAX_PUSH_ENTS = 32;
|
||
|
idEntity* ents[ MAX_PUSH_ENTS ];
|
||
|
|
||
|
int count = EntitiesWithinRadius( origin, radius, ents, MAX_PUSH_ENTS );
|
||
|
int i;
|
||
|
for( i = 0; i < count; i++ ) {
|
||
|
idVec3 dist = origin - ents[ i ]->GetPhysics()->GetOrigin();
|
||
|
|
||
|
float len = dist.LengthSqr();
|
||
|
if( len > rSquared ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
len = dist.Normalize();
|
||
|
len = len / radius;
|
||
|
len *= len;
|
||
|
|
||
|
idVec3 f = dist * force * len;
|
||
|
|
||
|
ents[ i ]->GetPhysics()->AddForce( 0, origin, f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idGameLocal::ProjectDecal
|
||
|
===============
|
||
|
*/
|
||
|
void idGameLocal::ProjectDecal( const idVec3 &origin, const idVec3 &dir, float depth, bool parallel, float size, const idMaterial *material, float angle ) {
|
||
|
float s, c;
|
||
|
idMat3 axis, axistemp;
|
||
|
idFixedWinding winding;
|
||
|
idVec3 windingOrigin, projectionOrigin;
|
||
|
|
||
|
static idVec3 decalWinding[4] = {
|
||
|
idVec3( 1.0f, 1.0f, 0.0f ),
|
||
|
idVec3( -1.0f, 1.0f, 0.0f ),
|
||
|
idVec3( -1.0f, -1.0f, 0.0f ),
|
||
|
idVec3( 1.0f, -1.0f, 0.0f )
|
||
|
};
|
||
|
|
||
|
if ( !g_decals.GetBool() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// randomly rotate the decal winding
|
||
|
idMath::SinCos16( DEG2RAD( angle ), s, c );
|
||
|
|
||
|
// winding orientation
|
||
|
axis[2] = dir;
|
||
|
axis[2].Normalize();
|
||
|
axis[2].NormalVectors( axistemp[0], axistemp[1] );
|
||
|
axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s;
|
||
|
axis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c;
|
||
|
|
||
|
windingOrigin = origin + depth * axis[2];
|
||
|
if ( parallel ) {
|
||
|
projectionOrigin = origin - depth * axis[2];
|
||
|
} else {
|
||
|
projectionOrigin = origin;
|
||
|
}
|
||
|
|
||
|
size *= 0.5f;
|
||
|
|
||
|
winding.Clear();
|
||
|
winding += idVec5( windingOrigin + ( axis * decalWinding[0] ) * size, idVec2( 1, 1 ) );
|
||
|
winding += idVec5( windingOrigin + ( axis * decalWinding[1] ) * size, idVec2( 0, 1 ) );
|
||
|
winding += idVec5( windingOrigin + ( axis * decalWinding[2] ) * size, idVec2( 0, 0 ) );
|
||
|
winding += idVec5( windingOrigin + ( axis * decalWinding[3] ) * size, idVec2( 1, 0 ) );
|
||
|
gameRenderWorld->ProjectDecalOntoWorld( winding, projectionOrigin, parallel, depth * 0.5f, material, time, time );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idGameLocal::CreateProjectedDecal
|
||
|
===============
|
||
|
*/
|
||
|
void idGameLocal::CreateProjectedDecal( const idVec3 &origin, const idVec3 &dir, float depth, bool parallel, float width, float height, float angle, const idVec4& color, idRenderModel* model ) {
|
||
|
if ( networkSystem->IsDedicated() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
float s, c;
|
||
|
idMat3 axis, axistemp;
|
||
|
idFixedWinding winding;
|
||
|
idVec3 windingOrigin, projectionOrigin;
|
||
|
|
||
|
if ( width <= 0.f || height <= 0.f ) {
|
||
|
Warning( "idGameLocal::CreateProjectedDecal Invalid Size %f x %f", width, height );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
width *= 0.5f;
|
||
|
height *= 0.5f;
|
||
|
idVec3 decalWinding[4] = {
|
||
|
idVec3( width, height, 0.0f ),
|
||
|
idVec3( -width, height, 0.0f ),
|
||
|
idVec3( -width, -height, 0.0f ),
|
||
|
idVec3( width, -height, 0.0f )
|
||
|
};
|
||
|
|
||
|
// randomly rotate the decal winding
|
||
|
idMath::SinCos16( DEG2RAD( angle ), s, c );
|
||
|
|
||
|
// winding orientation
|
||
|
axis[2] = dir;
|
||
|
axis[2].Normalize();
|
||
|
axis[2].NormalVectors( axistemp[0], axistemp[1] );
|
||
|
axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s;
|
||
|
axis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c;
|
||
|
|
||
|
windingOrigin = origin + depth * axis[2];
|
||
|
if ( parallel ) {
|
||
|
projectionOrigin = origin - depth * axis[2];
|
||
|
} else {
|
||
|
projectionOrigin = origin;
|
||
|
}
|
||
|
|
||
|
winding += idVec5( windingOrigin + ( axis * decalWinding[0] ), idVec2( 1, 1 ) );
|
||
|
winding += idVec5( windingOrigin + ( axis * decalWinding[1] ), idVec2( 0, 1 ) );
|
||
|
winding += idVec5( windingOrigin + ( axis * decalWinding[2] ), idVec2( 0, 0 ) );
|
||
|
winding += idVec5( windingOrigin + ( axis * decalWinding[3] ), idVec2( 1, 0 ) );
|
||
|
|
||
|
int spawnID = WORLD_SPAWN_ID;
|
||
|
gameRenderWorld->AddToProjectedDecal( winding, projectionOrigin, parallel, color, model, spawnID );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
idGameLocal::SetCamera
|
||
|
=============
|
||
|
*/
|
||
|
void idGameLocal::SetCamera( idCamera *cam ) {
|
||
|
camera = cam;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
idGameLocal::GetCamera
|
||
|
=============
|
||
|
*/
|
||
|
idCamera *idGameLocal::GetCamera( void ) const {
|
||
|
return camera;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
======================
|
||
|
idGameLocal::StartViewEffect
|
||
|
|
||
|
For passing in an effect triggered view effect
|
||
|
======================
|
||
|
*/
|
||
|
void idGameLocal::StartViewEffect( int type, float time, float scale ) {
|
||
|
switch( type ) {
|
||
|
case VIEWEFFECT_SHAKE:
|
||
|
gameLocal.playerView.SetShakeParms( time, scale );
|
||
|
break;
|
||
|
|
||
|
case VIEWEFFECT_TUNNEL:
|
||
|
gameLocal.playerView.SetTunnelParms( time, scale );
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
gameLocal.Warning( "Invalid view effect" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
======================
|
||
|
idGameLocal::GetPlayerView
|
||
|
======================
|
||
|
*/
|
||
|
void idGameLocal::GetPlayerView( idVec3& origin, idMat3& axis, float& fovx ) {
|
||
|
idPlayer* player = gameLocal.GetActiveViewer();
|
||
|
if ( player ) {
|
||
|
renderView_t* view = player->GetRenderView();
|
||
|
if ( view ) {
|
||
|
origin = view->vieworg;
|
||
|
axis = view->viewaxis;
|
||
|
fovx = view->fov_x;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
origin = vec3_origin;
|
||
|
axis = mat3_identity;
|
||
|
fovx = 90.f;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
======================
|
||
|
idGameLocal::Translation
|
||
|
|
||
|
small portion of physics required for the effects system
|
||
|
======================
|
||
|
*/
|
||
|
void idGameLocal::Translation( trace_t &trace, const idVec3 &source, const idVec3 &dest, const idTraceModel &trm, int clipMask ) {
|
||
|
idClipModel *cm = new idClipModel( trm, false );
|
||
|
clip.Translation( CLIP_DEBUG_PARMS trace, source, dest, cm, mat3_identity, clipMask, NULL );
|
||
|
gameLocal.clip.DeleteClipModel( cm );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
idGameLocal::TracePoint
|
||
|
==============
|
||
|
*/
|
||
|
void idGameLocal::TracePoint( trace_t& trace, const idVec3 &source, const idVec3 &dest, int clipMask ) {
|
||
|
clip.TracePoint( CLIP_DEBUG_PARMS trace, source, dest, clipMask, NULL );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
======================
|
||
|
idGameLocal::SpawnClientMoveable
|
||
|
======================
|
||
|
*/
|
||
|
rvClientMoveable* idGameLocal::SpawnClientMoveable ( const char* name, int lifetime, const idVec3& origin, const idMat3& axis, const idVec3& velocity, const idVec3& angular_velocity, int effectSet ) {
|
||
|
if ( !gameLocal.DoClientSideStuff() ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// Ensure client moveables never last forever
|
||
|
if ( lifetime <= 0 ) {
|
||
|
lifetime = 2500;
|
||
|
}
|
||
|
|
||
|
// find the debris def
|
||
|
const idDict& args = *gameLocal.FindEntityDefDict( name, false );
|
||
|
if ( &args == NULL ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// Spawn the debris
|
||
|
rvClientMoveable* cent = new rvClientMoveable;
|
||
|
cent->SetOrigin( origin );
|
||
|
cent->SetAxis( axis );
|
||
|
cent->Spawn( &args, effectSet );
|
||
|
|
||
|
renderEntity_t* renderEntity = cent->GetRenderEntity();
|
||
|
assert( renderEntity != NULL );
|
||
|
|
||
|
if ( gameLocal.mapSkinPool != NULL ) {
|
||
|
const char* skinKey = args.GetString( "climate_skin_key" );
|
||
|
if ( *skinKey != '\0' ) {
|
||
|
const char* skinName = gameLocal.mapSkinPool->GetDict().GetString( va( "skin_%s", skinKey ) );
|
||
|
if ( *skinName == '\0' ) {
|
||
|
gameLocal.Warning( "idGameLocal::SpawnClientMoveable No Skin Set For '%s'", skinKey );
|
||
|
} else {
|
||
|
const idDeclSkin* skin = gameLocal.declSkinType[ skinName ];
|
||
|
if ( skin == NULL ) {
|
||
|
gameLocal.Warning( "idGameLocal::SpawnClientMoveable Skin '%s' Not Found", skinName );
|
||
|
} else {
|
||
|
renderEntity->customSkin = skin;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cent->GetPhysics()->SetLinearVelocity ( velocity );
|
||
|
cent->GetPhysics()->SetAngularVelocity ( angular_velocity );
|
||
|
|
||
|
cent->PostEventMS( &CL_FadeOut, lifetime, 2500 );
|
||
|
|
||
|
return cent;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetPortalState
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetPortalState( qhandle_t portal, int blockingBits ) {
|
||
|
gameRenderWorld->SetPortalState( portal, blockingBits );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::SortSpawnsByAge
|
||
|
============
|
||
|
*/
|
||
|
int idGameLocal::SortSpawnsByAge( const void* a, const void* b ) {
|
||
|
const sdSpawnPoint* spotA = *( const sdSpawnPoint** )( a );
|
||
|
const sdSpawnPoint* spotB = *( const sdSpawnPoint** )( b );
|
||
|
|
||
|
if ( spotA->GetLastUsedTime() > spotB->GetLastUsedTime() ) {
|
||
|
return 1.f;
|
||
|
}
|
||
|
|
||
|
if ( spotB->GetLastUsedTime() > spotA->GetLastUsedTime() ) {
|
||
|
return -1.f;
|
||
|
}
|
||
|
|
||
|
return 0.f;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::SelectInitialSpawnPointForRepeaterClient
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::SelectInitialSpawnPointForRepeaterClient( idVec3& outputOrg, idAngles& outputAngles ) {
|
||
|
for ( int pass = 0; pass < 2; pass++ ) {
|
||
|
for ( int i = 0; i < spawnSpots.Num(); i++ ) {
|
||
|
const sdSpawnPoint& tempSpot = spawnSpots[ i ];
|
||
|
|
||
|
idEntity* other = tempSpot.GetOwner();
|
||
|
if ( pass == 0 ) {
|
||
|
if ( other != NULL ) {
|
||
|
sdRequirementContainer* requirements = other->GetSpawnRequirements();
|
||
|
if ( requirements && requirements->HasRequirements() ) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( tempSpot.GetRelativePositioning() ) {
|
||
|
if ( other != NULL ) {
|
||
|
outputOrg = other->GetPhysics()->GetOrigin() + ( other->GetPhysics()->GetAxis() * tempSpot.GetOffset() );
|
||
|
} else {
|
||
|
continue;
|
||
|
}
|
||
|
} else {
|
||
|
outputOrg = tempSpot.GetOffset();
|
||
|
}
|
||
|
|
||
|
// move up to make sure the player is at least an epsilon above the floor
|
||
|
outputOrg[ 2 ] += 4.0f + CM_BOX_EPSILON;
|
||
|
|
||
|
outputAngles = tempSpot.GetAngles();
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
idGameLocal::SelectInitialSpawnPoint
|
||
|
============
|
||
|
*/
|
||
|
const sdSpawnPoint* idGameLocal::SelectInitialSpawnPoint( idPlayer *player, idVec3& outputOrg, idAngles& outputAngles ) {
|
||
|
assert( !gameLocal.isClient );
|
||
|
|
||
|
const int MAX_AVAILABLE_SPAWN_POINTS = 256;
|
||
|
|
||
|
const sdSpawnPoint* spots[ MAX_AVAILABLE_SPAWN_POINTS ];
|
||
|
const sdSpawnPoint* blockedSpots[ MAX_AVAILABLE_SPAWN_POINTS ];
|
||
|
int availableSpots = 0;
|
||
|
int numBlockedSpots = 0;
|
||
|
|
||
|
if ( player->IsType( idBot::Type ) ) {
|
||
|
botThreadData.CheckBotSpawnLocation( player );
|
||
|
}
|
||
|
|
||
|
idEntity* chosenSpawnPoint = player->GetSpawnPoint();
|
||
|
idEntity* teamSpawnPoint = NULL;
|
||
|
|
||
|
sdInstanceCollector< idPlayer > players( true );
|
||
|
sdInstanceCollector< sdTransport > transports( true );
|
||
|
|
||
|
sdTeamInfo* team = player->GetGameTeam();
|
||
|
if ( team != NULL ) {
|
||
|
teamSpawnPoint = team->GetDefaultSpawn();
|
||
|
}
|
||
|
if ( chosenSpawnPoint == NULL ) {
|
||
|
chosenSpawnPoint = teamSpawnPoint;
|
||
|
}
|
||
|
|
||
|
while ( true ) {
|
||
|
for( int i = 0; i < spawnSpots.Num() && availableSpots < MAX_AVAILABLE_SPAWN_POINTS; i++ ) {
|
||
|
const sdSpawnPoint& tempSpot = spawnSpots[ i ];
|
||
|
|
||
|
if ( !tempSpot.InUse() ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
idEntity* other = tempSpot.GetOwner();
|
||
|
|
||
|
if ( chosenSpawnPoint && chosenSpawnPoint != other ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( other ) {
|
||
|
sdRequirementContainer* requirements = other->GetSpawnRequirements();
|
||
|
if ( requirements && !requirements->Check( player, other ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !tempSpot.GetRequirements().Check( player ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// check for obstructions
|
||
|
if ( players.Num() > 0 ) {
|
||
|
// calculate an approximate bounds for this spawn point based on the bounds of the first player
|
||
|
// figure out origin first
|
||
|
idEntity* owner = tempSpot.GetOwner();
|
||
|
|
||
|
idVec3 origin;
|
||
|
if ( owner && tempSpot.GetRelativePositioning() ) {
|
||
|
origin = owner->GetPhysics()->GetOrigin() + ( owner->GetPhysics()->GetAxis() * tempSpot.GetOffset() );
|
||
|
} else {
|
||
|
origin = tempSpot.GetOffset();
|
||
|
}
|
||
|
|
||
|
idBounds playerBounds;
|
||
|
idPhysics_Player::CalcNormalBounds( playerBounds );
|
||
|
|
||
|
idBox spawnBox( playerBounds, origin, mat3_identity );
|
||
|
|
||
|
bool blocked = false;
|
||
|
// check for players
|
||
|
// NOTE: could just check if the origin of the spawn is within the bounds of the other
|
||
|
// for a faster & less accurate check. Probably accurate enough though.
|
||
|
int j;
|
||
|
for ( j = 0; j < players.Num(); j++ ) {
|
||
|
idPlayer* player = players[ j ];
|
||
|
|
||
|
idBox playerBox( player->GetPhysics()->GetBounds(), player->GetPhysics()->GetOrigin(), mat3_identity );
|
||
|
if ( playerBox.IntersectsBox( spawnBox ) ) {
|
||
|
blocked = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if someone is standing on our spawn point, knock them out the way
|
||
|
sdDynamicSpawnPoint* spawnPoint = owner->Cast< sdDynamicSpawnPoint >();
|
||
|
if ( blocked && spawnPoint != NULL ) {
|
||
|
idPlayer* blockingPlayer = players[ j ];
|
||
|
|
||
|
idVec3 forward = blockingPlayer->GetViewAngles().ToForward();
|
||
|
forward.z = 0.f;
|
||
|
forward.NormalizeFast();
|
||
|
blockingPlayer->ApplyImpulse( gameLocal.world, -1, blockingPlayer->GetPhysics()->GetOrigin(), forward * 51200.f );
|
||
|
|
||
|
blocked = false;
|
||
|
}
|
||
|
|
||
|
if ( !blocked ) {
|
||
|
// check for transports
|
||
|
for ( int j = 0; j < transports.Num(); j++ ) {
|
||
|
sdTransport* transport = transports[ j ];
|
||
|
|
||
|
idBox vehicleBox( transport->GetPhysics()->GetBounds(), transport->GetPhysics()->GetOrigin(), transport->GetPhysics()->GetAxis() );
|
||
|
if ( vehicleBox.IntersectsBox( spawnBox ) ) {
|
||
|
blocked = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( blocked ) {
|
||
|
blockedSpots[ numBlockedSpots++ ] = &tempSpot;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
spots[ availableSpots++ ] = &tempSpot;
|
||
|
}
|
||
|
|
||
|
if ( availableSpots == 0 ) {
|
||
|
if ( chosenSpawnPoint != NULL && chosenSpawnPoint != teamSpawnPoint ) {
|
||
|
chosenSpawnPoint = teamSpawnPoint;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( availableSpots == 0 && numBlockedSpots == 0 ) {
|
||
|
outputOrg.Zero();
|
||
|
outputAngles.Zero();
|
||
|
//Warning( "No valid idPlayerStart on map." );
|
||
|
return NULL;
|
||
|
} else if ( availableSpots == 0 ) {
|
||
|
// the only available spots are blocked
|
||
|
// copy them across into the available spots - OUT OF DESPERATION
|
||
|
for ( int i = 0; i < numBlockedSpots; i++ ) {
|
||
|
spots[ i ] = blockedSpots [ i ];
|
||
|
}
|
||
|
availableSpots = numBlockedSpots;
|
||
|
}
|
||
|
|
||
|
qsort( spots, availableSpots, sizeof( spots[ 0 ] ), SortSpawnsByAge );
|
||
|
|
||
|
// New (hopefully improved) spawn point selection code
|
||
|
const sdSpawnPoint* spot = NULL;
|
||
|
if ( availableSpots > 2 ) {
|
||
|
// use a "roulette wheel" selection method to find the next spawn point used
|
||
|
// ie - weight the selection more heavily in favour of spawn points not used
|
||
|
// recently
|
||
|
// instead of randomly selecting from the least recent half, which could
|
||
|
// potentially cause telefrags if more than half are used in rapid
|
||
|
// succession
|
||
|
int totalLastUsedTime = 0;
|
||
|
const unsigned int halfSpots = availableSpots / 2;
|
||
|
for ( unsigned int i = 0; i < halfSpots; i++ ) {
|
||
|
totalLastUsedTime += spots[ i ]->GetLastUsedTime();
|
||
|
}
|
||
|
|
||
|
// now calculate the total inverse used time
|
||
|
float totalInverseUsedTime = (float) ( ( halfSpots - 1 ) * totalLastUsedTime );
|
||
|
|
||
|
// so that there are no divide by zeros
|
||
|
if ( totalInverseUsedTime == 0.0f ) {
|
||
|
totalInverseUsedTime = 1.0f;
|
||
|
}
|
||
|
|
||
|
// calculate the selection value
|
||
|
// note this uses a power scale to shift the distribution towards the higher values
|
||
|
const float randomSelection = idMath::Pow( random.RandomFloat(), 0.4f );
|
||
|
|
||
|
// find the slice of the wheel that this fits into
|
||
|
float cutoffUpto = 0.0f;
|
||
|
for ( unsigned int i = halfSpots - 1; i >= 0; i-- ) {
|
||
|
// calculate the cutoff value corresponding to this slice
|
||
|
const float lastUsed = (float) ( totalLastUsedTime - spots[ i ]->GetLastUsedTime() );
|
||
|
const float cutoff = lastUsed / totalInverseUsedTime + cutoffUpto;
|
||
|
cutoffUpto = cutoff;
|
||
|
|
||
|
// if the selection is greater than the last cutoff and less than this one
|
||
|
// then it fits into this slice of the wheel
|
||
|
if ( randomSelection < cutoff || i == 0 ) {
|
||
|
spot = spots[i];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// two or less spots - the first spot will always be the best option
|
||
|
spot = spots[ 0 ];
|
||
|
}
|
||
|
|
||
|
idEntity* owner = spot->GetOwner();
|
||
|
if ( owner && spot->GetRelativePositioning() ) {
|
||
|
outputOrg = owner->GetPhysics()->GetOrigin() + ( owner->GetPhysics()->GetAxis() * spot->GetOffset() );
|
||
|
} else {
|
||
|
outputOrg = spot->GetOffset();
|
||
|
}
|
||
|
|
||
|
// move up to make sure the player is at least an epsilon above the floor
|
||
|
outputOrg[ 2 ] += 4.0f + CM_BOX_EPSILON;
|
||
|
|
||
|
outputAngles = spot->GetAngles();
|
||
|
|
||
|
return spot;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::UpdateServerInfoFlags
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::UpdateServerInfoFlags( void ) {
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::SetGlobalMaterial
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::SetGlobalMaterial( const idMaterial *mat ) {
|
||
|
globalMaterial = mat;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetGlobalMaterial
|
||
|
================
|
||
|
*/
|
||
|
const idMaterial *idGameLocal::GetGlobalMaterial() {
|
||
|
return globalMaterial;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::UsercommandCallback
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::UsercommandCallback( usercmd_t& cmd ) {
|
||
|
idPlayer* player = GetLocalPlayer();
|
||
|
if ( !player ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
player->UsercommandCallback( cmd );
|
||
|
}
|
||
|
|
||
|
idCVar g_nextMap( "g_nextMap", "", CVAR_GAME | CVAR_NOCHEAT, "commands to execute when the current map/campaign ends" );
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::NextMap
|
||
|
================
|
||
|
*/
|
||
|
bool idGameLocal::NextMap( void ) {
|
||
|
idStr text = g_nextMap.GetString();
|
||
|
g_nextMap.SetString( "" );
|
||
|
int currentMapLoadCount = mapLoadCount;
|
||
|
cmdSystem->BufferCommandText( CMD_EXEC_NOW, text.c_str() );
|
||
|
return currentMapLoadCount != mapLoadCount;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::BeginLevelLoad
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::BeginLevelLoad() {
|
||
|
reloadingSameMap = false;
|
||
|
//sdDynamicBlockManagerBase::CompactPools();
|
||
|
|
||
|
uiManager->Clear();
|
||
|
uiManager->BeginLevelLoad();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::EndLevelLoad
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::EndLevelLoad() {
|
||
|
// SetBytesNeededForMapLoad( GetMapName(), fileSystem->GetReadCount() );
|
||
|
|
||
|
reloadingSameMap = true;
|
||
|
uiManager->EndLevelLoad();
|
||
|
UpdateLevelLoadScreen( L"" );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetEffectHandle
|
||
|
|
||
|
Get the handle of the effect with the given name
|
||
|
================
|
||
|
*/
|
||
|
idCVar bse_projectileEffect( "bse_projectileEffect", "", CVAR_CHEAT, "this effect will replace projectile explosions" );
|
||
|
|
||
|
int idGameLocal::GetEffectHandle ( const idDict& args, const char* effectName, const char* materialType ) {
|
||
|
//jrad - this is the least-nasty way to get the "gun fires effects" for development of particles
|
||
|
if( bse_projectileEffect.GetString()[ 0 ] && !idStr::Icmp( bse_projectileEffect.GetString(), effectName )) {
|
||
|
return( declHolder.declEffectsType.LocalFind( effectName )->Index() );
|
||
|
}
|
||
|
|
||
|
sdStringBuilder_Heap builder;
|
||
|
builder = "effectchance ";
|
||
|
builder += effectName;
|
||
|
float chance = args.GetFloat ( builder.c_str(), "1" );
|
||
|
if ( random.RandomFloat () > chance ) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if( !effectName || !effectName[ 0 ] ) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
// we should ALWAYS be playing effects from the def.
|
||
|
// hard-coded effects MUST be avoided at all times because they won't get pre-cached.
|
||
|
if( idStr::Icmpn( effectName, "fx", 2 ) ) {
|
||
|
Error( "Effects must be played via def file keys starting with 'fx'" );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if ( materialType && *materialType ) {
|
||
|
sdStringBuilder_Heap temp;
|
||
|
const char* result;
|
||
|
|
||
|
temp = effectName;
|
||
|
temp += "_";
|
||
|
temp += materialType;
|
||
|
|
||
|
// See if the given material effect is specified
|
||
|
result = args.GetString( temp.c_str() );
|
||
|
|
||
|
|
||
|
if ( result && *result ) {
|
||
|
if ( useSimpleEffect ) {
|
||
|
sdStringBuilder_Heap builder;
|
||
|
builder = result;
|
||
|
builder += "_simple";
|
||
|
const rvDeclEffect *effect = declHolder.declEffectsType.LocalFind( builder.c_str(), false );
|
||
|
if ( effect ) {
|
||
|
return effect->Index();
|
||
|
}
|
||
|
}
|
||
|
return( declHolder.declEffectsType.LocalFind( result )->Index() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// grab the non material effect name
|
||
|
effectName = args.GetString( effectName );
|
||
|
if ( !*effectName ) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if ( useSimpleEffect ) {
|
||
|
sdStringBuilder_Heap builder;
|
||
|
builder = effectName;
|
||
|
builder += "_simple";
|
||
|
const rvDeclEffect *effect = declHolder.declEffectsType.LocalFind( builder.c_str(), false );
|
||
|
if ( effect ) {
|
||
|
return effect->Index();
|
||
|
}
|
||
|
}
|
||
|
return( declHolder.declEffectsType.LocalFind( effectName )->Index() );
|
||
|
}
|
||
|
|
||
|
int idGameLocal::GetDecal ( const idDict& args, const char* decalName, const char* materialType ) {
|
||
|
|
||
|
// we should ALWAYS be playing effects from the def.
|
||
|
// hard-coded effects MUST be avoided at all times because they won't get pre-cached.
|
||
|
if( idStr::Icmpn( decalName, "dec_", 4 ) ) {
|
||
|
Error( "Decals must be played via def file keys starting with 'dec_'" );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if ( materialType && *materialType ) {
|
||
|
sdStringBuilder_Heap temp;
|
||
|
const char* result;
|
||
|
|
||
|
temp = decalName;
|
||
|
temp += "_";
|
||
|
temp += materialType;
|
||
|
|
||
|
// See if the given material effect is specified
|
||
|
result = args.GetString( temp.c_str() );
|
||
|
if ( result && *result ) {
|
||
|
if ( *result == '_' ) {
|
||
|
return -1;
|
||
|
} else {
|
||
|
return( declHolder.FindDecal( result )->Index() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// grab the non material effect name
|
||
|
decalName = args.GetString( decalName );
|
||
|
if ( !*decalName ) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return( declHolder.FindDecal( decalName )->Index() );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::FindEffect
|
||
|
================
|
||
|
*/
|
||
|
const rvDeclEffect *idGameLocal::FindEffect( const char* name, bool makeDefault ) {
|
||
|
if ( useSimpleEffect ) {
|
||
|
sdStringBuilder_Heap builder;
|
||
|
builder = name;
|
||
|
builder += "_simple";
|
||
|
const rvDeclEffect *effect = declHolder.declEffectsType.LocalFind( builder.c_str(), false );
|
||
|
if ( effect != NULL ) {
|
||
|
return effect;
|
||
|
}
|
||
|
}
|
||
|
return declHolder.FindEffect( name, makeDefault );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::PlayEffect
|
||
|
|
||
|
Plays an effect at the given origin using the given direction
|
||
|
================
|
||
|
*/
|
||
|
rvClientEffect* idGameLocal::PlayEffect( const int effectHandle, const idVec3& color, const idVec3& origin, const idMat3& axis, bool loop, const idVec3& endOrigin, float distanceOffset ) {
|
||
|
if ( !gameLocal.DoClientSideStuff() ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if ( !effectHandle ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
rvClientEffect* effect = new rvClientEffect( effectHandle );
|
||
|
effect->SetOrigin( origin );
|
||
|
effect->SetAxis( axis );
|
||
|
effect->SetGravity( GetGravity() );
|
||
|
effect->SetMaterialColor( color );
|
||
|
effect->SetDistanceOffset( distanceOffset );
|
||
|
if ( !effect->Play( gameLocal.time, loop, endOrigin ) ) {
|
||
|
delete effect;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return effect;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::RegisterClientEntity
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::RegisterClientEntity( rvClientEntity *cent ) {
|
||
|
int entityNumber;
|
||
|
|
||
|
sdScopedLock<true> scoped( clientEntLock );
|
||
|
|
||
|
assert ( cent );
|
||
|
|
||
|
// Find a free entity index to use
|
||
|
while( clientEntities[firstFreeClientIndex] && firstFreeClientIndex < MAX_CENTITIES ) {
|
||
|
firstFreeClientIndex++;
|
||
|
}
|
||
|
if( firstFreeClientIndex >= MAX_CENTITIES ) {
|
||
|
for (int i=0; i<MAX_CENTITIES; i++) {
|
||
|
const char *name = gameLocal.clientEntities[i]->GetName();
|
||
|
common->Printf( "%d '%s'\n", i, name ? name : "<unknown>" );
|
||
|
}
|
||
|
cent->PostEventMS ( &EV_Remove, 0 );
|
||
|
Error( "idGameLocal::RegisterClientEntity: no free client entities" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
entityNumber = firstFreeClientIndex++;
|
||
|
|
||
|
// Add the client entity to the lists
|
||
|
clientEntities[ entityNumber ] = cent;
|
||
|
assert( clientSpawnIds[ entityNumber ] < 0 );
|
||
|
int nextID = abs(clientSpawnIds[ entityNumber ]) + 1;
|
||
|
if ( nextID >= ( 1 << ( 32 - CENTITYNUM_BITS ) ) ) {
|
||
|
nextID = INITIAL_SPAWN_COUNT;
|
||
|
}
|
||
|
clientSpawnIds[ entityNumber ] = nextID;
|
||
|
cent->entityNumber = entityNumber;
|
||
|
cent->spawnNode.AddToEnd( clientSpawnedEntities );
|
||
|
|
||
|
if ( entityNumber >= num_clientEntities ) {
|
||
|
num_clientEntities++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::UnregisterClientEntity
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::UnregisterClientEntity( rvClientEntity* cent ) {
|
||
|
assert( cent );
|
||
|
|
||
|
sdScopedLock<true> scoped( clientEntLock );
|
||
|
|
||
|
// No entity number then it failed to register
|
||
|
if( cent->entityNumber == -1 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
cent->spawnNode.Remove();
|
||
|
cent->bindNode.Remove();
|
||
|
|
||
|
if( cent->entityNumber != -1 && clientEntities [ cent->entityNumber ] == cent ) {
|
||
|
cent->spawnNode.Remove();
|
||
|
cent->bindNode.Remove();
|
||
|
clientEntities[ cent->entityNumber ] = NULL;
|
||
|
assert( clientSpawnIds[ cent->entityNumber ] > 0 );
|
||
|
clientSpawnIds[ cent->entityNumber ] = -abs(clientSpawnIds[ cent->entityNumber ]);
|
||
|
if( cent->entityNumber < firstFreeClientIndex ) {
|
||
|
firstFreeClientIndex = cent->entityNumber;
|
||
|
}
|
||
|
cent->entityNumber = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::HandleNetworkMessage
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::HandleNetworkMessage( idPlayer* player, idEntity* entity, const char* message ) {
|
||
|
if ( entity == NULL ) {
|
||
|
gameLocal.Warning( "idGameLocal::HandleNetworkMessage entity is NULL" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sdNetworkInterface* iface = entity->GetNetworkInterface();
|
||
|
if ( !iface ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
iface->HandleNetworkMessage( player, message );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::ParseClamp
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::ParseClamp( angleClamp_t& clamp, const char* prefix, const idDict& dict ) {
|
||
|
clamp.flags.enabled = false;
|
||
|
|
||
|
clamp.flags.enabled |= dict.GetFloat( va( "%s_min", prefix ), "0", clamp.extents[ 0 ] );
|
||
|
clamp.flags.enabled |= dict.GetFloat( va( "%s_max", prefix ), "0", clamp.extents[ 1 ] );
|
||
|
|
||
|
clamp.flags.limitRate = dict.GetFloat( va( "%s_rate", prefix ), "0", clamp.rate[ 0 ] );
|
||
|
clamp.rate[ 1 ] = clamp.rate[ 0 ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::HandleNetworkEvent
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::HandleNetworkEvent( idEntity* entity, const char* message ) {
|
||
|
if ( !entity ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sdNetworkInterface* iface = entity->GetNetworkInterface();
|
||
|
if ( !iface ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
iface->HandleNetworkEvent( message );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
idGameLocal::HandleGuiScriptMessage
|
||
|
===================
|
||
|
*/
|
||
|
void idGameLocal::HandleGuiScriptMessage( idPlayer* player, idEntity* entity, const char* message ) {
|
||
|
if ( !entity ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sdGuiInterface* iface = entity->GetGuiInterface();
|
||
|
if ( !iface ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
iface->HandleGuiScriptMessage( player, message );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SurfaceTypePostParse
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SurfaceTypePostParse( idDecl* decl ) {
|
||
|
sdDeclSurfaceType* surfaceDecl = static_cast< sdDeclSurfaceType* >( decl );
|
||
|
assert( decl );
|
||
|
int index = decl->Index();
|
||
|
if( index >= surfaceTypes.Num() ) {
|
||
|
surfaceTypes.AssureSize( index + 1 );
|
||
|
}
|
||
|
|
||
|
assert( index < surfaceTypes.Num() );
|
||
|
surfaceTypes[ index ].friction = surfaceDecl->GetProperties().GetFloat( "friction", "1.0" );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetSurfaceTypeForIndex
|
||
|
============
|
||
|
*/
|
||
|
surfaceProperties_t& idGameLocal::GetSurfaceTypeForIndex( int index ) {
|
||
|
assert( index < surfaceTypes.Num() );
|
||
|
return surfaceTypes[ index ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SavePlayZoneMasks
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SavePlayZoneMasks( void ) {
|
||
|
for ( int i = 0; i < playZones.Num(); i++ ) {
|
||
|
playZones[ i ]->SaveMasks();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::ClearPlayZones
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::ClearPlayZones( void ) {
|
||
|
playZones.DeleteContents( true );
|
||
|
worldPlayZones.Clear();
|
||
|
choosablePlayZones.Clear();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::ClearDeployRequests
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::ClearDeployRequests( void ) {
|
||
|
int i;
|
||
|
for ( i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
if ( !deployRequests[ i ] ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !gameLocal.isClient ) {
|
||
|
deployRequests[ i ]->WriteDestroyEvent( i );
|
||
|
}
|
||
|
delete deployRequests[ i ];
|
||
|
deployRequests[ i ] = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::ClearDeployRequest
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::ClearDeployRequest( int deployIndex ) {
|
||
|
if ( deployIndex < 0 || deployIndex > MAX_CLIENTS ) {
|
||
|
gameLocal.Warning( "idGameLocal::ClearDeployRequest Index out of Bounds '%i'", deployIndex );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( deployRequests[ deployIndex ] ) {
|
||
|
if ( !gameLocal.isClient ) {
|
||
|
deployRequests[ deployIndex ]->WriteDestroyEvent( deployIndex );
|
||
|
}
|
||
|
delete deployRequests[ deployIndex ];
|
||
|
deployRequests[ deployIndex ] = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetDeploymentMask
|
||
|
============
|
||
|
*/
|
||
|
qhandle_t idGameLocal::GetDeploymentMask( const char* name ) {
|
||
|
for ( int i = 0; i < deploymentMasks.Num(); i++ ) {
|
||
|
if ( !deploymentMasks[ i ].Icmp( name ) ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
qhandle_t handle = deploymentMasks.Num();
|
||
|
deploymentMasks.Alloc() = name;
|
||
|
|
||
|
return handle;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::CreatePlayZone
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::CreatePlayZone( const idDict& info, const idBounds& bounds ) {
|
||
|
sdPlayZone* pz = new sdPlayZone( info, bounds );
|
||
|
playZones.Alloc() = pz;
|
||
|
if ( pz->GetFlags() & sdPlayZone::PZF_WORLD ) {
|
||
|
worldPlayZones.Alloc() = pz;
|
||
|
}
|
||
|
|
||
|
if ( pz->GetFlags() & sdPlayZone::PZF_CHOOSABLE ) {
|
||
|
choosablePlayZones.Alloc() = pz;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetPlayZoneAreaName
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetPlayZoneAreaName( int index, const char* name ) {
|
||
|
playZoneAreaNames[ index ] = name;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::LinkPlayZoneAreas
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::LinkPlayZoneAreas( void ) {
|
||
|
for ( int i = 0; i < playZoneAreas.Num(); i++ ) {
|
||
|
for ( int j = 0; j < playZones.Num(); j++ ) {
|
||
|
if ( !*playZones[ j ]->GetTarget() ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( playZoneAreaNames[ i ].Icmp( playZones[ j ]->GetTarget() ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( playZoneAreas[ i ] == NULL ) {
|
||
|
playZoneAreas[ i ] = new idList< sdPlayZone* >();
|
||
|
}
|
||
|
|
||
|
playZoneAreas[ i ]->Alloc() = playZones[ j ];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetWorldPlayZoneIndex
|
||
|
============
|
||
|
*/
|
||
|
int idGameLocal::GetWorldPlayZoneIndex( const idVec3& point ) const {
|
||
|
idVec2 point2d = point.ToVec2();
|
||
|
|
||
|
int bestPlayZone = -1;
|
||
|
|
||
|
for ( int i = 0; i < worldPlayZones.Num(); i++ ) {
|
||
|
const sdPlayZone* playZone = worldPlayZones[ i ];
|
||
|
|
||
|
if ( ( playZone->GetFlags() & sdPlayZone::PZF_WORLD ) == 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const sdBounds2D& bounds = playZone->GetBounds();
|
||
|
if ( !bounds.ContainsPoint( point2d ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
bestPlayZone = i;
|
||
|
}
|
||
|
|
||
|
return bestPlayZone;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetPlayZone
|
||
|
============
|
||
|
*/
|
||
|
const sdPlayZone* idGameLocal::GetPlayZone( const idVec3& point, int flags ) const {
|
||
|
int areaNum = gameRenderWorld->PointInArea( point );
|
||
|
if ( areaNum != -1 ) {
|
||
|
if ( playZoneAreas[ areaNum ] != NULL ) {
|
||
|
const idList< sdPlayZone* >& list = *playZoneAreas[ areaNum ];
|
||
|
for ( int i = 0; i < list.Num(); i++ ) {
|
||
|
if ( list[ i ]->GetFlags() & flags ) {
|
||
|
return list[ i ];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idVec2 point2d = point.ToVec2();
|
||
|
|
||
|
int bestPlayZonePriority = -1;
|
||
|
const sdPlayZone* bestPlayZone = NULL;
|
||
|
|
||
|
for ( int i = 0; i < playZones.Num(); i++ ) {
|
||
|
const sdPlayZone* playZone = playZones[ i ];
|
||
|
|
||
|
if ( ( playZone->GetFlags() & flags ) == 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( playZone->GetPriority() < bestPlayZonePriority ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const sdBounds2D& bounds = playZone->GetBounds();
|
||
|
|
||
|
if ( !bounds.ContainsPoint( point2d ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
bestPlayZonePriority = playZone->GetPriority();
|
||
|
bestPlayZone = playZone;
|
||
|
}
|
||
|
|
||
|
return bestPlayZone;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetChoosablePlayZone
|
||
|
============
|
||
|
*/
|
||
|
const sdPlayZone* idGameLocal::GetChoosablePlayZone( int id ) const {
|
||
|
if( id < 0 || id >= choosablePlayZones.Num() ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
return choosablePlayZones[ id ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetIndexForChoosablePlayZone
|
||
|
============
|
||
|
*/
|
||
|
int idGameLocal::GetIndexForChoosablePlayZone( const sdPlayZone* zone ) const {
|
||
|
for( int i = 0; i < choosablePlayZones.Num(); i++ ) {
|
||
|
if( choosablePlayZones[ i ] == zone ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetNumChoosablePlayZones
|
||
|
============
|
||
|
*/
|
||
|
int idGameLocal::GetNumChoosablePlayZones() const {
|
||
|
return choosablePlayZones.Num();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
=========================
|
||
|
idGameLocal::ClosestPlayZone
|
||
|
=========================
|
||
|
*/
|
||
|
const sdPlayZone* idGameLocal::ClosestPlayZone( const idVec3& point, float& dist, int flags ) const {
|
||
|
const idVec2 point2d = point.ToVec2();
|
||
|
const sdPlayZone* bestPlayZone = NULL;
|
||
|
float bestDist = -1.f;
|
||
|
|
||
|
for ( int i = 0; i < playZones.Num(); i++ ) {
|
||
|
sdPlayZone* pz = playZones[ i ];
|
||
|
if ( ( pz->GetFlags() & flags ) == 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const sdBounds2D& bounds = pz->GetBounds();
|
||
|
|
||
|
int sides = bounds.SideForPoint( point2d, sdBounds2D::SPACE_INCREASE_FROM_TOP );
|
||
|
if ( sides == sdBounds2D::SIDE_INTERIOR ) {
|
||
|
dist = 0.f;
|
||
|
return pz;
|
||
|
}
|
||
|
|
||
|
float a = 0.f;
|
||
|
if ( sides & sdBounds2D::SIDE_LEFT ) {
|
||
|
a = bounds.GetLeft() - point.x;
|
||
|
} else if ( sides & sdBounds2D::SIDE_RIGHT ) {
|
||
|
a = point.x - bounds.GetRight();
|
||
|
}
|
||
|
|
||
|
float b = 0.f;
|
||
|
if ( sides & sdBounds2D::SIDE_TOP ) {
|
||
|
b = bounds.GetTop() - point.y;
|
||
|
} else if ( sides & sdBounds2D::SIDE_BOTTOM ) {
|
||
|
b = point.y - bounds.GetBottom();
|
||
|
}
|
||
|
|
||
|
float d = ( a * a ) + ( b * b );
|
||
|
|
||
|
if ( bestDist < 0.f || d < bestDist ) {
|
||
|
bestDist = d;
|
||
|
bestPlayZone = pz;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( bestDist == -1.f ) {
|
||
|
dist = 0.f;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
dist = idMath::Sqrt( bestDist );
|
||
|
return bestPlayZone;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::DebugDeploymentMask
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::DebugDeploymentMask( qhandle_t handle ) {
|
||
|
for ( int i = 0; i < playZones.Num(); i++ ) {
|
||
|
const sdDeployMaskInstance* mask = playZones[ i ]->GetMask( handle );
|
||
|
if ( mask == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
mask->DebugDraw();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::RegisterTargetEntity
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::RegisterTargetEntity( idLinkList< idEntity >& node ) {
|
||
|
node.AddToEnd( targetEntities );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::RegisterIconEntity
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::RegisterIconEntity( idLinkList< idEntity >& node ) {
|
||
|
node.AddToEnd( iconEntities );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::RegisterSpawnPoint
|
||
|
============
|
||
|
*/
|
||
|
sdSpawnPoint& idGameLocal::RegisterSpawnPoint( idEntity* owner, const idVec3& offset, const idAngles& angles ) {
|
||
|
sdSpawnPoint* spot = NULL;
|
||
|
|
||
|
// see if theres a spawn point object available to reuse
|
||
|
for ( int i = 0; i < spawnSpots.Num(); i++ ) {
|
||
|
sdSpawnPoint& tempSpot = spawnSpots[ i ];
|
||
|
if ( tempSpot.InUse() ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
spot = &tempSpot;
|
||
|
}
|
||
|
|
||
|
if ( !spot ) {
|
||
|
// failed to reuse one, try to alloc a new one
|
||
|
spot = spawnSpots.Alloc();
|
||
|
}
|
||
|
|
||
|
if ( !spot ) {
|
||
|
Error( "idGameLocal::RegisterSpawnPoint No Free Spawn Points" );
|
||
|
}
|
||
|
|
||
|
// make sure everything from the last map is cleared out
|
||
|
spot->Clear();
|
||
|
|
||
|
spot->SetOwner( owner );
|
||
|
if ( owner != NULL ) {
|
||
|
spot->SetRelativePositioning( true );
|
||
|
}
|
||
|
spot->SetPosition( offset, angles );
|
||
|
spot->SetInUse( true );
|
||
|
|
||
|
return *spot;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::UnRegisterSpawnPoint
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::UnRegisterSpawnPoint( sdSpawnPoint* point ) {
|
||
|
point->Clear();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::CheckDeploymentRequestBlock
|
||
|
============
|
||
|
*/
|
||
|
deployResult_t idGameLocal::CheckDeploymentRequestBlock( const idBounds& bounds ) {
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
if ( !deployRequests[ i ] ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( deployRequests[ i ]->CheckBlock( bounds ) ) {
|
||
|
return DR_WARNING;
|
||
|
}
|
||
|
}
|
||
|
return DR_CLEAR;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetDeploymentRequest
|
||
|
============
|
||
|
*/
|
||
|
sdDeployRequest* idGameLocal::GetDeploymentRequest( idPlayer* player ) {
|
||
|
if ( !player ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return deployRequests[ player->entityNumber ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::RequestDeployment
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::RequestDeployment( idPlayer* player, const sdDeclDeployableObject* object, const idVec3& position, float rotation, int delayMS ) {
|
||
|
if ( gameLocal.isClient ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.rules->IsEndGame() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( !player || !object ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( deployRequests[ player->entityNumber ] ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// trace down to find the real position
|
||
|
idVec3 start = position;
|
||
|
|
||
|
// estimate where to start using the heightmap (if it exists)
|
||
|
const sdPlayZone* playZoneHeight = gameLocal.GetPlayZone( start, sdPlayZone::PZF_HEIGHTMAP );
|
||
|
if ( playZoneHeight != NULL ) {
|
||
|
const sdHeightMapInstance& heightMap = playZoneHeight->GetHeightMap();
|
||
|
if ( heightMap.IsValid() ) {
|
||
|
start.z = heightMap.GetInterpolatedHeight( start );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( start.z < position.z ) {
|
||
|
start.z = position.z;
|
||
|
}
|
||
|
|
||
|
// trace down to the ground
|
||
|
start.z += 64.0f;
|
||
|
idVec3 end = start - idVec3( 0.0f, 0.0f, 2048.0f );
|
||
|
trace_t trace;
|
||
|
clip.TracePoint( CLIP_DEBUG_PARMS trace, start, end, CONTENTS_SOLID | CONTENTS_VEHICLECLIP | CONTENTS_BODY | CONTENTS_MONSTER, player );
|
||
|
|
||
|
// if the endpos is significantly higher then the request position then
|
||
|
// it may be that the requester did it from under cover
|
||
|
if ( trace.endpos.z > position.z + 64.0f ) {
|
||
|
trace.endpos = position;
|
||
|
}
|
||
|
|
||
|
sdDeployRequest* request = new sdDeployRequest( object, player, trace.endpos, rotation, player->GetGameTeam(), delayMS );
|
||
|
|
||
|
deployRequests[ player->entityNumber ] = request;
|
||
|
request->WriteCreateEvent( player->entityNumber, sdReliableMessageClientInfoAll() );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::UpdateDeploymentRequests
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::UpdateDeploymentRequests( void ) {
|
||
|
idPlayer* player = gameLocal.GetLocalViewPlayer();
|
||
|
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
if ( !deployRequests[ i ] ) {
|
||
|
continue;
|
||
|
}
|
||
|
if ( !deployRequests[ i ]->Update( player ) ) {
|
||
|
ClearDeployRequest( i );
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::RunFrame
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::RunFrame() {
|
||
|
#ifdef _XENON
|
||
|
liveManager->RunFrame();
|
||
|
#else
|
||
|
|
||
|
// system notifications are in a static list that's reset to num 0 every frame
|
||
|
// this ensures that the pointers are valid when the notifications are picked up below during sdnet.RunFrame
|
||
|
sdnet.RunFrame();
|
||
|
systemNotifications.SetNum( 0 );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
/*
|
||
|
===============
|
||
|
idGameLocal::GetBytesNeededForMapLoad
|
||
|
===============
|
||
|
*/
|
||
|
int idGameLocal::GetBytesNeededForMapLoad( const char* map ) {
|
||
|
|
||
|
sdDeclStringMap* mapDef = const_cast< sdDeclStringMap* >( declMapMetaDataType.LocalFind( map ));
|
||
|
|
||
|
int retVal = mapDef->GetDict().GetInt( va("size%d", cvarSystem->GetCVarInteger( "com_machineSpec" )));
|
||
|
if( !retVal ) {
|
||
|
if ( cvarSystem->GetCVarInteger( "com_machineSpec" ) < 2 ) { return 200 * 1024 * 1024; } else { return 400 * 1024 * 1024; }
|
||
|
}
|
||
|
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idGameLocal::SetBytesNeededForMapLoad
|
||
|
===============
|
||
|
*/
|
||
|
void idGameLocal::SetBytesNeededForMapLoad( const char* map, int bytesNeeded ) {
|
||
|
if( !cvarSystem->GetCVarBool( "com_updateLoadSize" )) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sdDeclStringMap* mapDef = const_cast< sdDeclStringMap* >( declMapMetaDataType.LocalFind( GetMapName() ));
|
||
|
|
||
|
if ( mapDef ) {
|
||
|
mapDef->GetDict().SetInt( va("size%d", cvarSystem->GetCVarInteger( "com_machineSpec" )), bytesNeeded );
|
||
|
mapDef->Save();
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetEntityCollection
|
||
|
============
|
||
|
*/
|
||
|
sdEntityCollection* idGameLocal::GetEntityCollection( const char* name, bool allowCreate ) {
|
||
|
int key = entityCollectionsHash.GenerateKey( name, false );
|
||
|
for ( int index = entityCollectionsHash.GetFirst( key ); index != idHashIndex::NULL_INDEX; index = entityCollectionsHash.GetNext( index ) ) {
|
||
|
if ( idStr::Icmp( entityCollections[ index ]->GetName(), name ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
return entityCollections[ index ];
|
||
|
}
|
||
|
|
||
|
if ( allowCreate ) {
|
||
|
sdEntityCollection* collection = new sdEntityCollection();
|
||
|
collection->SetName( name );
|
||
|
int index = entityCollections.Num();
|
||
|
entityCollectionsHash.Add( key, index );
|
||
|
entityCollections.Alloc() = collection;
|
||
|
return collection;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::SetTargetTimer
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::SetTargetTimer( qhandle_t handle, idPlayer* player, int t ) {
|
||
|
if ( handle < 0 || handle >= targetTimers.Num() ) {
|
||
|
gameLocal.Error( "idGameLocal::SetTargetTimer Invalid handle %d", handle );
|
||
|
}
|
||
|
targetTimers[ handle ].endTimes[ player->entityNumber ] = t;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::AllocTargetTimer
|
||
|
================
|
||
|
*/
|
||
|
qhandle_t idGameLocal::AllocTargetTimer( const char* targetName ) {
|
||
|
int i;
|
||
|
|
||
|
for ( i = 0; i < targetTimers.Num(); i++ ) {
|
||
|
if ( !idStr::Icmp( targetName, targetTimers[ i ].name ) ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
i = targetTimers.Num();
|
||
|
|
||
|
targetTimer_t& timer = targetTimers.Alloc();
|
||
|
timer.name = targetName;
|
||
|
timer.serverHandle = -1;
|
||
|
|
||
|
if ( !gameLocal.isClient ) {
|
||
|
timer.serverHandle = i;
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.isServer ) {
|
||
|
sdReliableServerMessage outMsg( GAME_RELIABLE_SMESSAGE_CREATEPLAYERTARGETTIMER );
|
||
|
outMsg.WriteString( targetName );
|
||
|
outMsg.WriteShort( i );
|
||
|
outMsg.Send( sdReliableMessageClientInfoAll() );
|
||
|
}
|
||
|
|
||
|
for ( int j = 0; j < MAX_CLIENTS; j++ ) {
|
||
|
timer.endTimes[ j ] = -1;
|
||
|
}
|
||
|
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetTargetTimerForServerHandle
|
||
|
================
|
||
|
*/
|
||
|
qhandle_t idGameLocal::GetTargetTimerForServerHandle( qhandle_t timerHandle ) {
|
||
|
// assert( timerHandle < targetTimerLookup.Num() );
|
||
|
if ( timerHandle >= targetTimerLookup.Num() ) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return targetTimerLookup[ timerHandle ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetTargetTimerValue
|
||
|
================
|
||
|
*/
|
||
|
int idGameLocal::GetTargetTimerValue( qhandle_t timerHandle, idPlayer* player ) {
|
||
|
if ( timerHandle < 0 ) {
|
||
|
return 0;
|
||
|
}
|
||
|
return targetTimers[ timerHandle ].endTimes[ player->entityNumber ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::ClearTargetTimers
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::ClearTargetTimers() {
|
||
|
for ( int i = 0; i < targetTimers.Num(); i++ ) {
|
||
|
for ( int j = 0; j < MAX_CLIENTS; j++ ) {
|
||
|
targetTimers[ i ].endTimes[ j ] = -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::StartRecordingDemo
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::StartRecordingDemo( void ) {
|
||
|
idPlayer* localPlayer = GetLocalPlayer();
|
||
|
if ( !localPlayer ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idStr playerName = localPlayer->userInfo.name;
|
||
|
playerName.RemoveColors();
|
||
|
|
||
|
sysTime_t time;
|
||
|
sys->RealTime( &time );
|
||
|
|
||
|
idStr timeStr = va( "%d%02d%02d_%02d%02d%02d", 1900 + time.tm_year, 1 + time.tm_mon, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec );
|
||
|
|
||
|
idStr mapStr = mapFileName.c_str();
|
||
|
mapStr.ReplaceChar( '/', '_' );
|
||
|
mapStr.StripFileExtension();
|
||
|
|
||
|
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, va( "recordNetDemo \"%s_%s_%s_build_%d_%d.ndm\"", timeStr.c_str(), mapStr.c_str(), playerName.c_str(), ENGINE_SRC_REVISION, ENGINE_MEDIA_REVISION ) );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::TerritoryForPoint
|
||
|
============
|
||
|
*/
|
||
|
sdDeployZone* idGameLocal::TerritoryForPoint( const idVec3& point, sdTeamInfo* team, bool requireTeam, bool requireActive ) const {
|
||
|
sdInstanceCollector< sdDeployZone > territories( false );
|
||
|
|
||
|
int i;
|
||
|
for ( i = 0; i < territories.Num(); i++ ) {
|
||
|
sdDeployZone* territory = territories[ i ];
|
||
|
|
||
|
if ( requireTeam ) {
|
||
|
if ( territory->GetGameTeam() != team ) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
if ( requireActive ) {
|
||
|
if ( !territory->IsActive() ) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idClipModel* clip = territory->GetPhysics()->GetClipModel();
|
||
|
const idVec3& origin = territory->GetPhysics()->GetOrigin();
|
||
|
const idMat3& axes = territory->GetPhysics()->GetAxis();
|
||
|
if ( !gameLocal.clip.ContentsModel( CLIP_DEBUG_PARMS point, NULL, mat3_identity, -1, clip, origin, axes ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
return territory;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::AllocEntityState
|
||
|
============
|
||
|
*/
|
||
|
sdEntityState* idGameLocal::AllocEntityState( networkStateMode_t mode, idEntity* ent ) const {
|
||
|
if ( ent->freeStates[ mode ] ) {
|
||
|
sdEntityState* state = ent->freeStates[ mode ];
|
||
|
ent->freeStates[ mode ] = state->next;
|
||
|
state->next = NULL;
|
||
|
return state;
|
||
|
}
|
||
|
return new sdEntityState( ent, mode );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::AllocGameState
|
||
|
============
|
||
|
*/
|
||
|
sdGameState* idGameLocal::AllocGameState( const sdNetworkStateObject& object ) const {
|
||
|
if ( object.freeStates ) {
|
||
|
sdGameState* state = object.freeStates;
|
||
|
object.freeStates = state->next;
|
||
|
state->next = NULL;
|
||
|
return state;
|
||
|
}
|
||
|
return new sdGameState( object );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::OnUserStartMap
|
||
|
============
|
||
|
*/
|
||
|
userMapChangeResult_e idGameLocal::OnUserStartMap( const char* text, idStr& reason, idStr& mapName ) {
|
||
|
sys->FlushServerInfo();
|
||
|
MakeRules();
|
||
|
return rules->OnUserStartMap( text, reason, mapName );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::ArgCompletion_StartGame
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::ArgCompletion_StartGame( const idCmdArgs& args, argCompletionCallback_t callback ) {
|
||
|
idTypeInfo* type = GetRulesType( false );
|
||
|
if ( type == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idClass* instance = type->CreateInstance();
|
||
|
if ( instance == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sdGameRules* rules = instance->Cast< sdGameRules >();
|
||
|
assert( rules != NULL );
|
||
|
|
||
|
rules->ArgCompletion_StartGame( args, callback );
|
||
|
|
||
|
delete rules;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::ClassCount
|
||
|
============
|
||
|
*/
|
||
|
int idGameLocal::ClassCount( const sdDeclPlayerClass* pc, idPlayer* skip, sdTeamInfo* team ) {
|
||
|
int count = 0;
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
idPlayer* player = GetClient( i );
|
||
|
if ( !player || player == skip ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( team && player->GetGameTeam() != team ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const sdInventory& inv = player->GetInventory();
|
||
|
if ( inv.GetClass() == pc || inv.GetCachedClass() == pc ) {
|
||
|
count++;
|
||
|
}
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetWindVector
|
||
|
============
|
||
|
*/
|
||
|
const idVec3& idGameLocal::GetWindVector( const idVec3 & origin ) const {
|
||
|
static idVec3 wind( 100.f, 0.f, 0.f );
|
||
|
return sdAtmosphere::GetAtmosphereInstance() ? sdAtmosphere::GetAtmosphereInstance()->GetWindVector() : wind;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::AddCheapDecal
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::AddCheapDecal( const idDict& decalDict, idEntity *attachTo, idVec3 &origin, idVec3 &normal, int jointIdx, int id, const char* decalName, const char* materialName ) {
|
||
|
if ( isServer && networkSystem->IsDedicated() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( !g_decals.GetBool() ) { //mal: allow players to turn decals off if desired.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// HACK: don't apply decals to things that have MASK_SHOT_BOUNDINGBOX contents
|
||
|
// this includes players, flyer hives, repair drones, and icarii
|
||
|
// FIND A BETTER WAY TO DO THIS POST-E3
|
||
|
cheapDecalUsage_t attachmentType = ( attachTo ) ? attachTo->GetDecalUsage() : CDU_WORLD;
|
||
|
|
||
|
if ( attachmentType == CDU_INHIBIT ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
cheapDecalParameters_t params;
|
||
|
params.origin = origin;
|
||
|
params.normal = normal;
|
||
|
params.jointIdx = jointIdx;
|
||
|
int index = gameLocal.GetDecal( decalDict, decalName, materialName );
|
||
|
if ( index < 0 ) {
|
||
|
return;
|
||
|
}
|
||
|
params.decl = declHolder.FindDecalByIndex( index );
|
||
|
|
||
|
if ( attachmentType == CDU_WORLD ) {
|
||
|
gameRenderWorld->AddCheapDecal( -1, params, gameLocal.time );
|
||
|
} else {
|
||
|
gameRenderWorld->AddCheapDecal( attachTo->GetModelDefHandle( id ), params, gameLocal.time );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetSnapShotPlayer
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetSnapShotPlayer( idPlayer* player ) {
|
||
|
snapShotPlayer = player;
|
||
|
aorManager.SetSnapShotPlayer( player );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetSnapShotClient
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetSnapShotClient( idPlayer* client ) {
|
||
|
snapShotClient = client;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::UpdatePlayerShadows
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::UpdatePlayerShadows( void ) {
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
idPlayer* player = gameLocal.GetClient( i );
|
||
|
if ( !player ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
player->UpdateShadows();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::LogProficiency
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::LogProficiency( const char* message ) {
|
||
|
if ( proficiencyLog == NULL ) {
|
||
|
proficiencyLog = fileSystem->OpenFileWrite( "proficiency.log" );
|
||
|
}
|
||
|
if ( proficiencyLog == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
proficiencyLog->WriteFloatString( "%s\n", message );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::LogNetwork
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::LogNetwork( const char* message ) {
|
||
|
if ( networkLog == NULL ) {
|
||
|
networkLog = fileSystem->OpenFileWrite( "network.log" );
|
||
|
}
|
||
|
if ( networkLog == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
networkLog->Printf( message );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::LogObjective
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::LogObjective( const char* message ) {
|
||
|
if ( objectiveLog == NULL ) {
|
||
|
objectiveLog = fileSystem->OpenFileAppend( "objective.log" );
|
||
|
}
|
||
|
if ( objectiveLog == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
objectiveLog->WriteFloatString( "%s", message );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::LogDamage
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::LogDamage( const char* message ) {
|
||
|
if ( !rules || rules->GetState() != sdGameRules::GS_GAMEON ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( damageLogFile == NULL ) {
|
||
|
int index = 0;
|
||
|
while ( index < 1000 ) {
|
||
|
idFile* temp = fileSystem->OpenFileRead( va( "damageLog%i.txt", index ) );
|
||
|
if ( temp == NULL ) {
|
||
|
damageLogFile = fileSystem->OpenFileWrite( va( "damageLog%i.txt", index ) );
|
||
|
break;
|
||
|
}
|
||
|
index++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !damageLogFile ) {
|
||
|
Printf( "Damage Log: %s", message );
|
||
|
Warning( "Failed To Log Damage to File" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
damageLogFile->WriteFloatString( "%s", message );
|
||
|
damageLogFile->Flush();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::LogDebugText
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::LogDebugText( const idEntity* entityFrom, const char* fmt, ... ) {
|
||
|
if ( !isClient && debugLogFile != NULL && g_logDebugText.GetBool() ) {
|
||
|
va_list argptr;
|
||
|
char text[MAX_STRING_CHARS];
|
||
|
|
||
|
va_start( argptr, fmt );
|
||
|
idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
|
||
|
va_end( argptr );
|
||
|
|
||
|
// add some interesting
|
||
|
debugLogFile->Printf( "%i, ", time );
|
||
|
if ( entityFrom != NULL ) {
|
||
|
debugLogFile->Printf( "%i, ", entityFrom->entityNumber );
|
||
|
} else {
|
||
|
debugLogFile->Printf( "-1, " );
|
||
|
}
|
||
|
|
||
|
debugLogFile->Printf( "\"%s\"\n", text );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::SetActionCommand
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::SetActionCommand( const char* action ) {
|
||
|
actionArgs.TokenizeString( action, false );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::IsPaused
|
||
|
================
|
||
|
*/
|
||
|
bool idGameLocal::IsPaused( void ) {
|
||
|
if ( gameLocal.isServer && gameLocal.time > gameLocal.playerSpawnTime ) { // Gordon: give the entities a little time to settle down before the menus will pause the game
|
||
|
|
||
|
// don't do this if any clients are connected (development only, retail can't do this)
|
||
|
bool hasClients = false;
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
idPlayer* client = GetClient( i );
|
||
|
if ( client != NULL && !client->IsType( idBot::Type ) && client != gameLocal.GetLocalPlayer() ) {
|
||
|
hasClients = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !hasClients ) {
|
||
|
sdLimboMenu* menu = gameLocal.localPlayerProperties.GetLimboMenu();
|
||
|
if ( menu != NULL ) {
|
||
|
if ( menu->Active() ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.IsMainMenuActive() ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return isPaused;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::SetPaused
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::SetPaused( bool value ) {
|
||
|
if ( isPaused == value ) {
|
||
|
return;
|
||
|
}
|
||
|
isPaused = value;
|
||
|
|
||
|
if ( gameLocal.isServer ) {
|
||
|
SendPauseInfo( sdReliableMessageClientInfoAll() );
|
||
|
}
|
||
|
|
||
|
OnPausedChanged();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::OnPausedChanged
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::OnPausedChanged( void ) {
|
||
|
if ( isPaused ) {
|
||
|
pauseViewInited = false;
|
||
|
pauseStartGuiTime = gameLocal.ToGuiTime( gameLocal.time );
|
||
|
} else {
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetPausedView
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::GetPausedView( idVec3& origin, idMat3& axis ) {
|
||
|
if ( !pauseViewInited ) {
|
||
|
idPlayer* localPlayer = GetLocalPlayer();
|
||
|
if ( localPlayer == NULL ) {
|
||
|
origin.Zero();
|
||
|
axis.Identity();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
pauseViewOrg = localPlayer->firstPersonViewOrigin;
|
||
|
pauseViewAngles = localPlayer->firstPersonViewAxis.ToAngles();
|
||
|
pauseViewAngles.roll = 0.f;
|
||
|
pauseViewInited = true;
|
||
|
|
||
|
const usercmd_t& cmd = gameLocal.usercmds[ localPlayer->entityNumber ];
|
||
|
for ( int i = 0; i < 3; i++ ) {
|
||
|
pauseViewAnglesBase[ i ] = pauseViewAngles[ i ] - SHORT2ANGLE( cmd.angles[ i ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
origin = pauseViewOrg;
|
||
|
axis = pauseViewAngles.ToMat3();
|
||
|
}
|
||
|
|
||
|
idCVar g_pauseNoClipSpeed( "g_pauseNoClipSpeed", "100", CVAR_GAME | CVAR_FLOAT, "speed to move when in pause noclip mode" );
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::UpdatePauseNoClip
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::UpdatePauseNoClip( usercmd_t& cmd ) {
|
||
|
const float speed = g_pauseNoClipSpeed.GetFloat();
|
||
|
|
||
|
idVec3 dir;
|
||
|
dir.x = ( cmd.forwardmove / 127.f ) * speed;
|
||
|
dir.y = -( cmd.rightmove / 127.f ) * speed;
|
||
|
dir.z = ( cmd.upmove / 127.f ) * speed;
|
||
|
|
||
|
for ( int i = 0; i < 3; i++ ) {
|
||
|
pauseViewAngles[ i ] = idMath::AngleNormalize180( pauseViewAnglesBase[ i ] + SHORT2ANGLE( cmd.angles[ i ] ) );
|
||
|
}
|
||
|
|
||
|
pauseViewOrg += pauseViewAngles.ToMat3() * dir;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::SendPauseInfo
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::SendPauseInfo( const sdReliableMessageClientInfoBase& info ) {
|
||
|
sdReliableServerMessage msg( GAME_RELIABLE_SMESSAGE_PAUSEINFO );
|
||
|
msg.WriteBool( isPaused );
|
||
|
msg.WriteLong( time );
|
||
|
msg.WriteLong( timeOffset );
|
||
|
msg.Send( info );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::OnInputInit
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::OnInputInit( void ) {
|
||
|
defaultBindContext = keyInputManager->AllocBindContext( "default" );
|
||
|
|
||
|
sdInstanceCollector< sdTransport > transports( true );
|
||
|
for ( int i = 0; i < transports.Num(); i++ ) {
|
||
|
transports[ i ]->OnInputInit();
|
||
|
}
|
||
|
for ( int i = 0; i < declPlayerClassType.Num(); i++ ) {
|
||
|
const sdDeclPlayerClass* cls = declPlayerClassType.LocalFindByIndex( i, false );
|
||
|
if ( !cls->IsValid() ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
cls->OnInputInit();
|
||
|
}
|
||
|
|
||
|
uiManager->OnInputInit();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::OnInputShutdown
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::OnInputShutdown( void ) {
|
||
|
defaultBindContext = NULL;
|
||
|
|
||
|
sdInstanceCollector< sdTransport > transports( true );
|
||
|
for ( int i = 0; i < transports.Num(); i++ ) {
|
||
|
transports[ i ]->OnInputShutdown();
|
||
|
}
|
||
|
for ( int i = 0; i < declPlayerClassType.Num(); i++ ) {
|
||
|
const sdDeclPlayerClass* cls = declPlayerClassType.LocalFindByIndex( i, false );
|
||
|
if ( !cls->IsValid() ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
cls->OnInputShutdown();
|
||
|
}
|
||
|
uiManager->OnInputShutdown();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::OnLanguageInit
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::OnLanguageInit() {
|
||
|
uiManager->OnLanguageInit();
|
||
|
}
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::OnLanguageShutdown
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::OnLanguageShutdown() {
|
||
|
uiManager->OnLanguageShutdown();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::Translate
|
||
|
================
|
||
|
*/
|
||
|
sdKeyCommand* idGameLocal::Translate( const idKey& key ) {
|
||
|
sdKeyCommand* cmd;
|
||
|
if ( TranslateGuiBind( key, &cmd ) ) {
|
||
|
return cmd;
|
||
|
}
|
||
|
|
||
|
idPlayer* localPlayer = GetLocalPlayer();
|
||
|
if ( localPlayer != NULL ) {
|
||
|
sdBindContext* specialContext = NULL;
|
||
|
|
||
|
idEntity* proxy = localPlayer->GetProxyEntity();
|
||
|
if ( proxy != NULL ) {
|
||
|
specialContext = proxy->GetBindContext();
|
||
|
} else {
|
||
|
if ( !localPlayer->IsSpectator() ) {
|
||
|
const sdDeclPlayerClass* cls = localPlayer->GetInventory().GetClass();
|
||
|
if ( cls != NULL ) {
|
||
|
specialContext = cls->GetBindContext();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( specialContext != NULL ) {
|
||
|
sdKeyCommand* cmd = keyInputManager->GetCommand( specialContext, key );
|
||
|
if ( cmd != NULL ) {
|
||
|
return cmd;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return keyInputManager->GetCommand( defaultBindContext, key );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdSpawnPoint::Clear
|
||
|
============
|
||
|
*/
|
||
|
void sdSpawnPoint::Clear( void ) {
|
||
|
_angles.Zero();
|
||
|
_lastUsedTime = 0;
|
||
|
_offset.Zero();
|
||
|
_owner = NULL;
|
||
|
_relativePosition = false;
|
||
|
_requirements.Clear();
|
||
|
_parachute = false;
|
||
|
_inUse = false;
|
||
|
}
|
||
|
|
||
|
typedef struct {
|
||
|
const char* string;
|
||
|
usercmdbuttonType_t type;
|
||
|
int button;
|
||
|
} userCmdString_t;
|
||
|
|
||
|
userCmdString_t userCmdStrings[] = {
|
||
|
{ "_moveUp", B_BUTTON, UB_UP },
|
||
|
{ "_moveDown", B_BUTTON, UB_DOWN },
|
||
|
{ "_left", B_BUTTON, UB_LEFT },
|
||
|
{ "_right", B_BUTTON, UB_RIGHT },
|
||
|
{ "_forward", B_BUTTON, UB_FORWARD },
|
||
|
{ "_back", B_BUTTON, UB_BACK },
|
||
|
{ "_lookUp", B_BUTTON, UB_LOOKUP },
|
||
|
{ "_lookDown", B_BUTTON, UB_LOOKDOWN },
|
||
|
{ "_strafe", B_BUTTON, UB_STRAFE },
|
||
|
{ "_moveLeft", B_BUTTON, UB_MOVELEFT },
|
||
|
{ "_moveRight", B_BUTTON, UB_MOVERIGHT },
|
||
|
|
||
|
{ "_attack", B_BUTTON, UB_ATTACK },
|
||
|
{ "_modeswitch", B_BUTTON, UB_MODESWITCH },
|
||
|
{ "_speed", B_BUTTON, UB_SPEED },
|
||
|
{ "_sprint", B_BUTTON, UB_SPRINT },
|
||
|
{ "_activate", B_BUTTON, UB_ACTIVATE },
|
||
|
{ "_showScores", B_BUTTON, UB_SHOWSCORES },
|
||
|
{ "_voice", B_BUTTON, UB_VOICE },
|
||
|
{ "_teamVoice", B_BUTTON, UB_TEAMVOICE },
|
||
|
{ "_fireteamVoice", B_BUTTON, UB_FIRETEAMVOICE },
|
||
|
{ "_mlook", B_BUTTON, UB_MLOOK },
|
||
|
{ "_altattack", B_BUTTON, UB_ALTATTACK },
|
||
|
{ "_tophat", B_BUTTON, UB_TOPHAT },
|
||
|
{ "_leanLeft", B_BUTTON, UB_LEANLEFT },
|
||
|
{ "_leanRight", B_BUTTON, UB_LEANRIGHT },
|
||
|
|
||
|
{ "_weapon0", B_IMPULSE, UCI_WEAP0 },
|
||
|
{ "_weapon1", B_IMPULSE, UCI_WEAP1 },
|
||
|
{ "_weapon2", B_IMPULSE, UCI_WEAP2 },
|
||
|
{ "_weapon3", B_IMPULSE, UCI_WEAP3 },
|
||
|
{ "_weapon4", B_IMPULSE, UCI_WEAP4 },
|
||
|
{ "_weapon5", B_IMPULSE, UCI_WEAP5 },
|
||
|
{ "_weapon6", B_IMPULSE, UCI_WEAP6 },
|
||
|
{ "_weapon7", B_IMPULSE, UCI_WEAP7 },
|
||
|
{ "_weapon8", B_IMPULSE, UCI_WEAP8 },
|
||
|
{ "_weapon9", B_IMPULSE, UCI_WEAP9 },
|
||
|
{ "_weapon10", B_IMPULSE, UCI_WEAP10 },
|
||
|
{ "_weapon11", B_IMPULSE, UCI_WEAP11 },
|
||
|
{ "_weapon12", B_IMPULSE, UCI_WEAP12 },
|
||
|
{ "_reload", B_IMPULSE, UCI_RELOAD },
|
||
|
{ "_weapnext", B_IMPULSE, UCI_WEAPNEXT },
|
||
|
{ "_weapprev", B_IMPULSE, UCI_WEAPPREV },
|
||
|
{ "_stroyup", B_IMPULSE, UCI_STROYUP },
|
||
|
{ "_stroydown", B_IMPULSE, UCI_STROYDOWN },
|
||
|
{ "_usevehicle", B_IMPULSE, UCI_USE_VEHICLE },
|
||
|
{ "_vehiclecamera", B_IMPULSE, UCI_VEHICLE_CAMERA_MODE },
|
||
|
{ "_prone", B_IMPULSE, UCI_PRONE },
|
||
|
{ "_ready", B_IMPULSE, UCI_READY },
|
||
|
|
||
|
{ "_admin", B_LOCAL_IMPULSE, ULI_ADMIN_MENU },
|
||
|
{ "_votemenu", B_LOCAL_IMPULSE, ULI_VOTE_MENU },
|
||
|
{ "_quickchat", B_LOCAL_IMPULSE, ULI_QUICKCHAT },
|
||
|
{ "_limbomenu", B_LOCAL_IMPULSE, ULI_LIMBO_MENU },
|
||
|
{ "_commandmap", B_LOCAL_IMPULSE, ULI_COMMAND_MAP },
|
||
|
{ "_taskmenu", B_LOCAL_IMPULSE, ULI_TASK_MENU },
|
||
|
{ "_fireteam", B_LOCAL_IMPULSE, ULI_FIRETEAM },
|
||
|
{ "_menuClick", B_LOCAL_IMPULSE, ULI_MENU_EVENT_CLICK },
|
||
|
{ "_menuContext", B_LOCAL_IMPULSE, ULI_MENU_EVENT_CONTEXT },
|
||
|
{ "_menuNavForward", B_LOCAL_IMPULSE, ULI_MENU_EVENT_NAV_FORWARD },
|
||
|
{ "_menuNavBackward", B_LOCAL_IMPULSE, ULI_MENU_EVENT_NAV_BACKWARD },
|
||
|
{ "_menuAccept", B_LOCAL_IMPULSE, ULI_MENU_EVENT_ACCEPT },
|
||
|
{ "_menuCancel", B_LOCAL_IMPULSE, ULI_MENU_EVENT_CANCEL },
|
||
|
{ "_menuNewline", B_LOCAL_IMPULSE, ULI_MENU_NEWLINE },
|
||
|
{ "_menuEvent1", B_LOCAL_IMPULSE, ULI_MENU_EVENT_GENERAL1 },
|
||
|
{ "_menuEvent2", B_LOCAL_IMPULSE, ULI_MENU_EVENT_GENERAL2 },
|
||
|
{ "_menuEvent3", B_LOCAL_IMPULSE, ULI_MENU_EVENT_GENERAL3 },
|
||
|
{ "_menuEvent4", B_LOCAL_IMPULSE, ULI_MENU_EVENT_GENERAL4 },
|
||
|
{ "_menuEvent5", B_LOCAL_IMPULSE, ULI_MENU_EVENT_GENERAL5 },
|
||
|
{ "_menuEvent6", B_LOCAL_IMPULSE, ULI_MENU_EVENT_GENERAL6 },
|
||
|
{ "_showLocations", B_LOCAL_IMPULSE, ULI_SHOWLOCATIONS },
|
||
|
{ "_context", B_LOCAL_IMPULSE, ULI_CONTEXT },
|
||
|
{ "_showwaypoints", B_LOCAL_IMPULSE, ULI_SHOW_WAYPOINTS },
|
||
|
{ "_showFireTeam", B_LOCAL_IMPULSE, ULI_SHOW_FIRETEAM },
|
||
|
};
|
||
|
|
||
|
const int NUM_USERCMD_STRING = sizeof( userCmdStrings ) / sizeof( userCmdStrings[ 0 ] );
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::SetupBinding
|
||
|
================
|
||
|
*/
|
||
|
usercmdbuttonType_t idGameLocal::SetupBinding( const char* binding, int& action ) {
|
||
|
for ( int i = 0; i < NUM_USERCMD_STRING; i++ ) {
|
||
|
if ( idStr::Icmp( userCmdStrings[ i ].string, binding ) != 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
action = userCmdStrings[ i ].button;
|
||
|
return userCmdStrings[ i ].type;
|
||
|
}
|
||
|
|
||
|
action = -1;
|
||
|
return B_COMMAND;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::HandleLocalImpulse
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::HandleLocalImpulse( int action, bool down ) {
|
||
|
switch ( action ) {
|
||
|
case ULI_COMMAND_MAP: {
|
||
|
if ( down ) {
|
||
|
localPlayerProperties.ToggleCommandMap();
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case ULI_TASK_MENU: {
|
||
|
if ( down ) {
|
||
|
if ( sdDemoManager::GetInstance().InPlayBack() ) {
|
||
|
break;
|
||
|
}
|
||
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
||
|
if ( player != NULL ) {
|
||
|
player->SelectNextTask();
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case ULI_ADMIN_MENU: {
|
||
|
if ( down ) {
|
||
|
if ( sdDemoManager::GetInstance().InPlayBack() ) {
|
||
|
break;
|
||
|
}
|
||
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
||
|
if ( player != NULL ) {
|
||
|
if( sdUserInterfaceScope* scope = gameLocal.globalProperties.GetSubScope( "gameHud" ) ) {
|
||
|
if( sdProperties::sdProperty* property = scope->GetProperty( "wantAdmin", sdProperties::PT_FLOAT ) ) {
|
||
|
*property->value.floatValue = 1.0f;
|
||
|
}
|
||
|
} else {
|
||
|
gameLocal.Warning( "idGameLocal::HandleLocalImpulse: Couldn't find global 'gameHud' scope in guiGlobals." );
|
||
|
}
|
||
|
sdLimboMenu* limboMenu = gameLocal.localPlayerProperties.GetLimboMenu();
|
||
|
if ( !limboMenu->Enabled() ) {
|
||
|
limboMenu->Enable( true, true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case ULI_VOTE_MENU: {
|
||
|
if ( down ) {
|
||
|
if ( sdDemoManager::GetInstance().InPlayBack() ) {
|
||
|
break;
|
||
|
}
|
||
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
||
|
if ( player != NULL ) {
|
||
|
if( sdUserInterfaceScope* scope = gameLocal.globalProperties.GetSubScope( "gameHud" ) ) {
|
||
|
if( sdProperties::sdProperty* property = scope->GetProperty( "wantVote", sdProperties::PT_FLOAT ) ) {
|
||
|
*property->value.floatValue = 1.0f;
|
||
|
}
|
||
|
} else {
|
||
|
gameLocal.Warning( "idGameLocal::HandleLocalImpulse: Couldn't find global 'gameHud' scope in guiGlobals." );
|
||
|
}
|
||
|
sdLimboMenu* limboMenu = gameLocal.localPlayerProperties.GetLimboMenu();
|
||
|
if ( !limboMenu->Enabled() ) {
|
||
|
limboMenu->Enable( true, true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case ULI_LIMBO_MENU: {
|
||
|
if ( down ) {
|
||
|
if ( sdDemoManager::GetInstance().InPlayBack() ) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !networkSystem->IsDedicated() ) {
|
||
|
if( sdUserInterfaceScope* scope = gameLocal.globalProperties.GetSubScope( "gameHud" ) ) {
|
||
|
if( sdProperties::sdProperty* property = scope->GetProperty( "wantAdmin", sdProperties::PT_FLOAT ) ) {
|
||
|
*property->value.floatValue = 0.0f;
|
||
|
}
|
||
|
if( sdProperties::sdProperty* property = scope->GetProperty( "wantVote", sdProperties::PT_FLOAT ) ) {
|
||
|
*property->value.floatValue = 0.0f;
|
||
|
}
|
||
|
} else {
|
||
|
gameLocal.Warning( "idGameLocal::HandleLocalImpulse: Couldn't find global 'gameHud' scope in guiGlobals." );
|
||
|
}
|
||
|
|
||
|
sdLimboMenu* limboMenu = gameLocal.localPlayerProperties.GetLimboMenu();
|
||
|
if ( limboMenu->Enabled() ) {
|
||
|
limboMenu->Enable( false );
|
||
|
} else {
|
||
|
limboMenu->Enable( true, true );
|
||
|
}
|
||
|
|
||
|
sdQuickChatMenu* contextMenu = localPlayerProperties.GetContextMenu();
|
||
|
if ( contextMenu->Enabled() ) {
|
||
|
contextMenu->Enable( false );
|
||
|
}
|
||
|
|
||
|
sdQuickChatMenu* quickChatMenu = localPlayerProperties.GetQuickChatMenu();
|
||
|
if ( quickChatMenu->Enabled() ) {
|
||
|
quickChatMenu->Enable( false );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case ULI_QUICKCHAT: {
|
||
|
if ( down ) {
|
||
|
if ( sdDemoManager::GetInstance().InPlayBack() ) {
|
||
|
break;
|
||
|
}
|
||
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
||
|
if ( player != NULL ) {
|
||
|
if ( !player->IsSpectator() ) {
|
||
|
sdQuickChatMenu* contextMenu = localPlayerProperties.GetContextMenu();
|
||
|
if ( contextMenu->Enabled() ) {
|
||
|
contextMenu->Enable( false );
|
||
|
}
|
||
|
|
||
|
sdWeaponSelectionMenu* weaponMenu = localPlayerProperties.GetWeaponSelectionMenu();
|
||
|
if( weaponMenu->Enabled() ) {
|
||
|
weaponMenu->Enable( false );
|
||
|
}
|
||
|
|
||
|
sdChatMenu* chatMenu = localPlayerProperties.GetChatMenu();
|
||
|
if( chatMenu->Enabled() ) {
|
||
|
chatMenu->Enable( false );
|
||
|
}
|
||
|
|
||
|
sdFireTeamMenu* fireTeamMenu = localPlayerProperties.GetFireTeamMenu();
|
||
|
if ( fireTeamMenu->Enabled() ) {
|
||
|
fireTeamMenu->Enable( false );
|
||
|
}
|
||
|
|
||
|
sdQuickChatMenu* quickChatMenu = localPlayerProperties.GetQuickChatMenu();
|
||
|
if ( quickChatMenu->Enabled() ) {
|
||
|
quickChatMenu->Enable( false );
|
||
|
} else {
|
||
|
quickChatMenu->Enable( true, true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case ULI_CONTEXT: {
|
||
|
if ( down ) {
|
||
|
if ( sdDemoManager::GetInstance().InPlayBack() ) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
||
|
if ( player != NULL ) {
|
||
|
if ( !player->IsSpectator() ) {
|
||
|
sdQuickChatMenu* quickChatMenu = localPlayerProperties.GetQuickChatMenu();
|
||
|
if ( quickChatMenu->Enabled() ) {
|
||
|
quickChatMenu->Enable( false );
|
||
|
}
|
||
|
|
||
|
sdWeaponSelectionMenu* weaponMenu = localPlayerProperties.GetWeaponSelectionMenu();
|
||
|
if( weaponMenu->Enabled() ) {
|
||
|
weaponMenu->Enable( false );
|
||
|
}
|
||
|
|
||
|
sdChatMenu* chatMenu = localPlayerProperties.GetChatMenu();
|
||
|
if( chatMenu->Enabled() ) {
|
||
|
chatMenu->Enable( false );
|
||
|
}
|
||
|
sdFireTeamMenu* fireTeamMenu = localPlayerProperties.GetFireTeamMenu();
|
||
|
if ( fireTeamMenu->Enabled() ) {
|
||
|
fireTeamMenu->Enable( false );
|
||
|
}
|
||
|
|
||
|
sdQuickChatMenu* contextMenu = localPlayerProperties.GetContextMenu();
|
||
|
if ( contextMenu->Enabled() ) {
|
||
|
contextMenu->Enable( false );
|
||
|
} else {
|
||
|
contextMenu->Enable( true, true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case ULI_FIRETEAM: {
|
||
|
if ( down ) {
|
||
|
if ( sdDemoManager::GetInstance().InPlayBack() ) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
||
|
if ( player != NULL ) {
|
||
|
if ( !player->IsSpectator() ) {
|
||
|
sdFireTeamMenu* fireTeamMenu = localPlayerProperties.GetFireTeamMenu();
|
||
|
if ( fireTeamMenu->Enabled() ) {
|
||
|
fireTeamMenu->Enable( false );
|
||
|
} else {
|
||
|
fireTeamMenu->Enable( true, true );
|
||
|
}
|
||
|
}
|
||
|
sdQuickChatMenu* quickChatMenu = localPlayerProperties.GetQuickChatMenu();
|
||
|
if ( quickChatMenu->Enabled() ) {
|
||
|
quickChatMenu->Enable( false );
|
||
|
}
|
||
|
sdQuickChatMenu* contextMenu = localPlayerProperties.GetContextMenu();
|
||
|
if ( contextMenu->Enabled() ) {
|
||
|
contextMenu->Enable( false );
|
||
|
}
|
||
|
sdWeaponSelectionMenu* weaponMenu = localPlayerProperties.GetWeaponSelectionMenu();
|
||
|
if( weaponMenu->Enabled() ) {
|
||
|
weaponMenu->Enable( false );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case ULI_MENU_EVENT_CLICK: {
|
||
|
const sdSysEvent* event = sys->GenerateMouseButtonEvent( 1, down );
|
||
|
gameLocal.HandleGuiEvent( event );
|
||
|
sys->FreeEvent( event );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case ULI_MENU_EVENT_CONTEXT: {
|
||
|
const sdSysEvent* event = sys->GenerateMouseButtonEvent( 2, down );
|
||
|
gameLocal.HandleGuiEvent( event );
|
||
|
sys->FreeEvent( event );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case ULI_MENU_EVENT_GENERAL1:
|
||
|
case ULI_MENU_EVENT_GENERAL2:
|
||
|
case ULI_MENU_EVENT_GENERAL3:
|
||
|
case ULI_MENU_EVENT_GENERAL4:
|
||
|
case ULI_MENU_EVENT_GENERAL5:
|
||
|
case ULI_MENU_EVENT_GENERAL6:
|
||
|
case ULI_MENU_EVENT_NAV_FORWARD:
|
||
|
case ULI_MENU_EVENT_NAV_BACKWARD:
|
||
|
case ULI_MENU_EVENT_ACCEPT:
|
||
|
case ULI_MENU_EVENT_CANCEL:
|
||
|
case ULI_MENU_NEWLINE: {
|
||
|
if( down ) {
|
||
|
const sdSysEvent* event = sys->GenerateGuiEvent( action );
|
||
|
gameLocal.HandleGuiEvent( event );
|
||
|
sys->FreeEvent( event );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case ULI_SHOWLOCATIONS: {
|
||
|
sdLocationMarker::ShowLocations( down );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case ULI_SHOW_WAYPOINTS: {
|
||
|
sdWayPointManager::GetInstance().ShowWayPoints( down );
|
||
|
break;
|
||
|
}
|
||
|
case ULI_SHOW_FIRETEAM: {
|
||
|
localPlayerProperties.SetShowFireTeam( down );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetCookieString
|
||
|
================
|
||
|
*/
|
||
|
const char* idGameLocal::GetCookieString( const char* key ) {
|
||
|
sdNetUser* user = networkService->GetActiveUser();
|
||
|
if ( user == NULL ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return user->GetProfile().GetProperties().GetString( va( "cookie_%s", key ) );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetCookieInt
|
||
|
================
|
||
|
*/
|
||
|
int idGameLocal::GetCookieInt( const char* key ) {
|
||
|
sdNetUser* user = networkService->GetActiveUser();
|
||
|
if ( user == NULL ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return user->GetProfile().GetProperties().GetInt( va( "cookie_%s", key ) );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::SetCookieString
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::SetCookieString( const char* key, const char* value ) {
|
||
|
sdNetUser* user = networkService->GetActiveUser();
|
||
|
if ( user == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
user->GetProfile().GetProperties().Set( va( "cookie_%s", key ), value );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::SetCookieInt
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::SetCookieInt( const char* key, int value ) {
|
||
|
sdNetUser* user = networkService->GetActiveUser();
|
||
|
if ( user == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
user->GetProfile().GetProperties().SetInt( va( "cookie_%s", key ), value );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::PurgeAndLoadTeamAssets
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::PurgeAndLoadTeamAssets( const sdDeclStringMap* newPartialLoadTeamAssets ) {
|
||
|
if ( newPartialLoadTeamAssets == currentPartialLoadTeamAssets ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// unload old media
|
||
|
if ( currentPartialLoadTeamAssets != NULL ) {
|
||
|
const sdDeclStringMap* stringMap = currentPartialLoadTeamAssets;
|
||
|
if ( stringMap != NULL ) {
|
||
|
for ( int i = 0; i < stringMap->GetDict().GetNumKeyVals(); i++ ) {
|
||
|
const idStr& value = stringMap->GetDict().GetKeyVal( i )->GetValue();
|
||
|
|
||
|
idRenderModel* renderModel = NULL;
|
||
|
|
||
|
// this does a for loop rather than by name, as we don't actually want to load declarations
|
||
|
const idDeclModelDef* modelDef = NULL;
|
||
|
for ( int j = 0; j < gameLocal.declModelDefType.Num(); j++ ) {
|
||
|
modelDef = gameLocal.declModelDefType.LocalFindByIndex( j, false );
|
||
|
if ( value.Icmp( modelDef->GetName() ) == 0 ) {
|
||
|
break;
|
||
|
}
|
||
|
modelDef = NULL;
|
||
|
}
|
||
|
|
||
|
if ( modelDef != NULL ) {
|
||
|
if ( modelDef->GetState() == DS_PARSED ) {
|
||
|
renderModel = modelDef->ModelHandle();
|
||
|
}
|
||
|
} else {
|
||
|
renderModel = renderModelManager->GetModel( value.c_str() );
|
||
|
}
|
||
|
|
||
|
if ( renderModel != NULL ) {
|
||
|
renderModel->PurgePartialLoadableImages();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// load new media
|
||
|
if ( newPartialLoadTeamAssets != NULL ) {
|
||
|
const sdDeclStringMap* stringMap = newPartialLoadTeamAssets;
|
||
|
if ( stringMap != NULL ) {
|
||
|
for ( int i = 0; i < stringMap->GetDict().GetNumKeyVals(); i++ ) {
|
||
|
const idStr& value = stringMap->GetDict().GetKeyVal( i )->GetValue();
|
||
|
|
||
|
idRenderModel* renderModel = NULL;
|
||
|
|
||
|
// this does a for loop rather than by name, as we don't actually want to load declarations
|
||
|
const idDeclModelDef* modelDef = NULL;
|
||
|
for ( int j = 0; j < gameLocal.declModelDefType.Num(); j++ ) {
|
||
|
modelDef = gameLocal.declModelDefType.LocalFindByIndex( j, false );
|
||
|
if ( value.Icmp( modelDef->GetName() ) == 0 ) {
|
||
|
break;
|
||
|
}
|
||
|
modelDef = NULL;
|
||
|
}
|
||
|
|
||
|
if ( modelDef != NULL ) {
|
||
|
if ( modelDef->GetState() == DS_PARSED ) {
|
||
|
renderModel = modelDef->ModelHandle();
|
||
|
}
|
||
|
} else {
|
||
|
renderModel = renderModelManager->GetModel( value.c_str() );
|
||
|
}
|
||
|
|
||
|
if ( renderModel != NULL ) {
|
||
|
renderModel->LoadPartialLoadableImages();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
currentPartialLoadTeamAssets = newPartialLoadTeamAssets;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::AddEntityOcclusionQuery
|
||
|
================
|
||
|
*/
|
||
|
qhandle_t idGameLocal::AddEntityOcclusionQuery( idEntity *ent ) {
|
||
|
if ( ent != NULL ) {
|
||
|
if ( occlusionQueryList.FindIndex( ent ) < 0 ) {
|
||
|
occlusionQueryList.Append( ent );
|
||
|
return gameRenderWorld->AddOcclusionTestDef( &ent->GetOcclusionQueryInfo() );
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::FreeEntityOcclusionQuery
|
||
|
================
|
||
|
*/
|
||
|
void idGameLocal::FreeEntityOcclusionQuery( idEntity *ent ) {
|
||
|
gameRenderWorld->FreeOcclusionTestDef( ent->GetOcclusionQueryHandle() );
|
||
|
occlusionQueryList.Remove( ent );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::MessageBox
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::MessageBox( msgBoxType_t type, const wchar_t* message, const sdDeclLocStr* title ) {
|
||
|
using namespace sdProperties;
|
||
|
|
||
|
sdUserInterfaceScope* scope = gameLocal.globalProperties.GetSubScope( "messageBox" );
|
||
|
if( scope == NULL ) {
|
||
|
gameLocal.Warning( "idGameLocal::MessageBox: Couldn't find global 'messageBox' scope in guiGlobals." );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if( sdProperty* property = scope->GetProperty( "message", PT_WSTRING )) {
|
||
|
*property->value.wstringValue = message;
|
||
|
}
|
||
|
if( sdProperty* property = scope->GetProperty( "title", PT_INT )) {
|
||
|
*property->value.intValue = title->Index();
|
||
|
}
|
||
|
if( sdProperty* property = scope->GetProperty( "type", PT_FLOAT )) {
|
||
|
*property->value.floatValue = type;
|
||
|
}
|
||
|
if( sdProperty* property = scope->GetProperty( "active", PT_FLOAT )) {
|
||
|
*property->value.floatValue = 1.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::CloseMessageBox
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::CloseMessageBox() {
|
||
|
using namespace sdProperties;
|
||
|
|
||
|
sdUserInterfaceScope* scope = gameLocal.globalProperties.GetSubScope( "messageBox" );
|
||
|
if( scope == NULL ) {
|
||
|
gameLocal.Warning( "idGameLocal::MessageBox: Couldn't find global 'messageBox' scope in guiGlobals." );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if( sdProperty* property = scope->GetProperty( "active", PT_FLOAT )) {
|
||
|
*property->value.floatValue = 0.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
idGameLocal::EntitiesOfClass
|
||
|
==============
|
||
|
*/
|
||
|
int idGameLocal::EntitiesOfClass( const char *name, idList< idEntityPtr<idEntity> > &list ) const {
|
||
|
int entCount = 0;
|
||
|
|
||
|
for( idEntity* ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
idStr classname;
|
||
|
if ( ent->spawnArgs.GetString( "classname", "", classname ) ) {
|
||
|
if ( classname.Cmp( name ) == 0 ) {
|
||
|
idEntityPtr<idEntity> &entityPtr = list.Alloc();
|
||
|
entityPtr = ent;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return list.Num();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::LogComplaint
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::LogComplaint( idPlayer* player, idPlayer* attacker ) {
|
||
|
const sdUserGroup& kickGroup = sdUserGroupManager::GetInstance().GetGroup( attacker->GetUserGroup() );
|
||
|
if ( kickGroup.HasPermission( PF_NO_BAN ) || gameLocal.IsLocalPlayer( attacker ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
assert( player != NULL && attacker != NULL );
|
||
|
|
||
|
idWStrList parms;
|
||
|
parms.Alloc() = player->userInfo.wideName;
|
||
|
attacker->SendLocalisedMessage( declHolder.declLocStrType[ "game/complaint" ], parms );
|
||
|
|
||
|
int clientNum = attacker->entityNumber;
|
||
|
|
||
|
const char* reason = "engine/disconnect/toomanycomplaints";
|
||
|
|
||
|
clientCompaintCount[ clientNum ]++;
|
||
|
int maxComplaints = g_complaintLimit.GetInteger();
|
||
|
if ( maxComplaints > 0 && clientCompaintCount[ clientNum ] >= maxComplaints ) {
|
||
|
networkSystem->ServerKickClient( clientNum, reason, true );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
maxComplaints = g_complaintGUIDLimit.GetInteger();
|
||
|
if ( maxComplaints > 0 ) {
|
||
|
if ( networkService->GetDedicatedServerState() == sdNetService::DS_ONLINE ) {
|
||
|
sdNetClientId clientId;
|
||
|
networkSystem->ServerGetClientNetId( player->entityNumber, clientId );
|
||
|
|
||
|
if ( clientId.IsValid() ) {
|
||
|
clientUniqueComplaints[ clientNum ].AddUnique( clientId );
|
||
|
|
||
|
if ( clientUniqueComplaints[ clientNum ].Num() >= maxComplaints ) {
|
||
|
sdPlayerStatEntry* stat = sdGlobalStatsTracker::GetInstance().GetStat( sdGlobalStatsTracker::GetInstance().AllocStat( "times_kicked_complaints", sdNetStatKeyValue::SVT_INT ) );
|
||
|
stat->IncreaseValue( clientNum, 1 );
|
||
|
|
||
|
networkSystem->ServerKickClient( clientNum, reason, true );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::DoSkyCheck
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::DoSkyCheck( const idVec3& location ) const {
|
||
|
idVec3 end = location;
|
||
|
end.z += 65535.f;
|
||
|
|
||
|
trace_t trace;
|
||
|
memset( &trace, 0, sizeof( trace ) );
|
||
|
|
||
|
gameLocal.clip.TranslationWorld( CLIP_DEBUG_PARMS trace, location, end, NULL, mat3_identity, MASK_SHOT_RENDERMODEL );
|
||
|
|
||
|
if ( trace.c.material == NULL ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return ( trace.c.material->GetSurfaceFlags() & SURF_NOIMPACT ) != 0;
|
||
|
/*
|
||
|
// TWTODO: Check that this functions correctly
|
||
|
|
||
|
const sdPlayZone* playZoneHeight = gameLocal.GetPlayZone( location, sdPlayZone::PZF_HEIGHTMAP );
|
||
|
if ( playZoneHeight == NULL ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const sdHeightMapInstance& heightMap = playZoneHeight->GetHeightMap();
|
||
|
if ( !heightMap.IsValid() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( heightMap.GetHeight( location ) > location.z + 16.0f ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::MutePlayerLocal
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::MutePlayerLocal( idPlayer* player, int clientIndex ) {
|
||
|
if ( clientIndex < 0 || clientIndex >= MAX_CLIENTS ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( clientIndex == player->entityNumber ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.isClient ) {
|
||
|
sdReliableClientMessage msg( GAME_RELIABLE_CMESSAGE_SETMUTESTATUS );
|
||
|
msg.WriteBool( true );
|
||
|
msg.WriteByte( clientIndex );
|
||
|
msg.Send();
|
||
|
} else {
|
||
|
clientMuteMask[ clientIndex ].Set( player->entityNumber );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::UnMutePlayerLocal
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::UnMutePlayerLocal( idPlayer* player, int clientIndex ) {
|
||
|
if ( clientIndex < 0 || clientIndex >= MAX_CLIENTS ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( clientIndex == player->entityNumber ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.isClient ) {
|
||
|
sdReliableClientMessage msg( GAME_RELIABLE_CMESSAGE_SETMUTESTATUS );
|
||
|
msg.WriteBool( false );
|
||
|
msg.WriteByte( clientIndex );
|
||
|
msg.Send();
|
||
|
} else {
|
||
|
clientMuteMask[ clientIndex ].Clear( player->entityNumber );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::MutePlayerQuickChatLocal
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::MutePlayerQuickChatLocal( int clientIndex ) {
|
||
|
if ( clientIndex == localClientNum ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idPlayer* player = gameLocal.GetClient( clientIndex );
|
||
|
if ( player == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int i;
|
||
|
for ( i = 0; i < clientQuickChatMuteList.Num(); i++ ) {
|
||
|
if ( clientQuickChatMuteList[ i ].name == player->userInfo.rawName ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ( i == clientQuickChatMuteList.Num() ) {
|
||
|
quickChatMuteEntry_t& entry = clientQuickChatMuteList.Alloc();
|
||
|
entry.name = player->userInfo.rawName;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::UnMutePlayerQuickChatLocal
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::UnMutePlayerQuickChatLocal( int clientIndex ) {
|
||
|
if ( clientIndex == localClientNum ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idPlayer* player = gameLocal.GetClient( clientIndex );
|
||
|
if ( player == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < clientQuickChatMuteList.Num(); i++ ) {
|
||
|
if ( clientQuickChatMuteList[ i ].name == player->userInfo.rawName ) {
|
||
|
clientQuickChatMuteList.RemoveIndexFast( i );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::IsClientQuickChatMuted
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::IsClientQuickChatMuted( idPlayer* player ) {
|
||
|
if ( gameLocal.IsLocalPlayer( player ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < clientQuickChatMuteList.Num(); i++ ) {
|
||
|
if ( clientQuickChatMuteList[ i ].name == player->userInfo.rawName ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::OnUserNameChanged
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::OnUserNameChanged( idPlayer* player, idStr oldName, idStr newName ) {
|
||
|
for ( int i = 0; i < clientQuickChatMuteList.Num(); i++ ) {
|
||
|
if ( clientQuickChatMuteList[ i ].name == oldName ) {
|
||
|
clientQuickChatMuteList[ i ].name = newName;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::AllocEndGameStat
|
||
|
============
|
||
|
*/
|
||
|
int idGameLocal::AllocEndGameStat( void ) {
|
||
|
int index = endGameStats.Num();
|
||
|
savedPlayerStat_t& stat = endGameStats.Alloc();
|
||
|
stat.name = "";
|
||
|
stat.value = 0.f;
|
||
|
stat.rank = NULL;
|
||
|
stat.team = NULL;
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
stat.data[ i ] = -1.f;
|
||
|
}
|
||
|
return index;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetEndGameStatValue
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetEndGameStatValue( int statIndex, idPlayer* player, float value ) {
|
||
|
assert( player != NULL );
|
||
|
endGameStats[ statIndex ].data[ player->entityNumber ] = value;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetEndGameStatWinner
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetEndGameStatWinner( int statIndex, idPlayer* player ) {
|
||
|
savedPlayerStat_t& stat = endGameStats[ statIndex ];
|
||
|
if ( player == NULL ) {
|
||
|
stat.name = "";
|
||
|
stat.value = 0.f;
|
||
|
stat.rank = NULL;
|
||
|
stat.team = NULL;
|
||
|
} else {
|
||
|
stat.name = player->userInfo.name;
|
||
|
stat.value = stat.data[ player->entityNumber ];
|
||
|
stat.rank = player->GetProficiencyTable().GetRank();
|
||
|
stat.team = player->GetTeam();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SendEndGameStats
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SendEndGameStats( const sdReliableMessageClientInfoBase& target ) {
|
||
|
if ( gameLocal.isClient ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( target.SendToClients() && target.SendToAll() ) {
|
||
|
OnEndGameStatsReceived();
|
||
|
}
|
||
|
|
||
|
if ( endGameStats.Num() == 0 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.isServer ) {
|
||
|
if ( target.SendToClients() ) {
|
||
|
if ( target.SendToAll() ) {
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
if ( GetClient( i ) == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
sdReliableServerMessage msg( GAME_RELIABLE_SMESSAGE_ENDGAMESTATS );
|
||
|
msg.WriteLong( endGameStats.Num() );
|
||
|
for ( int j = 0; j < endGameStats.Num(); j++ ) {
|
||
|
savedPlayerStat_t& stat = endGameStats[ j ];
|
||
|
|
||
|
msg.WriteLong( stat.rank == NULL ? -1 : stat.rank->Index() );
|
||
|
msg.WriteString( stat.name.c_str() );
|
||
|
msg.WriteFloat( stat.value );
|
||
|
msg.WriteFloat( stat.data[ i ] );
|
||
|
}
|
||
|
msg.Send( sdReliableMessageClientInfo( i ) );
|
||
|
}
|
||
|
} else {
|
||
|
sdReliableServerMessage msg( GAME_RELIABLE_SMESSAGE_ENDGAMESTATS );
|
||
|
msg.WriteLong( endGameStats.Num() );
|
||
|
for ( int i = 0; i < endGameStats.Num(); i++ ) {
|
||
|
savedPlayerStat_t& stat = endGameStats[ i ];
|
||
|
|
||
|
msg.WriteLong( stat.rank == NULL ? -1 : stat.rank->Index() );
|
||
|
msg.WriteString( stat.name.c_str() );
|
||
|
msg.WriteFloat( stat.value );
|
||
|
msg.WriteFloat( stat.data[ target.GetClientNum() ] );
|
||
|
}
|
||
|
msg.Send( sdReliableMessageClientInfo( target.GetClientNum() ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( target.SendToRepeaterClients() ) {
|
||
|
sdReliableServerMessage msg( GAME_RELIABLE_SMESSAGE_ENDGAMESTATS );
|
||
|
msg.WriteLong( endGameStats.Num() );
|
||
|
for ( int j = 0; j < endGameStats.Num(); j++ ) {
|
||
|
savedPlayerStat_t& stat = endGameStats[ j ];
|
||
|
|
||
|
msg.WriteLong( stat.rank == NULL ? -1 : stat.rank->Index() );
|
||
|
msg.WriteString( stat.name.c_str() );
|
||
|
msg.WriteFloat( stat.value );
|
||
|
msg.WriteFloat( -1.f );
|
||
|
}
|
||
|
msg.Send( sdReliableMessageClientInfoRepeater( target.GetClientNum() ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::ClearEndGameStats
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::ClearEndGameStats( void ) {
|
||
|
if ( endGameStats.Num() == 0 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
endGameStats.SetNum( 0, false );
|
||
|
|
||
|
if ( gameLocal.isServer ) {
|
||
|
sdReliableServerMessage msg( GAME_RELIABLE_SMESSAGE_ENDGAMESTATS );
|
||
|
msg.WriteLong( 0 );
|
||
|
msg.Send( sdReliableMessageClientInfoAll() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::OnEndGameStatsReceived
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::OnEndGameStatsReceived( void ) {
|
||
|
if ( endGameStats.Num() == 0 ) {
|
||
|
if( sdUserInterfaceScope* scope = gameLocal.globalProperties.GetSubScope( "mapInfo" ) ) {
|
||
|
if( sdProperties::sdProperty* property = scope->GetProperty( "statsReady", sdProperties::PT_FLOAT ) ) {
|
||
|
*property->value.floatValue = 0.0f;
|
||
|
}
|
||
|
} else {
|
||
|
gameLocal.Warning( "idGameLocal::OnEndGameStatsReceived: Couldn't find global 'mapInfo' scope in guiGlobals." );
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// normal end of game stuff
|
||
|
if( sdUserInterfaceScope* scope = gameLocal.globalProperties.GetSubScope( "mapInfo" ) ) {
|
||
|
if( sdProperties::sdProperty* property = scope->GetProperty( "statsReady", sdProperties::PT_FLOAT ) ) {
|
||
|
// force a signal
|
||
|
*property->value.floatValue = 0.0f;
|
||
|
*property->value.floatValue = 1.0f;
|
||
|
}
|
||
|
} else {
|
||
|
gameLocal.Warning( "idGameLocal::OnEndGameStatsReceived: Couldn't find global 'mapInfo' scope in guiGlobals." );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::ListClientEntities_f
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::ListClientEntities_f(class idCmdArgs const & args ) {
|
||
|
common->Printf( "-------------------------\n" );
|
||
|
for (int i=0; i<MAX_CENTITIES; i++) {
|
||
|
if ( gameLocal.clientEntities[i] ) {
|
||
|
const char *name = gameLocal.clientEntities[i]->GetName();
|
||
|
common->Printf( "%d '%s'\n", i, name ? name : "<unknown>" );
|
||
|
}
|
||
|
}
|
||
|
common->Printf( "-------------------------\n" );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::LoadMainMenuPartialMedia
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::LoadMainMenuPartialMedia( bool blocking ) {
|
||
|
if( networkSystem->IsDedicated() ) {
|
||
|
return;
|
||
|
}
|
||
|
if( sdUserInterfaceLocal* mainMenu = GetUserInterface( uiMainMenuHandle ) ) {
|
||
|
if( mainMenu->GetDecl() == NULL ) {
|
||
|
assert( 0 );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const idStrList& models = mainMenu->GetDecl()->GetPartialLoadModels();
|
||
|
for( int i = 0; i < models.Num(); i++ ) {
|
||
|
idRenderModel* renderModel = NULL;
|
||
|
|
||
|
const idDeclModelDef* modelDef = gameLocal.declModelDefType.LocalFind( models[ i ].c_str(), false );
|
||
|
if ( modelDef != NULL ) {
|
||
|
renderModel = modelDef->ModelHandle();
|
||
|
} else {
|
||
|
if( blocking ) {
|
||
|
renderModel = renderModelManager->FindModel( models[ i ].c_str() );
|
||
|
} else {
|
||
|
renderModel = renderModelManager->GetModel( models[ i ].c_str() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( renderModel != NULL ) {
|
||
|
renderModel->LoadPartialLoadableImages( blocking );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::PurgeMainMenuPartialMedia
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::PurgeMainMenuPartialMedia() {
|
||
|
if( networkSystem->IsDedicated() ) {
|
||
|
return;
|
||
|
}
|
||
|
if( sdUserInterfaceLocal* mainMenu = GetUserInterface( uiMainMenuHandle ) ) {
|
||
|
const idStrList& models = mainMenu->GetDecl()->GetPartialLoadModels();
|
||
|
for( int i = 0; i < models.Num(); i++ ) {
|
||
|
idRenderModel* renderModel = NULL;
|
||
|
|
||
|
const idDeclModelDef* modelDef = gameLocal.declModelDefType.LocalFind( models[ i ].c_str(), false );
|
||
|
if ( modelDef != NULL ) {
|
||
|
renderModel = modelDef->ModelHandle();
|
||
|
} else {
|
||
|
renderModel = renderModelManager->GetModel( models[ i ].c_str() );
|
||
|
}
|
||
|
|
||
|
if ( renderModel != NULL ) {
|
||
|
renderModel->PurgePartialLoadableImages();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetUpdateAvailability
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetUpdateAvailability( updateAvailability_t type ) {
|
||
|
if( !networkSystem->IsDedicated() ) {
|
||
|
updateManager.SetAvailability( type );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetUpdateResponse
|
||
|
============
|
||
|
*/
|
||
|
guiUpdateResponse_t idGameLocal::GetUpdateResponse() {
|
||
|
return updateManager.GetResponse();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetUpdateProgress
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetUpdateProgress( float percent ) {
|
||
|
if( !networkSystem->IsDedicated() ) {
|
||
|
updateManager.SetUpdateProgress( percent );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetUpdateState
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetUpdateState( updateState_t state ) {
|
||
|
if( !networkSystem->IsDedicated() ) {
|
||
|
updateManager.SetUpdateState( state );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetUpdateFromServer
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetUpdateFromServer( bool fromServer ) {
|
||
|
if( !networkSystem->IsDedicated() ) {
|
||
|
updateManager.SetUpdateFromServer( fromServer );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::AddSystemNotification
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::AddSystemNotification( const wchar_t* text ) {
|
||
|
if( !networkSystem->IsDedicated() && systemNotifications.Num() < ( sdNotificationSystem::MAX_NOTIFICATIONS - notificationSystem->GetNumNotifications() ) ) {
|
||
|
systemNotifications.Append( idWStr( text ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SetUpdateMessage
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SetUpdateMessage( const wchar_t* text ) {
|
||
|
if( !networkSystem->IsDedicated() ) {
|
||
|
updateManager.SetUpdateMessage( text );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::DrawLCD
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::DrawLCD( sdLogitechLCDSystem* lcd ) {
|
||
|
bool drawSplash = false;
|
||
|
|
||
|
idPlayer* player = GetLocalPlayer();
|
||
|
const sdDeclPlayerClass* cls = NULL;
|
||
|
|
||
|
if ( player == NULL ) {
|
||
|
drawSplash = true;
|
||
|
} else if ( player->IsSpectator() ) {
|
||
|
drawSplash = true;
|
||
|
} else {
|
||
|
cls = player->GetInventory().GetClass();
|
||
|
if ( cls == NULL ) {
|
||
|
drawSplash = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( drawSplash ) {
|
||
|
sdLogitechLCDSystem::objectHandle_t texture = lcd->GetImageHandle( "textures/lcd/etqw_logo.tga" );
|
||
|
lcd->DrawImageHandle( texture, 0, 0 );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int offset[ 4 ][ 2 ] = {
|
||
|
{ 0, 0 },
|
||
|
{ 81, 0 },
|
||
|
{ 0, 22 },
|
||
|
{ 81, 22 },
|
||
|
};
|
||
|
|
||
|
sdLogitechLCDSystem::objectHandle_t starTexture = lcd->GetImageHandle( "textures/lcd/star.tga" );
|
||
|
|
||
|
int count = Min( 4, cls->GetNumProficiencies() );
|
||
|
for ( int i = 0; i < count; i++ ) {
|
||
|
int profIndex = cls->GetProficiency( i ).index;
|
||
|
const sdDeclProficiencyType* prof = gameLocal.declProficiencyTypeType[ profIndex ];
|
||
|
|
||
|
const char* fileName = va( "textures/lcd/skill_%s.tga", prof->GetName() );
|
||
|
|
||
|
sdLogitechLCDSystem::objectHandle_t texture = lcd->GetImageHandle( fileName );
|
||
|
lcd->DrawImageHandle( texture, offset[ i ][ 0 ], offset[ i ][ 1 ] );
|
||
|
|
||
|
sdProficiencyTable& table = player->GetProficiencyTable();
|
||
|
|
||
|
int level = Min( 4, table.GetLevel( profIndex ) );
|
||
|
for ( int j = 0; j < level; j++ ) {
|
||
|
lcd->DrawImageHandle( starTexture, offset[ i ][ 0 ] + 17 + ( j * 15 ), offset[ i ][ 1 ] );
|
||
|
}
|
||
|
|
||
|
lcd->DrawFilledRect( offset[ i ][ 0 ] + 17, offset[ i ][ 1 ] + 15, 60, 6, sdLogitechLCDSystem::BC_WHITE );
|
||
|
lcd->DrawFilledRect( offset[ i ][ 0 ] + 18, offset[ i ][ 1 ] + 16, 58, 4, sdLogitechLCDSystem::BC_BLACK );
|
||
|
lcd->DrawFilledRect( offset[ i ][ 0 ] + 19, offset[ i ][ 1 ] + 17, 56 * table.GetPercent( profIndex ), 2, sdLogitechLCDSystem::BC_WHITE );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::AddChatLine
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::AddChatLine( const wchar_t* text ) {
|
||
|
if ( rules == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
rules->AddChatLine( sdGameRules::CHAT_MODE_MESSAGE, colorWhite, L"%ls", text );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::CreateStatusResponseDict
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::CreateStatusResponseDict( const idDict& serverInfo, idDict& statusResponseDict ) {
|
||
|
statusResponseDict.SetBool( "si_teamDamage", serverInfo.GetBool( "si_teamDamage" ) );
|
||
|
statusResponseDict.SetBool( "si_needPass", serverInfo.GetBool( "si_needPass" ) );
|
||
|
statusResponseDict.Set( "si_rules", serverInfo.GetString( "si_rules" ) );
|
||
|
statusResponseDict.SetBool( "si_teamForceBalance", serverInfo.GetBool( "si_teamForceBalance" ) );
|
||
|
statusResponseDict.SetBool( "si_allowLateJoin", serverInfo.GetBool( "si_allowLateJoin" ) );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::WriteExtendedProbeData
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::WriteExtendedProbeData( idBitMsg& msg ) {
|
||
|
sdStatsTracker& tracker = sdGlobalStatsTracker::GetInstance();
|
||
|
sdPlayerStatEntry* totalKillsStat = tracker.GetStat( tracker.AllocStat( "total_kills", sdNetStatKeyValue::SVT_INT ) );
|
||
|
sdPlayerStatEntry* totalDeathsStat = tracker.GetStat( tracker.AllocStat( "total_deaths", sdNetStatKeyValue::SVT_INT ) );
|
||
|
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
idPlayer* player = GetClient( i );
|
||
|
if ( player == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
sdTeamInfo* team = player->GetGameTeam();
|
||
|
|
||
|
msg.WriteByte( i );
|
||
|
msg.WriteFloat( player->GetProficiencyTable().GetXP() );
|
||
|
msg.WriteString( team != NULL ? team->GetLookupName() : "" );
|
||
|
msg.WriteLong( totalKillsStat->GetValue( i ).GetInt() );
|
||
|
msg.WriteLong( totalDeathsStat->GetValue( i ).GetInt() );
|
||
|
}
|
||
|
msg.WriteByte( MAX_CLIENTS );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetProbeTime
|
||
|
============
|
||
|
*/
|
||
|
int idGameLocal::GetProbeTime() const {
|
||
|
if( rules == NULL ) {
|
||
|
return 0;
|
||
|
}
|
||
|
return rules->GetGameTime();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetProbeState
|
||
|
============
|
||
|
*/
|
||
|
byte idGameLocal::GetProbeState() const {
|
||
|
if( rules == NULL ) {
|
||
|
return false;
|
||
|
}
|
||
|
return rules->GetProbeState();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetRulesInstance
|
||
|
============
|
||
|
*/
|
||
|
sdGameRules* idGameLocal::GetRulesInstance( const char* type ) const {
|
||
|
rulesMap_t::ConstIterator iter = rulesCache.Find( type );
|
||
|
if( iter == rulesCache.End() ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
return iter->second;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idGameLocal::DownloadRequest
|
||
|
===============
|
||
|
*/
|
||
|
bool idGameLocal::DownloadRequest( const char* IP, const char* guid, const char* paks, char urls[ MAX_STRING_CHARS ] ) {
|
||
|
if ( net_serverDownload.GetInteger() == 0 ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( net_serverDownload.GetInteger() == 1 ) {
|
||
|
const char* serverURL = si_serverURL.GetString();
|
||
|
// 1: single URL redirect
|
||
|
if ( idStr::Length( serverURL ) == 0 ) {
|
||
|
common->Warning( "si_serverURL not set" );
|
||
|
return false;
|
||
|
}
|
||
|
idStr::snPrintf( urls, MAX_STRING_CHARS, "1;%s", serverURL );
|
||
|
return true;
|
||
|
} else {
|
||
|
// 2: table of pak URLs
|
||
|
// 3: table of pak URLs with built-in http server
|
||
|
// first token is the game pak if requested, empty if not requested by the client
|
||
|
// there may be empty tokens for paks the server couldn't pinpoint - the order matters
|
||
|
idStr reply = "2;";
|
||
|
idStrList dlTable, pakList;
|
||
|
bool matchAll = false;
|
||
|
int i, j;
|
||
|
|
||
|
if ( !idStr::Cmp( net_serverDlTable.GetString(), "*" ) ) {
|
||
|
matchAll = true;
|
||
|
} else {
|
||
|
idSplitStringIntoList( dlTable, net_serverDlTable.GetString(), ";" );
|
||
|
}
|
||
|
idSplitStringIntoList( pakList, paks, ";" );
|
||
|
|
||
|
for ( i = 0; i < pakList.Num(); i++ ) {
|
||
|
if ( i > 0 ) {
|
||
|
reply += ";";
|
||
|
}
|
||
|
if ( pakList[ i ][ 0 ] == '\0' ) {
|
||
|
if ( i == 0 ) {
|
||
|
// pak 0 will always miss when client doesn't ask for game bin
|
||
|
common->DPrintf( "no game pak request\n" );
|
||
|
} else {
|
||
|
common->DPrintf( "no pak %d\n", i );
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
idStr url = net_serverDlBaseURL.GetString();
|
||
|
if ( url.Length() == 0 ) {
|
||
|
if ( net_serverDownload.GetInteger() == 2 ) {
|
||
|
common->Warning( "net_serverDownload == 2 and net_serverDlBaseURL not set" );
|
||
|
} else {
|
||
|
url = cvarSystem->GetCVarString( "net_httpServerBaseURL" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( matchAll ) {
|
||
|
url.AppendPath( pakList[ i ] );
|
||
|
reply += url;
|
||
|
common->Printf( "download for %s: %s\n", IP, url.c_str() );
|
||
|
} else {
|
||
|
for ( j = 0; j < dlTable.Num(); j++ ) {
|
||
|
if ( !pakList[ i ].Icmp( dlTable[ j ] ) ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ( j == dlTable.Num() ) {
|
||
|
common->Printf( "download for %s: pak not matched: %s\n", IP, pakList[ i ].c_str() );
|
||
|
} else {
|
||
|
url.AppendPath( dlTable[ j ] );
|
||
|
reply += url;
|
||
|
common->Printf( "download for %s: %s\n", IP, url.c_str() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idStr::Copynz( urls, reply, MAX_STRING_CHARS );
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idGameLocal::HTTPRequest
|
||
|
===============
|
||
|
*/
|
||
|
bool idGameLocal::HTTPRequest( const char *IP, const char *file, bool isGamePak ) {
|
||
|
idStrList dlTable;
|
||
|
int i;
|
||
|
|
||
|
if ( !idStr::Cmp( net_serverDlTable.GetString(), "*" ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
idSplitStringIntoList( dlTable, net_serverDlTable.GetString(), ";" );
|
||
|
while ( *file == '/' ) ++file; // net_serverDlTable doesn't include the initial /
|
||
|
|
||
|
for ( i = 0; i < dlTable.Num(); i++ ) {
|
||
|
if ( !dlTable[ i ].Icmp( file ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::EnablePlayerHeadModels
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::EnablePlayerHeadModels( void ) {
|
||
|
// enable the relevant player head models
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
idPlayer* player = GetClient( i );
|
||
|
if ( player == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
bool enable = true;
|
||
|
if ( player->IsSpectator() ) {
|
||
|
enable = false;
|
||
|
}
|
||
|
if ( player->GetHealth() <= 0 ) {
|
||
|
enable = false;
|
||
|
}
|
||
|
idEntity* proxy = player->GetProxyEntity();
|
||
|
if ( proxy != NULL ) {
|
||
|
if ( !proxy->GetUsableInterface()->GetAllowPlayerDamage( player ) ) {
|
||
|
enable = false;
|
||
|
}
|
||
|
}
|
||
|
if ( enable ) {
|
||
|
player->GetPlayerPhysics().EnableHeadClipModel();
|
||
|
} else {
|
||
|
player->GetPlayerPhysics().DisableHeadClipModel();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::DisablePlayerHeadModels
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::DisablePlayerHeadModels( void ) {
|
||
|
// disable the player head models
|
||
|
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
idPlayer* player = GetClient( i );
|
||
|
if ( player == NULL ) {
|
||
|
continue;
|
||
|
}
|
||
|
player->GetPlayerPhysics().DisableHeadClipModel();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetDemoName
|
||
|
================
|
||
|
*/
|
||
|
idCVar g_autoDemoNameFormat( "g_autoDemoNameFormat", "$year$$month$$day$_$hour$$min$$sec$_$map$_$rules$_$name$_build_$srcrev$_$mediarev$.ndm", CVAR_GAME | CVAR_PROFILE | CVAR_ARCHIVE, "demo name format: date - $year$, $month$, $day$ time - $hour$, $min$, $sec$ map name - $map$ ruleset info - $rules$ player name - $name$ versions - $srcrev$ $mediarev$" );
|
||
|
|
||
|
void idGameLocal::GetDemoName( idStr& output ) {
|
||
|
idStr name = "na";
|
||
|
if ( gameLocal.isClient ) {
|
||
|
idPlayer* localPlayer = gameLocal.GetLocalPlayer();
|
||
|
if ( localPlayer != NULL ) {
|
||
|
name = localPlayer->userInfo.name;
|
||
|
name.RemoveColors();
|
||
|
}
|
||
|
} else {
|
||
|
name = "server";
|
||
|
}
|
||
|
|
||
|
// just in case
|
||
|
name.ReplaceChar( '/', '_' );
|
||
|
name.ReplaceChar( '\\', '_' );
|
||
|
|
||
|
sysTime_t time;
|
||
|
sys->RealTime( &time );
|
||
|
|
||
|
idStr mapStr = mapFileName.c_str();
|
||
|
mapStr.ReplaceChar( '/', '_' );
|
||
|
mapStr.ReplaceFirst( "maps_", "" ); // the maps tag is boring. die.
|
||
|
mapStr.StripFileExtension();
|
||
|
|
||
|
|
||
|
output = g_autoDemoNameFormat.GetString();
|
||
|
|
||
|
// date
|
||
|
output.Replace( "$year$", va( "%d", 1900 + time.tm_year ) );
|
||
|
output.Replace( "$month$", va( "%02d", 1 + time.tm_mon ) );
|
||
|
output.Replace( "$day$", va( "%02d", time.tm_mday ) );
|
||
|
|
||
|
// time
|
||
|
output.Replace( "$hour$", va( "%02d", time.tm_hour ) );
|
||
|
output.Replace( "$min$", va( "%02d", time.tm_min ) );
|
||
|
output.Replace( "$sec$", va( "%02d", time.tm_sec ) );
|
||
|
|
||
|
// other
|
||
|
output.Replace( "$map$", mapStr.c_str() );
|
||
|
output.Replace( "$rules$", rules->GetDemoNameInfo() );
|
||
|
output.Replace( "$name$", name.c_str() );
|
||
|
output.Replace( "$srcrev$", va( "%d", ENGINE_SRC_REVISION ) );
|
||
|
output.Replace( "$mediarev$", va( "%d", ENGINE_MEDIA_REVISION ) );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::StartAutorecording
|
||
|
============
|
||
|
*/
|
||
|
idCVar g_autoRecordDemos( "g_autoRecordDemos", "0", CVAR_GAME | CVAR_PROFILE | CVAR_ARCHIVE, "automatically start & stop demos at the start & end of a map" );
|
||
|
|
||
|
void idGameLocal::StartAutorecording() {
|
||
|
if ( sdDemoManager::GetInstance().InPlayBack() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( g_autoRecordDemos.GetBool() && !isAutorecording ) {
|
||
|
idStr demoName = "";
|
||
|
gameLocal.GetDemoName( demoName );
|
||
|
|
||
|
if ( demoName.Length() > 0 ) {
|
||
|
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, va( "recordNetDemo \"%s\"\n", demoName.c_str() ) );
|
||
|
isAutorecording = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::StopAutorecording
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::StopAutorecording() {
|
||
|
if ( sdDemoManager::GetInstance().InPlayBack() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( isAutorecording ) {
|
||
|
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "stopNetDemo\n" );
|
||
|
isAutorecording = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameLocal::GetScoreboardShotName
|
||
|
================
|
||
|
*/
|
||
|
idCVar g_autoScreenshotNameFormat( "g_autoScreenshotNameFormat", "screenshots/scoreboard_$year$$month$$day$_$hour$$min$$sec$_$map$_$rules$_$name$_build_$srcrev$_$mediarev$.tga", CVAR_GAME | CVAR_PROFILE | CVAR_ARCHIVE, "auto screenshot name format: date - $year$, $month$, $day$ time - $hour$, $min$, $sec$ map name - $map$ ruleset info - $rules$ player name - $name$ versions - $srcrev$ $mediarev$" );
|
||
|
|
||
|
void idGameLocal::GetScoreboardShotName( idStr& output ) {
|
||
|
idStr name = "na";
|
||
|
if ( gameLocal.isClient ) {
|
||
|
idPlayer* localPlayer = gameLocal.GetLocalPlayer();
|
||
|
if ( localPlayer != NULL ) {
|
||
|
name = localPlayer->userInfo.name;
|
||
|
name.RemoveColors();
|
||
|
}
|
||
|
} else {
|
||
|
name = "server";
|
||
|
}
|
||
|
|
||
|
// just in case
|
||
|
name.ReplaceChar( '/', '_' );
|
||
|
name.ReplaceChar( '\\', '_' );
|
||
|
|
||
|
sysTime_t time;
|
||
|
sys->RealTime( &time );
|
||
|
|
||
|
idStr mapStr = mapFileName.c_str();
|
||
|
mapStr.ReplaceFirst( "maps_", "" ); // the maps tag is boring. die.
|
||
|
mapStr.ReplaceChar( '/', '_' );
|
||
|
mapStr.StripFileExtension();
|
||
|
|
||
|
|
||
|
output = g_autoScreenshotNameFormat.GetString();
|
||
|
|
||
|
// date
|
||
|
output.Replace( "$year$", va( "%d", 1900 + time.tm_year ) );
|
||
|
output.Replace( "$month$", va( "%02d", 1 + time.tm_mon ) );
|
||
|
output.Replace( "$day$", va( "%02d", time.tm_mday ) );
|
||
|
|
||
|
// time
|
||
|
output.Replace( "$hour$", va( "%02d", time.tm_hour ) );
|
||
|
output.Replace( "$min$", va( "%02d", time.tm_min ) );
|
||
|
output.Replace( "$sec$", va( "%02d", time.tm_sec ) );
|
||
|
|
||
|
// other
|
||
|
output.Replace( "$map$", mapStr.c_str() );
|
||
|
output.Replace( "$rules$", rules->GetDemoNameInfo() );
|
||
|
output.Replace( "$name$", name.c_str() );
|
||
|
output.Replace( "$srcrev$", va( "%d", ENGINE_SRC_REVISION ) );
|
||
|
output.Replace( "$mediarev$", va( "%d", ENGINE_MEDIA_REVISION ) );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::OnEndGameScoreboardActive
|
||
|
============
|
||
|
*/
|
||
|
idCVar g_autoScreenshot( "g_autoScreenshot", "0", CVAR_BOOL | CVAR_GAME | CVAR_PROFILE | CVAR_ARCHIVE, "automatically take a screenshot of the scoreboard at the end of a map" );
|
||
|
|
||
|
void idGameLocal::OnEndGameScoreboardActive() {
|
||
|
if ( !hasTakenScoreShot && g_autoScreenshot.GetBool() ) {
|
||
|
idStr shotName;
|
||
|
GetScoreboardShotName( shotName );
|
||
|
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, va( "screenshot \"%s\"\n", shotName.c_str() ) );
|
||
|
hasTakenScoreShot = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::IsMultiPlayer
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::IsMultiPlayer( void ) {
|
||
|
return isClient || networkSystem->IsDedicated();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::IsMetaDataValidForPlay
|
||
|
============
|
||
|
*/
|
||
|
bool idGameLocal::IsMetaDataValidForPlay( const metaDataContext_t& context, bool checkBrowserStatus ) {
|
||
|
const idDict& dict = *context.meta;
|
||
|
if ( checkBrowserStatus ) {
|
||
|
if ( !dict.GetBool( "show_in_browser" ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.IsMultiPlayer() ) {
|
||
|
if ( dict.GetBool( "sp_only" ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
} else {
|
||
|
if ( dict.GetBool( "mp_only" ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::ChangeLocalSpectateClient
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::ChangeLocalSpectateClient( int spectateeNum ) {
|
||
|
if ( serverIsRepeater ) {
|
||
|
repeaterClientFollowIndex = spectateeNum;
|
||
|
idWeapon::UpdateWeaponVisibility();
|
||
|
OnLocalViewPlayerChanged();
|
||
|
} else if ( isClient ) {
|
||
|
sdReliableClientMessage outMsg( GAME_RELIABLE_CMESSAGE_SETSPECTATECLIENT );
|
||
|
outMsg.WriteByte( spectateeNum );
|
||
|
outMsg.Send();
|
||
|
} else {
|
||
|
idPlayer* localPlayer = GetLocalPlayer();
|
||
|
if ( localPlayer != NULL ) {
|
||
|
localPlayer->OnSetClientSpectatee( GetClient( spectateeNum ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::GetRepeaterFollowClientIndex
|
||
|
============
|
||
|
*/
|
||
|
int idGameLocal::GetRepeaterFollowClientIndex( void ) const {
|
||
|
if ( repeaterClientFollowIndex < -1 || repeaterClientFollowIndex >= MAX_ASYNC_CLIENTS ) {
|
||
|
repeaterClientFollowIndex = -1;
|
||
|
idWeapon::UpdateWeaponVisibility();
|
||
|
}
|
||
|
if ( repeaterClientFollowIndex != -1 ) {
|
||
|
if ( GetClient( repeaterClientFollowIndex ) == NULL ) {
|
||
|
repeaterClientFollowIndex = -1;
|
||
|
idWeapon::UpdateWeaponVisibility();
|
||
|
}
|
||
|
}
|
||
|
return repeaterClientFollowIndex;
|
||
|
}
|
||
|
|
||
|
#ifndef _XENON
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::UpdateGameSession
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::UpdateGameSession( void ) {
|
||
|
// handle game session advertising
|
||
|
if ( gameLocal.GetLocalPlayer() == NULL && sdnet.NeedsGameSession() ) {
|
||
|
if ( networkService->GetState() == sdNetService::SS_INITIALIZED ) {
|
||
|
sdnet.Connect();
|
||
|
} else if ( networkService->GetState() == sdNetService::SS_ONLINE ) {
|
||
|
if ( networkService->GetDedicatedServerState() == sdNetService::DS_OFFLINE ) {
|
||
|
// if ranked, don't automatically try to connect anymore if we've had a duplicate auth
|
||
|
// for dedicated servers using the key distribution, report and request a different key (TTimo)
|
||
|
if ( !networkSystem->IsRankedServer() || networkService->GetDisconnectReason() != sdNetService::DR_DUPLICATE_AUTH ) {
|
||
|
sdnet.SignInDedicated();
|
||
|
}
|
||
|
} else if ( networkService->GetDedicatedServerState() == sdNetService::DS_ONLINE ) {
|
||
|
if ( !sdnet.HasGameSession() ) {
|
||
|
reservedClientSlots.Clear(); // the session ID will change, so old invites aren't valid anymore anyway
|
||
|
sdnet.StartGameSession();
|
||
|
} else {
|
||
|
sdnet.UpdateGameSession( false, true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif // _XENON
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::StartSendingBanList
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::StartSendingBanList( idPlayer* player ) {
|
||
|
if ( player == NULL || gameLocal.IsLocalPlayer( player ) ) {
|
||
|
guidFile.ListBans();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
clientLastBanIndexReceived[ player->entityNumber ] = -1;
|
||
|
|
||
|
SendBanList( player );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
idGameLocal::SendBanList
|
||
|
============
|
||
|
*/
|
||
|
void idGameLocal::SendBanList( idPlayer* player ) {
|
||
|
assert( player != NULL );
|
||
|
|
||
|
sdReliableServerMessage msg( GAME_RELIABLE_SMESSAGE_BANLISTMESSAGE );
|
||
|
bool done = guidFile.WriteBans( clientLastBanIndexReceived[ player->entityNumber ], msg );
|
||
|
msg.Send( sdReliableMessageClientInfo( player->entityNumber ) );
|
||
|
if ( done ) {
|
||
|
clientLastBanIndexReceived[ player->entityNumber ] = -1;
|
||
|
|
||
|
sdReliableServerMessage msg( GAME_RELIABLE_SMESSAGE_BANLISTFINISHED );
|
||
|
msg.Send( sdReliableMessageClientInfo( player->entityNumber ) );
|
||
|
}
|
||
|
}
|