Skip to content

Commit

Permalink
Add CTakeDamageInfo natives to use with TakeDamage (#48)
Browse files Browse the repository at this point in the history
This PR adds natives for creating and applying custom `CTakeDamageInfo` parameters to entities using `CBaseEntity::TakeDamage()`. Plugins can use the `GetGlobalDamageInfo` native to specify custom parameters for use in `TakeDamage`.

`CBaseEntity::TakeDamage` should be used over `SDKHooks_OnTakeDamage` because it checks for damage filters and applies damage scaling before `OnTakeDamage` is called. `SDKHooks_OnTakeDamage` bypasses that and calls `CBaseEntity::OnTakeDamage` directly.

Other miscellaneous changes:
 - Fixed `CTakeDamageInfo.Set/AddDamageType` natives missing a parameter (reported by @Mikusch)
 - Fixed some `CTakeDamageInfo` natives not returning immediately after erroring
  • Loading branch information
KitRifty authored Dec 28, 2023
1 parent 7b6b5cd commit 0598845
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 10 deletions.
19 changes: 18 additions & 1 deletion extension/natives/baseentity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,22 @@ cell_t IsCombatCharacter(IPluginContext* context, const cell_t* params) {
return entity->MyCombatCharacterPointer() ? 1 : 0;
}

cell_t TakeDamage(IPluginContext* context, const cell_t* params) {
auto entity = Get(context, params[1]);
if (!entity) {
return 0;
}

CTakeDamageInfo* inputInfo = (CTakeDamageInfo*)PawnAddressToPtr(params[2]);
if (!inputInfo) {
context->ThrowNativeError("CTakeDamageInfo is a null ptr!");
return 0;
}

entity->TakeDamage(*inputInfo);
return 0;
}

void setup(std::vector<sp_nativeinfo_t>& natives) {
sp_nativeinfo_t list[] = {
{"CBaseEntity.iUpdateOnRemove", iUpdateOnRemove},
Expand Down Expand Up @@ -554,7 +570,8 @@ void setup(std::vector<sp_nativeinfo_t>& natives) {
{"CBaseEntity.MyNextBotPointer", MyNextBotPointer},
{"CBaseEntity.GetBaseAnimating", GetBaseAnimating},
{"CBaseEntity.MyCombatCharacterPointer", MyCombatCharacterPointer},
{"CBaseEntity.IsCombatCharacter", IsCombatCharacter}
{"CBaseEntity.IsCombatCharacter", IsCombatCharacter},
{"CBaseEntity.TakeDamage", TakeDamage}
};
natives.insert(natives.end(), std::begin(list), std::end(list));
}
Expand Down
77 changes: 72 additions & 5 deletions extension/natives/takedamageinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
#include <IGameHelpers.h>
extern SourceMod::IGameHelpers* gamehelpers;

CTakeDamageInfo::CTakeDamageInfo(){}

namespace natives::takedamageinfo {

CTakeDamageInfo g_GlobalDamageInfo;

inline CTakeDamageInfo* Get(IPluginContext* context, const cell_t param) {
CTakeDamageInfo* info = (CTakeDamageInfo*)PawnAddressToPtr(param);
if (!info) {
Expand All @@ -30,6 +30,69 @@ cell_t CTakeDamageInfo_Ctor(IPluginContext* context, const cell_t* params) {
return PtrToPawnAddress(PawnAddressToPtr(params[1]));
}

cell_t GetGlobalDamageInfo(IPluginContext* context, const cell_t* params) {
return PtrToPawnAddress(&g_GlobalDamageInfo);
}

cell_t Init(IPluginContext* context, const cell_t* params) {
CTakeDamageInfo* info = Get(context, params[1]);
if (!info) {
return 0;
}

CBaseEntity* inflictor = gamehelpers->ReferenceToEntity(params[2]);
if (!inflictor && params[2] != -1) {
return context->ThrowNativeError("Invalid inflictor index!");
}

CBaseEntity* attacker = gamehelpers->ReferenceToEntity(params[3]);
if (!attacker && params[3] != -1) {
return context->ThrowNativeError("Invalid attacker index!");
}

CBaseEntity* weapon = gamehelpers->ReferenceToEntity(params[4]);
if (!attacker && params[4] != -1) {
return context->ThrowNativeError("Invalid weapon index!");
}

cell_t* damageForceAddr;
context->LocalToPhysAddr(params[5], &damageForceAddr);
Vector damageForce;
if (context->GetNullRef(SP_NULL_VECTOR) == damageForceAddr) {
damageForce = vec3_origin;
}
else {
PawnVectorToVector(damageForceAddr, &damageForce);
}

cell_t* damagePositionAddr;
context->LocalToPhysAddr(params[6], &damagePositionAddr);
Vector damagePosition;
if (context->GetNullRef(SP_NULL_VECTOR) == damagePositionAddr) {
damagePosition = vec3_origin;
}
else {
PawnVectorToVector(damagePositionAddr, &damagePosition);
}

float damage = sp_ctof(params[7]);
int bitsDamageType = params[8];
int customDamage = params[9];

cell_t* reportedPositionAddr;
context->LocalToPhysAddr(params[10], &reportedPositionAddr);
Vector reportedPosition;
if (context->GetNullRef(SP_NULL_VECTOR) == reportedPositionAddr) {
reportedPosition = vec3_origin;
}
else {
PawnVectorToVector(reportedPositionAddr, &reportedPosition);
}

info->Set(inflictor, attacker, weapon, damageForce, damagePosition, damage, bitsDamageType, customDamage, &reportedPosition);
return 0;
}

cell_t GetInflictor(IPluginContext* context, const cell_t* params) {
CTakeDamageInfo* info = Get(context, params[1]);
if (!info) {
Expand All @@ -47,7 +110,7 @@ cell_t SetInflictor(IPluginContext* context, const cell_t* params) {

CBaseEntity* inflictor = gamehelpers->ReferenceToEntity(params[2]);
if (!inflictor && params[2] != -1) {
context->ThrowNativeError("Invalid inflictor index!");
return context->ThrowNativeError("Invalid inflictor index!");
}
info->m_hInflictor = inflictor;
return 0;
Expand All @@ -70,7 +133,7 @@ cell_t SetWeapon(IPluginContext* context, const cell_t* params) {

CBaseEntity* weapon = gamehelpers->ReferenceToEntity(params[2]);
if (!weapon && params[2] != -1) {
context->ThrowNativeError("Invalid weapon index!");
return context->ThrowNativeError("Invalid weapon index!");
}
info->m_hWeapon = weapon;
return 0;
Expand All @@ -90,7 +153,7 @@ cell_t SetAttacker(IPluginContext* context, const cell_t* params) {

CBaseEntity* attacker = gamehelpers->ReferenceToEntity(params[2]);
if (!attacker && params[2] != -1) {
context->ThrowNativeError("Invalid attacker index!");
return context->ThrowNativeError("Invalid attacker index!");
}
info->m_hAttacker = attacker;
return 0;
Expand Down Expand Up @@ -503,6 +566,10 @@ void setup(std::vector<sp_nativeinfo_t>& natives) {
sp_nativeinfo_t list[] = {
{"CTakeDamageInfo.CTakeDamageInfo", CTakeDamageInfo_Ctor},

{"GetGlobalDamageInfo", GetGlobalDamageInfo},

{"CTakeDamageInfo.Init", Init},

{"CTakeDamageInfo.GetInflictor", GetInflictor},
{"CTakeDamageInfo.SetInflictor", SetInflictor},

Expand Down
7 changes: 7 additions & 0 deletions extension/sourcesdk/baseentity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ VCall<CBaseAnimating*> CBaseEntity::vGetBaseAnimating;
VCall<INextBot*> CBaseEntity::vMyNextBotPointer;
MCall<void, int> CBaseEntity::mInvalidatePhysicsRecursive;
VCall<const Vector&> CBaseEntity::vWorldSpaceCenter;
MCall<void, const CTakeDamageInfo&> CBaseEntity::mTakeDamage;
VCall<int, const CTakeDamageInfo&> CBaseEntity::vOnTakeDamage;
VCall<bool> CBaseEntity::vIsAlive;
MCall<void> CBaseEntity::mCalcAbsolutePosition;
Expand Down Expand Up @@ -120,6 +121,7 @@ bool CBaseEntity::Init(SourceMod::IGameConfig* config, char* error, size_t maxle
vMyNextBotPointer.Init(config, "CBaseEntity::MyNextBotPointer");
vWorldSpaceCenter.Init(config, "CBaseEntity::WorldSpaceCenter");
vEyeAngles.Init(config, "CBaseEntity::EyeAngles");
mTakeDamage.Init(config, "CBaseEntity::TakeDamage");
vOnTakeDamage.Init(configSDKHooks, "OnTakeDamage");
vIsAlive.Init(config, "CBaseEntity::IsAlive");

Expand Down Expand Up @@ -303,6 +305,11 @@ const Vector& CBaseEntity::WorldSpaceCenter(void)
return vWorldSpaceCenter(this);
}

void CBaseEntity::TakeDamage(const CTakeDamageInfo& inputInfo)
{
return mTakeDamage(this, inputInfo);
}

int CBaseEntity::OnTakeDamage(const CTakeDamageInfo& info)
{
return vOnTakeDamage(this, info);
Expand Down
3 changes: 3 additions & 0 deletions extension/sourcesdk/baseentity.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ class CBaseEntity : public IServerEntity
void CalcAbsolutePosition(void);
void CalcAbsoluteVelocity(void);

static MCall<void, const CTakeDamageInfo&> mTakeDamage;
void TakeDamage(const CTakeDamageInfo& inputInfo);

static VCall<int, const CTakeDamageInfo&> vOnTakeDamage;
int OnTakeDamage(const CTakeDamageInfo& info);

Expand Down
52 changes: 52 additions & 0 deletions extension/sourcesdk/takedamageinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,58 @@
#include <isaverestore.h>
#include <takedamageinfo.h>

CTakeDamageInfo::CTakeDamageInfo()
{
Init( NULL, NULL, NULL, vec3_origin, vec3_origin, vec3_origin, 0, 0, 0 );
}

void CTakeDamageInfo::Init( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, const Vector &reportedPosition, float flDamage, int bitsDamageType, int iCustomDamage )
{
m_hInflictor = pInflictor;
if ( pAttacker )
{
m_hAttacker = pAttacker;
}
else
{
m_hAttacker = pInflictor;
}

m_hWeapon = pWeapon;

m_flDamage = flDamage;

m_flBaseDamage = BASEDAMAGE_NOT_SPECIFIED;

m_bitsDamageType = bitsDamageType;
m_iDamageCustom = iCustomDamage;

m_flMaxDamage = flDamage;
m_vecDamageForce = damageForce;
m_vecDamagePosition = damagePosition;
m_vecReportedPosition = reportedPosition;
m_iAmmoType = -1;
m_iDamagedOtherPlayers = 0;
m_iPlayerPenetrationCount = 0;
m_flDamageBonus = 0.f;
m_bForceFriendlyFire = false;
m_flDamageForForce = 0.f;

#if SOURCE_ENGINE == SE_TF2
m_eCritType = kCritType_None;
#endif
}

void CTakeDamageInfo::Set( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, float flDamage, int bitsDamageType, int iKillType, Vector *reportedPosition )
{
Vector vecReported = vec3_origin;
if ( reportedPosition )
{
vecReported = *reportedPosition;
}
Init( pInflictor, pAttacker, pWeapon, damageForce, damagePosition, vecReported, flDamage, bitsDamageType, iKillType );
}

#if SOURCE_ENGINE == SE_TF2
void CTakeDamageInfo::SetCritType( ECritType eType )
{
Expand Down
7 changes: 7 additions & 0 deletions gamedata/cbasenpc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,13 @@
"windows" ""
"linux" "@_ZN11CBaseEntity15SetGroundEntityEPS_"
}
// Find string "CBaseEntity::TakeDamage: with inputInfo.GetDamageForce() == vec3_origin\n"
"CBaseEntity::TakeDamage"
{
"library" "server"
"windows" "\x55\x8B\xEC\x81\xEC\x98\x00\x00\x00\x53\x8B\xD9"
"linux" "@_ZN11CBaseEntity10TakeDamageERK15CTakeDamageInfo"
}
// "g_EntityListPool"
"g_EntityListPool"
{
Expand Down
2 changes: 1 addition & 1 deletion product.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.10.3
1.11.0
12 changes: 12 additions & 0 deletions scripting/include/cbasenpc/baseentity.inc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#endif
#define _CBASENPC_BASEENTITY_INC_

#include "takedamageinfo.inc"

methodmap CBaseEntity
{
public CBaseEntity(int iEnt)
Expand Down Expand Up @@ -910,4 +912,14 @@ methodmap CBaseEntity
{
SetEntityMoveType(this.index, val);
}

/**
* Causes the entity to take damage with the given parameters.
* Filters are checked and damage scaling is applied before it calls the
* entity's OnTakeDamage().
*
* @param inputInfo The damage parameters to use
* @error Invalid entity or a NULL pointer is given
*/
public native void TakeDamage(CTakeDamageInfo inputInfo);
}
31 changes: 28 additions & 3 deletions scripting/include/cbasenpc/takedamageinfo.inc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ methodmap CTakeDamageInfo
*/
public native CTakeDamageInfo(Address ptr);

/**
* Initializes the CTakeDamageInfo's values.
*
* @param inflictor The inflictor entity index.
* @param attacker The attacker entity index.
* @param weapon The weapon entity index.
* @param damageForce The damage force vector.
* @param damagePosition The damage position vector.
* @param damage The amount of damage to set.
* @param bitsDamageType The damage type bitmask.
* @param customDamage The custom damage to set.
* @param reportedPosition The position to report to players on where the damage came from.
* @error Invalid takedamageinfo, or invalid inflictor, attacker, and weapon entity index.
*/
public native void Init(int inflictor = -1, int attacker = -1, int weapon = -1, const float damageForce[3] = NULL_VECTOR, const float damagePosition[3] = NULL_VECTOR, float damage = 0.0, int bitsDamageType = 0, int customDamage = 0, const float reportedPosition[3] = NULL_VECTOR);

/**
* Gets the inflictor.
*
Expand Down Expand Up @@ -249,15 +265,15 @@ methodmap CTakeDamageInfo
* @param bitmask The damage type bitmask.
* @error Invalid takedamageinfo.
*/
public native int SetDamageType();
public native int SetDamageType(int bitmask);

/**
* Adds the damage bitmask to the current damage bitmask.
*
* @param bitmask The damage type bitmask to add.
* @error Invalid takedamageinfo.
*/
public native int AddDamageType();
public native int AddDamageType(int bitmask);

/**
* Gets the custom damage.
Expand Down Expand Up @@ -370,4 +386,13 @@ methodmap CTakeDamageInfo
* @error Invalid takedamageinfo.
*/
public native TakeDamageInfo_CritType GetCritType();
};
};

/**
* Returns the address of a global CTakeDamageInfo object. Make sure to
* initialize the values of the object first using CTakeDamageInfo.Init()
* before modifying and using it in a function call.
*
* @return Address to the global CTakeDamageInfo object.
*/
native CTakeDamageInfo GetGlobalDamageInfo();

0 comments on commit 0598845

Please sign in to comment.