diff --git a/gamedata/tf2-comp-fixes.games.txt b/gamedata/tf2-comp-fixes.games.txt index 71f240d..fd3b19b 100644 --- a/gamedata/tf2-comp-fixes.games.txt +++ b/gamedata/tf2-comp-fixes.games.txt @@ -122,6 +122,42 @@ } } + "CTFGrenadePipebombProjectile::Create" { + /* CTFGrenadePipebombProjectile* CTFGrenadePipebombProjectile::Create( const Vector &position, const QAngle &angles, + const Vector &velocity, const AngularImpulse &angVelocity, + CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo, + int iPipeBombType, float flMultDmg ) */ + "signature" "CTFGrenadePipebombProjectile::Create" + "callconv" "cdecl" + "return" "cbaseentity" + "arguments" { + "position" { + "type" "vectorptr" + } + "angles" { + "type" "vectorptr" + } + "velocity" { + "type" "vectorptr" + } + "angVelocity" { + "type" "vectorptr" + } + "pOwner" { + "type" "objectptr" + } + "weaponInfo" { + "type" "objectptr" + } + "iPipeBombType" { + "type" "int" + } + "flMultDmg" { + "type" "float" + } + } + } + "CTFFlameThrower::DeflectEntity" { // virtual bool DeflectEntity( CBaseEntity *pTarget, CTFPlayer *pOwner, Vector &vecForward, Vector &vecCenter, Vector &vecSize ) "signature" "CTFFlameThrower::DeflectEntity" @@ -178,7 +214,7 @@ "signature" "CTFWeaponBaseGun::FirePipeBomb" "callconv" "thiscall" "return" "cbaseentity" - "this" "ignore" + "this" "entity" "arguments" { "player" { "type" "objectptr" @@ -189,6 +225,19 @@ } } + "CTFWeaponBaseGun::FireProjectile" { + // CBaseEntity *CTFWeaponBaseGun::FireProjectile( CTFPlayer *pPlayer ) + "signature" "CTFWeaponBaseGun::FireProjectile" + "callconv" "thiscall" + "return" "cbaseentity" + "this" "ignore" + "arguments" { + "player" { + "type" "objectptr" + } + } + } + "PassServerEntityFilter" { // bool PassServerEntityFilter( const IHandleEntity *pTouch, const IHandleEntity *pPass ) "signature" "PassServerEntityFilter" @@ -321,6 +370,15 @@ "windows" "\x55\x8B\xEC\x83\xEC\x0C\x56\x6A\x00\x8D\x45\x2A" } + + "CTFGrenadeLauncher::GetProjectileSpeed" { + "linux" "@_ZN18CTFGrenadeLauncher18GetProjectileSpeedEv" + } + + "CTFGrenadePipebombProjectile::Create" { + "linux" "@_ZN28CTFGrenadePipebombProjectile6CreateERK6VectorRK6QAngleS2_S2_P20CBaseCombatCharacterRK13CTFWeaponInfoif" + } + "CTFPlayer::OnTakeDamage_Alive" { "linux" "@_ZN9CTFPlayer18OnTakeDamage_AliveERK15CTakeDamageInfo" } @@ -337,6 +395,10 @@ "windows" "\x53\x8B\xDC\x83\xEC\x08\x83\xE4\xF0\x83\xC4\x04\x55\x8B\x6B\x04\x89\x6C\x24\x04\x8B\xEC\x81\xEC\x58\x01\x00\x00\x56\x8B\xF1\x57\x8B\x06" } + "CTFWeaponBaseGun::FireProjectile" { + "linux" "@_ZN16CTFWeaponBaseGun14FireProjectileEP9CTFPlayer" + } + "GEconItemSchema" { "linux" "@_Z15GEconItemSchemav" "windows" "\xE8\x2A\x2A\x2A\x2A\x83\xC0\x04\xC3" diff --git a/scripting/tf2-comp-fixes.sp b/scripting/tf2-comp-fixes.sp index 47080b9..ebc60c4 100644 --- a/scripting/tf2-comp-fixes.sp +++ b/scripting/tf2-comp-fixes.sp @@ -33,6 +33,7 @@ #include "tf2-comp-fixes/remove-halloween-souls.sp" #include "tf2-comp-fixes/remove-medic-attach-speed.sp" #include "tf2-comp-fixes/remove-pipe-spin.sp" +#include "tf2-comp-fixes/remove-pipes-spread.sp" #include "tf2-comp-fixes/solid-buildings.sp" #include "tf2-comp-fixes/tournament-end-ignores-whitelist.sp" #include "tf2-comp-fixes/winger-jump-bonus-when-fully-deployed.sp" @@ -79,7 +80,7 @@ void OnPluginStart() { RegConsoleCmd("sm_cf", Command_Cf, "Batch update of TF2 Competitive Fixes cvars"); Common_Setup(game_config); - Debug_Setup(); + Debug_Setup(game_config); // Here Concede_Setup(); @@ -101,6 +102,7 @@ void OnPluginStart() { RemoveHalloweenSouls_Setup(game_config); RemoveMedicAttachSpeed_Setup(game_config); RemovePipeSpin_Setup(); + RemovePipesSpread_Setup(game_config); SolidBuildings_Setup(); TournamentEndIgnoresWhitelist_Setup(game_config); WingerJumpBonusWhenFullyDeployed_Setup(game_config); @@ -173,6 +175,7 @@ Action Command_Cf(int client, int args) { ReplyDiffConVar(client, "sm_gunboats_always_apply"); ReplyDiffConVar(client, "sm_prevent_respawning"); ReplyDiffConVar(client, "sm_remove_medic_attach_speed"); + ReplyDiffConVar(client, "sm_remove_pipes_spread"); ReplyDiffConVar(client, "sm_solid_buildings"); ReplyDiffConVar(client, "sm_winger_jump_bonus_when_fully_deployed"); @@ -245,6 +248,9 @@ Action Command_Cf(int client, int args) { FindConVar("sm_remove_pipe_spin") .SetBool(all); + FindConVar("sm_remove_pipes_spread") + .SetBool(all); + FindConVar("sm_rest_in_peace_rick_may") .SetInt(all || fixes || rgl ? 128 : ozf ? 255 : 0); diff --git a/scripting/tf2-comp-fixes/common.sp b/scripting/tf2-comp-fixes/common.sp index f9424c8..3b821da 100644 --- a/scripting/tf2-comp-fixes/common.sp +++ b/scripting/tf2-comp-fixes/common.sp @@ -166,6 +166,10 @@ stock int GetEntityFromAddress(Address entity_ptr) { return entity_handle & 0xFFF; } +stock void GetEntityPosition(int entity, float position[3]) { + GetEntPropVector(entity, Prop_Data, "m_vecOrigin", position); +} + stock void ScaleVectorTo(const float vec[3], float scale, float result[3]) { result[0] = vec[0] * scale; result[1] = vec[1] * scale; diff --git a/scripting/tf2-comp-fixes/debug.sp b/scripting/tf2-comp-fixes/debug.sp index 325a806..d0ed709 100644 --- a/scripting/tf2-comp-fixes/debug.sp +++ b/scripting/tf2-comp-fixes/debug.sp @@ -1,8 +1,20 @@ static ConVar g_cvar; -void Debug_Setup() { +static StringMap g_projectiles_positions; +static int g_laser; + +static Handle g_detour_CTFWeaponBaseGun_FireProjectile; + +void Debug_Setup(Handle game_config) { + g_laser = PrecacheModel("sprites/laser.vmt"); + g_projectiles_positions = new StringMap(); + + g_detour_CTFWeaponBaseGun_FireProjectile = + CheckedDHookCreateFromConf(game_config, "CTFWeaponBaseGun::FireProjectile"); + g_cvar = CreateConVar("sm_cf_debug", "0", "Print debug statements in chat", FCVAR_NOTIFY, true, 0.0, true, 2.0); + CreateBoolConVar("sm_cf_show_projectiles_traces", OnConVarChange); } void LogDebug(const char[] format, any...) { @@ -22,3 +34,52 @@ void LogDebug(const char[] format, any...) { PrintToChatAll("[TF2 Competitive Fixes] Debug: %s", message); } } + +static void OnConVarChange(ConVar cvar, const char[] before, const char[] after) { + if (cvar.BoolValue == TruthyConVar(before)) { + return; + } + + if (!DHookToggleDetour(g_detour_CTFWeaponBaseGun_FireProjectile, HOOK_POST, + Detour_CTFWeaponBaseGun_FireProjectile, true)) { + SetFailState("Failed to toggle detour on CCTFWeaponBaseGun::FireProjectile"); + } +} + +static MRESReturn Detour_CTFWeaponBaseGun_FireProjectile(Handle hReturn, Handle hParams) { + if (g_cvar.IntValue < 2) { + return MRES_Ignored; + } + + int entity = DHookGetReturn(hReturn); + + if (entity == -1) { + return MRES_Ignored; + } + + float position[3]; + char entity_id[5]; + + GetEntityPosition(entity, position); + IntToString(entity, entity_id, sizeof(entity_id)); + + g_projectiles_positions.SetArray(entity_id, position, 3); + SDKHook(entity, SDKHook_SetTransmit, DrawProjectilePath); + + return MRES_Handled; +} + +static void DrawProjectilePath(int entity) { + float position[3], last_position[3]; + char entity_id[5]; + + GetEntityPosition(entity, position); + IntToString(entity, entity_id, sizeof(entity_id)); + + g_projectiles_positions.GetArray(entity_id, last_position, 3); + + TE_SetupBeamPoints(last_position, position, g_laser, 0, 0, 66, 20.0, 1.0, 1.0, 1, 0.1, {255, 0, 0, 255}, 0); + TE_SendToAll(); + + g_projectiles_positions.SetArray(entity_id, position, 3); +} diff --git a/scripting/tf2-comp-fixes/remove-pipes-spread.sp b/scripting/tf2-comp-fixes/remove-pipes-spread.sp new file mode 100644 index 0000000..2f8a805 --- /dev/null +++ b/scripting/tf2-comp-fixes/remove-pipes-spread.sp @@ -0,0 +1,120 @@ +#define PIPEBOMB_REMOTE 4 //sticky bomb launcher +#define PIPEBOMB_REMOTE_PRACTICE 14 //sticky jumper + +#define MASK 31 //0b11111 +#define MASK_SIZE 5 + +#define ANGULAR_VELOCITY_Y -1125.89 + +static Handle g_call_CTFGrenadeLauncher_GetProjectileSpeed; + +static Handle g_detour_CTFWeaponBaseGun_FirePipeBomb; +static Handle g_detour_CTFGrenadePipebombProjectile_Create; + +void RemovePipesSpread_Setup(Handle game_config) { + StartPrepSDKCall(SDKCall_Entity); + + if (!PrepSDKCall_SetFromConf(game_config, SDKConf_Signature, + "CTFGrenadeLauncher::GetProjectileSpeed")) { + SetFailState("Failed to finalize SDK call to CTFGrenadeLauncher::GetProjectileSpeed"); + } + + PrepSDKCall_SetReturnInfo(SDKType_Float, SDKPass_Plain); + g_call_CTFGrenadeLauncher_GetProjectileSpeed = EndPrepSDKCall(); + + g_detour_CTFWeaponBaseGun_FirePipeBomb = + CheckedDHookCreateFromConf(game_config, "CTFWeaponBaseGun::FirePipeBomb"); + g_detour_CTFGrenadePipebombProjectile_Create = + CheckedDHookCreateFromConf(game_config, "CTFGrenadePipebombProjectile::Create"); + + CreateBoolConVar("sm_remove_pipes_spread", OnConVarChange); +} + +static void OnConVarChange(ConVar cvar, const char[] before, const char[] after) { + if (cvar.BoolValue == TruthyConVar(before)) { + return; + } + + if (!DHookToggleDetour(g_detour_CTFWeaponBaseGun_FirePipeBomb, HOOK_PRE, + Detour_CTFWeaponBaseGun_FirePipeBomb, cvar.BoolValue)) { + SetFailState("Failed to toggle detour on CTFWeaponBaseGun::FirePipeBomb"); + } + + if (!DHookToggleDetour(g_detour_CTFGrenadePipebombProjectile_Create, HOOK_PRE, + Detour_CTFGrenadePipebombProjectile_Create, cvar.BoolValue)) { + SetFailState("Failed to toggle detour on CTFGrenadePipebombProjectile::Create"); + } +} + +static MRESReturn Detour_CTFWeaponBaseGun_FirePipeBomb(int weapon, Handle hReturn, DHookParam hParams) { + int projectile_type = hParams.Get(2); + + if (IsStickyBomb(projectile_type)) { + return MRES_Ignored; + } + + //The fractional part of the speed is always zero, so we don't lose anything by using RoundToFloor + int speed = RoundToFloor(SDKCall(g_call_CTFGrenadeLauncher_GetProjectileSpeed, weapon)); + + LogDebug("Projectile speed: %d, type: %d", speed, projectile_type); + + /* + We use iPipeBombType parameter to store two values: + - projectile speed + - projectile type + Since the max value of iPipeBombType is 17 (Cannonball), we only need 5 bits to store the projectile type + The remaining bits are used to store the speed value + */ + speed = speed << MASK_SIZE; + hParams.Set(2, speed | projectile_type); + + return MRES_ChangedHandled; +} + +static MRESReturn Detour_CTFGrenadePipebombProjectile_Create(Handle hReturn, DHookParam hParams) { + //now we need to "unpack" the projectile speed and type from iPipeBombType + int param = hParams.Get(7); + int projectile_type = param & MASK; + + if (IsStickyBomb(projectile_type)) { + return MRES_Ignored; + } + + hParams.Set(7, projectile_type); + + int speed = (param & ~MASK) >> MASK_SIZE; + float launchSpeed = float(speed); + + LogDebug("Pipe launch speed: %.2f", launchSpeed); + + float eye_angles[3], velocity[3], fwd[3], up[3]; + + hParams.GetVector(2, eye_angles); + GetAngleVectors(eye_angles, fwd, NULL_VECTOR, up); + + //velocity: launchSpeed * forward + 200 * up; + ScaleVector(fwd, launchSpeed); + ScaleVector(up, 200.0); + AddVectors(fwd, up, velocity); + + hParams.SetVector(3, velocity); + + float ang_velocity[3]; + hParams.GetVector(4, ang_velocity); + + LogDebug("Angular velocity before: { %.2f, %.2f, %.2f }", ang_velocity[0], ang_velocity[1], ang_velocity[2]); + + //if the x-component is nonzero, then the y-component is randomized + if (ang_velocity[0] != 0) { + ang_velocity[1] = ANGULAR_VELOCITY_Y; + hParams.SetVector(4, ang_velocity); + } + + LogDebug("Angular velocity after: { %.2f, %.2f, %.2f }", ang_velocity[0], ang_velocity[1], ang_velocity[2]); + + return MRES_ChangedHandled; +} + +static bool IsStickyBomb(int projectile_type) { + return projectile_type == PIPEBOMB_REMOTE || projectile_type == PIPEBOMB_REMOTE_PRACTICE; +} \ No newline at end of file