v1.2.3.b9
- #### Fixes
- **Pyro** - Fire damage killed the holder at low health. The regeneration now runs before the damage is applied instead of after.
- **Iana** - The clone did not absorb a lethal hit, same cause as Pyro.
- **Dracula** - Lifesteal used the raw damage, so an AWP headshot healed for 400. It is now capped to the health the victim actually had, works on the killing blow, and can no longer leave the attacker alive at 0 HP.
- **RobinHood** - Stolen money used the raw damage and a hardcoded 16000 cap. It is now capped to the victim's real health, works on the killing blow, and respects `mp_maxmoney`. The victim loses exactly what the attacker gains.
- **Voodoo** - Reflected damage used the raw damage, now capped to the victim's real health.
- **Config** - The `Rifle`, `Pistol` and `Grenade` weapon pools grew every time the config was loaded, so removed weapons came back on the next start.
- **Use button** - Skills no longer trigger when a CT presses `Use` while looking at a planted C4, so the skill key no longer competes with the defuse key.
- #### Other
- Built against CounterStrikeSharp 1.0.373.
This commit is contained in:
parent
d5fefbe178
commit
a62ec611ca
17 changed files with 150 additions and 62 deletions
|
|
@ -31,7 +31,7 @@ namespace src
|
|||
public override string ModuleName => "[CS2] [ jRandomSkills ]";
|
||||
public override string ModuleAuthor => "D3X (Original), Juzlus (Modifier), ByDexterTR (Contributor)";
|
||||
public override string ModuleDescription => "Plugin adds random skills every round for CS2 by D3X. Modified by Juzlus.";
|
||||
public override string ModuleVersion => "1.2.3.b8";
|
||||
public override string ModuleVersion => "1.2.3.b9";
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
|
|
@ -89,6 +89,7 @@ namespace src.player
|
|||
lock (setLock)
|
||||
{
|
||||
CountDamageResolution(info);
|
||||
SkillUtils.TrackHealthBeforeHit(entity, info);
|
||||
|
||||
object[] args = [entity, info];
|
||||
DispatchOnTakeDamage(entity, info, args);
|
||||
|
|
|
|||
|
|
@ -798,6 +798,8 @@ namespace src.player
|
|||
return;
|
||||
}
|
||||
|
||||
if (designer == "planted_c4" && player.Team == CsTeam.CounterTerrorist) return;
|
||||
|
||||
if (designer.Contains("button") || designer.Contains("weapon") || designer.Contains("blocker")) return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Modules.Admin;
|
||||
|
|
@ -182,6 +182,7 @@ namespace src.player
|
|||
Instance.RemoveListener<CheckTransmit>(CheckTransmit);
|
||||
|
||||
Fortnite.skillInThisRound = false;
|
||||
SkillUtils.ClearHealthBeforeHit();
|
||||
|
||||
EntityManager.SuppressKills = true;
|
||||
try
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
|
|
@ -9,7 +9,6 @@ namespace src.player.skills
|
|||
public class Dracula : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.Dracula;
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
|
|
@ -20,11 +19,11 @@ namespace src.player.skills
|
|||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
|
||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim) || attacker == victim) return;
|
||||
if (!Instance.IsPlayerValid(attacker) || victim == null || !victim.IsValid || attacker == victim) return;
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||
if (playerInfo?.Skill != skillName) return;
|
||||
|
||||
if (playerInfo?.Skill == skillName && victim!.PawnIsAlive)
|
||||
HealAttacker(attacker!, @event.DmgHealth);
|
||||
HealAttacker(attacker!, SkillUtils.CapToVictimHealth(victim, @event.DmgHealth));
|
||||
}
|
||||
|
||||
public static void DisableSkill(CCSPlayerController player)
|
||||
|
|
@ -46,22 +45,30 @@ namespace src.player.skills
|
|||
|
||||
private static void HealAttacker(CCSPlayerController attacker, float damage)
|
||||
{
|
||||
var attackerPawn = attacker.PlayerPawn?.Value;
|
||||
if (attackerPawn == null || !attackerPawn.IsValid) return;
|
||||
|
||||
if (attackerPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || attackerPawn.Health <= 0) return;
|
||||
|
||||
int extraHealth = (int)(damage * SkillsInfo.GetValue<float>(skillName, "healthRegainScale"));
|
||||
if (extraHealth <= 0) return;
|
||||
|
||||
attackerPawn.Health += extraHealth;
|
||||
Utilities.SetStateChanged(attackerPawn, "CBaseEntity", "m_iHealth");
|
||||
uint attackerIndex = attacker.Index;
|
||||
|
||||
if (attackerPawn.MaxHealth < attackerPawn.Health)
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
attackerPawn.MaxHealth = attackerPawn.Health;
|
||||
Utilities.SetStateChanged(attackerPawn, "CBaseEntity", "m_iMaxHealth");
|
||||
}
|
||||
var target = Utilities.GetPlayerFromIndex((int)attackerIndex);
|
||||
if (target == null || !target.IsValid) return;
|
||||
|
||||
var attackerPawn = target.PlayerPawn?.Value;
|
||||
if (attackerPawn == null || !attackerPawn.IsValid) return;
|
||||
|
||||
if (attackerPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || attackerPawn.Health <= 0) return;
|
||||
|
||||
attackerPawn.Health += extraHealth;
|
||||
Utilities.SetStateChanged(attackerPawn, "CBaseEntity", "m_iHealth");
|
||||
|
||||
if (attackerPawn.MaxHealth < attackerPawn.Health)
|
||||
{
|
||||
attackerPawn.MaxHealth = attackerPawn.Health;
|
||||
Utilities.SetStateChanged(attackerPawn, "CBaseEntity", "m_iMaxHealth");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FA050D", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float healthRegainScale = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
|
|
|
|||
|
|
@ -302,13 +302,43 @@ namespace src.player.skills
|
|||
return shooter != null && shooter.IsValid ? shooter : null;
|
||||
}
|
||||
|
||||
private static void AbsorbWithClone(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||
{
|
||||
if (damageInfo.Damage <= 0) return;
|
||||
|
||||
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||
if (!victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
||||
|
||||
var victimController = victimPawn.Controller?.Value;
|
||||
if (victimController == null || !victimController.IsValid) return;
|
||||
|
||||
var victim = PlayerManager.GetPlayerEvent(victimController.As<CCSPlayerController>());
|
||||
if (victim == null || !victim.IsValid) return;
|
||||
|
||||
if (PlayerManager.GetPlayerByIndex(victim.Index)?.Skill != skillName) return;
|
||||
if (!playersInfo.TryGetValue(victim.Index, out var playerSkill)) return;
|
||||
|
||||
bool hasClone = playerSkill.CloneProp != null;
|
||||
if (!hasClone && playerSkill.LastHit + 4 <= Server.TickCount) return;
|
||||
|
||||
damageInfo.Damage = 0;
|
||||
|
||||
if (!hasClone) return;
|
||||
|
||||
KillClone(playerSkill);
|
||||
playerSkill.LastHit = Server.TickCount;
|
||||
}
|
||||
|
||||
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||
{
|
||||
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrEmpty(damagedEntity.Entity.Name)) return;
|
||||
if (!damagedEntity.Entity.Name.StartsWith("IanaClone_")) return;
|
||||
if (string.IsNullOrEmpty(damagedEntity.Entity.Name) || !damagedEntity.Entity.Name.StartsWith("IanaClone_"))
|
||||
{
|
||||
AbsorbWithClone(damagedEntity, damageInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
var nameParams = damagedEntity.Entity.Name.Split('_')[2];
|
||||
_ = uint.TryParse(nameParams, out uint userIndex);
|
||||
|
|
@ -357,26 +387,6 @@ namespace src.player.skills
|
|||
}
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
{
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
if (victim == null || !victim.IsValid) return;
|
||||
|
||||
if (PlayerManager.GetPlayerByIndex(victim.Index)?.Skill != skillName) return;
|
||||
|
||||
if (playersInfo.TryGetValue(victim.Index, out var playerSkill))
|
||||
{
|
||||
if (playerSkill.CloneProp != null)
|
||||
{
|
||||
KillClone(playerSkill);
|
||||
playerSkill.LastHit = Server.TickCount;
|
||||
}
|
||||
|
||||
if (playerSkill.LastHit + 4 > Server.TickCount)
|
||||
SkillUtils.RestoreHealth(victim);
|
||||
}
|
||||
}
|
||||
|
||||
private static void BlockWeapon(CCSPlayerController player, bool block)
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
|
|
@ -17,19 +17,46 @@ namespace src.player.skills
|
|||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
private const string infernoDamage = "inferno";
|
||||
|
||||
private static bool IsInfernoDamage(CTakeDamageInfo damageInfo)
|
||||
{
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
int damage = @event.DmgHealth;
|
||||
string weapon = @event.Weapon;
|
||||
var ability = damageInfo.Ability?.Value;
|
||||
if (ability != null && ability.IsValid && ability.DesignerName == infernoDamage) return true;
|
||||
|
||||
if (weapon != "inferno" || !Instance.IsPlayerValid(victim)) return;
|
||||
var victimInfo = PlayerManager.GetPlayerByIndex(victim!.Index);
|
||||
var inflictor = damageInfo.Inflictor?.Value;
|
||||
return inflictor != null && inflictor.IsValid && inflictor.DesignerName == infernoDamage;
|
||||
}
|
||||
|
||||
if (victimInfo == null || victimInfo.Skill != skillName) return;
|
||||
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||
{
|
||||
if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return;
|
||||
if (damageInfo.Damage <= 0 || !IsInfernoDamage(damageInfo)) return;
|
||||
|
||||
var pawn = victim!.PlayerPawn.Value;
|
||||
SkillUtils.AddHealth(pawn, (int)(damage * SkillsInfo.GetValue<float>(skillName, "regenerationMultiplier")));
|
||||
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||
if (!victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
||||
|
||||
var victimController = victimPawn.Controller?.Value;
|
||||
if (victimController == null || !victimController.IsValid) return;
|
||||
|
||||
var victim = PlayerManager.GetPlayerEvent(victimController.As<CCSPlayerController>());
|
||||
if (victim == null || !victim.IsValid) return;
|
||||
|
||||
if (PlayerManager.GetPlayerByIndex(victim.Index)?.Skill != skillName) return;
|
||||
|
||||
float damage = damageInfo.Damage;
|
||||
float net = damage * (SkillsInfo.GetValue<float>(skillName, "regenerationMultiplier") - 1f);
|
||||
|
||||
if (net < 0)
|
||||
{
|
||||
damageInfo.Damage = -net;
|
||||
return;
|
||||
}
|
||||
|
||||
damageInfo.Damage = 0;
|
||||
|
||||
int heal = (int)MathF.Round(net);
|
||||
if (heal > 0) SkillUtils.AddHealth(victimPawn, heal);
|
||||
}
|
||||
|
||||
public static void GrenadeThrown(EventGrenadeThrown @event)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using static src.jRandomSkills;
|
||||
|
|
@ -26,7 +26,6 @@ namespace src.player.skills
|
|||
{
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
int damage = @event.DmgHealth;
|
||||
|
||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim) || attacker == victim) return;
|
||||
var attackerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using static src.jRandomSkills;
|
||||
|
|
@ -15,16 +16,18 @@ namespace src.player.skills
|
|||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
private static int GetMaxMoney() => ConVar.Find("mp_maxmoney")?.GetPrimitiveValue<int>() ?? 16000;
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
{
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
var damage = @event.DmgHealth;
|
||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim) || attacker == victim) return;
|
||||
if (!Instance.IsPlayerValid(attacker) || victim == null || !victim.IsValid || attacker == victim) return;
|
||||
|
||||
var attackerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||
if (attackerInfo?.Skill != skillName) return;
|
||||
|
||||
int damage = SkillUtils.CapToVictimHealth(victim, @event.DmgHealth);
|
||||
int moneyToSteal = damage * SkillsInfo.GetValue<int>(skillName, "moneyMultiplier");
|
||||
StealMoney(victim!, attacker!, moneyToSteal);
|
||||
}
|
||||
|
|
@ -35,11 +38,17 @@ namespace src.player.skills
|
|||
var attackerMoneyServices = attacker?.InGameMoneyServices;
|
||||
if (victimMoneyServices == null || attackerMoneyServices == null) return;
|
||||
|
||||
var moneyToAdd = victimMoneyServices.Account < money ? victimMoneyServices.Account : money;
|
||||
victimMoneyServices.Account = Math.Max(victimMoneyServices.Account - money, 0);
|
||||
int maxMoney = GetMaxMoney();
|
||||
int headroom = Math.Max(maxMoney - attackerMoneyServices.Account, 0);
|
||||
|
||||
int transfer = Math.Min(money, victimMoneyServices.Account);
|
||||
transfer = Math.Min(transfer, headroom);
|
||||
if (transfer <= 0) return;
|
||||
|
||||
victimMoneyServices.Account -= transfer;
|
||||
Utilities.SetStateChanged(victim!, "CCSPlayerController", "m_pInGameMoneyServices");
|
||||
|
||||
attackerMoneyServices.Account = Math.Min(attackerMoneyServices.Account + moneyToAdd, 16000);
|
||||
attackerMoneyServices.Account += transfer;
|
||||
Utilities.SetStateChanged(attacker!, "CCSPlayerController", "m_pInGameMoneyServices");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using jRandomSkills.src.utils;
|
||||
|
|
@ -138,7 +138,7 @@ namespace src.player.skills
|
|||
if (victimPawn == null || !victimPawn.IsValid || victimPawn.Health <= 0) return;
|
||||
if (victimPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
|
||||
|
||||
int reflected = (int)MathF.Round(@event.DmgHealth * SkillsInfo.GetValue<float>(skillName, "reflectPercent"));
|
||||
int reflected = (int)MathF.Round(SkillUtils.CapToVictimHealth(owner, @event.DmgHealth) * SkillsInfo.GetValue<float>(skillName, "reflectPercent"));
|
||||
if (reflected < 1) return;
|
||||
|
||||
SkillUtils.TakeHealth(victimPawn, reflected, owner, KillfeedIcons.Fist);
|
||||
|
|
|
|||
|
|
@ -290,8 +290,11 @@ namespace src.utils
|
|||
|
||||
public class WeaponPools
|
||||
{
|
||||
[JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)]
|
||||
public required List<string> Rifle { get; set; }
|
||||
[JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)]
|
||||
public required List<string> Pistol { get; set; }
|
||||
[JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)]
|
||||
public required List<string> Grenade { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using RayTraceAPI;
|
|||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using TraceOptions = RayTraceAPI.TraceOptions;
|
||||
using TraceResult = RayTraceAPI.TraceResult;
|
||||
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
|
||||
|
||||
namespace src.utils
|
||||
|
|
|
|||
|
|
@ -619,6 +619,34 @@ namespace src.utils
|
|||
.Cast<CCSPlayerController>()];
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<uint, int> healthBeforeHit = [];
|
||||
|
||||
public static void TrackHealthBeforeHit(CBaseEntity? damagedEntity, CTakeDamageInfo? damageInfo)
|
||||
{
|
||||
if (damagedEntity == null || damageInfo == null || damageInfo.Damage <= 0) return;
|
||||
|
||||
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||
if (!victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
||||
|
||||
var victimController = victimPawn.Controller?.Value;
|
||||
if (victimController == null || !victimController.IsValid) return;
|
||||
|
||||
var victim = PlayerManager.GetPlayerEvent(victimController.As<CCSPlayerController>());
|
||||
if (victim == null || !victim.IsValid) return;
|
||||
|
||||
healthBeforeHit[victim.Index] = victimPawn.Health;
|
||||
}
|
||||
|
||||
public static int CapToVictimHealth(CCSPlayerController? victim, int dmgHealth)
|
||||
{
|
||||
if (victim == null || !victim.IsValid) return dmgHealth;
|
||||
if (!healthBeforeHit.TryGetValue(victim.Index, out int healthBefore) || healthBefore <= 0) return dmgHealth;
|
||||
|
||||
return dmgHealth > healthBefore ? healthBefore : dmgHealth;
|
||||
}
|
||||
|
||||
public static void ClearHealthBeforeHit() => healthBeforeHit.Clear();
|
||||
|
||||
public static bool TakeHealth(CCSPlayerPawn? pawn, int damage, CCSPlayerController? damageAttacker = null, KillfeedIcons? killfeedIcon = null)
|
||||
{
|
||||
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue