v1.2.4.b1
This commit is contained in:
parent
7ad753e3b5
commit
dcf4020981
39 changed files with 434 additions and 204 deletions
|
|
@ -1,5 +1,4 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
|
|
@ -86,7 +85,7 @@ namespace src.player.skills
|
|||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
int flashbangLimit = ConVar.Find("ammo_grenade_limit_flashbang")?.GetPrimitiveValue<int>() ?? 2;
|
||||
int flashbangLimit = SkillUtils.CvarValue("ammo_grenade_limit_flashbang", 2);
|
||||
int grenadeLimit = SkillsInfo.GetValue<int>(skillName, "grenadeLimit");
|
||||
|
||||
if (grenadeLimit > flashbangLimit)
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ namespace src.player.skills
|
|||
damageInfo.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
|
||||
}
|
||||
|
||||
if (owner != null && damageInfo.Damage >= victimPawn.Health)
|
||||
if (owner != null && SkillUtils.IsPredictedLethal(damageInfo, victimPawn))
|
||||
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using System.Collections.Concurrent;
|
||||
|
|
@ -152,7 +151,7 @@ namespace src.player.skills
|
|||
killerEvent.PrintToChat($" {ChatColors.Green}{killerEvent.GetTranslation("bounty_claimed_info", victim.PlayerName, reward)}");
|
||||
}
|
||||
|
||||
private static int GetMaxMoney() => ConVar.Find("mp_maxmoney")?.GetPrimitiveValue<int>() ?? 16000;
|
||||
private static int GetMaxMoney() => SkillUtils.CvarValue("mp_maxmoney", 16000);
|
||||
|
||||
private static bool GiveMoney(CCSPlayerController player, int amount)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ namespace src.player.skills
|
|||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
|
||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim) || attacker == victim) return;
|
||||
if (!SkillUtils.FiresBullets(@event.Weapon)) return;
|
||||
var attackerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||
|
||||
if (Heavyweight.Resists(victim)) return;
|
||||
|
|
@ -29,8 +30,9 @@ namespace src.player.skills
|
|||
if (Instance.Random.NextDouble() <= attackerInfo.SkillChance)
|
||||
{
|
||||
var victimPawn = victim.PlayerPawn?.Value;
|
||||
if (victimPawn != null)
|
||||
victimPawn.AbsVelocity.Z = 300f;
|
||||
if (victimPawn == null || !victimPawn.IsValid || victimPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
|
||||
|
||||
victimPawn.AbsVelocity.Z = 300f;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ namespace src.player.skills
|
|||
float currentTime = Server.CurrentTime;
|
||||
float extraTime = SkillsInfo.GetValue<float>(skillName, "bombArmedTime");
|
||||
|
||||
foreach (var player in PlayerManager.GetTickPlayers().Where(p => p.Team == CsTeam.Terrorist))
|
||||
foreach (var player in PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid && p.Team == CsTeam.Terrorist))
|
||||
{
|
||||
if (player == null || !player.IsValid) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ namespace src.player.skills
|
|||
damageInfo.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
|
||||
}
|
||||
|
||||
if (owner != null && damageInfo.Damage >= victimPawn.Health)
|
||||
if (owner != null && SkillUtils.IsPredictedLethal(damageInfo, victimPawn))
|
||||
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ namespace src.player.skills
|
|||
damageInfo.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
|
||||
}
|
||||
|
||||
if (owner != null && damageInfo.Damage >= victimPawn.Health)
|
||||
if (owner != null && SkillUtils.IsPredictedLethal(damageInfo, victimPawn))
|
||||
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ namespace src.player.skills
|
|||
private const Skills skillName = Skills.ExplosiveShot;
|
||||
|
||||
private static readonly QAngle angle = new(5, 10, -4);
|
||||
private static int lastTick = 0;
|
||||
private static byte pendingTeam = (byte)CsTeam.None;
|
||||
private static readonly ConcurrentDictionary<int, (byte Team, uint Owner)> nades = [];
|
||||
private static readonly ConcurrentDictionary<uint, int> lastTickByPlayer = [];
|
||||
private static readonly ConcurrentDictionary<int, ConcurrentQueue<(byte Team, uint Owner)>> nades = [];
|
||||
|
||||
public static void NewRound()
|
||||
{
|
||||
nades.Clear();
|
||||
lastTickByPlayer.Clear();
|
||||
}
|
||||
|
||||
public static void LoadSkill()
|
||||
|
|
@ -43,9 +43,8 @@ namespace src.player.skills
|
|||
|
||||
private static void SpawnExplosion(Vector vector, CCSPlayerController player)
|
||||
{
|
||||
lastTick = Server.TickCount;
|
||||
pendingTeam = player.TeamNum;
|
||||
nades.AddOrUpdate(Server.TickCount, (player.TeamNum, player.Index), (_, _) => (player.TeamNum, player.Index));
|
||||
lastTickByPlayer[player.Index] = Server.TickCount;
|
||||
nades.GetOrAdd(Server.TickCount, static _ => new ConcurrentQueue<(byte, uint)>()).Enqueue((player.TeamNum, player.Index));
|
||||
SkillUtils.CreateHEGrenadeProjectile(vector, angle, new Vector(0, 0, 0), player.TeamNum);
|
||||
}
|
||||
|
||||
|
|
@ -64,14 +63,16 @@ namespace src.player.skills
|
|||
if (!(NearlyEquals(angle.X, heProjectile.AbsRotation.X) && NearlyEquals(angle.Y, heProjectile.AbsRotation.Y) && NearlyEquals(angle.Z, heProjectile.AbsRotation.Z)))
|
||||
return;
|
||||
|
||||
if (!nades.TryGetValue(spawnTick, out var queue) || !queue.TryDequeue(out var source)) return;
|
||||
if (queue.IsEmpty) nades.TryRemove(spawnTick, out _);
|
||||
|
||||
heProjectile.TicksAtZeroVelocity = 100;
|
||||
heProjectile.TeamNum = pendingTeam;
|
||||
heProjectile.TeamNum = source.Team;
|
||||
heProjectile.Damage = SkillsInfo.GetValue<float>(skillName, "damage");
|
||||
heProjectile.DmgRadius = SkillsInfo.GetValue<float>(skillName, "damageRadius");
|
||||
heProjectile.DetonateTime = 0;
|
||||
|
||||
if (nades.TryRemove(spawnTick, out var source))
|
||||
heProjectile.Globalname = $"explosiveshot_team_{source.Team}_{source.Owner}_{heProjectile.Index}";
|
||||
heProjectile.Globalname = $"explosiveshot_team_{source.Team}_{source.Owner}_{heProjectile.Index}";
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +113,7 @@ namespace src.player.skills
|
|||
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
|
||||
if (owner != null && !owner.IsValid) owner = null;
|
||||
|
||||
if (owner != null && damageInfo.Damage >= victimPawn.Health)
|
||||
if (owner != null && SkillUtils.IsPredictedLethal(damageInfo, victimPawn))
|
||||
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
||||
}
|
||||
|
||||
|
|
@ -120,11 +121,12 @@ namespace src.player.skills
|
|||
|
||||
public static void BulletImpact(EventBulletImpact @event)
|
||||
{
|
||||
if (lastTick == Server.TickCount) return;
|
||||
|
||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
// One explosion per player per tick: a shotgun reports every pellet as its own impact.
|
||||
if (lastTickByPlayer.TryGetValue(player.Index, out int last) && last == Server.TickCount) return;
|
||||
|
||||
var pos = new Vector(@event.X, @event.Y, @event.Z);
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@ namespace src.player.skills
|
|||
{
|
||||
private const Skills skillName = Skills.FireRain;
|
||||
private static readonly ConcurrentDictionary<uint, byte> decoys = [];
|
||||
private static int rainTick = -1;
|
||||
private static CCSPlayerPawn? rainThrower;
|
||||
private static byte rainTeam = (byte)CsTeam.None;
|
||||
private static readonly ConcurrentDictionary<int, ConcurrentQueue<RainBatch>> rainBatches = [];
|
||||
private static readonly ConcurrentDictionary<uint, (uint ThrowerRaw, byte Team)> rainMolotovs = [];
|
||||
|
||||
public static void LoadSkill()
|
||||
|
|
@ -25,6 +23,7 @@ namespace src.player.skills
|
|||
public static void NewRound()
|
||||
{
|
||||
KillAllDecoys();
|
||||
rainBatches.Clear();
|
||||
rainMolotovs.Clear();
|
||||
DecoyRing.ClearAll(skillName);
|
||||
}
|
||||
|
|
@ -54,10 +53,6 @@ namespace src.player.skills
|
|||
const float spawnHeight = 1500.0f;
|
||||
const float approachDistance = 600.0f;
|
||||
|
||||
rainTick = Server.TickCount;
|
||||
rainThrower = pawn;
|
||||
rainTeam = player.TeamNum;
|
||||
|
||||
float startAngle = Random.Shared.NextSingle() * MathF.Tau;
|
||||
|
||||
Vector? skyCenter = null;
|
||||
|
|
@ -88,10 +83,12 @@ namespace src.player.skills
|
|||
|
||||
if (!foundPosition || skyCenter == null)
|
||||
{
|
||||
QueueRain(pawn, player.TeamNum, grenadeGroundCount);
|
||||
CreateMolotovSplash(targetPos, grenadeGroundCount, player.TeamNum);
|
||||
return;
|
||||
}
|
||||
|
||||
QueueRain(pawn, player.TeamNum, grenadeCount);
|
||||
CreateMolotovRaid(targetPos, skyCenter, grenadeCount, player.TeamNum);
|
||||
}
|
||||
|
||||
|
|
@ -202,24 +199,45 @@ namespace src.player.skills
|
|||
}
|
||||
}
|
||||
|
||||
private sealed class RainBatch
|
||||
{
|
||||
public required CCSPlayerPawn Thrower;
|
||||
public required byte Team;
|
||||
public int Remaining;
|
||||
}
|
||||
|
||||
private static void QueueRain(CCSPlayerPawn thrower, byte team, int count)
|
||||
{
|
||||
rainBatches.GetOrAdd(Server.TickCount, static _ => new ConcurrentQueue<RainBatch>())
|
||||
.Enqueue(new RainBatch { Thrower = thrower, Team = team, Remaining = count });
|
||||
}
|
||||
|
||||
private static void ConsumeOne(ConcurrentQueue<RainBatch> queue, RainBatch batch)
|
||||
{
|
||||
if (--batch.Remaining > 0) return;
|
||||
queue.TryDequeue(out _);
|
||||
}
|
||||
|
||||
public static void OnEntitySpawned(CEntityInstance entity)
|
||||
{
|
||||
var name = entity.DesignerName;
|
||||
|
||||
if (name == "molotov_projectile")
|
||||
{
|
||||
if (Server.TickCount != rainTick) return;
|
||||
if (!rainBatches.TryGetValue(Server.TickCount, out var queue) || !queue.TryPeek(out var batch)) return;
|
||||
|
||||
var thrower = rainThrower;
|
||||
if (thrower == null || !thrower.IsValid) return;
|
||||
var thrower = batch.Thrower;
|
||||
if (thrower == null || !thrower.IsValid) { ConsumeOne(queue, batch); return; }
|
||||
|
||||
var molotov = entity.As<CMolotovProjectile>();
|
||||
if (molotov == null || !molotov.IsValid) return;
|
||||
if (molotov == null || !molotov.IsValid) { ConsumeOne(queue, batch); return; }
|
||||
|
||||
molotov.TeamNum = rainTeam;
|
||||
ConsumeOne(queue, batch);
|
||||
|
||||
molotov.TeamNum = batch.Team;
|
||||
molotov.Thrower.Raw = thrower.EntityHandle.Raw;
|
||||
molotov.OwnerEntity.Raw = thrower.EntityHandle.Raw;
|
||||
rainMolotovs[molotov.Index] = (thrower.EntityHandle.Raw, rainTeam);
|
||||
rainMolotovs[molotov.Index] = (thrower.EntityHandle.Raw, batch.Team);
|
||||
|
||||
Server.NextWorldUpdate(() =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
|
|
@ -32,7 +31,7 @@ namespace src.player.skills
|
|||
if (!SkillsInfo.GetValue<bool>(skillName, "manageAutoKick")) return false;
|
||||
|
||||
bool live;
|
||||
try { live = ConVar.Find("mp_autokick")?.GetPrimitiveValue<bool>() ?? false; }
|
||||
try { live = SkillUtils.CvarValue("mp_autokick", false); }
|
||||
catch { return false; }
|
||||
|
||||
if (!live) return false;
|
||||
|
|
|
|||
|
|
@ -150,6 +150,27 @@ namespace src.player.skills
|
|||
}
|
||||
}
|
||||
|
||||
public static void WeaponEquip(EventItemEquip @event)
|
||||
{
|
||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
if (playersWithSkill.TryGetValue(player.Index, out int grenadesLeft) && grenadesLeft > 1)
|
||||
SkillUtils.UpdateGrenadeCount(player, CsItem.DecoyGrenade, grenadesLeft);
|
||||
}
|
||||
|
||||
public static void WeaponPickup(EventItemPickup @event)
|
||||
{
|
||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
var weapon = @event.Item;
|
||||
if (string.IsNullOrEmpty(weapon) || weapon != "decoy") return;
|
||||
|
||||
if (playersWithSkill.TryGetValue(player.Index, out int grenadesLeft) && grenadesLeft > 1)
|
||||
SkillUtils.UpdateGrenadeCount(player, CsItem.DecoyGrenade, grenadesLeft);
|
||||
}
|
||||
|
||||
public static void EnableSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using jRandomSkills.src.utils;
|
||||
|
|
@ -79,7 +78,7 @@ namespace src.player.skills
|
|||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
int flashbangLimit = ConVar.Find("ammo_grenade_limit_flashbang")?.GetPrimitiveValue<int>() ?? 2;
|
||||
int flashbangLimit = SkillUtils.CvarValue("ammo_grenade_limit_flashbang", 2);
|
||||
int grenadeLimit = SkillsInfo.GetValue<int>(skillName, "grenadeLimit");
|
||||
|
||||
if (grenadeLimit > flashbangLimit)
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ namespace src.player.skills
|
|||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||
if (!SkillUtils.HasMenu(player)) continue;
|
||||
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid && p.PawnIsAlive && p.Team != player.Team && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
||||
|
||||
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString())));
|
||||
SkillUtils.UpdateMenu(player, menuItems);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using System.Collections.Concurrent;
|
||||
|
|
@ -20,7 +19,7 @@ namespace src.player.skills
|
|||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
defaultNoSpread = ConVar.Find("weapon_accuracy_nospread")?.GetPrimitiveValue<bool>() ?? false;
|
||||
defaultNoSpread = SkillUtils.CvarValue("weapon_accuracy_nospread", false);
|
||||
}
|
||||
|
||||
public static void NewRound()
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ namespace src.player.skills
|
|||
public class Phoenix : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.Phoenix;
|
||||
private const float DefaultHeadshotMultiplier = 4f;
|
||||
private static readonly ConcurrentDictionary<uint, int> phoenixTicks = new();
|
||||
|
||||
public static void LoadSkill()
|
||||
|
|
@ -53,11 +52,7 @@ namespace src.player.skills
|
|||
|
||||
if (SkillUtils.IsFriendlyFireBlocked(damageInfo, victimPawn)) return;
|
||||
|
||||
float effectiveDamage = damageInfo.Damage;
|
||||
if (SkillUtils.GetHitGroup(damageInfo) == HitGroup_t.HITGROUP_HEAD)
|
||||
effectiveDamage *= GetHeadshotMultiplier(damageInfo);
|
||||
|
||||
if (effectiveDamage < victimPawn.Health) return;
|
||||
if (!SkillUtils.IsPredictedLethal(damageInfo, victimPawn)) return;
|
||||
|
||||
if (TryConsumeRevive(victim, victimPawn))
|
||||
damageInfo.Damage = 0;
|
||||
|
|
@ -99,20 +94,6 @@ namespace src.player.skills
|
|||
return true;
|
||||
}
|
||||
|
||||
private static float GetHeadshotMultiplier(CTakeDamageInfo info)
|
||||
{
|
||||
var ability = info.Ability?.Value;
|
||||
if (ability == null || !ability.IsValid) return DefaultHeadshotMultiplier;
|
||||
|
||||
var weapon = ability.As<CCSWeaponBase>();
|
||||
if (weapon == null || !weapon.IsValid) return DefaultHeadshotMultiplier;
|
||||
|
||||
var vdata = weapon.GetVData<CCSWeaponBaseVData>();
|
||||
if (vdata == null || vdata.HeadshotMultiplier <= 0) return DefaultHeadshotMultiplier;
|
||||
|
||||
return vdata.HeadshotMultiplier;
|
||||
}
|
||||
|
||||
public static void EnableSkill(CCSPlayerController player)
|
||||
{
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
|
|
@ -20,7 +19,7 @@ namespace src.player.skills
|
|||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
defaultC4Timer = ConVar.Find("mp_c4timer")?.GetPrimitiveValue<int>() ?? 40;
|
||||
defaultC4Timer = SkillUtils.CvarValue("mp_c4timer", 40);
|
||||
}
|
||||
|
||||
public static void EnableSkill(CCSPlayerController player)
|
||||
|
|
@ -96,7 +95,7 @@ namespace src.player.skills
|
|||
float currentTime = Server.CurrentTime;
|
||||
bool hudFrame = SkillUtils.IsHudFrame();
|
||||
|
||||
foreach (var player in PlayerManager.GetTickPlayers().Where(p => p.Team == CsTeam.Terrorist))
|
||||
foreach (var player in PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid && p.Team == CsTeam.Terrorist))
|
||||
{
|
||||
if (!Instance.IsPlayerValid(player)) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ namespace src.player.skills
|
|||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim) || attacker == victim)
|
||||
return;
|
||||
|
||||
if (!SkillUtils.FiresBullets(@event.Weapon)) return;
|
||||
|
||||
if (Heavyweight.Resists(victim)) return;
|
||||
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ namespace src.player.skills
|
|||
public class ReZombie : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.ReZombie;
|
||||
private const float DefaultHeadshotMultiplier = 4f;
|
||||
private static readonly ConcurrentDictionary<uint, int> zombies = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
|
|
@ -88,11 +87,7 @@ namespace src.player.skills
|
|||
// Friendly-fire-off teammate hit deals 0 damage; don't zombify over a hit that never lands.
|
||||
if (SkillUtils.IsFriendlyFireBlocked(damageInfo, victimPawn)) return;
|
||||
|
||||
float effectiveDamage = damageInfo.Damage;
|
||||
if (SkillUtils.GetHitGroup(damageInfo) == HitGroup_t.HITGROUP_HEAD)
|
||||
effectiveDamage *= GetHeadshotMultiplier(damageInfo);
|
||||
|
||||
if (effectiveDamage < victimPawn.Health) return; // survivable, let it through
|
||||
if (!SkillUtils.IsPredictedLethal(damageInfo, victimPawn)) return; // survivable, let it through
|
||||
|
||||
if (TryBecomeZombie(victim, victimPawn))
|
||||
damageInfo.Damage = 0;
|
||||
|
|
@ -138,20 +133,6 @@ namespace src.player.skills
|
|||
holdTime: 600);
|
||||
}
|
||||
|
||||
private static float GetHeadshotMultiplier(CTakeDamageInfo info)
|
||||
{
|
||||
var ability = info.Ability?.Value;
|
||||
if (ability == null || !ability.IsValid) return DefaultHeadshotMultiplier;
|
||||
|
||||
var weapon = ability.As<CCSWeaponBase>();
|
||||
if (weapon == null || !weapon.IsValid) return DefaultHeadshotMultiplier;
|
||||
|
||||
var vdata = weapon.GetVData<CCSWeaponBaseVData>();
|
||||
if (vdata == null || vdata.HeadshotMultiplier <= 0) return DefaultHeadshotMultiplier;
|
||||
|
||||
return vdata.HeadshotMultiplier;
|
||||
}
|
||||
|
||||
private static void DropAllBotWeapons(CCSPlayerController player)
|
||||
{
|
||||
if (player == null || !player.IsValid || !player.IsBot) return;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using System.Collections.Concurrent;
|
||||
using static src.jRandomSkills;
|
||||
|
||||
namespace src.player.skills
|
||||
|
|
@ -11,48 +11,49 @@ namespace src.player.skills
|
|||
{
|
||||
private const Skills skillName = Skills.RichBoy;
|
||||
|
||||
private static readonly ConcurrentDictionary<uint, int> accountBeforeBonus = [];
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
private static int GetMaxMoney() => ConVar.Find("mp_maxmoney")?.GetPrimitiveValue<int>() ?? 16000;
|
||||
private static int GetMaxMoney() => SkillUtils.CvarValue("mp_maxmoney", 16000);
|
||||
|
||||
public static void EnableSkill(CCSPlayerController player)
|
||||
{
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (playerInfo == null) return;
|
||||
int moneyBonus = Instance.Random.Next(SkillsInfo.GetValue<int>(skillName, "minMoney"), SkillsInfo.GetValue<int>(skillName, "maxMoney"));
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
var moneyServices = player.InGameMoneyServices;
|
||||
if (moneyServices == null) return;
|
||||
|
||||
int moneyBonus = Instance.Random.Next(SkillsInfo.GetValue<int>(skillName, "minMoney"), SkillsInfo.GetValue<int>(skillName, "maxMoney"));
|
||||
moneyBonus = Math.Min(moneyBonus, GetMaxMoney() - moneyServices.Account);
|
||||
if (moneyBonus <= 0) return;
|
||||
|
||||
playerInfo.SkillChance = moneyBonus;
|
||||
AddMoney(player, moneyBonus);
|
||||
accountBeforeBonus[player.Index] = moneyServices.Account;
|
||||
|
||||
moneyServices.Account += moneyBonus;
|
||||
Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInGameMoneyServices");
|
||||
}
|
||||
|
||||
public static void DisableSkill(CCSPlayerController player)
|
||||
{
|
||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (playerInfo == null) return;
|
||||
if (player == null || !player.IsValid) return;
|
||||
if (!accountBeforeBonus.TryRemove(player.Index, out int accountBefore)) return;
|
||||
|
||||
var moneyServices = player.InGameMoneyServices;
|
||||
if (moneyServices == null) return;
|
||||
|
||||
int money = Math.Abs((int)playerInfo.SkillChance! - moneyServices.CashSpentThisRound);
|
||||
AddMoney(player, -money, 3000);
|
||||
if (moneyServices.Account <= accountBefore) return;
|
||||
|
||||
moneyServices.Account = accountBefore;
|
||||
Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInGameMoneyServices");
|
||||
}
|
||||
|
||||
private static void AddMoney(CCSPlayerController player, int money, int minimum = 0)
|
||||
public static void PlayerDisconnect(uint playerIndex)
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
var moneyServices = player.InGameMoneyServices;
|
||||
if (moneyServices == null) return;
|
||||
|
||||
moneyServices.Account = Math.Clamp(moneyServices.Account + money, minimum, GetMaxMoney());
|
||||
Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInGameMoneyServices");
|
||||
accountBeforeBonus.TryRemove(playerIndex, out _);
|
||||
}
|
||||
|
||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#D4AF37", 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, int minMoney = 5000, int maxMoney = 15000) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using static src.jRandomSkills;
|
||||
|
|
@ -16,7 +15,7 @@ namespace src.player.skills
|
|||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
}
|
||||
|
||||
private static int GetMaxMoney() => ConVar.Find("mp_maxmoney")?.GetPrimitiveValue<int>() ?? 16000;
|
||||
private static int GetMaxMoney() => SkillUtils.CvarValue("mp_maxmoney", 16000);
|
||||
|
||||
public static void PlayerHurt(EventPlayerHurt @event)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ namespace src.player.skills
|
|||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
|
||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim) || attacker == victim) return;
|
||||
if (!SkillUtils.FiresBullets(@event.Weapon)) return;
|
||||
var attackerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||
|
||||
if (Heavyweight.Resists(victim)) return;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ namespace src.player.skills
|
|||
public class SecondLife : ISkill
|
||||
{
|
||||
private const Skills skillName = Skills.SecondLife;
|
||||
private const float DefaultHeadshotMultiplier = 4f;
|
||||
private static readonly ConcurrentDictionary<nint, int> usedThisRound = [];
|
||||
private static readonly object setLock = new();
|
||||
|
||||
|
|
@ -45,11 +44,7 @@ namespace src.player.skills
|
|||
|
||||
if (SkillUtils.IsFriendlyFireBlocked(damageInfo, victimPawn)) return;
|
||||
|
||||
float effectiveDamage = damageInfo.Damage;
|
||||
if (SkillUtils.GetHitGroup(damageInfo) == HitGroup_t.HITGROUP_HEAD)
|
||||
effectiveDamage *= GetHeadshotMultiplier(damageInfo);
|
||||
|
||||
if (effectiveDamage < victimPawn.Health) return;
|
||||
if (!SkillUtils.IsPredictedLethal(damageInfo, victimPawn)) return;
|
||||
|
||||
if (usedThisRound.TryGetValue(victim.Handle, out int savedTick))
|
||||
{
|
||||
|
|
@ -62,20 +57,6 @@ namespace src.player.skills
|
|||
damageInfo.Damage = 0;
|
||||
}
|
||||
|
||||
private static float GetHeadshotMultiplier(CTakeDamageInfo info)
|
||||
{
|
||||
var ability = info.Ability?.Value;
|
||||
if (ability == null || !ability.IsValid) return DefaultHeadshotMultiplier;
|
||||
|
||||
var weapon = ability.As<CCSWeaponBase>();
|
||||
if (weapon == null || !weapon.IsValid) return DefaultHeadshotMultiplier;
|
||||
|
||||
var vdata = weapon.GetVData<CCSWeaponBaseVData>();
|
||||
if (vdata == null || vdata.HeadshotMultiplier <= 0) return DefaultHeadshotMultiplier;
|
||||
|
||||
return vdata.HeadshotMultiplier;
|
||||
}
|
||||
|
||||
public static bool TryConsumeRevive(CCSPlayerController? victim, CCSPlayerPawn? victimPawn)
|
||||
{
|
||||
if (victim == null || !victim.IsValid) return false;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ namespace src.player.skills
|
|||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim)) return;
|
||||
|
||||
if (attacker!.Index == victim!.Index) return;
|
||||
if (!SkillUtils.FiresBullets(@event.Weapon)) return;
|
||||
|
||||
var victimInfo = PlayerManager.GetPlayerByIndex(victim!.Index);
|
||||
var attackerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using src.utils;
|
||||
using static src.jRandomSkills;
|
||||
|
|
@ -17,7 +16,7 @@ namespace src.player.skills
|
|||
public static void LoadSkill()
|
||||
{
|
||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||
defaultC4Timer = ConVar.Find("mp_c4timer")?.GetPrimitiveValue<int>() ?? 40;
|
||||
defaultC4Timer = SkillUtils.CvarValue("mp_c4timer", 40);
|
||||
}
|
||||
|
||||
public static void EnableSkill(CCSPlayerController player)
|
||||
|
|
|
|||
|
|
@ -26,23 +26,30 @@ namespace src.player.skills
|
|||
}
|
||||
|
||||
private static bool roundEnded;
|
||||
private static readonly ConcurrentDictionary<uint, byte> owedKnife = [];
|
||||
|
||||
public static void NewRound()
|
||||
{
|
||||
roundEnded = false;
|
||||
KillAllKnives();
|
||||
owedKnife.Clear();
|
||||
}
|
||||
|
||||
public static void RoundEnd()
|
||||
{
|
||||
roundEnded = true;
|
||||
KillAllKnives();
|
||||
KillAllKnives(rememberOwed: true);
|
||||
}
|
||||
|
||||
private static void KillAllKnives()
|
||||
private static void KillAllKnives(bool rememberOwed = false)
|
||||
{
|
||||
foreach (var knifeInfo in knivesInfo.Values.ToArray())
|
||||
{
|
||||
if (rememberOwed && knifeInfo.IsDropped)
|
||||
owedKnife[knifeInfo.PlayerIndex] = 0;
|
||||
|
||||
DisableKnifeSkill(knifeInfo);
|
||||
}
|
||||
|
||||
knivesInfo.Clear();
|
||||
}
|
||||
|
|
@ -155,18 +162,24 @@ namespace src.player.skills
|
|||
|
||||
public static void DisableSkill(CCSPlayerController player)
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
bool wasDropped = owedKnife.TryRemove(player.Index, out _);
|
||||
|
||||
if (knivesInfo.TryRemove(player.Index, out KnifeInfo? knifeInfo) && knifeInfo != null)
|
||||
{
|
||||
bool wasDropped = knifeInfo.IsDropped;
|
||||
wasDropped |= knifeInfo.IsDropped;
|
||||
DisableKnifeSkill(knifeInfo);
|
||||
|
||||
if (wasDropped && player != null && player.IsValid && player.PawnIsAlive)
|
||||
player.GiveNamedItem("weapon_knife");
|
||||
}
|
||||
|
||||
if (wasDropped && player.PawnIsAlive)
|
||||
player.GiveNamedItem("weapon_knife");
|
||||
}
|
||||
|
||||
public static void PlayerDisconnect(uint playerIndex)
|
||||
{
|
||||
owedKnife.TryRemove(playerIndex, out _);
|
||||
|
||||
if (knivesInfo.TryRemove(playerIndex, out KnifeInfo? knifeInfo) && knifeInfo != null)
|
||||
DisableKnifeSkill(knifeInfo);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace src.player.skills
|
|||
private const Skills skillName = Skills.TrueArmor;
|
||||
|
||||
private static readonly ConcurrentDictionary<uint, byte> holders = [];
|
||||
private static readonly ConcurrentDictionary<uint, int> pending = [];
|
||||
private static readonly ConcurrentDictionary<uint, PendingArmor> pending = [];
|
||||
|
||||
public static void LoadSkill()
|
||||
{
|
||||
|
|
@ -62,13 +62,16 @@ namespace src.player.skills
|
|||
|
||||
info.Damage = damage - absorbed;
|
||||
|
||||
pending[victim.Index] = (int)MathF.Round(armor - absorbed);
|
||||
int restoredArmor = (int)MathF.Round(armor - absorbed);
|
||||
bool hadHelmet = victimPawn.ItemServices?.As<CCSPlayer_ItemServices>()?.HasHelmet ?? false;
|
||||
|
||||
pending[victim.Index] = new PendingArmor(restoredArmor, hadHelmet);
|
||||
victimPawn.ArmorValue = 0;
|
||||
|
||||
Utilities.SetStateChanged(victimPawn, "CCSPlayerPawn", "m_ArmorValue");
|
||||
|
||||
Debug.WriteToDebug($"[TrueArmor] {victim.PlayerName}: raw={damage:0.#} absorbed={absorbed:0.#} " +
|
||||
$"passed={info.Damage:0.#} armor={armor}->{pending[victim.Index]} hp={victimPawn.Health}", DebugCategory.Damage);
|
||||
$"passed={info.Damage:0.#} armor={armor}->{restoredArmor} helmet={hadHelmet} hp={victimPawn.Health}", DebugCategory.Damage);
|
||||
}
|
||||
|
||||
public static void OnTakeDamagePost(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, CTakeDamageResult damageResult)
|
||||
|
|
@ -76,12 +79,22 @@ namespace src.player.skills
|
|||
if (pending.IsEmpty) return;
|
||||
|
||||
if (!TryResolveVictim(damagedEntity, damageInfo, out var victim, out var victimPawn, out _)) return;
|
||||
if (!pending.TryRemove(victim!.Index, out int restored)) return;
|
||||
if (!pending.TryRemove(victim!.Index, out var restored)) return;
|
||||
|
||||
victimPawn!.ArmorValue = restored;
|
||||
victimPawn!.ArmorValue = restored.Armor;
|
||||
Utilities.SetStateChanged(victimPawn, "CCSPlayerPawn", "m_ArmorValue");
|
||||
|
||||
if (!restored.HasHelmet) return;
|
||||
|
||||
var itemServices = victimPawn.ItemServices?.As<CCSPlayer_ItemServices>();
|
||||
if (itemServices == null || itemServices.HasHelmet) return;
|
||||
|
||||
itemServices.HasHelmet = true;
|
||||
Utilities.SetStateChanged(victim, "CCSPlayerController", "m_bPawnHasHelmet");
|
||||
}
|
||||
|
||||
private readonly record struct PendingArmor(int Armor, bool HasHelmet);
|
||||
|
||||
private static bool TryResolveVictim(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, out CCSPlayerController? victim, out CCSPlayerPawn? pawn, out CTakeDamageInfo? info)
|
||||
{
|
||||
victim = null;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue