Skip to content

Commit

Permalink
feat(wip): shotgun's special ability
Browse files Browse the repository at this point in the history
  • Loading branch information
Nopied committed Aug 28, 2023
1 parent 0fd3feb commit aa73293
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 33 deletions.
12 changes: 12 additions & 0 deletions addons/sourcemod/gamedata/potry.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
"linux" "@_ZN18CTFWeaponBaseMelee5SmackEv"
"windows" " "
}
"CTFShotgun::PrimaryAttack"
{
"linux" "@_ZN10CTFShotgun13PrimaryAttackEv"
"windows" " "
}
"CTFPlayer::CanPlayerMove"
{
"linux" "@_ZNK9CTFPlayer13CanPlayerMoveEv"
Expand Down Expand Up @@ -182,6 +187,13 @@
"return" "void"
"this" "entity"
}
"CTFShotgun::PrimaryAttack"
{
"signature" "CTFShotgun::PrimaryAttack"
"callconv" "thiscall"
"return" "void"
"this" "entity"
}
"CTFPlayer::CanPlayerMove"
{
"signature" "CTFPlayer::CanPlayerMove"
Expand Down
47 changes: 14 additions & 33 deletions addons/sourcemod/scripting/freak_fortress_2.sp
Original file line number Diff line number Diff line change
Expand Up @@ -2840,17 +2840,17 @@ public Action TF2Items_OnGiveNamedItem(int client, char[] classname, int iItemDe

if(TF2_GetPlayerClass(client)==TFClass_Heavy)
{
if(!StrContains(classname, "tf_weapon_shotgun", false))
{
Handle itemOverride=PrepareItemHandle(item, _, _, "741 ; 50.0");
//741: On Hit: Gain up to +%1$s health per attack

if(itemOverride!=null)
{
item=itemOverride;
return Plugin_Changed;
}
}
// if(!StrContains(classname, "tf_weapon_shotgun", false))
// {
// Handle itemOverride=PrepareItemHandle(item, _, _, "741 ; 50.0");
// //741: On Hit: Gain up to +%1$s health per attack

// if(itemOverride!=null)
// {
// item=itemOverride;
// return Plugin_Changed;
// }
// }

if(!StrContains(classname, "tf_weapon_minigun", false))
{
Expand Down Expand Up @@ -5909,28 +5909,9 @@ public Action OnTakeDamageAlive(int client, int& iAttacker, int& inflictor, floa
}
}

if(TF2_GetPlayerClass(iAttacker) == TFClass_Heavy)
{
if(StrContains(classname, "tf_weapon_shotgun")!=-1)
{
int maxHealth = GetEntProp(iAttacker, Prop_Data, "m_iMaxHealth");
int currentHealth = GetEntProp(iAttacker, Prop_Data, "m_iHealth");
if(currentHealth <= maxHealth * 2)
{
currentHealth += 50;
TF2Util_TakeHealth(iAttacker, 50.0, TAKEHEALTH_IGNORE_MAXHEALTH);

if(currentHealth > maxHealth * 2)
{
SetEntProp(iAttacker, Prop_Data, "m_iHealth", maxHealth * 2);
}
}
}

float charge = GetEntPropFloat(iAttacker, Prop_Send, "m_flRageMeter") + (damage * 0.056);
SetEntPropFloat(iAttacker, Prop_Send, "m_flRageMeter",
charge > 100.0 ? 100.0 : charge);
}
// float charge = GetEntPropFloat(iAttacker, Prop_Send, "m_flRageMeter") + (damage * 0.056);
// SetEntPropFloat(iAttacker, Prop_Send, "m_flRageMeter",
// charge > 100.0 ? 100.0 : charge);

if(damagecustom==TF_WEAPON_SENTRY_BULLET)
{
Expand Down
226 changes: 226 additions & 0 deletions addons/sourcemod/scripting/freak_fortress_2/shotgun_special.sp
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
#include <sourcemod>
#include <sdkhooks>
#include <sdktools>
#include <tf2attributes>
#include <dhooks>
#include <tf2utils>
#include <freak_fortress_2>
#include <ff2_modules/general>

#define PLUGIN_VERSION "20230828"

public Plugin myinfo=
{
name="Freak Fortress 2: Shotgun Special",
author="Nopied◎",
description="FF2: Shotgun's special abilities",
version=PLUGIN_VERSION,
};

#define min(%1,%2) (((%1) < (%2)) ? (%1) : (%2))
#define max(%1,%2) (((%1) > (%2)) ? (%1) : (%2))

#define FOREACH_PLAYER(%1) for(int %1 = 1; %1 <= MaxClients; %1++)

enum
{
Shotgun_Normal = 0,
Shotgun_Vampire,

ShotgunType_MAX
};

bool g_bShotgunFired = false;
int g_iCurrentShotgunType = Shotgun_Normal;

int g_iPlayerShotgunType[MAXPLAYERS + 1];


public void OnPluginStart()
{
GameData gamedata = new GameData("potry");
CreateDynamicDetour(gamedata, "CTFShotgun::PrimaryAttack", DHookCallback_PrimaryAttack_Pre);
}

static void CreateDynamicDetour(GameData gamedata, const char[] name, DHookCallback callbackPre = INVALID_FUNCTION, DHookCallback callbackPost = INVALID_FUNCTION)
{
DynamicDetour detour = DynamicDetour.FromConf(gamedata, name);
if (detour)
{
if (callbackPre != INVALID_FUNCTION)
detour.Enable(Hook_Pre, callbackPre);

if (callbackPost != INVALID_FUNCTION)
detour.Enable(Hook_Post, callbackPost);
}
else
{
LogError("Failed to create detour setup handle for %s", name);
}
}

public MRESReturn DHookCallback_PrimaryAttack_Pre(int weapon)
{
int owner = GetEntPropEnt(weapon, Prop_Send, "m_hOwnerEntity");
if(!IsValidClient(owner)) return MRES_Ignored;

ShotgunAbility_Ready(g_iPlayerShotgunType[owner]);
switch(g_iCurrentShotgunType)
{
case Shotgun_Vampire:
{
ShotgunVampire_Init(owner);
}
}

return MRES_Ignored;
}

public void OnClientPostAdminCheck(int client)
{
g_iPlayerShotgunType[client] = Shotgun_Normal;
SDKHook(client, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
}

// public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float vel[3], const float angles[3], int weapon, int subtype, int cmdnum, int tickcount, int seed, const int mouse[2])
// {
// HealOnHit = false;
// }

public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3], float angles[3], int& newWeapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2])
{
if(!IsValidClient(client) || IsBoss(client)) return Plugin_Continue;

int weapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");
if(IsValidEntity(weapon))
{
// Is buttons called only once?
if((buttons & IN_ATTACK2) > 0 && IsShotgun(weapon))
{
g_iPlayerShotgunType[client] = ++g_iPlayerShotgunType[client] % ShotgunType_MAX;

// TODO: Sound
}

// float attackTime = GetEntPropFloat(weapon, Prop_Send, "m_flNextPrimaryAttack");
// if(attackTime < GetGameTime()
// && (buttons & IN_ATTACK2) > 0)
// {
// if(IsShotgun(weapon))
// {
// buttons &= ~IN_ATTACK2;
// buttons |= IN_ATTACK;
// bChanged = true;
// }
// }
}

return Plugin_Continue;
}

public void FF2_OnCalledQueue(FF2HudQueue hudQueue, int client)
{
int weapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");

// NOTE: IsShotgun is quite heavy, on this call.
if(!IsValidEntity(weapon) || !IsShotgun(weapon)) return;

FF2HudDisplay hudDisplay = null;
char text[60];
hudQueue.GetName(text, sizeof(text));

if(StrEqual(text, "Player Additional"))
{
// TODO:
if(g_iPlayerShotgunType[client] == Shotgun_Vampire)
{
Format(text, sizeof(text), "Shotgun Vampire");
hudDisplay = FF2HudDisplay.CreateDisplay("Shotgun Special", text);
hudQueue.PushDisplay(hudDisplay);
}
}
}

public Action OnTakeDamageAlive(int client, int& iAttacker, int& inflictor, float& damage, int& damagetype, int& weapon, float damageForce[3], float damagePosition[3], int damagecustom)
{
if(!g_bShotgunFired) return Plugin_Continue;

bool bChanged = false;

switch(g_iCurrentShotgunType)
{
case Shotgun_Vampire:
{
bChanged = true;
damagetype = DMG_BULLET;

static float healMaxCap = 30.0;
int maxHealth = TF2Util_GetPlayerMaxHealthBoost(iAttacker, false, false),
currentHealth = GetEntProp(iAttacker, Prop_Data, "m_iHealth");

float realDamage = damage;
if(TF2_IsPlayerInCondition(iAttacker, TFCond_Buffed))
realDamage *= 1.35;

float heal = min(healMaxCap, realDamage);

currentHealth += RoundFloat(heal);
if(currentHealth > maxHealth)
heal -= currentHealth - maxHealth;

TF2Util_TakeHealth(iAttacker, heal, TAKEHEALTH_IGNORE_MAXHEALTH);
}
}

return bChanged ? Plugin_Changed : Plugin_Continue;
}

// On Fired
void ShotgunVampire_Init(int client)
{
ShotgunAbility_Ready(Shotgun_Vampire);
float tickInterval = 0.1;
// GetTickInterval() is sometimes broken

TF2Attrib_AddCustomPlayerAttribute(client, "crits_become_minicrits", 1.0, tickInterval);
TF2Attrib_AddCustomPlayerAttribute(client, "damage penalty", 0.5, tickInterval);
}

public void CancelFF2WeaponAbility(int client)
{
g_bShotgunFired = false;
}

void ShotgunAbility_Ready(int type)
{
g_bShotgunFired = true;
g_iCurrentShotgunType = type;
}

bool IsShotgun(int weapon)
{
char classname[64];
GetEntityClassname(weapon, classname, sizeof(classname));

if((StrContains(classname, "tf_weapon_shotgun") != -1
|| StrContains(classname, "tf_weapon_sentry_revenge") != -1
|| StrContains(classname, "tf_weapon_scattergun") != -1)
// except this below.
// TODO: NOT classname specific, use attribute.
&& !StrEqual(classname, "tf_weapon_shotgun_building_rescue"))
{
return true;
}

return false;
}

stock bool IsValidClient(int client)
{
return (0 < client && client <= MaxClients && IsClientInGame(client));
}

stock bool IsBoss(int client)
{
return FF2_GetBossIndex(client) != -1;
}

0 comments on commit aa73293

Please sign in to comment.