Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ragdoll style overrides #876

Merged
merged 4 commits into from Nov 23, 2019
Merged
Changes from 3 commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -21,9 +21,21 @@
<List width="180">
<AutoVariable width="fill" target="visual.night-mode" label="Nightmode" min="0" max="100"/>
<AutoVariable width="fill" target="explosionspheres.enabled" label="Draw Explosion Spheres"/>
<LabeledObject width="fill" label="Ragdoll Override">
<Select target="visual.ragdoll-mode">
<Option name="Auto" value="0"/>
<Option name="Gib" value="1"/>
<Option name="Burning" value="2"/>
<Option name="Electrocuted" value="3"/>
<Option name="Ash" value="4"/>
<Option name="Gold" value="5"/>
<Option name="Ice" value="6"/>
</Select>
</LabeledObject>
<AutoVariable width="fill" target="visual.ragdoll-only-local" label="Only Override Local Player Kills"/>
</List>
</Box>
<Box padding="12 6 6 6" width="content" height="content" name="Remove" x="195" y="52">
<Box padding="12 6 6 6" width="content" height="content" name="Remove" x="195" y="85">
<List width="180">
<AutoVariable width="fill" target="remove.arms" label="Remove Arms"/>
<AutoVariable width="fill" target="remove.cloak" label="Remove Cloak"/>
@@ -174,6 +174,8 @@ class NetVars
offset_t m_iHealing_Resource;
offset_t m_iHealingAssist_Resource;
offset_t m_iPlayerLevel_Resource;

offset_t m_iPlayerIndex;
};

extern NetVars netvar;
@@ -122,6 +122,7 @@ void NetVars::Init()
this->m_iHealing_Resource = gNetvars.get_offset("DT_TFPlayerResource", "m_iHealing");
this->m_iHealingAssist_Resource = gNetvars.get_offset("DT_TFPlayerResource", "m_iHealingAssist");
this->m_iPlayerLevel_Resource = gNetvars.get_offset("DT_TFPlayerResource", "m_iPlayerLevel");
this->m_iPlayerIndex = gNetvars.get_offset("DT_TFRagdoll", "m_iPlayerIndex");
}
IF_GAME(IsTF2C())
{
@@ -51,7 +51,8 @@ if(EnableVisuals)
"${CMAKE_CURRENT_LIST_DIR}/SkinChanger.cpp"
"${CMAKE_CURRENT_LIST_DIR}/SpyAlert.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Thirdperson.cpp"
"${CMAKE_CURRENT_LIST_DIR}/MCHealthbar.cpp")
"${CMAKE_CURRENT_LIST_DIR}/MCHealthbar.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Ragdolls.cpp")
target_sources(cathook PRIVATE ${files})
list(REMOVE_ITEM ignore_files ${files})
set(ignore_files ${ignore_files} CACHE INTERNAL "")
@@ -0,0 +1,243 @@
/*
* Ragdolls.cpp
*
* Created on: Nov 6, 2019
* Author: Roboot
*/

#include "common.hpp"
#include "sdk/dt_recv_redef.h"

namespace hacks::shared::ragdolls
{

static settings::Int mode{ "visual.ragdoll-mode", "0" };
static settings::Boolean only_local{ "visual.ragdoll-only-local", "1" };

/**
* Simple helper class for swapping out a RecvVarProxyFn
* and restoring it later on.
* You MUST call init(...) before calling setHook() or restore()
*/
class ProxyFnHook
This conversation was marked as resolved by BenCat07

This comment has been minimized.

Copy link
@BenCat07

BenCat07 Nov 10, 2019

Collaborator

Oh niceeeeeeee

This comment has been minimized.

Copy link
@BenCat07

BenCat07 Nov 10, 2019

Collaborator

You could check if (proxy) in setHook and restore though to avoid issues in Shutdown possibly

{
public:
ProxyFnHook() : hooked(false)
{
}
void init(RecvPropRedef *prop)
{
this->prop = prop;
}
void setHook(RecvVarProxyFn new_fn)
{
if (hooked)
return;
hooked = true;
original_fn = prop->m_ProxyFn;
prop->m_ProxyFn = new_fn;
}
void restore()
{
if (hooked)
{
prop->m_ProxyFn = original_fn;
hooked = false;
}
}

private:
bool hooked;
RecvPropRedef *prop;
RecvVarProxyFn original_fn;
};

// Ragdoll override style
This conversation was marked as resolved by BenCat07

This comment has been minimized.

Copy link
@BenCat07

BenCat07 Nov 10, 2019

Collaborator

Is there like a cowmangler one? (plasma + floats upwards)

This comment has been minimized.

Copy link
@robootgit

robootgit Nov 11, 2019

Author Collaborator

There's no single variable for controlling the cow mangler effect. Maybe it's a combination of the existing variables? I can check.

enum RagdollOverride_t
{
NONE = 0,
GIB = 1,
BURNING = 2,
ELECTROCUTED = 3,
ASH = 4,
GOLD = 5,
ICE = 6
};

ProxyFnHook gib_hook;
ProxyFnHook burn_hook;
ProxyFnHook electro_hook;
ProxyFnHook ash_hook;
ProxyFnHook gold_hook;
ProxyFnHook ice_hook;

/**
* Check to see if a ragdoll belongs to a player killed by the local player
*/
bool ragdollKillByLocal(void *ragdoll)
{
// Get the owner of the ragdoll (TFPlayer)
auto owner = g_IEntityList->GetClientEntity(NET_INT(ragdoll, netvar.m_iPlayerIndex));
if (!owner || owner->IsDormant())
{
return false;
}
// Make sure this isn't the own player's ragdoll
// The player will spectate iteself when the player dies via suicide
if (owner->entindex() == g_pLocalPlayer->entity_idx)
{
return false;
}
// Check to see if the owner is spectating the local player
auto owner_observer = g_IEntityList->GetClientEntityFromHandle(NET_VAR(owner, netvar.hObserverTarget, CBaseHandle));
if (!owner_observer || owner_observer->IsDormant())
{
return false;
}
return owner_observer->entindex() == g_pLocalPlayer->entity_idx;
}

/**
* Called for m_bGib
*/
void overrideGib(const CRecvProxyData *data, void *structure, void *out)
{
auto gib = reinterpret_cast<bool *>(out);
if (*mode == RagdollOverride_t::GIB && (!*only_local || ragdollKillByLocal(structure)))
*gib = true;
else
*gib = data->m_Value.m_Int;
}

/**
* Called for m_bBurning
*/
void overrideBurning(const CRecvProxyData *data, void *structure, void *out)
{
auto burning = reinterpret_cast<bool *>(out);
if (*mode == RagdollOverride_t::BURNING && (!*only_local || ragdollKillByLocal(structure)))
*burning = true;
else
*burning = data->m_Value.m_Int;
}

/**
* Called for m_bElectrocuted
*/
void overrideElectrocuted(const CRecvProxyData *data, void *structure, void *out)
{
auto electrocuted = reinterpret_cast<bool *>(out);
if (*mode == RagdollOverride_t::ELECTROCUTED && (!*only_local || ragdollKillByLocal(structure)))
*electrocuted = true;
else
*electrocuted = data->m_Value.m_Int;
}

/**
* Called for m_bBecomeAsh
*/
void overrideAsh(const CRecvProxyData *data, void *structure, void *out)
{
auto ash = reinterpret_cast<bool *>(out);
if (*mode == RagdollOverride_t::ASH && (!*only_local || ragdollKillByLocal(structure)))
*ash = true;
else
*ash = data->m_Value.m_Int;
}

/**
* Called for m_bGoldRagdoll
*/
void overrideGold(const CRecvProxyData *data, void *structure, void *out)
{
auto gold = reinterpret_cast<bool *>(out);
if (*mode == RagdollOverride_t::GOLD && (!*only_local || ragdollKillByLocal(structure)))
*gold = true;
else
*gold = data->m_Value.m_Int;
}

/**
* Called for m_bIceRagdoll
*/
void overrideIce(const CRecvProxyData *data, void *structure, void *out)
{
auto ice = reinterpret_cast<bool *>(out);
if (*mode == RagdollOverride_t::ICE && (!*only_local || ragdollKillByLocal(structure)))
*ice = true;
else
*ice = data->m_Value.m_Int;
}

/**
* Swap out the RecvVarProxyFns for TFRagdoll style props
*/
void hook()
{
for (auto dt_class = g_IBaseClient->GetAllClasses(); dt_class; dt_class = dt_class->m_pNext)
{
auto table = dt_class->m_pRecvTable;

if (strcmp(table->m_pNetTableName, "DT_TFRagdoll") == 0)
{
for (int i = 0; i < table->m_nProps; ++i)
{
auto prop = reinterpret_cast<RecvPropRedef *>(&table->m_pProps[i]);
if (prop == nullptr)
continue;

auto prop_name = prop->m_pVarName;
if (strcmp(prop_name, "m_bGib") == 0)
{
gib_hook.init(prop);
gib_hook.setHook(overrideGib);
}
else if (strcmp(prop_name, "m_bBurning") == 0)
{
burn_hook.init(prop);
burn_hook.setHook(overrideBurning);
}
else if (strcmp(prop_name, "m_bElectrocuted") == 0)
{
electro_hook.init(prop);
electro_hook.setHook(overrideElectrocuted);
}
else if (strcmp(prop_name, "m_bBecomeAsh") == 0)
{
ash_hook.init(prop);
ash_hook.setHook(overrideAsh);
}
else if (strcmp(prop_name, "m_bGoldRagdoll") == 0)
{
gold_hook.init(prop);
gold_hook.setHook(overrideGold);
}
else if (strcmp(prop_name, "m_bIceRagdoll") == 0)
{
ice_hook.init(prop);
ice_hook.setHook(overrideIce);
}
}
}
}
}

/**
* Restore the RecvVarProxyFns that were swapped out earlier
*/
void unhook()
{
gib_hook.restore();
burn_hook.restore();
electro_hook.restore();
ash_hook.restore();
gold_hook.restore();
ice_hook.restore();
}

static InitRoutine init([]() {
hook();
EC::Register(EC::Shutdown, unhook, "ragdoll_shutdown");
});

} // namespace hacks::shared::ragdolls
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.