This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
2020-08-04 13:13:01 -04:00

386 lines
12 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef EDICT_H
#define EDICT_H
#ifdef _WIN32
#pragma once
#endif
#include "bitvec.h"
#include "cmodel.h"
#include "const.h"
#include "engine/ICollideable.h"
#include "globalvars_base.h"
#include "iserverentity.h"
#include "iservernetworkable.h"
#include "mathlib/vector.h"
struct edict_t;
//-----------------------------------------------------------------------------
// Purpose: Defines the ways that a map can be loaded.
//-----------------------------------------------------------------------------
enum MapLoadType_t {
MapLoad_NewGame = 0,
MapLoad_LoadGame,
MapLoad_Transition,
MapLoad_Background,
};
//-----------------------------------------------------------------------------
// Purpose: Global variables shared between the engine and the game .dll
//-----------------------------------------------------------------------------
class CGlobalVars : public CGlobalVarsBase {
public:
CGlobalVars(bool bIsClient);
public:
// Current map
string_t mapname;
int mapversion;
string_t startspot;
MapLoadType_t eLoadType; // How the current map was loaded
bool bMapLoadFailed; // Map has failed to load, we need to kick back to the
// main menu
// game specific flags
bool deathmatch;
bool coop;
bool teamplay;
// current maxentities
int maxEntities;
int serverCount;
};
inline CGlobalVars::CGlobalVars(bool bIsClient) : CGlobalVarsBase(bIsClient) {
serverCount = 0;
}
class CPlayerState;
class IServerNetworkable;
class IServerEntity;
#define FL_EDICT_CHANGED \
(1 << 0) // Game DLL sets this when the entity state changes
// Mutually exclusive with FL_EDICT_PARTIAL_CHANGE.
#define FL_EDICT_FREE (1 << 1) // this edict if free for reuse
#define FL_EDICT_FULL (1 << 2) // this is a full server entity
#define FL_EDICT_FULLCHECK \
(0 << 0) // call ShouldTransmit() each time, this is a fake flag
#define FL_EDICT_ALWAYS (1 << 3) // always transmit this entity
#define FL_EDICT_DONTSEND (1 << 4) // don't transmit this entity
#define FL_EDICT_PVSCHECK \
(1 << 5) // always transmit entity, but cull against PVS
// Used by local network backdoor.
#define FL_EDICT_PENDING_DORMANT_CHECK (1 << 6)
// This is always set at the same time EFL_DIRTY_PVS_INFORMATION is set, but it
// gets cleared in a different place.
#define FL_EDICT_DIRTY_PVS_INFORMATION (1 << 7)
// This is used internally to edict_t to remember that it's carrying a
// "full change list" - all its properties might have changed their value.
#define FL_FULL_EDICT_CHANGED (1 << 8)
// Max # of variable changes we'll track in an entity before we treat it
// like they all changed.
#define MAX_CHANGE_OFFSETS 19
#define MAX_EDICT_CHANGE_INFOS 100
class CEdictChangeInfo {
public:
// Edicts remember the offsets of properties that change
unsigned short m_ChangeOffsets[MAX_CHANGE_OFFSETS];
unsigned short m_nChangeOffsets;
};
// Shared between engine and game DLL.
class CSharedEdictChangeInfo {
public:
CSharedEdictChangeInfo() { m_iSerialNumber = 1; }
// Matched against edict_t::m_iChangeInfoSerialNumber to determine if its
// change info is valid.
unsigned short m_iSerialNumber;
CEdictChangeInfo m_ChangeInfos[MAX_EDICT_CHANGE_INFOS];
unsigned short m_nChangeInfos; // How many are in use this frame.
};
extern CSharedEdictChangeInfo *g_pSharedChangeInfo;
class IChangeInfoAccessor {
public:
inline void SetChangeInfo(unsigned short info) { m_iChangeInfo = info; }
inline void SetChangeInfoSerialNumber(unsigned short sn) {
m_iChangeInfoSerialNumber = sn;
}
inline unsigned short GetChangeInfo() const { return m_iChangeInfo; }
inline unsigned short GetChangeInfoSerialNumber() const {
return m_iChangeInfoSerialNumber;
}
private:
unsigned short m_iChangeInfo;
unsigned short m_iChangeInfoSerialNumber;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
// NOTE: YOU CAN'T CHANGE THE LAYOUT OR SIZE OF CBASEEDICT AND REMAIN COMPATIBLE
// WITH HL2_VC6!!!!!
class CBaseEdict {
public:
// Returns an IServerEntity if FL_FULLEDICT is set or NULL if this
// is a lightweight networking entity.
IServerEntity *GetIServerEntity();
const IServerEntity *GetIServerEntity() const;
IServerNetworkable *GetNetworkable();
IServerUnknown *GetUnknown();
// Set when initting an entity. If it's only a networkable, this is false.
void SetEdict(IServerUnknown *pUnk, bool bFullEdict);
int AreaNum() const;
const char *GetClassName() const;
bool IsFree() const;
void SetFree();
void ClearFree();
bool HasStateChanged() const;
void ClearStateChanged();
void StateChanged();
void StateChanged(unsigned short offset);
void ClearTransmitState();
void SetChangeInfo(unsigned short info);
void SetChangeInfoSerialNumber(unsigned short sn);
unsigned short GetChangeInfo() const;
unsigned short GetChangeInfoSerialNumber() const;
public:
// NOTE: this is in the edict instead of being accessed by a virtual because
// the engine needs fast access to it. NOTE: YOU CAN'T CHANGE THE LAYOUT OR
// SIZE OF CBASEEDICT AND REMAIN COMPATIBLE WITH HL2_VC6!!!!!
#ifdef _XBOX
unsigned short m_fStateFlags;
#else
int m_fStateFlags;
#endif
// NOTE: this is in the edict instead of being accessed by a virtual because
// the engine needs fast access to it. int m_NetworkSerialNumber;
// NOTE: m_EdictIndex is an optimization since computing the edict index
// from a CBaseEdict* pointer otherwise requires divide-by-20. values for
// m_NetworkSerialNumber all fit within a 16-bit integer range, so we're
// repurposing the other 16 bits to cache off the index without changing
// the overall layout or size of this struct. existing mods compiled with
// a full 32-bit serial number field should still work. henryg 8/17/2011
#if VALVE_LITTLE_ENDIAN
short m_NetworkSerialNumber;
short m_EdictIndex;
#else
short m_EdictIndex;
short m_NetworkSerialNumber;
#endif
// NOTE: this is in the edict instead of being accessed by a virtual because
// the engine needs fast access to it.
IServerNetworkable *m_pNetworkable;
protected:
IServerUnknown *m_pUnk;
public:
IChangeInfoAccessor *GetChangeAccessor(); // The engine implements this and
// the game .dll implements as
const IChangeInfoAccessor *GetChangeAccessor()
const; // The engine implements this and the game .dll implements as
// as callback through to the engine!!!
// NOTE: YOU CAN'T CHANGE THE LAYOUT OR SIZE OF CBASEEDICT AND REMAIN
// COMPATIBLE WITH HL2_VC6!!!!! This breaks HL2_VC6!!!!! References a
// CEdictChangeInfo with a list of modified network props.
// unsigned short m_iChangeInfo;
// unsigned short m_iChangeInfoSerialNumber;
friend void InitializeEntityDLLFields(edict_t *pEdict);
};
//-----------------------------------------------------------------------------
// CBaseEdict inlines.
//-----------------------------------------------------------------------------
inline IServerEntity *CBaseEdict::GetIServerEntity() {
if (m_fStateFlags & FL_EDICT_FULL)
return (IServerEntity *)m_pUnk;
else
return 0;
}
inline bool CBaseEdict::IsFree() const {
return (m_fStateFlags & FL_EDICT_FREE) != 0;
}
inline bool CBaseEdict::HasStateChanged() const {
return (m_fStateFlags & FL_EDICT_CHANGED) != 0;
}
inline void CBaseEdict::ClearStateChanged() {
m_fStateFlags &= ~(FL_EDICT_CHANGED | FL_FULL_EDICT_CHANGED);
SetChangeInfoSerialNumber(0);
}
inline void CBaseEdict::StateChanged() {
// Note: this should only happen for properties in data tables that used
// some kind of pointer dereference. If the data is directly offsetable
m_fStateFlags |= (FL_EDICT_CHANGED | FL_FULL_EDICT_CHANGED);
SetChangeInfoSerialNumber(0);
}
inline void CBaseEdict::StateChanged(unsigned short offset) {
if (m_fStateFlags & FL_FULL_EDICT_CHANGED) return;
m_fStateFlags |= FL_EDICT_CHANGED;
IChangeInfoAccessor *accessor = GetChangeAccessor();
if (accessor->GetChangeInfoSerialNumber() ==
g_pSharedChangeInfo->m_iSerialNumber) {
// Ok, I still own this one.
CEdictChangeInfo *p =
&g_pSharedChangeInfo->m_ChangeInfos[accessor->GetChangeInfo()];
// Now add this offset to our list of changed variables.
for (unsigned short i = 0; i < p->m_nChangeOffsets; i++)
if (p->m_ChangeOffsets[i] == offset) return;
if (p->m_nChangeOffsets == MAX_CHANGE_OFFSETS) {
// Invalidate our change info.
accessor->SetChangeInfoSerialNumber(0);
m_fStateFlags |=
FL_FULL_EDICT_CHANGED; // So we don't get in here again.
} else {
p->m_ChangeOffsets[p->m_nChangeOffsets++] = offset;
}
} else {
if (g_pSharedChangeInfo->m_nChangeInfos == MAX_EDICT_CHANGE_INFOS) {
// Shucks.. have to mark the edict as fully changed because we don't
// have room to remember this change.
accessor->SetChangeInfoSerialNumber(0);
m_fStateFlags |= FL_FULL_EDICT_CHANGED;
} else {
// Get a new CEdictChangeInfo and fill it out.
accessor->SetChangeInfo(g_pSharedChangeInfo->m_nChangeInfos);
g_pSharedChangeInfo->m_nChangeInfos++;
accessor->SetChangeInfoSerialNumber(
g_pSharedChangeInfo->m_iSerialNumber);
CEdictChangeInfo *p =
&g_pSharedChangeInfo->m_ChangeInfos[accessor->GetChangeInfo()];
p->m_ChangeOffsets[0] = offset;
p->m_nChangeOffsets = 1;
}
}
}
inline void CBaseEdict::SetFree() { m_fStateFlags |= FL_EDICT_FREE; }
// WARNING: Make sure you don't really want to call ED_ClearFreeFlag which will
// also
// remove this edict from the g_FreeEdicts bitset.
inline void CBaseEdict::ClearFree() { m_fStateFlags &= ~FL_EDICT_FREE; }
inline void CBaseEdict::ClearTransmitState() {
m_fStateFlags &= ~(FL_EDICT_ALWAYS | FL_EDICT_PVSCHECK | FL_EDICT_DONTSEND);
}
inline const IServerEntity *CBaseEdict::GetIServerEntity() const {
if (m_fStateFlags & FL_EDICT_FULL)
return (IServerEntity *)m_pUnk;
else
return 0;
}
inline IServerUnknown *CBaseEdict::GetUnknown() { return m_pUnk; }
inline IServerNetworkable *CBaseEdict::GetNetworkable() {
return m_pNetworkable;
}
inline void CBaseEdict::SetEdict(IServerUnknown *pUnk, bool bFullEdict) {
m_pUnk = pUnk;
if ((pUnk != NULL) && bFullEdict) {
m_fStateFlags = FL_EDICT_FULL;
} else {
m_fStateFlags = 0;
}
}
inline int CBaseEdict::AreaNum() const {
if (!m_pUnk) return 0;
return m_pNetworkable->AreaNum();
}
inline const char *CBaseEdict::GetClassName() const {
if (!m_pUnk) return "";
return m_pNetworkable->GetClassName();
}
inline void CBaseEdict::SetChangeInfo(unsigned short info) {
GetChangeAccessor()->SetChangeInfo(info);
}
inline void CBaseEdict::SetChangeInfoSerialNumber(unsigned short sn) {
GetChangeAccessor()->SetChangeInfoSerialNumber(sn);
}
inline unsigned short CBaseEdict::GetChangeInfo() const {
return GetChangeAccessor()->GetChangeInfo();
}
inline unsigned short CBaseEdict::GetChangeInfoSerialNumber() const {
return GetChangeAccessor()->GetChangeInfoSerialNumber();
}
//-----------------------------------------------------------------------------
// Purpose: The engine's internal representation of an entity, including some
// basic collision and position info and a pointer to the class wrapped on top
// of the structure
//-----------------------------------------------------------------------------
struct edict_t : public CBaseEdict {
public:
ICollideable *GetCollideable();
// The server timestampe at which the edict was freed (so we can try to use
// other edicts before reallocating this one)
float freetime;
};
inline ICollideable *edict_t::GetCollideable() {
IServerEntity *pEnt = GetIServerEntity();
if (pEnt)
return pEnt->GetCollideable();
else
return NULL;
}
#endif // EDICT_H