395 lines
15 KiB
C++
Raw Normal View History

Mapbase v5.0 - Added keyvalue to hl2_gamerules which allows respawning in singleplayer - Added the game instructor system (including env_instructor_hint) from later Valve games using a VDC tutorial which adjusts the version from the Alien Swarm SDK to FPS rules and a Source 2013 environment; Also added new KV and icons for further control from mappers (tutorial mentioned by Maestra Fenix) - Added L4D/TF2 glows + point_glow entity as an all-purpose SDK-based off-shoot of tf_glow - Fixed weapon pickup sound not playing (reported by Sl0th and later Cvoxulary) - Fixed env_projectedtextures not updating on save/load - Added func_fake_worldportal, a spatial point_camera inspired by linked_portal_door based on SDK code alone (WIP, may be changed a lot in future updates) - Added option for point_camera and func_reflective_glass to use different render targets, therefore allowing multiple cameras and mirrors to be active at the same time - Added additional RT camera textures to choose from with a default of 3, but also controllable through a -numcameratextures command line param - Added adjustable convars for main view NearZ and skybox NearZ (suggested by someone recently, also suggested by Klems over a year ago) - Fixed map-specific localization files, cleaned up map-specific file code - Added a new block to gameinfo.txt which allows mods to automatically append their own command line parameters - Fixed math_lightpattern corruption when setting pattern/style while active - Fixed the "Touch" input crashing when given no entity - Added a way to add EFlags via keyvalue (suggested by Niker107) - Fixed ai_script_conditions not working without a NPC actor (reported by MetroHam) - Fixed point_radiation_source causing huge problems when intensity is 0, even though it was already advised against (reported by beefbacon) - Added "Mapbase" header to Mapbase-specific code files - Fixed an issue with updating sky_camera not obtaining area correctly, causing some entities to not draw in the skybox - Added "CopyFogController" and "CopyFogControllerWithScale" inputs to sky_camera, which copy fog parameters directly from a fog controller - Added "SetScale" input to sky_camera for live scale changing - Added convar to control player crouch speed multiplier (suggested by ArtyIF) - Added a ton of fixes for people running the Debug configuration of the codebase (partial credit to stepa2) - Added support for pre-defined enums and constants in VScript, starting with various values from the SDK code (damage types, trace masks, etc.) - Added limited support for Valve's Quaternion class in VScript - Added new instance helper capabilities, destructible game instances, and other misc. changes to VScript library - Replaced most of the VScript "accessor" classes with direct references to the original classes, as they were getting complicated fast and adding new VScript-only functions to the original classes might not be as bad as previously thought - Added base NPC hooks for AI sensing in VScript (allows control over sight and hearing), also exposed CSound for it - Added various functions and hooks for VPhysics integration in VScript - Added VScript-based custom suit devices - Expanded trace info exposed to VScript to allow plane and surface access (suggested by krassell) - Added ability to insert localization strings through VScript - Added various misc. VScript functions with various purposes, including reading/writing EFlags, movetypes, collision groups, etc. - Fixed VBSP not being able to correctly parse parallax corrected cubemaps in maps with instances
2020-08-14 21:21:25 +00:00
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: A special entity for afflicting damage as specific as possible.
//
//=============================================================================
#include "cbase.h"
//-----------------------------------------------------------------------------
// Purpose: Advanced damage information afflicting.
//-----------------------------------------------------------------------------
class CPointDamageInfo : public CLogicalEntity
{
DECLARE_CLASS( CPointDamageInfo, CLogicalEntity );
DECLARE_DATADESC();
virtual bool KeyValue( const char *szKeyName, const char *szValue );
virtual bool KeyValue( const char *szKeyName, const Vector &vecValue );
virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen );
public:
CPointDamageInfo();
CTakeDamageInfo m_info;
// The actual inflictor, attacker, and weapon in CTakeDamageInfo are direct entity pointers.
// This is needed to ensure !activator functionality, entities that might not exist yet, etc.
string_t m_iszInflictor;
string_t m_iszAttacker;
string_t m_iszWeapon;
// The maximum number of entities to be damaged if they share m_target's targetname or classname.
int m_iMaxEnts;
// Suppresses death sounds in the best way we possibly can.
bool m_bSuppressDeathSound;
//bool m_bDisabled;
// Inputs
void InputSetInflictor( inputdata_t &inputdata ) { m_iszInflictor = inputdata.value.StringID(); } //{ m_info.SetInflictor(inputdata.value.Entity()); }
void InputSetAttacker( inputdata_t &inputdata ) { m_iszAttacker = inputdata.value.StringID(); } //{ m_info.SetAttacker(inputdata.value.Entity()); }
void InputSetWeapon( inputdata_t &inputdata ) { m_iszWeapon = inputdata.value.StringID(); } //{ m_info.SetWeapon(inputdata.value.Entity()); }
void InputSetDamage( inputdata_t &inputdata ) { m_info.SetDamage(inputdata.value.Float()); }
void InputSetMaxDamage( inputdata_t &inputdata ) { m_info.SetMaxDamage(inputdata.value.Float()); }
void InputSetDamageBonus( inputdata_t &inputdata ) { m_info.SetDamageBonus(inputdata.value.Float()); }
void InputSetDamageType( inputdata_t &inputdata ) { m_info.SetDamageType(inputdata.value.Int()); }
void InputSetDamageCustom( inputdata_t &inputdata ) { m_info.SetDamageCustom(inputdata.value.Int()); }
void InputSetDamageStats( inputdata_t &inputdata ) { m_info.SetDamageStats(inputdata.value.Int()); }
void InputSetForceFriendlyFire( inputdata_t &inputdata ) { m_info.SetForceFriendlyFire(inputdata.value.Bool()); }
void InputSetAmmoType( inputdata_t &inputdata ) { m_info.SetAmmoType(inputdata.value.Int()); }
void InputSetPlayerPenetrationCount( inputdata_t &inputdata ) { m_info.SetPlayerPenetrationCount( inputdata.value.Int() ); }
void InputSetDamagedOtherPlayers( inputdata_t &inputdata ) { m_info.SetDamagedOtherPlayers( inputdata.value.Int() ); }
void InputSetDamageForce( inputdata_t &inputdata ) { Vector vec; inputdata.value.Vector3D(vec); m_info.SetDamageForce(vec); }
void InputSetDamagePosition( inputdata_t &inputdata ) { Vector vec; inputdata.value.Vector3D(vec); m_info.SetDamagePosition(vec); }
void InputSetReportedPosition( inputdata_t &inputdata ) { Vector vec; inputdata.value.Vector3D(vec); m_info.SetReportedPosition(vec); }
void HandleDamage(CBaseEntity *pTarget);
void ApplyDamage( const char *target, inputdata_t &inputdata );
void ApplyDamage( CBaseEntity *target, inputdata_t &inputdata );
void InputApplyDamage( inputdata_t &inputdata );
void InputApplyDamageToEntity( inputdata_t &inputdata );
// Outputs
COutputEvent m_OnApplyDamage;
COutputEvent m_OnApplyDeath;
};
LINK_ENTITY_TO_CLASS(point_damageinfo, CPointDamageInfo);
BEGIN_DATADESC( CPointDamageInfo )
DEFINE_EMBEDDED( m_info ),
// Keys
DEFINE_KEYFIELD( m_iszInflictor, FIELD_STRING, "Inflictor" ),
DEFINE_KEYFIELD( m_iszAttacker, FIELD_STRING, "Attacker" ),
DEFINE_KEYFIELD( m_iszWeapon, FIELD_STRING, "Weapon" ),
DEFINE_KEYFIELD( m_iMaxEnts, FIELD_INTEGER, "MaxEnts" ),
DEFINE_KEYFIELD( m_bSuppressDeathSound, FIELD_BOOLEAN, "SuppressDeathSound" ),
// Inputs
DEFINE_INPUTFUNC( FIELD_STRING, "SetInflictor", InputSetInflictor ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetAttacker", InputSetAttacker ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetWeapon", InputSetWeapon ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDamage", InputSetDamage ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMaxDamage", InputSetMaxDamage ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDamageBonus", InputSetDamageBonus ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDamageType", InputSetDamageType ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDamageCustom", InputSetDamageCustom ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDamageStats", InputSetDamageStats ),
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetForceFriendlyFire", InputSetForceFriendlyFire ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetAmmoType", InputSetAmmoType ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetPlayerPenetrationCount", InputSetPlayerPenetrationCount ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDamagedOtherPlayers", InputSetDamagedOtherPlayers ),
DEFINE_INPUTFUNC( FIELD_VECTOR, "SetDamageForce", InputSetDamageForce ),
DEFINE_INPUTFUNC( FIELD_VECTOR, "SetDamagePosition", InputSetDamagePosition ),
DEFINE_INPUTFUNC( FIELD_VECTOR, "SetReportedPosition", InputSetReportedPosition ),
DEFINE_INPUTFUNC( FIELD_VOID, "ApplyDamage", InputApplyDamage ),
DEFINE_INPUTFUNC( FIELD_EHANDLE, "ApplyDamageToEntity", InputApplyDamageToEntity ),
// Outputs
DEFINE_OUTPUT(m_OnApplyDamage, "OnApplyDamage"),
DEFINE_OUTPUT(m_OnApplyDeath, "OnApplyDeath"),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPointDamageInfo::CPointDamageInfo()
{
m_info = CTakeDamageInfo(this, this, 24, DMG_GENERIC);
m_iMaxEnts = 1;
}
//-----------------------------------------------------------------------------
// Purpose: Cache user entity field values until spawn is called.
// Input : szKeyName - Key to handle.
// szValue - Value for key.
// Output : Returns true if the key was handled, false if not.
//-----------------------------------------------------------------------------
bool CPointDamageInfo::KeyValue( const char *szKeyName, const char *szValue )
{
/*if (FStrEq(szKeyName, "Inflictor"))
m_info.SetInflictor(gEntList.FindEntityByName(NULL, szValue, this, NULL, NULL));
else if (FStrEq(szKeyName, "Attacker"))
m_info.SetAttacker(gEntList.FindEntityByName(NULL, szValue, this, NULL, NULL));
else if (FStrEq(szKeyName, "Weapon"))
m_info.SetWeapon(gEntList.FindEntityByName(NULL, szValue, this, NULL, NULL));
else*/ if (FStrEq(szKeyName, "Damage"))
m_info.SetDamage(atof(szValue));
else if (FStrEq(szKeyName, "MaxDamage"))
m_info.SetMaxDamage(atof(szValue));
else if (FStrEq(szKeyName, "DamageBonus"))
m_info.SetDamageBonus(atof(szValue));
else if (FStrEq(szKeyName, "DamageType") || FStrEq(szKeyName, "DamagePresets"))
m_info.AddDamageType(atoi(szValue));
else if (FStrEq(szKeyName, "DamageOr"))
m_info.AddDamageType(atoi(szValue));
else if (FStrEq(szKeyName, "DamageCustom"))
m_info.SetDamageCustom(atoi(szValue));
else if (FStrEq(szKeyName, "DamageStats"))
m_info.SetDamageStats(atoi(szValue));
else if (FStrEq(szKeyName, "ForceFriendlyFire"))
m_info.SetForceFriendlyFire(FStrEq(szValue, "1"));
else if (FStrEq(szKeyName, "AmmoType"))
m_info.SetAmmoType(atoi(szValue));
else if (FStrEq(szKeyName, "PlayerPenetrationCount"))
m_info.SetPlayerPenetrationCount(atoi(szValue));
else if (FStrEq(szKeyName, "DamagedOtherPlayers"))
m_info.SetDamagedOtherPlayers(atoi(szValue));
else
{
if (!BaseClass::KeyValue(szKeyName, szValue))
{
// Ripped from variant_t::Convert()...
Vector tmpVec = vec3_origin;
if (sscanf(szValue, "[%f %f %f]", &tmpVec[0], &tmpVec[1], &tmpVec[2]) == 0)
{
// Try sucking out 3 floats with no []s
sscanf(szValue, "%f %f %f", &tmpVec[0], &tmpVec[1], &tmpVec[2]);
}
return KeyValue(szKeyName, tmpVec);
}
}
return true;
}
bool CPointDamageInfo::KeyValue( const char *szKeyName, const Vector &vecValue )
{
if (FStrEq(szKeyName, "DamageForce"))
m_info.SetDamageForce(vecValue);
else if (FStrEq(szKeyName, "DamagePosition"))
m_info.SetDamagePosition(vecValue);
else if (FStrEq(szKeyName, "ReportedPosition"))
m_info.SetReportedPosition(vecValue);
else
return CBaseEntity::KeyValue( szKeyName, vecValue );
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
bool CPointDamageInfo::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen )
{
/*
if (FStrEq(szKeyName, "Inflictor"))
Q_snprintf(szValue, iMaxLen, "%s", m_info.GetInflictor() ? "" : m_info.GetInflictor()->GetDebugName());
else if (FStrEq(szKeyName, "Attacker"))
Q_snprintf(szValue, iMaxLen, "%s", m_info.GetAttacker() ? "" : m_info.GetAttacker()->GetDebugName());
else if (FStrEq(szKeyName, "Weapon"))
Q_snprintf(szValue, iMaxLen, "%s", m_info.GetWeapon() ? "" : m_info.GetWeapon()->GetDebugName());
else*/ if (FStrEq(szKeyName, "Damage"))
Q_snprintf(szValue, iMaxLen, "%f", m_info.GetDamage());
else if (FStrEq(szKeyName, "MaxDamage"))
Q_snprintf(szValue, iMaxLen, "%f", m_info.GetMaxDamage());
else if (FStrEq(szKeyName, "DamageBonus"))
Q_snprintf(szValue, iMaxLen, "%f", m_info.GetDamageBonus());
else if (FStrEq(szKeyName, "DamageType"))
Q_snprintf(szValue, iMaxLen, "%i", m_info.GetDamageType());
else if (FStrEq(szKeyName, "DamageCustom"))
Q_snprintf(szValue, iMaxLen, "%i", m_info.GetDamageCustom());
else if (FStrEq(szKeyName, "DamageStats"))
Q_snprintf(szValue, iMaxLen, "%i", m_info.GetDamageStats());
else if (FStrEq(szKeyName, "ForceFriendlyFire"))
Q_snprintf(szValue, iMaxLen, "%s", m_info.IsForceFriendlyFire() ? "1" : "0");
else if (FStrEq(szKeyName, "AmmoType"))
Q_snprintf(szValue, iMaxLen, "%i", m_info.GetAmmoType());
else if (FStrEq(szKeyName, "PlayerPenetrationCount"))
Q_snprintf(szValue, iMaxLen, "%i", m_info.GetPlayerPenetrationCount());
else if (FStrEq(szKeyName, "DamagedOtherPlayers"))
Q_snprintf(szValue, iMaxLen, "%i", m_info.GetDamagedOtherPlayers());
else if (FStrEq(szKeyName, "DamageForce"))
Q_snprintf(szValue, iMaxLen, "%f %f %f", m_info.GetDamageForce().x, m_info.GetDamageForce().y, m_info.GetDamageForce().z);
else if (FStrEq(szKeyName, "DamagePosition"))
Q_snprintf(szValue, iMaxLen, "%f %f %f", m_info.GetDamagePosition().x, m_info.GetDamagePosition().y, m_info.GetDamagePosition().z);
else if (FStrEq(szKeyName, "ReportedPosition"))
Q_snprintf(szValue, iMaxLen, "%f %f %f", m_info.GetReportedPosition().x, m_info.GetReportedPosition().y, m_info.GetReportedPosition().z);
else
return BaseClass::GetKeyValue(szKeyName, szValue, iMaxLen);
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPointDamageInfo::HandleDamage( CBaseEntity *pTarget )
{
pTarget->TakeDamage(m_info);
m_OnApplyDamage.FireOutput(pTarget, this);
if (pTarget->m_lifeState == LIFE_DYING)
m_OnApplyDeath.FireOutput(pTarget, this);
// This is the best we could do, as nodeathsound is exclusive to response system NPCs
if (m_bSuppressDeathSound)
pTarget->EmitSound("AI_BaseNPC.SentenceStop");
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CPointDamageInfo::ApplyDamage( const char *target, inputdata_t &inputdata )
{
if (m_iszAttacker != NULL_STRING)
m_info.SetAttacker( gEntList.FindEntityByName(NULL, STRING(m_iszAttacker), this, inputdata.pActivator, inputdata.pCaller) );
if (m_iszInflictor != NULL_STRING)
m_info.SetInflictor( gEntList.FindEntityByName(NULL, STRING(m_iszInflictor), this, inputdata.pActivator, inputdata.pCaller) );
else
{
CBaseCombatCharacter *pBCC = ToBaseCombatCharacter(m_info.GetAttacker());
if (pBCC != NULL && pBCC->GetActiveWeapon())
{
m_info.SetInflictor(pBCC->GetActiveWeapon());
}
}
if (m_iszWeapon != NULL_STRING)
m_info.SetWeapon( gEntList.FindEntityByName(NULL, STRING(m_iszWeapon), this, inputdata.pActivator, inputdata.pCaller) );
else
{
CBaseCombatCharacter *pBCC = ToBaseCombatCharacter(m_info.GetAttacker());
if (pBCC != NULL && pBCC->GetActiveWeapon())
{
m_info.SetWeapon(pBCC->GetActiveWeapon());
}
}
if (!m_info.GetAttacker())
m_info.SetAttacker( this );
if (!m_info.GetInflictor())
m_info.SetInflictor( this );
if (!m_info.GetWeapon())
m_info.SetWeapon( this );
CBaseEntity *pTarget = NULL;
if (m_iMaxEnts > 0)
{
for (int i = 0; i < m_iMaxEnts; i++)
{
pTarget = gEntList.FindEntityGeneric(pTarget, target, this, inputdata.pActivator, inputdata.pCaller);
if (pTarget)
{
HandleDamage( pTarget );
}
}
}
else
{
pTarget = gEntList.FindEntityGeneric(NULL, target, this, inputdata.pActivator, inputdata.pCaller);
while (pTarget)
{
HandleDamage( pTarget );
pTarget = gEntList.FindEntityGeneric(pTarget, target, this, inputdata.pActivator, inputdata.pCaller);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Applies damage to a specific entity
// Input :
// Output :
//-----------------------------------------------------------------------------
void CPointDamageInfo::ApplyDamage( CBaseEntity *target, inputdata_t &inputdata )
{
if (!target)
return;
if (m_iszAttacker != NULL_STRING)
m_info.SetAttacker( gEntList.FindEntityByName(NULL, STRING(m_iszAttacker), this, inputdata.pActivator, inputdata.pCaller) );
else
m_info.SetAttacker( this );
if (m_iszInflictor != NULL_STRING)
m_info.SetInflictor( gEntList.FindEntityByName(NULL, STRING(m_iszInflictor), this, inputdata.pActivator, inputdata.pCaller) );
else
{
CBaseCombatCharacter *pBCC = ToBaseCombatCharacter(m_info.GetAttacker());
if (pBCC != NULL && pBCC->GetActiveWeapon())
{
m_info.SetInflictor(pBCC->GetActiveWeapon());
}
else
m_info.SetInflictor(this);
}
if (m_iszWeapon != NULL_STRING)
m_info.SetWeapon( gEntList.FindEntityByName(NULL, STRING(m_iszWeapon), this, inputdata.pActivator, inputdata.pCaller) );
else
{
CBaseCombatCharacter *pBCC = ToBaseCombatCharacter(m_info.GetAttacker());
if (pBCC != NULL && pBCC->GetActiveWeapon())
{
m_info.SetWeapon(pBCC->GetActiveWeapon());
}
else
m_info.SetWeapon(this);
}
target->TakeDamage(m_info);
m_OnApplyDamage.FireOutput(target, this);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CPointDamageInfo::InputApplyDamage( inputdata_t &inputdata )
{
ApplyDamage(STRING(m_target), inputdata);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CPointDamageInfo::InputApplyDamageToEntity( inputdata_t &inputdata )
{
ApplyDamage(inputdata.value.Entity(), inputdata);
}