Files
HL2Overcharged/game/server/world.cpp
2025-05-21 21:20:08 +03:00

734 lines
18 KiB
C++

//========= 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;
}