//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Precaches and defs for entities and other data that must always be available. // // $NoKeywords: $ //===========================================================================// #include "cbase.h" #include "soundent.h" #include "client.h" #include "decals.h" #include "EnvMessage.h" #include "player.h" #include "gamerules.h" #include "teamplay_gamerules.h" #include "physics.h" #include "isaverestore.h" #include "activitylist.h" #include "eventlist.h" #include "eventqueue.h" #include "ai_network.h" #include "ai_schedule.h" #include "ai_networkmanager.h" #include "ai_utils.h" #include "basetempentity.h" #include "world.h" #include "mempool.h" #include "igamesystem.h" #include "engine/IEngineSound.h" #include "globals.h" #include "engine/IStaticPropMgr.h" #include "particle_parse.h" #include "globalstate.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern CBaseEntity *g_pLastSpawn; void InitBodyQue(void); extern void W_Precache(void); extern void ActivityList_Free( void ); extern CUtlMemoryPool g_EntityListPool; #define SF_DECAL_NOTINDEATHMATCH 2048 class CDecal : public CPointEntity { public: DECLARE_CLASS( CDecal, CPointEntity ); void Spawn( void ); bool KeyValue( const char *szKeyName, const char *szValue ); // Need to apply static decals here to get them into the signon buffer for the server appropriately virtual void Activate(); void TriggerDecal( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); // Input handlers. void InputActivate( inputdata_t &inputdata ); DECLARE_DATADESC(); public: int m_nTexture; bool m_bLowPriority; private: void StaticDecal( void ); }; BEGIN_DATADESC( CDecal ) DEFINE_FIELD( m_nTexture, FIELD_INTEGER ), DEFINE_KEYFIELD( m_bLowPriority, FIELD_BOOLEAN, "LowPriority" ), // Don't mark as FDECAL_PERMANENT so not save/restored and will be reused on the client preferentially // Function pointers DEFINE_FUNCTION( StaticDecal ), DEFINE_FUNCTION( TriggerDecal ), DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ), END_DATADESC() LINK_ENTITY_TO_CLASS( infodecal, CDecal ); // UNDONE: These won't get sent to joining players in multi-player void CDecal::Spawn( void ) { if ( m_nTexture < 0 || (gpGlobals->deathmatch && HasSpawnFlags( SF_DECAL_NOTINDEATHMATCH )) ) { UTIL_Remove( this ); return; } } void CDecal::Activate() { BaseClass::Activate(); if ( !GetEntityName() ) { StaticDecal(); } else { // if there IS a targetname, the decal sprays itself on when it is triggered. SetThink ( &CDecal::SUB_DoNothing ); SetUse(&CDecal::TriggerDecal); } } void CDecal::TriggerDecal ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { // this is set up as a USE function for info_decals that have targetnames, so that the // decal doesn't get applied until it is fired. (usually by a scripted sequence) trace_t trace; int entityIndex; UTIL_TraceLine( GetAbsOrigin() - Vector(5,5,5), GetAbsOrigin() + Vector(5,5,5), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trace ); entityIndex = trace.m_pEnt ? trace.m_pEnt->entindex() : 0; CBroadcastRecipientFilter filter; te->BSPDecal( filter, 0.0, &GetAbsOrigin(), entityIndex, m_nTexture ); SetThink( &CDecal::SUB_Remove ); SetNextThink( gpGlobals->curtime + 0.1f ); } void CDecal::InputActivate( inputdata_t &inputdata ) { TriggerDecal( inputdata.pActivator, inputdata.pCaller, USE_ON, 0 ); } void CDecal::StaticDecal( void ) { class CTraceFilterValidForDecal : public CTraceFilterSimple { public: CTraceFilterValidForDecal(const IHandleEntity *passentity, int collisionGroup ) : CTraceFilterSimple( passentity, collisionGroup ) { } virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask ) { static const char *ppszIgnoredClasses[] = { "weapon_*", "item_*", "prop_ragdoll", "prop_dynamic", "prop_static", "prop_physics", "npc_bullseye", // Tracker 15335 }; CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity ); // Tracker 15335: Never impact decals against entities which are not rendering, either. if ( pEntity->IsEffectActive( EF_NODRAW ) ) return false; for ( int i = 0; i < ARRAYSIZE(ppszIgnoredClasses); i++ ) { if ( pEntity->ClassMatches( ppszIgnoredClasses[i] ) ) return false; } return CTraceFilterSimple::ShouldHitEntity( pServerEntity, contentsMask ); } }; trace_t trace; CTraceFilterValidForDecal traceFilter( this, COLLISION_GROUP_NONE ); int entityIndex, modelIndex = 0; Vector position = GetAbsOrigin(); UTIL_TraceLine( position - Vector(5,5,5), position + Vector(5,5,5), MASK_SOLID, &traceFilter, &trace ); bool canDraw = true; entityIndex = trace.m_pEnt ? (short)trace.m_pEnt->entindex() : 0; if ( entityIndex ) { CBaseEntity *ent = trace.m_pEnt; if ( ent ) { modelIndex = ent->GetModelIndex(); VectorITransform( GetAbsOrigin(), ent->EntityToWorldTransform(), position ); canDraw = ( modelIndex != 0 ); if ( !canDraw ) { Warning( "Suppressed StaticDecal which would have hit entity %i (class:%s, name:%s) with modelindex = 0\n", ent->entindex(), ent->GetClassname(), STRING( ent->GetEntityName() ) ); } } } if ( canDraw ) { engine->StaticDecal( position, m_nTexture, entityIndex, modelIndex, m_bLowPriority ); } SUB_Remove(); } bool CDecal::KeyValue( const char *szKeyName, const char *szValue ) { if (FStrEq(szKeyName, "texture")) { // FIXME: should decals all be preloaded? m_nTexture = UTIL_PrecacheDecal( szValue, true ); // Found if (m_nTexture >= 0 ) return true; Warning( "Can't find decal %s\n", szValue ); } else { return BaseClass::KeyValue( szKeyName, szValue ); } return true; } //----------------------------------------------------------------------------- // Purpose: Projects a decal against a prop //----------------------------------------------------------------------------- class CProjectedDecal : public CPointEntity { public: DECLARE_CLASS( CProjectedDecal, CPointEntity ); void Spawn( void ); bool KeyValue( const char *szKeyName, const char *szValue ); // Need to apply static decals here to get them into the signon buffer for the server appropriately virtual void Activate(); void TriggerDecal( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); // Input handlers. void InputActivate( inputdata_t &inputdata ); DECLARE_DATADESC(); public: int m_nTexture; float m_flDistance; private: void ProjectDecal( CRecipientFilter& filter ); void StaticDecal( void ); }; BEGIN_DATADESC( CProjectedDecal ) DEFINE_FIELD( m_nTexture, FIELD_INTEGER ), DEFINE_KEYFIELD( m_flDistance, FIELD_FLOAT, "Distance" ), // Function pointers DEFINE_FUNCTION( StaticDecal ), DEFINE_FUNCTION( TriggerDecal ), DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ), END_DATADESC() LINK_ENTITY_TO_CLASS( info_projecteddecal, CProjectedDecal ); // UNDONE: These won't get sent to joining players in multi-player void CProjectedDecal::Spawn( void ) { if ( m_nTexture < 0 || (gpGlobals->deathmatch && HasSpawnFlags( SF_DECAL_NOTINDEATHMATCH )) ) { UTIL_Remove( this ); return; } } void CProjectedDecal::Activate() { BaseClass::Activate(); if ( !GetEntityName() ) { StaticDecal(); } else { // if there IS a targetname, the decal sprays itself on when it is triggered. SetThink ( &CProjectedDecal::SUB_DoNothing ); SetUse(&CProjectedDecal::TriggerDecal); } } void CProjectedDecal::InputActivate( inputdata_t &inputdata ) { TriggerDecal( inputdata.pActivator, inputdata.pCaller, USE_ON, 0 ); } void CProjectedDecal::ProjectDecal( CRecipientFilter& filter ) { te->ProjectDecal( filter, 0.0, &GetAbsOrigin(), &GetAbsAngles(), m_flDistance, m_nTexture ); } void CProjectedDecal::TriggerDecal ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { CBroadcastRecipientFilter filter; ProjectDecal( filter ); SetThink( &CProjectedDecal::SUB_Remove ); SetNextThink( gpGlobals->curtime + 0.1f ); } void CProjectedDecal::StaticDecal( void ) { CBroadcastRecipientFilter initFilter; initFilter.MakeInitMessage(); ProjectDecal( initFilter ); SUB_Remove(); } bool CProjectedDecal::KeyValue( const char *szKeyName, const char *szValue ) { if (FStrEq(szKeyName, "texture")) { // FIXME: should decals all be preloaded? m_nTexture = UTIL_PrecacheDecal( szValue, true ); // Found if (m_nTexture >= 0 ) return true; Warning( "Can't find decal %s\n", szValue ); } else { return BaseClass::KeyValue( szKeyName, szValue ); } return true; } //======================= // CWorld // // This spawns first when each level begins. //======================= LINK_ENTITY_TO_CLASS( worldspawn, CWorld ); BEGIN_DATADESC( CWorld ) DEFINE_FIELD( m_flWaveHeight, FIELD_FLOAT ), // keyvalues are parsed from map, but not saved/loaded DEFINE_KEYFIELD( m_iszChapterTitle, FIELD_STRING, "chaptertitle" ), DEFINE_KEYFIELD( m_bStartDark, FIELD_BOOLEAN, "startdark" ), DEFINE_KEYFIELD( m_bDisplayTitle, FIELD_BOOLEAN, "gametitle" ), DEFINE_FIELD( m_WorldMins, FIELD_VECTOR ), DEFINE_FIELD( m_WorldMaxs, FIELD_VECTOR ), #ifdef _X360 DEFINE_KEYFIELD( m_flMaxOccludeeArea, FIELD_FLOAT, "maxoccludeearea_x360" ), DEFINE_KEYFIELD( m_flMinOccluderArea, FIELD_FLOAT, "minoccluderarea_x360" ), #else DEFINE_KEYFIELD( m_flMaxOccludeeArea, FIELD_FLOAT, "maxoccludeearea" ), DEFINE_KEYFIELD( m_flMinOccluderArea, FIELD_FLOAT, "minoccluderarea" ), #endif DEFINE_KEYFIELD( m_flMaxPropScreenSpaceWidth, FIELD_FLOAT, "maxpropscreenwidth" ), DEFINE_KEYFIELD( m_flMinPropScreenSpaceWidth, FIELD_FLOAT, "minpropscreenwidth" ), DEFINE_KEYFIELD( m_iszDetailSpriteMaterial, FIELD_STRING, "detailmaterial" ), DEFINE_KEYFIELD( m_bColdWorld, FIELD_BOOLEAN, "coldworld" ), END_DATADESC() // SendTable stuff. IMPLEMENT_SERVERCLASS_ST(CWorld, DT_WORLD) SendPropFloat (SENDINFO(m_flWaveHeight), 8, SPROP_ROUNDUP, 0.0f, 8.0f), SendPropVector (SENDINFO(m_WorldMins), -1, SPROP_COORD), SendPropVector (SENDINFO(m_WorldMaxs), -1, SPROP_COORD), SendPropInt (SENDINFO(m_bStartDark), 1, SPROP_UNSIGNED ), SendPropFloat (SENDINFO(m_flMaxOccludeeArea), 0, SPROP_NOSCALE ), SendPropFloat (SENDINFO(m_flMinOccluderArea), 0, SPROP_NOSCALE ), SendPropFloat (SENDINFO(m_flMaxPropScreenSpaceWidth), 0, SPROP_NOSCALE ), SendPropFloat (SENDINFO(m_flMinPropScreenSpaceWidth), 0, SPROP_NOSCALE ), SendPropStringT (SENDINFO(m_iszDetailSpriteMaterial) ), SendPropInt (SENDINFO(m_bColdWorld), 1, SPROP_UNSIGNED ), END_SEND_TABLE() // // Just to ignore the "wad" field. // bool CWorld::KeyValue( const char *szKeyName, const char *szValue ) { if ( FStrEq(szKeyName, "skyname") ) { // Sent over net now. ConVarRef skyname( "sv_skyname" ); skyname.SetValue( szValue ); } else if ( FStrEq(szKeyName, "newunit") ) { // Single player only. Clear save directory if set if ( atoi(szValue) ) { extern void Game_SetOneWayTransition(); Game_SetOneWayTransition(); } } else if ( FStrEq(szKeyName, "world_mins") ) { Vector vec; sscanf( szValue, "%f %f %f", &vec.x, &vec.y, &vec.z ); m_WorldMins = vec; } else if ( FStrEq(szKeyName, "world_maxs") ) { Vector vec; sscanf( szValue, "%f %f %f", &vec.x, &vec.y, &vec.z ); m_WorldMaxs = vec; } else return BaseClass::KeyValue( szKeyName, szValue ); return true; } extern bool g_fGameOver; static CWorld *g_WorldEntity = NULL; CWorld* GetWorldEntity() { return g_WorldEntity; } CWorld::CWorld( ) { AddEFlags( EFL_NO_AUTO_EDICT_ATTACH | EFL_KEEP_ON_RECREATE_ENTITIES ); NetworkProp()->AttachEdict( INDEXENT(RequiredEdictIndex()) ); ActivityList_Init(); EventList_Init(); SetSolid( SOLID_BSP ); SetMoveType( MOVETYPE_NONE ); m_bColdWorld = false; } CWorld::~CWorld( ) { EventList_Free(); ActivityList_Free(); if ( g_pGameRules ) { g_pGameRules->LevelShutdown(); delete g_pGameRules; g_pGameRules = NULL; } g_WorldEntity = NULL; } //------------------------------------------------------------------------------ // Purpose : Add a decal to the world // Input : // Output : //------------------------------------------------------------------------------ void CWorld::DecalTrace( trace_t *pTrace, char const *decalName) { int index = decalsystem->GetDecalIndexForName( decalName ); if ( index < 0 ) return; CBroadcastRecipientFilter filter; if ( pTrace->hitbox != 0 ) { te->Decal( filter, 0.0f, &pTrace->endpos, &pTrace->startpos, 0, pTrace->hitbox, index ); } else { te->WorldDecal( filter, 0.0, &pTrace->endpos, index ); } } void CWorld::RegisterSharedActivities( void ) { ActivityList_RegisterSharedActivities(); } void CWorld::RegisterSharedEvents( void ) { EventList_RegisterSharedEvents(); } void CWorld::Spawn( void ) { SetLocalOrigin( vec3_origin ); SetLocalAngles( vec3_angle ); // NOTE: SHOULD NEVER BE ANYTHING OTHER THAN 1!!! SetModelIndex( 1 ); // world model SetModelName( AllocPooledString( modelinfo->GetModelName( GetModel() ) ) ); AddFlag( FL_WORLDBRUSH ); g_EventQueue.Init(); Precache( ); GlobalEntity_Add( "is_console", STRING(gpGlobals->mapname), ( IsConsole() ) ? GLOBAL_ON : GLOBAL_OFF ); GlobalEntity_Add( "is_pc", STRING(gpGlobals->mapname), ( !IsConsole() ) ? GLOBAL_ON : GLOBAL_OFF ); } static const char *g_DefaultLightstyles[] = { // 0 normal "m", // 1 FLICKER (first variety) "mmnmmommommnonmmonqnmmo", // 2 SLOW STRONG PULSE "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba", // 3 CANDLE (first variety) "mmmmmaaaaammmmmaaaaaabcdefgabcdefg", // 4 FAST STROBE "mamamamamama", // 5 GENTLE PULSE 1 "jklmnopqrstuvwxyzyxwvutsrqponmlkj", // 6 FLICKER (second variety) "nmonqnmomnmomomno", // 7 CANDLE (second variety) "mmmaaaabcdefgmmmmaaaammmaamm", // 8 CANDLE (third variety) "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa", // 9 SLOW STROBE (fourth variety) "aaaaaaaazzzzzzzz", // 10 FLUORESCENT FLICKER "mmamammmmammamamaaamammma", // 11 SLOW PULSE NOT FADE TO BLACK "abcdefghijklmnopqrrqponmlkjihgfedcba", // 12 UNDERWATER LIGHT MUTATION // this light only distorts the lightmap - no contribution // is made to the brightness of affected surfaces "mmnnmmnnnmmnn", }; const char *GetDefaultLightstyleString( int styleIndex ) { if ( styleIndex < ARRAYSIZE(g_DefaultLightstyles) ) { return g_DefaultLightstyles[styleIndex]; } return "m"; } void CWorld::Precache( void ) { g_WorldEntity = this; g_fGameOver = false; g_pLastSpawn = NULL; ConVarRef stepsize( "sv_stepsize" ); stepsize.SetValue( 18 ); ConVarRef roomtype( "room_type" ); roomtype.SetValue( 0 ); // Set up game rules Assert( !g_pGameRules ); if (g_pGameRules) { delete g_pGameRules; } InstallGameRules(); Assert( g_pGameRules ); g_pGameRules->Init(); CSoundEnt::InitSoundEnt(); // Only allow precaching between LevelInitPreEntity and PostEntity CBaseEntity::SetAllowPrecache( true ); IGameSystem::LevelInitPreEntityAllSystems( STRING( GetModelName() ) ); // Create the player resource g_pGameRules->CreateStandardEntities(); // UNDONE: Make most of these things server systems or precache_registers // ================================================= // Activities // ================================================= ActivityList_Free(); RegisterSharedActivities(); EventList_Free(); RegisterSharedEvents(); InitBodyQue(); // init sentence group playback stuff from sentences.txt. // ok to call this multiple times, calls after first are ignored. SENTENCEG_Init(); // Precache standard particle systems PrecacheStandardParticleSystems( ); // the area based ambient sounds MUST be the first precache_sounds // player precaches W_Precache (); // get weapon precaches ClientPrecache(); g_pGameRules->Precache(); // precache all temp ent stuff CBaseTempEntity::PrecacheTempEnts(); g_Language.SetValue( LANGUAGE_ENGLISH ); // TODO use VGUI to get current language if ( g_Language.GetInt() == LANGUAGE_GERMAN ) { PrecacheModel( "models/germangibs.mdl" ); } else { PrecacheModel( "models/gibs/hgibs.mdl" ); } PrecacheScriptSound( "BaseEntity.EnterWater" ); PrecacheScriptSound( "BaseEntity.ExitWater" ); // // Setup light animation tables. 'a' is total darkness, 'z' is maxbright. // for ( int i = 0; i < ARRAYSIZE(g_DefaultLightstyles); i++ ) { engine->LightStyle( i, GetDefaultLightstyleString(i) ); } // styles 32-62 are assigned by the light program for switchable lights // 63 testing engine->LightStyle(63, "a"); // ================================================= // Load and Init AI Networks // ================================================= CAI_NetworkManager::InitializeAINetworks(); // ================================================= // Load and Init AI Schedules // ================================================= g_AI_SchedulesManager.LoadAllSchedules(); // ================================================= // Initialize NPC Relationships // ================================================= g_pGameRules->InitDefaultAIRelationships(); CBaseCombatCharacter::InitInteractionSystem(); // Call all registered precachers. CPrecacheRegister::Precache(); if ( m_iszChapterTitle != NULL_STRING ) { DevMsg( 2, "Chapter title: %s\n", STRING(m_iszChapterTitle) ); CMessage *pMessage = (CMessage *)CBaseEntity::Create( "env_message", vec3_origin, vec3_angle, NULL ); if ( pMessage ) { pMessage->SetMessage( m_iszChapterTitle ); m_iszChapterTitle = NULL_STRING; // send the message entity a play message command, delayed by 1 second pMessage->AddSpawnFlags( SF_MESSAGE_ONCE ); pMessage->SetThink( &CMessage::SUB_CallUseToggle ); pMessage->SetNextThink( gpGlobals->curtime + 1.0f ); } } g_iszFuncBrushClassname = AllocPooledString("func_brush"); } //----------------------------------------------------------------------------- // Purpose: // Output : float //----------------------------------------------------------------------------- float GetRealTime() { return engine->Time(); } bool CWorld::GetDisplayTitle() const { return m_bDisplayTitle; } bool CWorld::GetStartDark() const { return m_bStartDark; } void CWorld::SetDisplayTitle( bool display ) { m_bDisplayTitle = display; } void CWorld::SetStartDark( bool startdark ) { m_bStartDark = startdark; } bool CWorld::IsColdWorld( void ) { return m_bColdWorld; }