diff --git a/WASDMenuAPI - SRC Files/Classes/WasdMenu.cs b/WASDMenuAPI - SRC Files/Classes/WasdMenu.cs index de2aa0a..b7fae9c 100644 --- a/WASDMenuAPI - SRC Files/Classes/WasdMenu.cs +++ b/WASDMenuAPI - SRC Files/Classes/WasdMenu.cs @@ -1,14 +1,15 @@ using CounterStrikeSharp.API.Core; +using System.Diagnostics.CodeAnalysis; using WASDSharedAPI; namespace WASDMenuAPI.Classes; public class WasdMenu : IWasdMenu { - public string Title { get; set; } = ""; - public string ItemText { get; set; } = ""; - public string ItemHoverText { get; set; } = ""; - public string ControlText { get; set; } = ""; + [AllowNull] public string Title { get; set; } = ""; + [AllowNull] public string ItemText { get; set; } = ""; + [AllowNull] public string ItemHoverText { get; set; } = ""; + [AllowNull] public string ControlText { get; set; } = ""; public LinkedList? Options { get; set; } = new(); public LinkedListNode? Prev { get; set; } = null; public LinkedListNode Add(string display, Action onChoice) diff --git a/WASDMenuAPI - SRC Files/Classes/WasdMenuPlayer.cs b/WASDMenuAPI - SRC Files/Classes/WasdMenuPlayer.cs index a08c06f..69b5eac 100644 --- a/WASDMenuAPI - SRC Files/Classes/WasdMenuPlayer.cs +++ b/WASDMenuAPI - SRC Files/Classes/WasdMenuPlayer.cs @@ -13,7 +13,8 @@ public class WasdMenuPlayer public LinkedListNode? CurrentChoice = null; public LinkedListNode? MenuStart = null; public string CenterHtml = ""; - public int VisibleOptions = 3; + public static int DefaultVisibleOptions = 3; + public int VisibleOptions => DefaultVisibleOptions; public PlayerButtons Buttons { get; set; } public bool Paused = false; @@ -245,10 +246,11 @@ public class WasdMenuPlayer return; } - if (MainMenu == null || MainMenu.Options == null) + var options = MainMenu?.Options; + if (MainMenu == null || options == null) return; - MainMenu.Options.Clear(); + options.Clear(); bool choiceExists = false; foreach (var item in list) @@ -257,19 +259,19 @@ public class WasdMenuPlayer { OptionDisplay = item.Key, OnChoose = item.Value, - Index = MainMenu.Options.Count, + Index = options.Count, Parent = MainMenu }; - MainMenu.Options.AddLast(newOption); + options.AddLast(newOption); if (CurrentChoice?.Value?.OptionDisplay == item.Key) { - CurrentChoice = MainMenu.Options?.Last; + CurrentChoice = options.Last; choiceExists = true; } } if (!choiceExists) - CurrentChoice = MainMenu.Options?.First; + CurrentChoice = options.First; if (CurrentChoice != null) { diff --git a/jRandomSkills - SRC Files/src/jRandomSkills.cs b/jRandomSkills - SRC Files/src/jRandomSkills.cs index d9f8592..49e474e 100644 --- a/jRandomSkills - SRC Files/src/jRandomSkills.cs +++ b/jRandomSkills - SRC Files/src/jRandomSkills.cs @@ -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.b7"; + public override string ModuleVersion => "1.2.3.b8"; public override void Load(bool hotReload) { @@ -165,7 +165,7 @@ namespace src long perfStart = PerfLog.Start(); var result = method.Invoke(null, param); - PerfLog.End($"SkillAction {skill}.{methodName}", perfStart, 2.0); + PerfLog.Sample($"SkillAction {skill}.{methodName}", perfStart, 30.0, 1.0); return result; } @@ -176,7 +176,7 @@ namespace src CommandManager.RegisterCommand(definition); } - internal bool IsPlayerValid(CCSPlayerController? player) + internal bool IsPlayerValid([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] CCSPlayerController? player) { return player != null && player.IsValid && player.PlayerPawn?.Value != null && player.PlayerPawn.Value.IsValid && player.PlayerPawn.Value.LifeState == (byte)LifeState_t.LIFE_ALIVE; } diff --git a/jRandomSkills - SRC Files/src/player/Debug.cs b/jRandomSkills - SRC Files/src/player/Debug.cs index 5e25081..b1a0857 100644 --- a/jRandomSkills - SRC Files/src/player/Debug.cs +++ b/jRandomSkills - SRC Files/src/player/Debug.cs @@ -31,7 +31,7 @@ namespace src.player { var player = PlayerManager.GetPlayerEvent(@event.Userid); if (player == null || !player.IsValid) return HookResult.Continue; - WriteToDebug($"{(player.IsBot ? "Bot" : "Player")} {player.PlayerName} joined the game.", DebugCategory.Round); + WriteToDebug($"{(player.IsBot ? "Bot" : "Player")} {player.PlayerName} joined the game.", DebugCategory.Round, ignoreIdle: true); return HookResult.Continue; }); @@ -39,7 +39,7 @@ namespace src.player { var player = PlayerManager.GetPlayerEvent(@event.Userid); if (player == null || !player.IsValid) return HookResult.Continue; - WriteToDebug($"{(player.IsBot ? "Bot" : "Player")} {player.PlayerName} disconnected.", DebugCategory.Round); + WriteToDebug($"{(player.IsBot ? "Bot" : "Player")} {player.PlayerName} disconnected.", DebugCategory.Round, ignoreIdle: true); return HookResult.Continue; }); @@ -101,7 +101,7 @@ namespace src.player if (Config.DebugEnabled(DebugCategory.Damage)) { - VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Hook(OnTakeDamage, HookMode.Pre); + Instance.RegisterListener(OnTakeDamage); damageHooked = true; } } @@ -110,7 +110,7 @@ namespace src.player { if (damageHooked) { - try { VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Unhook(OnTakeDamage, HookMode.Pre); } + try { Instance.RemoveListener(OnTakeDamage); } catch { } damageHooked = false; } @@ -118,16 +118,13 @@ namespace src.player lock (_writeLock) { _writer?.Dispose(); _writer = null; } } - private static HookResult OnTakeDamage(DynamicHook h) + private static HookResult OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return HookResult.Continue; - CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle); - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player") return HookResult.Continue; @@ -141,10 +138,10 @@ namespace src.player var playerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index); if (playerInfo == null) return HookResult.Continue; - var nativeHitGroup = SkillUtils.GetHitGroup(param2); + var nativeHitGroup = SkillUtils.GetHitGroup(damageInfo); WriteToDebug($"{(victim.IsBot ? "Bot" : "Player")} {victim.PlayerName} took damage from {(attacker.IsBot ? "bot" : "player")} {attacker.PlayerName}. " + - $"[dmg={param2.Damage:0.#} hp={victimPawn.Health}/{victimPawn.MaxHealth} armor={victimPawn.ArmorValue} hitgroup={nativeHitGroup} " + + $"[dmg={damageInfo.Damage:0.#} hp={victimPawn.Health}/{victimPawn.MaxHealth} armor={victimPawn.ArmorValue} hitgroup={nativeHitGroup} " + $"takes={victimPawn.TakesDamage} vskill={PlayerManager.GetPlayerByIndex(victim.Index)?.Skill} askill={playerInfo.Skill}]", DebugCategory.Damage); return HookResult.Continue; } @@ -163,11 +160,14 @@ namespace src.player return gameRules.WarmupPeriod ? " [WARMUP]" : string.Empty; } - public static void WriteToDebug(string message, DebugCategory category = DebugCategory.Core) + public static void WriteToDebug(string message, DebugCategory category = DebugCategory.Core, bool ignoreIdle = false) { if (!Config.DebugEnabled(category)) return; + if (!ignoreIdle && category != DebugCategory.Core && PlayerManager.IsServerIdle()) + return; + lock (_writeLock) { _writer ??= CreateWriter(); @@ -209,4 +209,4 @@ namespace src.player } } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/EntityEvents.cs b/jRandomSkills - SRC Files/src/player/EntityEvents.cs index e83ba2b..825b860 100644 --- a/jRandomSkills - SRC Files/src/player/EntityEvents.cs +++ b/jRandomSkills - SRC Files/src/player/EntityEvents.cs @@ -84,26 +84,58 @@ namespace src.player } } - private static HookResult OnTakeDamage(DynamicHook h) + private static HookResult OnEntityTakeDamagePre(CBaseEntity entity, CTakeDamageInfo info) { lock (setLock) { - DispatchOnTakeDamage(h); + CountDamageResolution(info); + + object[] args = [entity, info]; + DispatchOnTakeDamage(entity, info, args); if (Fortnite.skillInThisRound == true && !Instance.SkillPlayer.Any(p => !p.IsDrawing && p.Skill == Skills.Fortnite)) - Instance.SkillAction("Fortnite", "OnTakeDamage", [h]); + Instance.SkillAction("Fortnite", "OnTakeDamage", args); return HookResult.Continue; } } - private static HookResult OnTakeDamagePost(DynamicHook h) + private static int dmgTotal, dmgAttackerOk, dmgAbilityOk, dmgSameTeam; + private static DateTime dmgWindowStart = DateTime.Now; + + private static void CountDamageResolution(CTakeDamageInfo info) + { + if (!PerfLog.Enabled || info == null) return; + + dmgTotal++; + + var attacker = info.Attacker?.Value; + if (attacker != null && attacker.IsValid) + { + dmgAttackerOk++; + + var attackerPawn = new CCSPlayerPawn(attacker.Handle); + if (attackerPawn.IsValid && attackerPawn.DesignerName == "player") dmgSameTeam++; + } + + var ability = info.Ability?.Value; + if (ability != null && ability.IsValid) dmgAbilityOk++; + + if ((DateTime.Now - dmgWindowStart).TotalSeconds < 60) return; + + if (dmgTotal > 0) + PerfLog.Info($"DMGHOOK total={dmgTotal} attacker={dmgAttackerOk} ability={dmgAbilityOk} playerAttacker={dmgSameTeam}"); + + dmgTotal = dmgAttackerOk = dmgAbilityOk = dmgSameTeam = 0; + dmgWindowStart = DateTime.Now; + } + + private static void OnEntityTakeDamagePost(CBaseEntity entity, CTakeDamageInfo info, CTakeDamageResult result) { lock (setLock) { - DispatchOnTakeDamage(h, true); - return HookResult.Continue; + DispatchOnTakeDamage(entity, info, [entity, info, result], true); } } diff --git a/jRandomSkills - SRC Files/src/player/ISkill.cs b/jRandomSkills - SRC Files/src/player/ISkill.cs index f69d2f5..e09f10b 100644 --- a/jRandomSkills - SRC Files/src/player/ISkill.cs +++ b/jRandomSkills - SRC Files/src/player/ISkill.cs @@ -13,8 +13,8 @@ public interface ISkill public static void UseSkill(CCSPlayerController _) { } public static void TypeSkill(CCSPlayerController _, string[] __) { } - public static void OnTakeDamage(DynamicHook _) { } - public static void OnTakeDamagePost(DynamicHook _) { } + public static void OnTakeDamage(CBaseEntity _, CTakeDamageInfo __) { } + public static void OnTakeDamagePost(CBaseEntity _, CTakeDamageInfo __, CTakeDamageResult ___) { } public static void OnEntitySpawned(CEntityInstance _) { } public static void OnTick() { } public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList _) { } diff --git a/jRandomSkills - SRC Files/src/player/PerfLog.cs b/jRandomSkills - SRC Files/src/player/PerfLog.cs index a63c9ac..594bb93 100644 --- a/jRandomSkills - SRC Files/src/player/PerfLog.cs +++ b/jRandomSkills - SRC Files/src/player/PerfLog.cs @@ -47,39 +47,98 @@ namespace src.player Write($"{label} took {ms:F2}ms"); } + private static readonly double[] bucketBoundsMs = [0.05, 0.1, 0.25, 0.5, 1, 2, 4, 8, 16, 32, double.MaxValue]; + private sealed class Aggregate { public double TotalMs; public double MaxMs; public int Count; + public readonly int[] Buckets = new int[bucketBoundsMs.Length]; public DateTime WindowStart = DateTime.Now; + + public void Add(double ms) + { + TotalMs += ms; + Count++; + if (ms > MaxMs) MaxMs = ms; + + for (int i = 0; i < bucketBoundsMs.Length; i++) + if (ms <= bucketBoundsMs[i]) { Buckets[i]++; break; } + } + + public double Percentile(double fraction) + { + int target = (int)Math.Ceiling(Count * fraction); + if (target < 1) target = 1; + + int seen = 0; + for (int i = 0; i < bucketBoundsMs.Length; i++) + { + seen += Buckets[i]; + if (seen >= target) + return bucketBoundsMs[i] == double.MaxValue ? MaxMs : bucketBoundsMs[i]; + } + return MaxMs; + } + + public void Reset() + { + TotalMs = 0; + MaxMs = 0; + Count = 0; + Array.Clear(Buckets); + WindowStart = DateTime.Now; + } } private static readonly ConcurrentDictionary _aggregates = new(); - // Per-tick measurement: accumulates and logs an avg/max summary every few seconds, + // Per-tick measurement: accumulates and logs an avg/percentile summary every few seconds, // so tick paths do not produce one log line per tick. public static void Sample(string label, long startTimestamp, double reportSeconds = 5.0, double maxThresholdMs = 0.5) { if (startTimestamp == 0 || !Enabled) return; - double ms = (Stopwatch.GetTimestamp() - startTimestamp) * 1000.0 / Stopwatch.Frequency; var agg = _aggregates.GetOrAdd(label, _ => new Aggregate()); + + if (PlayerManager.IsServerIdle()) + { + lock (agg) agg.Reset(); + return; + } + + double ms = (Stopwatch.GetTimestamp() - startTimestamp) * 1000.0 / Stopwatch.Frequency; lock (agg) { - agg.TotalMs += ms; - agg.Count++; - if (ms > agg.MaxMs) agg.MaxMs = ms; + agg.Add(ms); if ((DateTime.Now - agg.WindowStart).TotalSeconds < reportSeconds) return; if (agg.MaxMs >= maxThresholdMs) - Write($"{label} avg={agg.TotalMs / agg.Count:F2}ms max={agg.MaxMs:F2}ms samples={agg.Count}"); + Write($"{label} avg={agg.TotalMs / agg.Count:F2}ms p50={agg.Percentile(.5):F2}ms " + + $"p95={agg.Percentile(.95):F2}ms p99={agg.Percentile(.99):F2}ms max={agg.MaxMs:F2}ms " + + $"samples={agg.Count}{ContextSuffix()}"); - agg.TotalMs = 0; - agg.MaxMs = 0; - agg.Count = 0; - agg.WindowStart = DateTime.Now; + agg.Reset(); + } + } + + public static Func? ContextProvider; + + private static string ContextSuffix() + { + var provider = ContextProvider; + if (provider == null) return string.Empty; + + try + { + string context = provider(); + return string.IsNullOrEmpty(context) ? string.Empty : $" {context}"; + } + catch + { + return string.Empty; } } diff --git a/jRandomSkills - SRC Files/src/player/PlayerEvents.cs b/jRandomSkills - SRC Files/src/player/PlayerEvents.cs index bf87bda..a92791f 100644 --- a/jRandomSkills - SRC Files/src/player/PlayerEvents.cs +++ b/jRandomSkills - SRC Files/src/player/PlayerEvents.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Cvars; using CounterStrikeSharp.API.Modules.Events; @@ -80,8 +80,10 @@ namespace src.player Instance.HookUserMessage(208, PlayerMakeSound); Instance.HookUserMessage(207, GetPrintToCenterHtml); - VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Hook(OnTakeDamage, HookMode.Pre); - VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Hook(OnTakeDamagePost, HookMode.Post); + Instance.RegisterListener(OnEntityTakeDamagePre); + Instance.RegisterListener(OnEntityTakeDamagePost); + + PerfLog.ContextProvider = PerfContext; Instance.RegisterEventHandler(BulletImpact); @@ -96,8 +98,8 @@ namespace src.player public static void Unload() { - TryUnhook(() => VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Unhook(OnTakeDamage, HookMode.Pre)); - TryUnhook(() => VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Unhook(OnTakeDamagePost, HookMode.Post)); + TryUnhook(() => Instance.RemoveListener(OnEntityTakeDamagePre)); + TryUnhook(() => Instance.RemoveListener(OnEntityTakeDamagePost)); TryUnhook(() => VirtualFunctions.CBaseTrigger_StartTouchFunc.Unhook(OnTriggerEnter, HookMode.Post)); TryUnhook(() => VirtualFunctions.CBaseTrigger_EndTouchFunc.Unhook(OnTriggerExit, HookMode.Pre)); TryUnhook(() => VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Unhook(OnWeaponCanAcquire, HookMode.Pre)); @@ -170,9 +172,8 @@ namespace src.player } } - private static void DispatchOnTakeDamage(DynamicHook h, bool post = false) + private static void DispatchOnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, object[] args, bool post = false) { - object[] args = [h]; var seen = new HashSet(); List? deferred = null; @@ -186,15 +187,15 @@ namespace src.player continue; } - InvokeOnTakeDamage(p.Skill, h, args, post); + InvokeOnTakeDamage(p.Skill, damagedEntity, damageInfo, args, post); } if (deferred == null) return; foreach (var skill in deferred) - InvokeOnTakeDamage(skill, h, args, post); + InvokeOnTakeDamage(skill, damagedEntity, damageInfo, args, post); } - private static void InvokeOnTakeDamage(Skills skill, DynamicHook h, object[] args, bool post) + private static void InvokeOnTakeDamage(Skills skill, CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, object[] args, bool post) { if (!Config.DebugEnabled(DebugCategory.Damage)) { @@ -202,24 +203,22 @@ namespace src.player return; } - var info = h.GetParam(1); - float before = info == null ? 0f : info.Damage; + float before = damageInfo == null ? 0f : damageInfo.Damage; InvokeSkill(skill, post ? "OnTakeDamagePost" : "OnTakeDamage", args); - float after = info == null ? 0f : info.Damage; + float after = damageInfo == null ? 0f : damageInfo.Damage; if (Math.Abs(before - after) > 0.01f) - Debug.WriteToDebug($"[DMG] {skill} changed damage {before:0.#} -> {after:0.#}{DescribeDamageTarget(h)}", DebugCategory.Damage); + Debug.WriteToDebug($"[DMG] {skill} changed damage {before:0.#} -> {after:0.#}{DescribeDamageTarget(damagedEntity)}", DebugCategory.Damage); } - private static string DescribeDamageTarget(DynamicHook h) + private static string DescribeDamageTarget(CBaseEntity damagedEntity) { try { - var victimEntity = h.GetParam(0); - if (victimEntity == null || !victimEntity.IsValid) return string.Empty; + if (damagedEntity == null || !damagedEntity.IsValid) return string.Empty; - var pawn = victimEntity.As(); + var pawn = damagedEntity.As(); if (pawn == null || !pawn.IsValid || pawn.DesignerName != "player") return string.Empty; var controller = pawn.Controller.Value?.As(); @@ -445,8 +444,61 @@ namespace src.player public static void InvalidateFreezeDisabledCache() => _freezeDisabledSkills = null; + private const double stallThresholdMs = 100.0; + private static long lastTickTimestamp; + + private static void ReportStall() + { + if (!PerfLog.Enabled) + { + lastTickTimestamp = 0; + return; + } + + long now = System.Diagnostics.Stopwatch.GetTimestamp(); + long previous = lastTickTimestamp; + lastTickTimestamp = now; + + if (previous == 0) return; + + double gapMs = (now - previous) * 1000.0 / System.Diagnostics.Stopwatch.Frequency; + if (gapMs >= stallThresholdMs && !PlayerManager.IsServerIdle()) + { + var (tracked, owners) = EntityManager.GetStatistics(); + PerfLog.Info($"STALL gap={gapMs:F2}ms tick={Server.TickCount} tracked={tracked} owners={owners}{PerfContext()}"); + } + } + + public static string PerfContext() + { + try + { + int players = 0, alive = 0, bots = 0; + foreach (var player in PlayerManager.GetTickPlayers()) + { + if (player == null || !player.IsValid || player.IsHLTV) continue; + if (player.Team != CsTeam.Terrorist && player.Team != CsTeam.CounterTerrorist) continue; + + players++; + if (player.IsBot) bots++; + if (player.PawnIsAlive) alive++; + } + + var gameRules = jRandomSkills.Instance?.GameRules; + int round = gameRules == null || gameRules.Handle == IntPtr.Zero ? -1 : gameRules.TotalRoundsPlayed; + + return $" players={players} alive={alive} bots={bots} skills={_activeSkillsList.Count} round={round}"; + } + catch + { + return string.Empty; + } + } + private static void OnTick() { + ReportStall(); + long perfStart = PerfLog.Start(); lock (setLock) { diff --git a/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs b/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs index 8a3c402..3ed11ec 100644 --- a/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs +++ b/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs @@ -17,11 +17,11 @@ namespace src.player UpdateGameRules(); if (!SkillUtils.IsHudFrame()) return; - if (PerfLog.Enabled && Server.TickCount % 1920 == 0) + if (PerfLog.Enabled && Server.TickCount % 1920 == 0 && !PlayerManager.IsServerIdle()) { int server = Utilities.GetAllEntities().Count(e => e != null && e.IsValid); var (tracked, owners) = EntityManager.GetStatistics(); - PerfLog.Info($"ENTITIES server={server} tracked={tracked} owners={owners}"); + PerfLog.Info($"ENTITIES server={server} tracked={tracked} owners={owners}{EntityManager.DescribeTracked()}{Event.PerfContext()}"); } long perfStart = PerfLog.Start(); @@ -108,7 +108,7 @@ namespace src.player } else if (skillPlayer.IsDrawing && player.PawnIsAlive) { - var randomSkill = skills[Instance.Random.Next(skills.Length)]; + var randomSkill = skills[Random.Shared.Next(skills.Length)]; infoLine = player.GetTranslationWithoutIlliterate("drawing_skill"); skillLine = $"{player.GetSkillName(randomSkill.Skill)}"; @@ -148,9 +148,7 @@ namespace src.player var observerTarget = pawn.ObserverServices.ObserverTarget?.Value; if (observerTarget == null || !observerTarget.IsValid) return; - var observedPlayer = PlayerManager.GetTickPlayers().FirstOrDefault(p => - p != null && p.IsValid && p.Pawn?.Value?.Handle == observerTarget.Handle); - + var observedPlayer = PlayerManager.GetControllerByPawn(observerTarget.Handle); if (observedPlayer == null) return; var observedEvent = PlayerManager.GetPlayerEvent(observedPlayer); diff --git a/jRandomSkills - SRC Files/src/player/skills/Aimbot.cs b/jRandomSkills - SRC Files/src/player/skills/Aimbot.cs index 9650e5b..7d9f0ae 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Aimbot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Aimbot.cs @@ -17,15 +17,12 @@ namespace src.player.skills SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); + if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null || damageInfo.Handle == nint.Zero) return; + if (damageInfo.Attacker == null || !damageInfo.Attacker.IsValid || damageInfo.Attacker.Value == null) return; - if (param == null || !param.IsValid || param2 == null || param2.Handle == nint.Zero) return; - if (param2.Attacker == null || !param2.Attacker.IsValid || param2.Attacker.Value == null) return; - - var attackerEnt = param2.Attacker.Value; + var attackerEnt = damageInfo.Attacker.Value; if (attackerEnt == null || !attackerEnt.IsValid) return; var attackerPawn = attackerEnt.As(); @@ -40,12 +37,12 @@ namespace src.player.skills var playerInfo = PlayerManager.GetPlayerByIndex(attacker.Index); if (playerInfo == null || playerInfo.Skill != skillName) return; - if (!SkillUtils.IsBulletDamage(param2)) return; + if (!SkillUtils.IsBulletDamage(damageInfo)) return; int offset = GameData.GetOffset("CTakeDamageInfo_HitGroup"); if (offset <= 0) return; - nint hitGroupPointer = Marshal.ReadIntPtr(param2.Handle, offset); + nint hitGroupPointer = Marshal.ReadIntPtr(damageInfo.Handle, offset); if (hitGroupPointer == nint.Zero) return; nint hitGroupData = Marshal.ReadIntPtr(hitGroupPointer, 16); @@ -60,14 +57,12 @@ namespace src.player.skills Marshal.WriteInt32(address, (int)HitGroup_t.HITGROUP_HEAD); } - public static void OnTakeDamagePost(DynamicHook h) + public static void OnTakeDamagePost(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, CTakeDamageResult damageResult) { - var info = h.GetParam(1); - - if (info == null || info.Handle == nint.Zero) + if (damageInfo == null || damageInfo.Handle == nint.Zero) return; - if (!SkillUtils.IsBulletDamage(info)) return; + if (!SkillUtils.IsBulletDamage(damageInfo)) return; if (_restoreStack.Value!.Count > 0) { @@ -84,4 +79,4 @@ namespace src.player.skills { } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Armored.cs b/jRandomSkills - SRC Files/src/player/skills/Armored.cs index 4a3205b..e95e22e 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Armored.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Armored.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Utils; @@ -31,14 +31,11 @@ namespace src.player.skills border: !PlayerManager.GetTickPlayers().Any(p => p.IsValid && p.Team == player.Team && p != player) ? "tb" : "t"); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); + if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return; - if (param == null || !param.IsValid || param2 == null) return; - - var victimPawn = param.As(); + var victimPawn = damagedEntity.As(); if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return; var victimController = victimPawn.Controller.Value; @@ -53,7 +50,7 @@ namespace src.player.skills if (playerInfo.Skill == skillName && victim.PawnIsAlive) { float? skillChance = playerInfo.SkillChance; - param2.Damage *= skillChance ?? 1f; + damageInfo.Damage *= skillChance ?? 1f; } } @@ -63,4 +60,4 @@ namespace src.player.skills public float ChanceTo { get; set; } = chanceTo; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Assassin.cs b/jRandomSkills - SRC Files/src/player/skills/Assassin.cs index 767ce50..81636b5 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Assassin.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Assassin.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Utils; using src.utils; @@ -17,20 +17,19 @@ namespace src.player.skills SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return; - CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle); - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player") return; + if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return; + if (attackerPawn.Controller?.Value == null || victimPawn.Controller?.Value == null) return; @@ -42,12 +41,12 @@ namespace src.player.skills var playerInfo = PlayerManager.GetPlayerByIndex(attacker.Index); if (playerInfo?.Skill != skillName) return; - var weapon = param2.Ability?.Value; + var weapon = damageInfo.Ability?.Value; if (weapon == null || !weapon.IsValid) return; if (IsNade(weapon.DesignerName)) return; if (IsBehind(attacker, victim)) - param2.Damage *= SkillsInfo.GetValue(skillName, "damageMultiplier"); + damageInfo.Damage *= SkillsInfo.GetValue(skillName, "damageMultiplier"); } private static bool IsBehind(CCSPlayerController attacker, CCSPlayerController victim) @@ -81,10 +80,11 @@ namespace src.player.skills return (target >= a || target <= b); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d9d9d9", 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 damageMultiplier = 2f, float toleranceDeg = 45f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d9d9d9", 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 damageMultiplier = 2f, float toleranceDeg = 45f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { public float DamageMultiplier { get; set; } = damageMultiplier; public float ToleranceDeg { get; set; } = toleranceDeg; + public bool FriendlyFire { get; set; } = friendlyFire; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Berserker.cs b/jRandomSkills - SRC Files/src/player/skills/Berserker.cs index 0892dc1..9fef90e 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Berserker.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Berserker.cs @@ -12,6 +12,7 @@ namespace src.player.skills { private const Skills skillName = Skills.Berserker; public static readonly ConcurrentDictionary jumpedPlayers = []; + private static readonly List holderBuffer = []; public static void LoadSkill() { @@ -35,20 +36,19 @@ namespace src.player.skills return Math.Max(1, Math.Min(maxValue, newValue)); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return; - CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle); - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player") return; + if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return; + if (attackerPawn == null || attackerPawn.Controller?.Value == null || victimPawn == null || victimPawn.Controller?.Value == null) return; @@ -60,7 +60,7 @@ namespace src.player.skills if (playerInfo.Skill == skillName) { float damageMultiplier = CalculateNewVelocity(attackerPawn, SkillsInfo.GetValue(skillName, "maxDamageVelocity")); - param2.Damage *= damageMultiplier; + damageInfo.Damage *= damageMultiplier; } } @@ -90,18 +90,20 @@ namespace src.player.skills public static void OnTick() { - foreach (var player in PlayerManager.GetTickPlayers()) + PlayerManager.FillSkillHolders(skillName, holderBuffer); + if (holderBuffer.Count == 0) return; + + float maxSpeedVelocity = SkillsInfo.GetValue(skillName, "maxSpeedVelocity"); + + foreach (var player in holderBuffer) { if (!Instance.IsPlayerValid(player)) continue; - var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); - if (playerInfo?.Skill != skillName) continue; - var playerPawn = player.PlayerPawn?.Value; if (playerPawn == null || playerPawn.VelocityModifier == 0) continue; var buttons = player.Buttons; - float newSpeedVelocity = CalculateNewVelocity(playerPawn, SkillsInfo.GetValue(skillName, "maxSpeedVelocity")); + float newSpeedVelocity = CalculateNewVelocity(playerPawn, maxSpeedVelocity); if (buttons.HasFlag(PlayerButtons.Moveleft) || buttons.HasFlag(PlayerButtons.Moveright) || buttons.HasFlag(PlayerButtons.Forward) || buttons.HasFlag(PlayerButtons.Back)) playerPawn.VelocityModifier = newSpeedVelocity; @@ -109,15 +111,18 @@ namespace src.player.skills if (jumpedPlayers.TryGetValue(player.Index, out var time) && time > Server.TickCount) continue; + if (playerPawn.MoveType == MoveType_t.MOVETYPE_NOCLIP) continue; + if (!((PlayerFlags)player.Flags).HasFlag(PlayerFlags.FL_ONGROUND)) playerPawn.AbsVelocity.Z = Math.Min(playerPawn.AbsVelocity.Z, 10); } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#cc2929", 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 maxSpeedVelocity = 2f, float maxDamageVelocity = 2f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#cc2929", 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 maxSpeedVelocity = 2f, float maxDamageVelocity = 2f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { public float MaxSpeedVelocity { get; set; } = maxSpeedVelocity; public float MaxDamageVelocity { get; set; } = maxDamageVelocity; + public bool FriendlyFire { get; set; } = friendlyFire; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/BladeMaster.cs b/jRandomSkills - SRC Files/src/player/skills/BladeMaster.cs index 96143d5..a1a048f 100644 --- a/jRandomSkills - SRC Files/src/player/skills/BladeMaster.cs +++ b/jRandomSkills - SRC Files/src/player/skills/BladeMaster.cs @@ -10,6 +10,7 @@ namespace src.player.skills { private const Skills skillName = Skills.BladeMaster; private static readonly string[] noReflectionWeapon = ["inferno", "flashbang", "smokegrenade", "decoy", "hegrenade", "knife", "taser", "bayonet"]; + private static readonly List holderBuffer = []; public static void LoadSkill() { @@ -20,13 +21,11 @@ namespace src.player.skills { var modifier = SkillsInfo.GetValue(skillName, "velocityModifier"); - foreach (var player in PlayerManager.GetTickPlayers()) + PlayerManager.FillSkillHolders(skillName, holderBuffer); + if (holderBuffer.Count == 0) return; + + foreach (var player in holderBuffer) { - if (player == null || !player.IsValid) continue; - - var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); - if (playerInfo?.Skill != skillName) continue; - var playerPawn = player.PlayerPawn?.Value; if (playerPawn == null || !playerPawn.IsValid || playerPawn.VelocityModifier == 0) continue; diff --git a/jRandomSkills - SRC Files/src/player/skills/BlastShot.cs b/jRandomSkills - SRC Files/src/player/skills/BlastShot.cs index 80a5c72..3671de9 100644 --- a/jRandomSkills - SRC Files/src/player/skills/BlastShot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/BlastShot.cs @@ -50,6 +50,8 @@ namespace src.player.skills public static void OnTick() { + if (SkillPlayerInfo.IsEmpty) return; + float cooldown = SkillsInfo.GetValue(skillName, "cooldown"); foreach (var player in PlayerManager.GetTickPlayers()) @@ -140,14 +142,11 @@ namespace src.player.skills }); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null) return; - if (param == null || param.Entity == null || param2 == null) return; - - var nade = param2.Attacker.Value; + var nade = damageInfo.Attacker.Value; if (nade == null || !nade.IsValid) return; if (nade.DesignerName != "hegrenade_projectile") return; @@ -158,7 +157,7 @@ namespace src.player.skills if (!int.TryParse(parts[2], out int nadeTeam)) return; if (!uint.TryParse(parts[3], out uint ownerIndex)) return; - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (victimPawn.DesignerName != "player") return; if (victimPawn.Controller?.Value == null) return; @@ -170,9 +169,17 @@ namespace src.player.skills if (owner != null && !owner.IsValid) owner = null; if (victimPawn.TeamNum == nadeTeam) - param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName); + { + if (!SkillsInfo.GetValue(skillName, "friendlyFire")) + { + damageInfo.Damage = 0; + return; + } - if (owner != null && param2.Damage >= victimPawn.Health) + damageInfo.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName); + } + + if (owner != null && damageInfo.Damage >= victimPawn.Health) SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion); } @@ -185,13 +192,14 @@ namespace src.player.skills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7740c9", 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 explosionRadius = 400.0f, int explosionDamage = 60, float dmgReductionForTeamates = 0.5f, float cooldown = 10f, float force = 1000f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7740c9", 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 explosionRadius = 400.0f, int explosionDamage = 60, float dmgReductionForTeamates = 0.5f, float cooldown = 10f, float force = 1000f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { public float ExplosionRadius { get; set; } = explosionRadius; public int ExplosionDamage { get; set; } = explosionDamage; public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates; public float Cooldown { get; set; } = cooldown; public float Force { get; set; } = force; + public bool FriendlyFire { get; set; } = friendlyFire; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/BunnyHop.cs b/jRandomSkills - SRC Files/src/player/skills/BunnyHop.cs index 904c04e..fc8b364 100644 --- a/jRandomSkills - SRC Files/src/player/skills/BunnyHop.cs +++ b/jRandomSkills - SRC Files/src/player/skills/BunnyHop.cs @@ -10,6 +10,7 @@ namespace src.player.skills { private const Skills skillName = Skills.BunnyHop; private static readonly ConcurrentDictionary playersLastJump = []; + private static readonly List holderBuffer = []; public static void NewRound() { @@ -28,13 +29,15 @@ namespace src.player.skills public static void OnTick() { - foreach (var player in PlayerManager.GetTickPlayers()) + PlayerManager.FillSkillHolders(skillName, holderBuffer); + if (holderBuffer.Count == 0) return; + + foreach (var playerEvent in holderBuffer) { + var player = PlayerManager.GetPlayerFromEvent(playerEvent) ?? playerEvent; if (player == null || !player.IsValid) continue; - var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index)); - if (playerInfo?.Skill == skillName) - GiveBunnyHop(player); + GiveBunnyHop(player); } } diff --git a/jRandomSkills - SRC Files/src/player/skills/CarefulBullets.cs b/jRandomSkills - SRC Files/src/player/skills/CarefulBullets.cs index 2f29a87..b1ae6b2 100644 --- a/jRandomSkills - SRC Files/src/player/skills/CarefulBullets.cs +++ b/jRandomSkills - SRC Files/src/player/skills/CarefulBullets.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Utils; @@ -174,16 +174,13 @@ namespace src.player.skills return owner != null && owner.IsValid ? owner : null; } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return; - CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle); - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player") return; diff --git a/jRandomSkills - SRC Files/src/player/skills/Chicken.cs b/jRandomSkills - SRC Files/src/player/skills/Chicken.cs index 2ce7bb0..d8c8734 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Chicken.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Chicken.cs @@ -142,6 +142,8 @@ namespace src.player.skills public static void OnTick() { + if (chickens.IsEmpty) return; + var pairs = chickens.ToArray(); foreach (var valuePair in pairs) { diff --git a/jRandomSkills - SRC Files/src/player/skills/Cutter.cs b/jRandomSkills - SRC Files/src/player/skills/Cutter.cs index 32d1d06..75cac5b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Cutter.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Cutter.cs @@ -14,20 +14,19 @@ namespace src.player.skills SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return; - CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle); - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player") return; + if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return; + if (attackerPawn.Controller?.Value == null || victimPawn.Controller?.Value == null) return; @@ -38,15 +37,16 @@ namespace src.player.skills var playerInfo = PlayerManager.GetPlayerByIndex(attacker.Index); if (playerInfo?.Skill != skillName) return; - var weapon = param2.Ability?.Value; + var weapon = damageInfo.Ability?.Value; if (weapon == null || !weapon.IsValid) return; if (weapon.DesignerName != "weapon_knife" && weapon.DesignerName != "weapon_bayonet") return; - param2.Damage = 9999f; + damageInfo.Damage = 9999f; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#88a31a", 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) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#88a31a", 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, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public bool FriendlyFire { get; set; } = friendlyFire; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Cypher.cs b/jRandomSkills - SRC Files/src/player/skills/Cypher.cs index e9d6ffc..44baa5b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Cypher.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Cypher.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Commands.Targeting; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; @@ -292,18 +292,15 @@ namespace src.player.skills return camera; } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return; - if (string.IsNullOrEmpty(param.Entity.Name)) return; - if (!param.Entity.Name.StartsWith("CypherCamera_")) return; + if (string.IsNullOrEmpty(damagedEntity.Entity.Name)) return; + if (!damagedEntity.Entity.Name.StartsWith("CypherCamera_")) return; - var nameParams = param.Entity.Name.Split('_')[2]; + var nameParams = damagedEntity.Entity.Name.Split('_')[2]; _ = uint.TryParse(nameParams, out uint userIndex); if (userIndex == 0) return; @@ -420,4 +417,4 @@ namespace src.player.skills public float Cooldown { get; set; } = cooldown; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Dash.cs b/jRandomSkills - SRC Files/src/player/skills/Dash.cs index 3a4d54c..beaaa3a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Dash.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Dash.cs @@ -67,6 +67,8 @@ namespace src.player.skills public static void OnTick() { + if (SkillPlayerInfo.IsEmpty) return; + foreach (var player in PlayerManager.GetTickPlayers()) { if (player == null || !player.IsValid) return; diff --git a/jRandomSkills - SRC Files/src/player/skills/DeathBomb.cs b/jRandomSkills - SRC Files/src/player/skills/DeathBomb.cs index 70cfa46..4facc37 100644 --- a/jRandomSkills - SRC Files/src/player/skills/DeathBomb.cs +++ b/jRandomSkills - SRC Files/src/player/skills/DeathBomb.cs @@ -36,7 +36,6 @@ namespace src.player.skills Server.NextWorldUpdate(() => { - if (player == null || !player.IsValid || player.Team != lastTeam) return; var pawn = player.PlayerPawn.Value; @@ -92,14 +91,11 @@ namespace src.player.skills }); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null) return; - if (param == null || param.Entity == null || param2 == null) return; - - var nade = param2.Attacker.Value; + var nade = damageInfo.Attacker.Value; if (nade == null || !nade.IsValid) return; if (nade.DesignerName != "hegrenade_projectile") return; @@ -110,7 +106,7 @@ namespace src.player.skills if (!int.TryParse(parts[2], out int nadeTeam)) return; if (!uint.TryParse(parts[3], out uint ownerIndex)) return; - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (victimPawn.DesignerName != "player") return; if (victimPawn.Controller?.Value == null) return; @@ -122,9 +118,17 @@ namespace src.player.skills if (owner != null && !owner.IsValid) owner = null; if (victimPawn.TeamNum == nadeTeam) - param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName); + { + if (!SkillsInfo.GetValue(skillName, "friendlyFire")) + { + damageInfo.Damage = 0; + return; + } - if (owner != null && param2.Damage >= victimPawn.Health) + damageInfo.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName); + } + + if (owner != null && damageInfo.Damage >= victimPawn.Health) SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion); } @@ -135,12 +139,13 @@ namespace src.player.skills return player != null && player.IsValid && player.PlayerPawn?.Value != null; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#F5CB42", 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 explosionRadius = 500.0f, int explosionDamage = 999, float dmgReductionForTeamates = 0.5f, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#F5CB42", 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 explosionRadius = 500.0f, int explosionDamage = 999, float dmgReductionForTeamates = 0.5f, float soundVolume = 1f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { public float SoundVolume { get; set; } = soundVolume; public float ExplosionRadius { get; set; } = explosionRadius; public int ExplosionDamage { get; set; } = explosionDamage; public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates; + public bool FriendlyFire { get; set; } = friendlyFire; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/DemonEye.cs b/jRandomSkills - SRC Files/src/player/skills/DemonEye.cs index f4847dd..141fa5d 100644 --- a/jRandomSkills - SRC Files/src/player/skills/DemonEye.cs +++ b/jRandomSkills - SRC Files/src/player/skills/DemonEye.cs @@ -11,6 +11,7 @@ namespace src.player.skills { private const Skills skillName = Skills.DemonEye; private static readonly Skills[] hidingSkills = [Skills.Ghost, Skills.Ninja, Skills.C4Camouflage]; + private static readonly List holderBuffer = []; public static void LoadSkill() { @@ -24,14 +25,14 @@ namespace src.player.skills int damage = SkillsInfo.GetValue(skillName, "damage"); - foreach (var player in PlayerManager.GetTickPlayers()) + PlayerManager.FillSkillHolders(skillName, holderBuffer); + if (holderBuffer.Count == 0) return; + + foreach (var playerEvent in holderBuffer) { - var playerEvent = PlayerManager.GetPlayerEvent(player); if (!Instance.IsPlayerValid(playerEvent)) continue; - var playerInfo = PlayerManager.GetPlayerByIndex(playerEvent!.Index); - if (playerInfo?.Skill == skillName) - HitEnemies(playerEvent, damage); + HitEnemies(playerEvent, damage); } } diff --git a/jRandomSkills - SRC Files/src/player/skills/EmpGrenade.cs b/jRandomSkills - SRC Files/src/player/skills/EmpGrenade.cs index 1b24cf5..b240e05 100644 --- a/jRandomSkills - SRC Files/src/player/skills/EmpGrenade.cs +++ b/jRandomSkills - SRC Files/src/player/skills/EmpGrenade.cs @@ -24,13 +24,11 @@ namespace src.player.skills public static void NewRound() { RestoreAll(); - jammedUntilTick.Clear(); } public static void RoundEnd() { RestoreAll(); - jammedUntilTick.Clear(); } public static void PlayerDisconnect(uint playerIndex) @@ -89,6 +87,8 @@ namespace src.player.skills { foreach (var playerIndex in jammedUntilTick.Keys) SetHud(playerIndex, false); + + jammedUntilTick.Clear(); } private static void SetHud(uint playerIndex, bool jam) @@ -142,6 +142,10 @@ namespace src.player.skills if (player == null || !player.IsValid) return; playersWithSkill.TryRemove(player.Index, out _); + + if (playersWithSkill.IsEmpty) + RestoreAll(); + SkillUtils.UpdateGrenadeCount(player, CsItem.HEGrenade, 1); } diff --git a/jRandomSkills - SRC Files/src/player/skills/ExplodingBarrel.cs b/jRandomSkills - SRC Files/src/player/skills/ExplodingBarrel.cs index 4dc5ff6..04ea71d 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ExplodingBarrel.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ExplodingBarrel.cs @@ -130,12 +130,16 @@ namespace src.player.skills barrel.Entity!.Name = barrel.Globalname = $"Barrel_{Server.TickCount}_{player.TeamNum}_{player.Index}"; barrel.Collision.SolidType = SolidType_t.SOLID_VPHYSICS; barrel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(barrel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2)); - barrel.DispatchSpawn(); + + using (var keys = new CEntityKeyValues()) + { + keys.SetString("model", SkillsInfo.GetValue(skillName, "propModel")); + barrel.DispatchSpawn(keys); + } Server.NextFrame(() => { if (barrel == null || !barrel.IsValid) return; - barrel.SetModel(SkillsInfo.GetValue(skillName, "propModel")); barrel.Teleport(pos, rotation, null); }); } @@ -165,27 +169,24 @@ namespace src.player.skills }); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null) return; - if (param == null || param.Entity == null || param2 == null) return; - - if (HandleBarrelHit(param)) return; - HandleNadeDamage(param, param2); + if (HandleBarrelHit(damagedEntity)) return; + HandleNadeDamage(damagedEntity, damageInfo); } - private static bool HandleBarrelHit(CEntityInstance param) + private static bool HandleBarrelHit(CEntityInstance damagedEntity) { - if (string.IsNullOrEmpty(param.Entity!.Name)) return false; - if (!param.Entity.Name.StartsWith("Barrel_")) return false; + if (string.IsNullOrEmpty(damagedEntity.Entity!.Name)) return false; + if (!damagedEntity.Entity.Name.StartsWith("Barrel_")) return false; - var barrel = param.As(); + var barrel = damagedEntity.As(); if (barrel == null || !barrel.IsValid) return true; if (!consumedBarrels.TryAdd(barrel.Index, 0)) return true; - var nameParts = param.Entity.Name.Split('_'); + var nameParts = damagedEntity.Entity.Name.Split('_'); if (nameParts.Length < 4) return true; if (!int.TryParse(nameParts[2], out int teamNum)) return true; if (!uint.TryParse(nameParts[3], out uint ownerIndex)) return true; @@ -207,9 +208,9 @@ namespace src.player.skills return true; } - private static void HandleNadeDamage(CEntityInstance param, CTakeDamageInfo param2) + private static void HandleNadeDamage(CEntityInstance damagedEntity, CTakeDamageInfo damageInfo) { - var nade = param2.Attacker?.Value; + var nade = damageInfo.Attacker?.Value; if (nade == null || !nade.IsValid) return; if (nade.DesignerName != "hegrenade_projectile") return; if (string.IsNullOrEmpty(nade.Globalname) || !nade.Globalname.StartsWith("barrel_team_")) return; @@ -219,7 +220,7 @@ namespace src.player.skills if (!int.TryParse(parts[2], out int nadeTeam)) return; if (!uint.TryParse(parts[3], out uint ownerIndex)) return; - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (victimPawn.DesignerName != "player" || victimPawn.Controller?.Value == null) return; var victim = victimPawn.Controller.Value.As(); @@ -229,9 +230,17 @@ namespace src.player.skills if (owner != null && !owner.IsValid) owner = null; if (victimPawn.TeamNum == nadeTeam) - param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName); + { + if (!SkillsInfo.GetValue(skillName, "friendlyFire")) + { + damageInfo.Damage = 0; + return; + } - if (owner != null && param2.Damage >= victimPawn.Health) + damageInfo.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName); + } + + if (owner != null && damageInfo.Damage >= victimPawn.Health) SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion); } @@ -244,13 +253,14 @@ namespace src.player.skills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c0392b", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Common, float cooldown = 20f, float explosionRadius = 600f, int explosionDamage = 50, string propModel = "models/props/de_train/hr_t/barrel_a/barrel_a.vmdl", float dmgReductionForTeamates = 0.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c0392b", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Common, float cooldown = 20f, float explosionRadius = 600f, int explosionDamage = 50, string propModel = "models/props/de_train/hr_t/barrel_a/barrel_a.vmdl", float dmgReductionForTeamates = 0.5f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { public float Cooldown { get; set; } = cooldown; public float ExplosionRadius { get; set; } = explosionRadius; public int ExplosionDamage { get; set; } = explosionDamage; public string PropModel { get; set; } = propModel; public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates; + public bool FriendlyFire { get; set; } = friendlyFire; } } } diff --git a/jRandomSkills - SRC Files/src/player/skills/ExplosiveShot.cs b/jRandomSkills - SRC Files/src/player/skills/ExplosiveShot.cs index f778536..70ff794 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ExplosiveShot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ExplosiveShot.cs @@ -75,14 +75,11 @@ namespace src.player.skills }); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null) return; - if (param == null || param.Entity == null || param2 == null) return; - - var nade = param2.Attacker?.Value; + var nade = damageInfo.Attacker?.Value; if (nade == null || !nade.IsValid) return; if (nade.DesignerName != "hegrenade_projectile") return; @@ -93,7 +90,7 @@ namespace src.player.skills if (!int.TryParse(parts[2], out int nadeTeam)) return; if (!uint.TryParse(parts[3], out uint ownerIndex)) return; - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (victimPawn.DesignerName != "player") return; if (victimPawn.Controller?.Value == null) return; @@ -102,12 +99,20 @@ namespace src.player.skills if (victim == null || !victim.IsValid || victim.Index == ownerIndex) return; if (victimPawn.TeamNum == nadeTeam) - param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName); + { + if (!SkillsInfo.GetValue(skillName, "friendlyFire")) + { + damageInfo.Damage = 0; + return; + } + + damageInfo.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName); + } var owner = Utilities.GetPlayerFromIndex((int)ownerIndex); if (owner != null && !owner.IsValid) owner = null; - if (owner != null && param2.Damage >= victimPawn.Health) + if (owner != null && damageInfo.Damage >= victimPawn.Health) SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion); } @@ -129,13 +134,14 @@ namespace src.player.skills SpawnExplosion(pos, player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9c0000", 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 damage = 25f, float damageRadius = 210f, float chanceFrom = .15f, float chanceTo = .3f, float dmgReductionForTeamates = 0.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9c0000", 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 damage = 25f, float damageRadius = 210f, float chanceFrom = .15f, float chanceTo = .3f, float dmgReductionForTeamates = 0.5f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { public float Damage { get; set; } = damage; public float DamageRadius { get; set; } = damageRadius; public float ChanceFrom { get; set; } = chanceFrom; public float ChanceTo { get; set; } = chanceTo; public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates; + public bool FriendlyFire { get; set; } = friendlyFire; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs b/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs index 6f3555a..78f2a18 100644 --- a/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs +++ b/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs @@ -60,6 +60,8 @@ namespace src.player.skills public static void OnTick() { + if (cameras.IsEmpty) return; + foreach (var player in PlayerManager.GetTickPlayers()) if (cameras.TryGetValue(player.Index, out var cameraInfo) && cameraInfo.Item2 != 0) { diff --git a/jRandomSkills - SRC Files/src/player/skills/FastReload.cs b/jRandomSkills - SRC Files/src/player/skills/FastReload.cs index 3a94122..ed8202c 100644 --- a/jRandomSkills - SRC Files/src/player/skills/FastReload.cs +++ b/jRandomSkills - SRC Files/src/player/skills/FastReload.cs @@ -2,6 +2,7 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using src.utils; +using System.Collections.Concurrent; using static src.jRandomSkills; namespace src.player.skills @@ -9,12 +10,75 @@ namespace src.player.skills public class FastReload : ISkill { private const Skills skillName = Skills.FastReload; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; public static void LoadSkill() { SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } + public static void NewRound() + { + SkillPlayerInfo.Clear(); + } + + public static void EnableSkill(CCSPlayerController player) + { + if (player == null || !player.IsValid) return; + + SkillPlayerInfo[player.Index] = new PlayerSkillInfo + { + SteamID = player.Index, + CanUse = true, + Cooldown = DateTime.MinValue, + }; + } + + public static void DisableSkill(CCSPlayerController player) + { + if (player == null || !player.IsValid) return; + + SkillPlayerInfo.TryRemove(player.Index, out _); + SkillUtils.ResetPrintHTML(player); + } + + public static void PlayerDisconnect(uint playerIndex) + { + SkillPlayerInfo.TryRemove(playerIndex, out _); + } + + public static void OnTick() + { + if (SkillPlayerInfo.IsEmpty) return; + if (!SkillUtils.IsHudFrame()) return; + if (SkillUtils.IsFreezeTime()) return; + + foreach (var player in PlayerManager.GetTickPlayers()) + { + var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); + if (playerInfo?.Skill != skillName) continue; + if (!SkillPlayerInfo.TryGetValue(player.Index, out var skillInfo)) continue; + + UpdateHUD(player, skillInfo); + } + } + + private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) + { + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); + float cooldown = Math.Max(time, 0); + + if (cooldown == 0 && !skillInfo.CanUse) + skillInfo.CanUse = true; + + var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); + if (playerInfo == null) return; + + playerInfo.PrintHTML = cooldown != 0 + ? $"{player.GetTranslation("hud_info", $"{cooldown}")}" + : null; + } + public static void UseSkill(CCSPlayerController player) { var playerPawn = player.PlayerPawn.Value; @@ -24,24 +88,40 @@ namespace src.player.skills if (playerInfo?.Skill != skillName) return; if (!player.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return; - InstaReload(playerPawn); + if (!SkillPlayerInfo.TryGetValue(player.Index, out var skillInfo)) return; + if (!skillInfo.CanUse) return; + if (skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) > DateTime.Now) return; + + if (!InstaReload(playerPawn)) return; + + skillInfo.CanUse = false; + skillInfo.Cooldown = DateTime.Now; } - private static void InstaReload(CCSPlayerPawn pawn) + private static bool InstaReload(CCSPlayerPawn pawn) { - if (pawn == null || !pawn.IsValid) return; + if (pawn == null || !pawn.IsValid) return false; var weaponServices = pawn.WeaponServices; - if (weaponServices == null || weaponServices.ActiveWeapon == null || !weaponServices.ActiveWeapon.IsValid) return; + if (weaponServices == null || weaponServices.ActiveWeapon == null || !weaponServices.ActiveWeapon.IsValid) return false; var activeWeapon = weaponServices.ActiveWeapon.Value; - if (activeWeapon == null || !activeWeapon.IsValid || activeWeapon.VData == null) return; + if (activeWeapon == null || !activeWeapon.IsValid || activeWeapon.VData == null) return false; activeWeapon.Clip1 = activeWeapon.VData.MaxClip1; Utilities.SetStateChanged(activeWeapon, "CBasePlayerWeapon", "m_iClip1"); + return true; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffc061", 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) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class PlayerSkillInfo { + public ulong SteamID { get; set; } + public bool CanUse { get; set; } + public DateTime Cooldown { get; set; } + } + + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffc061", 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 cooldown = 5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + { + public float Cooldown { get; set; } = cooldown; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Flash.cs b/jRandomSkills - SRC Files/src/player/skills/Flash.cs index b5daafc..6e1be3b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Flash.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Flash.cs @@ -12,6 +12,7 @@ namespace src.player.skills { private const Skills skillName = Skills.Flash; public static readonly ConcurrentDictionary jumpedPlayers = []; + private static readonly List holderBuffer = []; public static void LoadSkill() { @@ -75,13 +76,14 @@ namespace src.player.skills public static void OnTick() { - foreach (var player in PlayerManager.GetTickPlayers()) + PlayerManager.FillSkillHolders(skillName, holderBuffer); + if (holderBuffer.Count == 0) return; + + foreach (var player in holderBuffer) { if (!Instance.IsPlayerValid(player)) continue; var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); - if (playerInfo?.Skill != skillName) continue; - var playerPawn = player.PlayerPawn?.Value; if (playerPawn == null || playerPawn.VelocityModifier == 0) continue; @@ -93,6 +95,8 @@ namespace src.player.skills if (jumpedPlayers.TryGetValue(player.Index, out var time) && time > Server.TickCount) continue; + if (playerPawn.MoveType == MoveType_t.MOVETYPE_NOCLIP) continue; + if (!((PlayerFlags)player.Flags).HasFlag(PlayerFlags.FL_ONGROUND)) playerPawn.AbsVelocity.Z = Math.Min(playerPawn.AbsVelocity.Z, 10); } diff --git a/jRandomSkills - SRC Files/src/player/skills/Flashlight.cs b/jRandomSkills - SRC Files/src/player/skills/Flashlight.cs index b9f73b7..5cb3754 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Flashlight.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Flashlight.cs @@ -41,6 +41,8 @@ namespace src.player.skills public static void OnTick() { + if (SkillPlayerInfo.IsEmpty) return; + foreach (var player in PlayerManager.GetTickPlayers()) { if (player == null || !player.IsValid) @@ -288,10 +290,14 @@ namespace src.player.skills if (needEnemySees && !enemySeesPlayer) continue; + var targetOrigin = targetPawn.AbsOrigin; + if (targetOrigin == null) + continue; + Vector targetEyePos = new( - targetPawn.AbsOrigin.X, - targetPawn.AbsOrigin.Y, - targetPawn.AbsOrigin.Z + targetPawn.ViewOffset.Z + targetOrigin.X, + targetOrigin.Y, + targetOrigin.Z + targetPawn.ViewOffset.Z ); Vector toTarget = new( diff --git a/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs b/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs index 384eb3e..3d802ab 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Utils; @@ -141,24 +141,21 @@ namespace src.player.skills }); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return; - if (string.IsNullOrEmpty(param.Entity.Name)) return; - if (!param.Entity.Name.StartsWith("FortniteWall")) return; + if (string.IsNullOrEmpty(damagedEntity.Entity.Name)) return; + if (!damagedEntity.Entity.Name.StartsWith("FortniteWall")) return; - var box = param.As(); + var box = damagedEntity.As(); if (box == null || !box.IsValid) return; box.EmitSound("Wood_Plank.BulletImpact", volume: SkillsInfo.GetValue(skillName, "soundVolume")); if (barricades.TryGetValue(box.Index, out int health)) { - int newHealth = health - (int)param2.Damage; + int newHealth = health - (int)damageInfo.Damage; if (newHealth <= 0) { @@ -186,4 +183,4 @@ namespace src.player.skills public string PropModel { get; set; } = propModel; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/FriendlyFire.cs b/jRandomSkills - SRC Files/src/player/skills/FriendlyFire.cs index a2b914b..5641477 100644 --- a/jRandomSkills - SRC Files/src/player/skills/FriendlyFire.cs +++ b/jRandomSkills - SRC Files/src/player/skills/FriendlyFire.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Cvars; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; @@ -29,19 +29,16 @@ namespace src.player.skills Server.ExecuteCommand($"mp_autokick {(defaultAutoKick ? 1 : 0)}"); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return; - if (param2.AmmoType == 255) + if (damageInfo.AmmoType == 255) return; - CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle); - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (attackerPawn == null || !attackerPawn.IsValid || victimPawn == null || !victimPawn.IsValid) return; @@ -67,8 +64,8 @@ namespace src.player.skills var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(attacker)?.Index ?? attacker.Index)); if (playerInfo?.Skill != skillName || attacker!.Team != victim!.Team) return; - float damage = param2.Damage; - param2.Damage = 0; + float damage = damageInfo.Damage; + damageInfo.Damage = 0; if (!autoKickOverridden) { @@ -88,4 +85,4 @@ namespace src.player.skills public float HealthDamageMultiplier { get; set; } = healthDamageMultiplier; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Gambler.cs b/jRandomSkills - SRC Files/src/player/skills/Gambler.cs index aa00b0a..5b9a604 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Gambler.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Gambler.cs @@ -46,9 +46,15 @@ namespace src.player.skills return; } - if (skill.Skill == skillName && !TakeMoney(player)) + if (skill.Skill == skillName) { - playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("gambler_no_money")); + if (!TakeMoney(player)) + { + playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("gambler_no_money")); + return; + } + + EnableSkill(player); return; } diff --git a/jRandomSkills - SRC Files/src/player/skills/GravityDecoy.cs b/jRandomSkills - SRC Files/src/player/skills/GravityDecoy.cs index 3cce790..6a583b8 100644 --- a/jRandomSkills - SRC Files/src/player/skills/GravityDecoy.cs +++ b/jRandomSkills - SRC Files/src/player/skills/GravityDecoy.cs @@ -11,11 +11,9 @@ namespace src.player.skills { private const Skills skillName = Skills.GravityDecoy; - private const float defaultGravity = 1f; - private static readonly ConcurrentDictionary playersWithSkill = []; - private static readonly ConcurrentDictionary affected = []; - private static readonly ConcurrentDictionary restoreOnRespawn = []; + private static readonly ConcurrentDictionary affected = []; + private static readonly ConcurrentDictionary restoreOnRespawn = []; public static void LoadSkill() { @@ -78,8 +76,8 @@ namespace src.player.skills if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) continue; if (pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) continue; - if (restoreOnRespawn.TryRemove(eventPlayer.Index, out _)) - pawn.ActualGravityScale = defaultGravity; + if (restoreOnRespawn.TryRemove(eventPlayer.Index, out float respawnGravity)) + pawn.ActualGravityScale = respawnGravity; bool inside = false; foreach (var decoyPos in decoyPositions) @@ -87,11 +85,11 @@ namespace src.player.skills if (inside) { - if (!affected.TryAdd(eventPlayer.Index, 0)) continue; + if (!affected.TryAdd(eventPlayer.Index, pawn.ActualGravityScale)) continue; pawn.ActualGravityScale = gravity; } - else if (affected.TryRemove(eventPlayer.Index, out _)) - pawn.ActualGravityScale = defaultGravity; + else if (affected.TryRemove(eventPlayer.Index, out float ownGravity)) + pawn.ActualGravityScale = ownGravity; } } @@ -100,10 +98,10 @@ namespace src.player.skills var victim = PlayerManager.GetPlayerEvent(@event.Userid); if (victim == null || !victim.IsValid) return; - if (!affected.TryRemove(victim.Index, out _)) return; + if (!affected.TryRemove(victim.Index, out float ownGravity)) return; - Restore(victim.Index); - restoreOnRespawn[victim.Index] = 0; + Restore(victim.Index, ownGravity); + restoreOnRespawn[victim.Index] = ownGravity; } public static void PlayerDisconnect(uint playerIndex) @@ -115,23 +113,23 @@ namespace src.player.skills private static void RestoreAll() { - foreach (uint playerIndex in affected.Keys) - Restore(playerIndex); + foreach (var entry in affected) + Restore(entry.Key, entry.Value); - foreach (uint playerIndex in restoreOnRespawn.Keys) - Restore(playerIndex); + foreach (var entry in restoreOnRespawn) + Restore(entry.Key, entry.Value); affected.Clear(); restoreOnRespawn.Clear(); } - private static void Restore(uint playerIndex) + private static void Restore(uint playerIndex, float gravity) { var player = Utilities.GetPlayerFromIndex((int)playerIndex); var pawn = player?.PlayerPawn?.Value; if (pawn != null && pawn.IsValid) - pawn.ActualGravityScale = defaultGravity; + pawn.ActualGravityScale = gravity; } public static void GrenadeThrown(EventGrenadeThrown @event) diff --git a/jRandomSkills - SRC Files/src/player/skills/HealingChicken.cs b/jRandomSkills - SRC Files/src/player/skills/HealingChicken.cs index f521c38..c1cedca 100644 --- a/jRandomSkills - SRC Files/src/player/skills/HealingChicken.cs +++ b/jRandomSkills - SRC Files/src/player/skills/HealingChicken.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Utils; @@ -35,21 +35,18 @@ namespace src.player.skills chickenOwners.Clear(); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); + if (damagedEntity == null || damageInfo == null) return; - if (param == null || param2 == null) return; - - if (!chickenOwners.TryGetValue(param.Index, out var ownerData)) return; + if (!chickenOwners.TryGetValue(damagedEntity.Index, out var ownerData)) return; uint ownerIndex = ownerData.ownerIndex; int health = ownerData.health; var owner = Utilities.GetPlayerFromIndex((int)ownerIndex); if (owner == null || !owner.IsValid) return; - var attackerEnt = param2.Attacker?.Value; + var attackerEnt = damageInfo.Attacker?.Value; if (attackerEnt == null || !attackerEnt.IsValid) return; var attackerPawn = new CCSPlayerPawn(attackerEnt.Handle); @@ -60,15 +57,15 @@ namespace src.player.skills if (attackerPawn.Index != ownerPawn.Index && attackerPawn.TeamNum == owner.TeamNum) { - param2.Damage = 0; + damageInfo.Damage = 0; return; } - int newHealth = Math.Max(0, health - (int)param2.Damage); - chickenOwners[param.Index] = (ownerIndex, newHealth); + int newHealth = Math.Max(0, health - (int)damageInfo.Damage); + chickenOwners[damagedEntity.Index] = (ownerIndex, newHealth); if (newHealth > 0) - CreateHitParticles(param.Index); + CreateHitParticles(damagedEntity.Index); } private static void CreateHitParticles(uint chickenIndex) @@ -231,4 +228,4 @@ namespace src.player.skills public float BoostFactor { get; set; } = boostFactor; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs b/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs index 069b5cb..b9dbf5c 100644 --- a/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs +++ b/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs @@ -10,7 +10,14 @@ namespace src.player.skills public class HealingSmoke : ISkill { private const Skills skillName = Skills.HealingSmoke; - private static readonly ConcurrentDictionary smokes = []; + private static readonly ConcurrentDictionary smokes = []; + + private static void RemoveOwnerSmokes(uint ownerIndex) + { + foreach (var smoke in smokes) + if (smoke.Value == ownerIndex) + smokes.TryRemove(smoke.Key, out _); + } private readonly static ConcurrentDictionary playersWithSkill = []; public static void LoadSkill() @@ -31,7 +38,7 @@ namespace src.player.skills var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); if (playerInfo?.Skill != skillName) return; - smokes.TryAdd(new Vector(@event.X, @event.Y, @event.Z), 0); + smokes.TryAdd(new Vector(@event.X, @event.Y, @event.Z), player.Index); } public static void SmokegrenadeExpired(EventSmokegrenadeExpired @event) @@ -39,9 +46,6 @@ namespace src.player.skills var player = PlayerManager.GetPlayerEvent(@event.Userid); if (player == null || !player.IsValid) return; - var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); - if (playerInfo?.Skill != skillName) return; - foreach (var smoke in smokes.Keys.Where(v => v.X == @event.X && v.Y == @event.Y && v.Z == @event.Z)) smokes.TryRemove(smoke, out _); } @@ -153,6 +157,7 @@ namespace src.player.skills if (player == null || !player.IsValid) return; playersWithSkill.TryRemove(player.Index, out _); + RemoveOwnerSmokes(player.Index); SkillUtils.UpdateGrenadeCount(player, CsItem.SmokeGrenade, 1); } diff --git a/jRandomSkills - SRC Files/src/player/skills/HotBomb.cs b/jRandomSkills - SRC Files/src/player/skills/HotBomb.cs index 50c3170..2dbb5c7 100644 --- a/jRandomSkills - SRC Files/src/player/skills/HotBomb.cs +++ b/jRandomSkills - SRC Files/src/player/skills/HotBomb.cs @@ -22,10 +22,10 @@ namespace src.player.skills public static void OnTick() { + if (players.IsEmpty || jRandomSkills.Instance.GameRules?.FreezePeriod == true) return; + int cooldown = Math.Max(1, (int)(SkillsInfo.GetValue(skillName, "cooldown") * 64)); if (Server.TickCount % cooldown != 0) return; - - if (players.IsEmpty || jRandomSkills.Instance.GameRules?.FreezePeriod == true) return; int damage = SkillsInfo.GetValue(skillName, "damage"); var owner = GetSkillOwner(); diff --git a/jRandomSkills - SRC Files/src/player/skills/Iana.cs b/jRandomSkills - SRC Files/src/player/skills/Iana.cs index d272ae1..f160033 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Iana.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Iana.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Timers; @@ -65,7 +65,6 @@ namespace src.player.skills { UpdateWeapons(player, playerSkill); CreateClone(playerSkill); - SkillUtils.SetPlayerCollisions(player, false); } else if (playerSkill.CloneProp != null) KillClone(playerSkill); @@ -138,8 +137,6 @@ namespace src.player.skills if (eventTarget != null && eventTarget.IsValid) SkillUtils.ApplyScreenColor(eventTarget, 0, 0, 0, 0, 10, 0, 2); - SkillUtils.SetPlayerCollisions(player, true); - BlockWeapon(player, false); playerSkill.NextUse = Server.TickCount + SkillsInfo.GetValue(skillName, "Cooldown") * 64; playerSkill.UseTime = 0; @@ -305,25 +302,22 @@ namespace src.player.skills return shooter != null && shooter.IsValid ? shooter : null; } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null) return; - if (string.IsNullOrEmpty(param.Entity.Name)) return; - if (!param.Entity.Name.StartsWith("IanaClone_")) return; + if (string.IsNullOrEmpty(damagedEntity.Entity.Name)) return; + if (!damagedEntity.Entity.Name.StartsWith("IanaClone_")) return; - var nameParams = param.Entity.Name.Split('_')[2]; + var nameParams = damagedEntity.Entity.Name.Split('_')[2]; _ = uint.TryParse(nameParams, out uint userIndex); if (userIndex == 0) return; var player = Utilities.GetPlayerFromIndex((int)userIndex); if (player == null || !player.IsValid) return; - float dealDamage = param2.Damage; + float dealDamage = damageInfo.Damage; if (playersInfo.TryGetValue(player.Index, out var playerSkill)) { @@ -333,7 +327,7 @@ namespace src.player.skills if (cloneProp != null && cloneProp.IsValid && cloneProp.AbsOrigin != null) { - Vector pos = param2.DamagePosition; + Vector pos = damageInfo.DamagePosition; Vector posCl = cloneProp.AbsOrigin; bool isHead = false; @@ -343,8 +337,8 @@ namespace src.player.skills if (pos.Z >= posCl.Z + viewOffset - diff) isHead = true; - float damage = param2.Damage; - string? weapon = param2.Ability?.Value?.DesignerName; + float damage = damageInfo.Damage; + string? weapon = damageInfo.Ability?.Value?.DesignerName; if (weapon != null) dealDamage = CalculateDamage(player, weapon, damage, isHead); } @@ -357,8 +351,8 @@ namespace src.player.skills var playerPawn = player.PlayerPawn.Value; if (playerPawn != null) { - KillfeedIcons? killfeed = KillfeedIconsExtensions.FromWeaponName(param2.Ability?.Value?.DesignerName); - SkillUtils.TakeHealth(playerPawn, (int)dealDamage, GetShooter(param2), killfeed ?? KillfeedIcons.Leg); + KillfeedIcons? killfeed = KillfeedIconsExtensions.FromWeaponName(damageInfo.Ability?.Value?.DesignerName); + SkillUtils.TakeHealth(playerPawn, (int)dealDamage, GetShooter(damageInfo), killfeed ?? KillfeedIcons.Leg); } } } @@ -475,4 +469,4 @@ namespace src.player.skills public float Duration { get; set; } = duration; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs b/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs index 45ae2a3..7b31561 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Illusionist.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Timers; @@ -183,18 +183,15 @@ namespace src.player.skills }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return; - if (string.IsNullOrEmpty(param.Entity.Name)) return; - if (!param.Entity.Name.StartsWith("Illusionist_")) return; + if (string.IsNullOrEmpty(damagedEntity.Entity.Name)) return; + if (!damagedEntity.Entity.Name.StartsWith("Illusionist_")) return; - var replica = param.As(); + var replica = damagedEntity.As(); if (replica == null || !replica.IsValid) return; if (!consumedReplicas.TryAdd(replica.Index, 0)) return; @@ -205,7 +202,7 @@ namespace src.player.skills var owner = GetReplicaOwner(replica.Index); EntityManager.DestroyEntity(replica.Index); - CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle); + CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle); if (attackerPawn.DesignerName != "player") return; var attackerTeam = attackerPawn.TeamNum; @@ -240,4 +237,4 @@ namespace src.player.skills public int EnemyTeamDamage { get; set; } = enemyTeamDamage; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/JetKick.cs b/jRandomSkills - SRC Files/src/player/skills/JetKick.cs index 94602e8..acaded1 100644 --- a/jRandomSkills - SRC Files/src/player/skills/JetKick.cs +++ b/jRandomSkills - SRC Files/src/player/skills/JetKick.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using src.utils; @@ -58,7 +58,7 @@ namespace src.player.skills if (playerInfo == null || playerInfo.Skill != skillName) continue; if (!SkillUtils.HasMenu(player)) continue; - var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); + var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).OfType().ToArray(); ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); @@ -117,7 +117,7 @@ namespace src.player.skills var playerEvent = PlayerManager.GetPlayerFromEvent(player); if (playerEvent == null || !playerEvent.IsValid) return; - var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); + var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).OfType().ToArray(); if (enemies.Length > 0) { diff --git a/jRandomSkills - SRC Files/src/player/skills/JumpBan.cs b/jRandomSkills - SRC Files/src/player/skills/JumpBan.cs index bffa306..f034845 100644 --- a/jRandomSkills - SRC Files/src/player/skills/JumpBan.cs +++ b/jRandomSkills - SRC Files/src/player/skills/JumpBan.cs @@ -46,6 +46,8 @@ namespace src.player.skills public static void OnTick() { + if (bannedPlayers.IsEmpty) return; + foreach (var item in bannedPlayers) { var playerIndex = item.Key; diff --git a/jRandomSkills - SRC Files/src/player/skills/LifeSwap.cs b/jRandomSkills - SRC Files/src/player/skills/LifeSwap.cs index 29cc08f..daf9f7e 100644 --- a/jRandomSkills - SRC Files/src/player/skills/LifeSwap.cs +++ b/jRandomSkills - SRC Files/src/player/skills/LifeSwap.cs @@ -33,7 +33,7 @@ namespace src.player.skills var enemies = SkillUtils.GetSelectableEnemies(player, true); - ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"\u202A{e.PlayerName}\u202C : {e?.PlayerPawn?.Value?.Health ?? 0} HP", e.Index.ToString()))); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"\u202A{e.PlayerName}\u202C : {e.PlayerPawn?.Value?.Health ?? 0} HP", e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Nemesis.cs b/jRandomSkills - SRC Files/src/player/skills/Nemesis.cs index 278211c..a4153ea 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Nemesis.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Nemesis.cs @@ -125,15 +125,13 @@ namespace src.player.skills enemyEvent.PrintToChat($" {ChatColors.Red}{enemyEvent.GetTranslation("nemesis_enemy_info")}"); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { if (markedPlayers.IsEmpty) return; - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - if (param == null || !param.IsValid || param2 == null) return; + if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return; - var victimPawn = param.As(); + var victimPawn = damagedEntity.As(); if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return; var victimController = victimPawn.Controller.Value; @@ -145,12 +143,15 @@ namespace src.player.skills uint victimIndex = PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index; if (!markedPlayers.ContainsKey(victimIndex)) return; - param2.Damage *= 1f + SkillsInfo.GetValue(skillName, "extraDamagePercent"); + if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return; + + damageInfo.Damage *= 1f + SkillsInfo.GetValue(skillName, "extraDamagePercent"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#b8003a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Rare, float extraDamagePercent = .25f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#b8003a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Rare, float extraDamagePercent = .25f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { public float ExtraDamagePercent { get; set; } = extraDamagePercent; + public bool FriendlyFire { get; set; } = friendlyFire; } } } diff --git a/jRandomSkills - SRC Files/src/player/skills/OneShot.cs b/jRandomSkills - SRC Files/src/player/skills/OneShot.cs index 4d4b423..d6befdc 100644 --- a/jRandomSkills - SRC Files/src/player/skills/OneShot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/OneShot.cs @@ -15,20 +15,19 @@ namespace src.player.skills SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return; - CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle); - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player") return; + if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return; + if (attackerPawn == null || attackerPawn.Controller?.Value == null || victimPawn == null || victimPawn.Controller?.Value == null) return; @@ -39,11 +38,12 @@ namespace src.player.skills if (playerInfo == null) return; if (playerInfo.Skill == skillName) - param2.Damage = 1000f; + damageInfo.Damage = 1000f; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5CD9", 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) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5CD9", 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, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { + public bool FriendlyFire { get; set; } = friendlyFire; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/OnlyHead.cs b/jRandomSkills - SRC Files/src/player/skills/OnlyHead.cs index 5bc540d..19e46a6 100644 --- a/jRandomSkills - SRC Files/src/player/skills/OnlyHead.cs +++ b/jRandomSkills - SRC Files/src/player/skills/OnlyHead.cs @@ -20,10 +20,12 @@ namespace src.player.skills var attacker = PlayerManager.GetPlayerEvent(@event.Attacker); var victim = PlayerManager.GetPlayerEvent(@event.Userid); - if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim)) return false; - if ((HitGroup_t)@event.Hitgroup == HitGroup_t.HITGROUP_HEAD) return false; + if (!Instance.IsPlayerValid(victim)) return false; if (PlayerManager.GetPlayerByIndex(victim!.Index)?.Skill != skillName) return false; + bool worldDamage = !Instance.IsPlayerValid(attacker) || attacker!.Index == victim.Index; + if (!worldDamage && (HitGroup_t)@event.Hitgroup == HitGroup_t.HITGROUP_HEAD) return false; + SkillUtils.RestoreHealth(victim); return true; } diff --git a/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs b/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs index b81da67..c613023 100644 --- a/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs +++ b/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs @@ -10,6 +10,7 @@ namespace src.player.skills { private const Skills skillName = Skills.PawelJumper; private const string jumpSound = "Default.WalkJump"; + private static readonly List holderBuffer = []; private static readonly int?[] J = new int?[64]; private static readonly PlayerButtons[] LB = new PlayerButtons[64]; @@ -20,14 +21,15 @@ namespace src.player.skills public static void OnTick() { - foreach (var player in PlayerManager.GetTickPlayers()) - { - if (player == null || !player.IsValid) return; - var playerEvent = PlayerManager.GetPlayerEvent(player); - var playerInfo = PlayerManager.GetPlayerByIndex(playerEvent!.Index); + PlayerManager.FillSkillHolders(skillName, holderBuffer); + if (holderBuffer.Count == 0) return; - if (playerInfo?.Skill == skillName) - GiveAdditionalJump(player, playerEvent); + foreach (var playerEvent in holderBuffer) + { + var player = PlayerManager.GetPlayerFromEvent(playerEvent) ?? playerEvent; + if (player == null || !player.IsValid) continue; + + GiveAdditionalJump(player, playerEvent); } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Phoenix.cs b/jRandomSkills - SRC Files/src/player/skills/Phoenix.cs index b3a39b5..b31581b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Phoenix.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Phoenix.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Utils; @@ -35,13 +35,11 @@ namespace src.player.skills phoenixTicks.TryRemove(playerIndex, out _); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - var victimEntity = h.GetParam(0); - var info = h.GetParam(1); - if (victimEntity == null || !victimEntity.IsValid || info == null) return; + if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return; - var victimPawn = victimEntity.As(); + var victimPawn = damagedEntity.As(); if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return; var victimController = victimPawn.Controller.Value; @@ -53,16 +51,16 @@ namespace src.player.skills var victimInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index); if (victimInfo == null || victimInfo.Skill != skillName) return; - if (SkillUtils.IsFriendlyFireBlocked(info, victimPawn)) return; + if (SkillUtils.IsFriendlyFireBlocked(damageInfo, victimPawn)) return; - float effectiveDamage = info.Damage; - if (SkillUtils.GetHitGroup(info) == HitGroup_t.HITGROUP_HEAD) - effectiveDamage *= GetHeadshotMultiplier(info); + float effectiveDamage = damageInfo.Damage; + if (SkillUtils.GetHitGroup(damageInfo) == HitGroup_t.HITGROUP_HEAD) + effectiveDamage *= GetHeadshotMultiplier(damageInfo); if (effectiveDamage < victimPawn.Health) return; if (TryConsumeRevive(victim, victimPawn)) - info.Damage = 0; + damageInfo.Damage = 0; } public static bool TryConsumeRevive(CCSPlayerController? victim, CCSPlayerPawn? victimPawn) @@ -133,4 +131,4 @@ namespace src.player.skills public float ChanceTo { get; set; } = chanceTo; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Pilot.cs b/jRandomSkills - SRC Files/src/player/skills/Pilot.cs index 4ca9cfe..8999925 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Pilot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Pilot.cs @@ -11,6 +11,7 @@ namespace src.player.skills { private const Skills skillName = Skills.Pilot; private static readonly ConcurrentDictionary PlayerPilotInfo = []; + private static readonly List holderBuffer = []; public static void LoadSkill() { @@ -40,12 +41,11 @@ namespace src.player.skills public static void OnTick() { - foreach (var player in PlayerManager.GetTickPlayers()) - { - var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); - if (playerInfo?.Skill == skillName) - HandlePilot(player); - } + PlayerManager.FillSkillHolders(skillName, holderBuffer); + if (holderBuffer.Count == 0) return; + + foreach (var player in holderBuffer) + HandlePilot(player); } public static void EnableSkill(CCSPlayerController player) diff --git a/jRandomSkills - SRC Files/src/player/skills/Poison.cs b/jRandomSkills - SRC Files/src/player/skills/Poison.cs index 5b46fc5..6fae45d 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Poison.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Poison.cs @@ -15,6 +15,7 @@ namespace src.player.skills private static readonly ConcurrentDictionary playersToTarget = []; private static readonly ConcurrentDictionary targetToPlayer = []; private static readonly object setLock = new(); + private static readonly List holderBuffer = []; public static void LoadSkill() { @@ -49,9 +50,9 @@ namespace src.player.skills public static void OnTick() { - int cooldown = Math.Max(1, (int)(64 * SkillsInfo.GetValue(skillName, "Cooldown"))); + int cooldown = poisonedPlayers.IsEmpty ? 0 : Math.Max(1, (int)(64 * SkillsInfo.GetValue(skillName, "Cooldown"))); - if (Server.TickCount % cooldown == 0) + if (cooldown != 0 && Server.TickCount % cooldown == 0) { int cooldown2 = cooldown * 2; @@ -75,11 +76,12 @@ namespace src.player.skills } if (Server.TickCount % 32 != 0) return; - foreach (var player in PlayerManager.GetTickPlayers()) - { - var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); - if (playerInfo == null || playerInfo.Skill != skillName) continue; + PlayerManager.FillSkillHolders(skillName, holderBuffer); + if (holderBuffer.Count == 0) return; + + foreach (var player in holderBuffer) + { if (!SkillUtils.HasMenu(player)) continue; var enemies = SkillUtils.GetSelectableEnemies(player, true); diff --git a/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs b/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs index 299aac7..ca1d06b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs +++ b/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs @@ -52,10 +52,19 @@ namespace src.player.skills } public static void BombPlanted(EventBombPlanted _) + { + ResolveBombLocation(); + } + + private static void ResolveBombLocation() { var plantedBomb = Utilities.FindAllEntitiesByDesignerName("planted_c4").FirstOrDefault(); - if (plantedBomb != null) - bombLocation = plantedBomb.AbsOrigin; + if (plantedBomb == null || !plantedBomb.IsValid || !plantedBomb.BombTicking || plantedBomb.BombDefused) return; + + var origin = plantedBomb.AbsOrigin; + if (origin == null) return; + + bombLocation = new Vector(origin.X, origin.Y, origin.Z); } public static void OnTick() @@ -92,7 +101,7 @@ namespace src.player.skills if (plantedBomb != null && plantedBomb.IsValid && plantedBomb.BombTicking && !plantedBomb.BombDefused) { plantedBomb.AddEntityIOEvent("Kill", plantedBomb, delay: 0.1f); - SkillUtils.TerminateRound(CsTeam.CounterTerrorist); + SkillUtils.TerminateRound(CsTeam.CounterTerrorist, player); } SkillUtils.ResetPrintHTML(player); SkillPlayerInfo.Clear(); @@ -105,8 +114,7 @@ namespace src.player.skills public static void EnableSkill(CCSPlayerController player) { - var pawn = player.PlayerPawn.Value; - if (pawn == null || !pawn.IsValid) return; + if (player == null || !player.IsValid) return; SkillPlayerInfo.TryAdd(player.Index, new PlayerSkillInfo { @@ -114,6 +122,9 @@ namespace src.player.skills Defusing = false, DefusingTime = SkillsInfo.GetValue(skillName, "defusingTime"), }); + + if (!roundEnded && bombLocation == null) + ResolveBombLocation(); } public static void DisableSkill(CCSPlayerController player) diff --git a/jRandomSkills - SRC Files/src/player/skills/Punisher.cs b/jRandomSkills - SRC Files/src/player/skills/Punisher.cs index 3b4bf02..f678928 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Punisher.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Punisher.cs @@ -50,15 +50,13 @@ namespace src.player.skills storedDamage[victim.Index] = 0f; } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { if (storedDamage.IsEmpty) return; - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - if (param == null || !param.IsValid || param2 == null) return; + if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return; - var victimPawn = param.As(); + var victimPawn = damagedEntity.As(); if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return; var victimController = victimPawn.Controller.Value; @@ -67,9 +65,11 @@ namespace src.player.skills var victim = victimController.As(); if (victim == null || !victim.IsValid) return; + if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return; + uint victimIndex = PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index; - var attacker = param2.Attacker?.Value?.As(); + var attacker = damageInfo.Attacker?.Value?.As(); var attackerController = attacker?.Controller?.Value?.As(); uint? attackerIndex = attackerController != null && attackerController.IsValid ? PlayerManager.GetPlayerEvent(attackerController)?.Index ?? attackerController.Index @@ -78,7 +78,7 @@ namespace src.player.skills if (attackerIndex.HasValue && attackerIndex.Value != victimIndex && storedDamage.TryGetValue(attackerIndex.Value, out float banked) && banked > 0f) { storedDamage[attackerIndex.Value] = 0f; - param2.Damage += banked; + damageInfo.Damage += banked; var puncher = CounterStrikeSharp.API.Utilities.GetPlayerFromIndex((int)attackerIndex.Value); var puncherPawn = puncher?.PlayerPawn?.Value; @@ -97,14 +97,15 @@ namespace src.player.skills float ratio = SkillsInfo.GetValue(skillName, "storePercent"); float cap = SkillsInfo.GetValue(skillName, "maxStored"); - float stored = storedDamage[victimIndex] + (param2.Damage * ratio); + float stored = storedDamage[victimIndex] + (damageInfo.Damage * ratio); storedDamage[victimIndex] = MathF.Min(stored, cap); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffb347", 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 storePercent = .5f, float maxStored = 100f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffb347", 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 storePercent = .5f, float maxStored = 100f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { public float StorePercent { get; set; } = storePercent; public float MaxStored { get; set; } = maxStored; + public bool FriendlyFire { get; set; } = friendlyFire; } } } diff --git a/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs b/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs index fe99125..fa7eb27 100644 --- a/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs @@ -1,4 +1,5 @@ using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Utils; using src.utils; @@ -9,6 +10,7 @@ namespace src.player.skills public class QuickShot : ISkill { private const Skills skillName = Skills.QuickShot; + private static readonly List holderBuffer = []; public static void LoadSkill() { @@ -17,14 +19,16 @@ namespace src.player.skills public static void OnTick() { - foreach (var player in PlayerManager.GetTickPlayers()) - { - if (player == null) continue; + PlayerManager.FillSkillHolders(skillName, holderBuffer); + if (holderBuffer.Count == 0) return; - if (PlayerManager.GetPlayerByIndex(player.Index)?.Skill != skillName) continue; + foreach (var player in holderBuffer) + { if (!Instance.IsPlayerValid(player)) continue; - var pawn = player.PlayerPawn.Value!; + var pawn = player.PlayerPawn.Value; + if (pawn == null || !pawn.IsValid) continue; + var weaponServices = pawn.WeaponServices; if (weaponServices == null || weaponServices.ActiveWeapon == null || !weaponServices.ActiveWeapon.IsValid) continue; @@ -50,4 +54,4 @@ namespace src.player.skills { } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs b/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs index 9864f97..e9fd2fd 100644 --- a/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs +++ b/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs @@ -10,6 +10,7 @@ namespace src.player.skills { private const Skills skillName = Skills.RadarHack; private static readonly Skills[] hidingSkills = [Skills.Ghost, Skills.Ninja, Skills.C4Camouflage]; + private static readonly List holderBuffer = []; public static void LoadSkill() { @@ -18,14 +19,14 @@ namespace src.player.skills public static void OnTick() { - foreach (var player in PlayerManager.GetTickPlayers()) - { - var playerEvent = PlayerManager.GetPlayerEvent(player); - if (playerEvent == null) continue; + PlayerManager.FillSkillHolders(skillName, holderBuffer); + if (holderBuffer.Count == 0) return; - if (PlayerManager.GetPlayerByIndex(playerEvent.Index)?.Skill != skillName) continue; + foreach (var playerEvent in holderBuffer) + { if (!Instance.IsPlayerValid(playerEvent)) continue; + var player = PlayerManager.GetPlayerFromEvent(playerEvent) ?? playerEvent; SetEnemiesVisibleOnRadar(player); } } diff --git a/jRandomSkills - SRC Files/src/player/skills/ReZombie.cs b/jRandomSkills - SRC Files/src/player/skills/ReZombie.cs index 03bca00..4a4d93a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ReZombie.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ReZombie.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Utils; @@ -69,13 +69,11 @@ namespace src.player.skills zombies.Clear(); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - var victimEntity = h.GetParam(0); - var info = h.GetParam(1); - if (victimEntity == null || !victimEntity.IsValid || info == null) return; + if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return; - var victimPawn = victimEntity.As(); + var victimPawn = damagedEntity.As(); if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return; var victimController = victimPawn.Controller.Value; @@ -88,16 +86,16 @@ namespace src.player.skills if (victimInfo == null || victimInfo.Skill != skillName) return; // Friendly-fire-off teammate hit deals 0 damage; don't zombify over a hit that never lands. - if (SkillUtils.IsFriendlyFireBlocked(info, victimPawn)) return; + if (SkillUtils.IsFriendlyFireBlocked(damageInfo, victimPawn)) return; - float effectiveDamage = info.Damage; - if (SkillUtils.GetHitGroup(info) == HitGroup_t.HITGROUP_HEAD) - effectiveDamage *= GetHeadshotMultiplier(info); + 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 (TryBecomeZombie(victim, victimPawn)) - info.Damage = 0; + damageInfo.Damage = 0; } public static bool TryBecomeZombie(CCSPlayerController? victim, CCSPlayerPawn? victimPawn) @@ -213,4 +211,4 @@ namespace src.player.skills public int A { get; set; } = a; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs b/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs index 95ef445..48b2f36 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs @@ -40,9 +40,6 @@ namespace src.player.skills var victim = @event.Userid; if (victim == null || !victim.IsValid || !victim.PawnIsAlive) return false; - var attacker = PlayerManager.GetPlayerEvent(@event.Attacker); - if (attacker == null || !attacker.IsValid) return false; - var victimEvent = PlayerManager.GetPlayerEvent(victim); if (victimEvent == null || !victimEvent.IsValid) return false; diff --git a/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs b/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs index d4f52ea..63b0295 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs @@ -1,4 +1,5 @@ using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using src.utils; using static src.jRandomSkills; @@ -8,6 +9,7 @@ namespace src.player.skills public class Regeneration : ISkill { private const Skills skillName = Skills.Regeneration; + private static readonly List holderBuffer = []; public static void LoadSkill() { @@ -18,11 +20,11 @@ namespace src.player.skills { int cooldown = Math.Max(1, (int)(64 * SkillsInfo.GetValue(skillName, "cooldown"))); if (Server.TickCount % cooldown != 0) return; - foreach (var player in PlayerManager.GetTickPlayers()) - { - var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); - if (playerInfo?.Skill != skillName) continue; + PlayerManager.FillSkillHolders(skillName, holderBuffer); + if (holderBuffer.Count == 0) return; + foreach (var player in holderBuffer) + { var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid) continue; SkillUtils.AddHealth(pawn, SkillsInfo.GetValue(skillName, "healthToAdd")); diff --git a/jRandomSkills - SRC Files/src/player/skills/Replicator.cs b/jRandomSkills - SRC Files/src/player/skills/Replicator.cs index e07b2ad..d23a8b9 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Replicator.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Replicator.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Utils; @@ -136,18 +136,15 @@ namespace src.player.skills replica.DispatchSpawn(); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return; - if (string.IsNullOrEmpty(param.Entity.Name)) return; - if (!param.Entity.Name.StartsWith("Replica_")) return; + if (string.IsNullOrEmpty(damagedEntity.Entity.Name)) return; + if (!damagedEntity.Entity.Name.StartsWith("Replica_")) return; - var replica = param.As(); + var replica = damagedEntity.As(); if (replica == null || !replica.IsValid) return; if (!consumedReplicas.TryAdd(replica.Index, 0)) return; @@ -157,7 +154,7 @@ namespace src.player.skills var owner = GetReplicaOwner(replica.Index); EntityManager.DestroyEntity(replica.Index); - CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle); + CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle); if (attackerPawn.DesignerName != "player") return; @@ -191,4 +188,4 @@ namespace src.player.skills public int EnemyTeamDamage { get; set; } = enemyTeamDamage; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Rubber.cs b/jRandomSkills - SRC Files/src/player/skills/Rubber.cs index 61c426a..1d6b817 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Rubber.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Rubber.cs @@ -42,6 +42,8 @@ namespace src.player.skills public static void OnTick() { + if (playersToSlow.IsEmpty) return; + foreach (var item in playersToSlow) { var playerIndex = item.Key; diff --git a/jRandomSkills - SRC Files/src/player/skills/SecondLife.cs b/jRandomSkills - SRC Files/src/player/skills/SecondLife.cs index 3e25769..b32357b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/SecondLife.cs +++ b/jRandomSkills - SRC Files/src/player/skills/SecondLife.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Utils; @@ -27,13 +27,11 @@ namespace src.player.skills // Must intercept pre-damage: player_hurt fires after the death is committed, so a // lethal hit (esp. fall damage) can't be undone there. Cancelling the blow here keeps the respawn clean. - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - var victimEntity = h.GetParam(0); - var info = h.GetParam(1); - if (victimEntity == null || !victimEntity.IsValid || info == null) return; + if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return; - var victimPawn = victimEntity.As(); + var victimPawn = damagedEntity.As(); if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return; var victimController = victimPawn.Controller.Value; @@ -45,23 +43,23 @@ namespace src.player.skills var victimInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index)); if (victimInfo == null || victimInfo.Skill != skillName) return; - if (SkillUtils.IsFriendlyFireBlocked(info, victimPawn)) return; + if (SkillUtils.IsFriendlyFireBlocked(damageInfo, victimPawn)) return; - float effectiveDamage = info.Damage; - if (SkillUtils.GetHitGroup(info) == HitGroup_t.HITGROUP_HEAD) - effectiveDamage *= GetHeadshotMultiplier(info); + float effectiveDamage = damageInfo.Damage; + if (SkillUtils.GetHitGroup(damageInfo) == HitGroup_t.HITGROUP_HEAD) + effectiveDamage *= GetHeadshotMultiplier(damageInfo); if (effectiveDamage < victimPawn.Health) return; if (usedThisRound.TryGetValue(victim.Handle, out int savedTick)) { if (savedTick == Server.TickCount) - info.Damage = 0; + damageInfo.Damage = 0; return; } if (TryConsumeRevive(victim, victimPawn)) - info.Damage = 0; + damageInfo.Damage = 0; } private static float GetHeadshotMultiplier(CTakeDamageInfo info) diff --git a/jRandomSkills - SRC Files/src/player/skills/Shade.cs b/jRandomSkills - SRC Files/src/player/skills/Shade.cs index 36984f3..9ca3a91 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Shade.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Shade.cs @@ -61,6 +61,8 @@ namespace src.player.skills public static void OnTick() { + if (noSpace.IsEmpty) return; + foreach (var (playerIndex, time) in noSpace) { var player = Utilities.GetPlayerFromIndex((int)playerIndex); diff --git a/jRandomSkills - SRC Files/src/player/skills/Soldier.cs b/jRandomSkills - SRC Files/src/player/skills/Soldier.cs index 83dcd3a..7c434c3 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Soldier.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Soldier.cs @@ -29,20 +29,19 @@ namespace src.player.skills border: !PlayerManager.GetTickPlayers().Any(p => p.Team == player.Team && p != player) ? "tb" : "t"); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - - if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null) + if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null) return; - CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle); - CCSPlayerPawn victimPawn = new(param.Handle); + CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle); + CCSPlayerPawn victimPawn = new(damagedEntity.Handle); if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player") return; + if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return; + if (attackerPawn == null || attackerPawn.Controller?.Value == null || victimPawn == null || victimPawn.Controller?.Value == null) return; @@ -54,14 +53,15 @@ namespace src.player.skills if (playerInfo.Skill == skillName) { float? skillChance = playerInfo.SkillChance; - param2.Damage *= skillChance ?? 1f; + damageInfo.Damage *= skillChance ?? 1f; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#09ba00", 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 chanceFrom = 1.15f, float chanceTo = 1.35f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#09ba00", 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 chanceFrom = 1.15f, float chanceTo = 1.35f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { public float ChanceFrom { get; set; } = chanceFrom; public float ChanceTo { get; set; } = chanceTo; + public bool FriendlyFire { get; set; } = friendlyFire; } } -} \ No newline at end of file +} diff --git a/jRandomSkills - SRC Files/src/player/skills/Spectator.cs b/jRandomSkills - SRC Files/src/player/skills/Spectator.cs index c3ad5ae..3a64f07 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Spectator.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Spectator.cs @@ -74,6 +74,8 @@ namespace src.player.skills public static void OnTick() { + if (cameras.IsEmpty) return; + foreach (var player in PlayerManager.GetTickPlayers()) if (cameras.TryGetValue(player.Index, out var cameraInfo) && cameraInfo.Item2 != 0) { diff --git a/jRandomSkills - SRC Files/src/player/skills/TakeAmmo.cs b/jRandomSkills - SRC Files/src/player/skills/TakeAmmo.cs index 7f61d18..14e3444 100644 --- a/jRandomSkills - SRC Files/src/player/skills/TakeAmmo.cs +++ b/jRandomSkills - SRC Files/src/player/skills/TakeAmmo.cs @@ -81,6 +81,8 @@ namespace src.player.skills public static void OnTick() { + if (SkillPlayerInfo.IsEmpty) return; + var players = PlayerManager.GetTickPlayers().ToArray(); foreach (var player in players) { diff --git a/jRandomSkills - SRC Files/src/player/skills/TeamTeleport.cs b/jRandomSkills - SRC Files/src/player/skills/TeamTeleport.cs index e64f1dc..5887e9d 100644 --- a/jRandomSkills - SRC Files/src/player/skills/TeamTeleport.cs +++ b/jRandomSkills - SRC Files/src/player/skills/TeamTeleport.cs @@ -35,6 +35,8 @@ namespace src.player.skills public static void OnTick() { + if (SkillPlayerInfo.IsEmpty) return; + foreach (var player in PlayerManager.GetTickPlayers()) { if (player == null || !player.IsValid) continue; diff --git a/jRandomSkills - SRC Files/src/player/skills/ThirdEye.cs b/jRandomSkills - SRC Files/src/player/skills/ThirdEye.cs index aa3f91e..5d931da 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ThirdEye.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ThirdEye.cs @@ -63,6 +63,8 @@ namespace src.player.skills public static void OnTick() { + if (cameras.IsEmpty) return; + foreach (var player in PlayerManager.GetTickPlayers()) if (cameras.TryGetValue(player.Index, out var cameraInfo) && cameraInfo.Item2 != 0) { diff --git a/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs b/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs index a2bc871..d32f35f 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ThrowingKnife.cs @@ -156,7 +156,13 @@ namespace src.player.skills public static void DisableSkill(CCSPlayerController player) { if (knivesInfo.TryRemove(player.Index, out KnifeInfo? knifeInfo) && knifeInfo != null) + { + bool wasDropped = knifeInfo.IsDropped; DisableKnifeSkill(knifeInfo); + + if (wasDropped && player != null && player.IsValid && player.PawnIsAlive) + player.GiveNamedItem("weapon_knife"); + } } public static void PlayerDisconnect(uint playerIndex) diff --git a/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs b/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs index c1d4c1e..b91cc76 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs @@ -12,6 +12,13 @@ namespace src.player.skills { private const Skills skillName = Skills.ToxicSmoke; private static readonly ConcurrentDictionary smokes = []; + + private static void RemoveOwnerSmokes(uint ownerIndex) + { + foreach (var smoke in smokes) + if (smoke.Value == ownerIndex) + smokes.TryRemove(smoke.Key, out _); + } private readonly static ConcurrentDictionary playersWithSkill = []; public static void LoadSkill() @@ -40,9 +47,6 @@ namespace src.player.skills var player = PlayerManager.GetPlayerEvent(@event.Userid); if (player == null || !player.IsValid) return; - var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); - if (playerInfo?.Skill != skillName) return; - foreach (var smoke in smokes.Keys.Where(v => v.X == @event.X && v.Y == @event.Y && v.Z == @event.Z)) smokes.TryRemove(smoke, out _); } @@ -100,6 +104,8 @@ namespace src.player.skills int damage = smokeDamage; if (thrower != null && eventPlayer.Index != thrower.Index && pawn.TeamNum == thrower.TeamNum) { + if (!SkillsInfo.GetValue(skillName, "friendlyFire")) continue; + damage = (int)MathF.Round(smokeDamage * SkillUtils.GetTeamDamageMultiplier(skillName)); if (damage <= 0) continue; } @@ -167,10 +173,11 @@ namespace src.player.skills if (player == null || !player.IsValid) return; playersWithSkill.TryRemove(player.Index, out _); + RemoveOwnerSmokes(player.Index); SkillUtils.UpdateGrenadeCount(player, CsItem.SmokeGrenade, 1); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", 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 smokeDamage = 2, float smokeRadius = 180, int tickCooldown = 17, int grenadeLimit = 1, float dmgReductionForTeamates = 0.5f, float soundVolume = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", 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 smokeDamage = 2, float smokeRadius = 180, int tickCooldown = 17, int grenadeLimit = 1, float dmgReductionForTeamates = 0.5f, float soundVolume = .3f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity) { public float SoundVolume { get; set; } = soundVolume; public int SmokeDamage { get; set; } = smokeDamage; @@ -178,6 +185,7 @@ namespace src.player.skills public int TickCooldown { get; set; } = tickCooldown; public int GrenadeLimit { get; set; } = grenadeLimit; public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates; + public bool FriendlyFire { get; set; } = friendlyFire; } } } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/player/skills/TrueArmor.cs b/jRandomSkills - SRC Files/src/player/skills/TrueArmor.cs index 9cca7bc..39a1582 100644 --- a/jRandomSkills - SRC Files/src/player/skills/TrueArmor.cs +++ b/jRandomSkills - SRC Files/src/player/skills/TrueArmor.cs @@ -1,4 +1,4 @@ -using CounterStrikeSharp.API; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Utils; @@ -43,11 +43,11 @@ namespace src.player.skills pending.TryRemove(playerIndex, out _); } - public static void OnTakeDamage(DynamicHook h) + public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo) { if (holders.IsEmpty) return; - if (!TryResolveVictim(h, out var victim, out var victimPawn, out var info)) return; + if (!TryResolveVictim(damagedEntity, damageInfo, out var victim, out var victimPawn, out var info)) return; if (!holders.ContainsKey(victim!.Index)) return; int armor = victimPawn!.ArmorValue; @@ -71,28 +71,26 @@ namespace src.player.skills $"passed={info.Damage:0.#} armor={armor}->{pending[victim.Index]} hp={victimPawn.Health}", DebugCategory.Damage); } - public static void OnTakeDamagePost(DynamicHook h) + public static void OnTakeDamagePost(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, CTakeDamageResult damageResult) { if (pending.IsEmpty) return; - if (!TryResolveVictim(h, out var victim, out var victimPawn, out _)) return; + if (!TryResolveVictim(damagedEntity, damageInfo, out var victim, out var victimPawn, out _)) return; if (!pending.TryRemove(victim!.Index, out int restored)) return; victimPawn!.ArmorValue = restored; Utilities.SetStateChanged(victimPawn, "CCSPlayerPawn", "m_ArmorValue"); } - private static bool TryResolveVictim(DynamicHook h, out CCSPlayerController? victim, out CCSPlayerPawn? pawn, out CTakeDamageInfo? info) + private static bool TryResolveVictim(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, out CCSPlayerController? victim, out CCSPlayerPawn? pawn, out CTakeDamageInfo? info) { victim = null; pawn = null; info = null; - CEntityInstance param = h.GetParam(0); - CTakeDamageInfo param2 = h.GetParam(1); - if (param == null || !param.IsValid || param2 == null) return false; + if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return false; - var victimPawn = param.As(); + var victimPawn = damagedEntity.As(); if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return false; var controller = victimPawn.Controller.Value; @@ -104,7 +102,7 @@ namespace src.player.skills var routed = PlayerManager.GetPlayerEvent(resolved); victim = routed != null && routed.IsValid ? routed : resolved; pawn = victimPawn; - info = param2; + info = damageInfo; return true; } diff --git a/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs b/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs index 89466f0..529f051 100644 --- a/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs +++ b/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs @@ -47,6 +47,8 @@ namespace src.player.skills public static void OnTick() { + if (SkillPlayerInfo.IsEmpty) return; + var players = PlayerManager.GetTickPlayers().ToArray(); foreach (var player in players) { diff --git a/jRandomSkills - SRC Files/src/player/skills/WildThrow.cs b/jRandomSkills - SRC Files/src/player/skills/WildThrow.cs index 913dd7e..7a67f3b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/WildThrow.cs +++ b/jRandomSkills - SRC Files/src/player/skills/WildThrow.cs @@ -114,7 +114,7 @@ namespace src.player.skills !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None - ).ToArray(); + ).OfType().ToArray(); ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); diff --git a/jRandomSkills - SRC Files/src/utils/Config.cs b/jRandomSkills - SRC Files/src/utils/Config.cs index edb6c3d..35020be 100644 --- a/jRandomSkills - SRC Files/src/utils/Config.cs +++ b/jRandomSkills - SRC Files/src/utils/Config.cs @@ -63,10 +63,7 @@ namespace src.utils var current = Newtonsoft.Json.Linq.JObject.Parse(json); var expected = Newtonsoft.Json.Linq.JObject.FromObject(new SettingsModel()); - foreach (var property in expected.Properties()) - if (current[property.Name] == null) return true; - - return false; + return HasMissingKeys(current, expected); } catch { @@ -74,6 +71,21 @@ namespace src.utils } } + private static bool HasMissingKeys(Newtonsoft.Json.Linq.JObject current, Newtonsoft.Json.Linq.JObject expected) + { + foreach (var property in expected.Properties()) + { + var currentValue = current[property.Name]; + if (currentValue == null) return true; + + if (property.Value is Newtonsoft.Json.Linq.JObject expectedChild + && currentValue is Newtonsoft.Json.Linq.JObject currentChild + && HasMissingKeys(currentChild, expectedChild)) return true; + } + + return false; + } + private static bool IsLegacyDebugMode(string json) { try @@ -227,6 +239,7 @@ namespace src.utils WSADMenuItemLineColor = "white", WSADMenuItemHoverLineColor = "orange", WSADMenuItemLineSize = "sm", + WSADMenuVisibleItems = 3, WSADMenuControllsLineSize = "sm", WSADMenuControllsLineColor1 = "cyan", WSADMenuControllsLineColor2 = "white", @@ -308,6 +321,7 @@ namespace src.utils public required string WSADMenuItemLineColor { get; set; } public required string WSADMenuItemHoverLineColor { get; set; } public required string WSADMenuItemLineSize { get; set; } + public required int WSADMenuVisibleItems { get; set; } public required string WSADMenuControllsLineSize { get; set; } public required string WSADMenuControllsLineColor1 { get; set; } public required string WSADMenuControllsLineColor2 { get; set; } diff --git a/jRandomSkills - SRC Files/src/utils/DecoyRing.cs b/jRandomSkills - SRC Files/src/utils/DecoyRing.cs index ee431bf..db0add8 100644 --- a/jRandomSkills - SRC Files/src/utils/DecoyRing.cs +++ b/jRandomSkills - SRC Files/src/utils/DecoyRing.cs @@ -23,12 +23,15 @@ namespace src.utils private const float maxLifetime = 25f; + private const int spawnFrames = 4; + private static readonly ConcurrentDictionary rings = []; private sealed class Ring { public uint? Disc { get; set; } public List Beams { get; } = []; + public bool Cancelled { get; set; } } public static void PreloadAssets() @@ -48,9 +51,11 @@ namespace src.utils if (!EntityManager.OverBudget()) { - Color color = GetSkillColor(skill); - SpawnRing(center, radius, outerSegments, color, ring); - SpawnRing(center, radius * .5f, innerSegments, color, ring); + var segments = new List<(Vector From, Vector To)>(outerSegments + innerSegments); + CollectRing(center, radius, outerSegments, segments); + CollectRing(center, radius * .5f, innerSegments, segments); + + SpawnBeamChunk(ring, segments, 0, GetSkillColor(skill)); } jRandomSkills.Instance.AddTimer(maxLifetime, () => Hide(skill, id), TimerFlags.STOP_ON_MAPCHANGE); @@ -77,6 +82,8 @@ namespace src.utils private static void Destroy(Ring ring) { + ring.Cancelled = true; + foreach (uint beamIndex in ring.Beams) EntityManager.DestroyBeam(beamIndex); @@ -166,7 +173,7 @@ namespace src.utils attribute.InteractsExclude = 0; } - private static void SpawnRing(Vector center, float radius, int segments, Color color, Ring ring) + private static void CollectRing(Vector center, float radius, int segments, List<(Vector From, Vector To)> output) { if (segments < 3 || radius <= 0) return; @@ -176,23 +183,37 @@ namespace src.utils for (int i = 1; i <= segments; i++) { Vector next = PointOn(center, radius, step * i); - - var beam = EntityManager.CreateTrackedBeam(EntityManager.SystemOwnerIndex, previous, next, color); - if (beam != null && beam.IsValid) - { - beam.Width = 1.2f; - beam.EndWidth = 1.2f; - - Utilities.SetStateChanged(beam, "CBeam", "m_fWidth"); - Utilities.SetStateChanged(beam, "CBeam", "m_fEndWidth"); - - ring.Beams.Add(beam.Index); - } - + output.Add((previous, next)); previous = next; } } + private static void SpawnBeamChunk(Ring ring, List<(Vector From, Vector To)> segments, int start, Color color) + { + if (ring.Cancelled || start >= segments.Count) return; + + int perFrame = (segments.Count + spawnFrames - 1) / spawnFrames; + int end = Math.Min(start + perFrame, segments.Count); + + for (int i = start; i < end; i++) + { + var beam = EntityManager.CreateTrackedBeam(EntityManager.SystemOwnerIndex, segments[i].From, segments[i].To, color); + if (beam == null || !beam.IsValid) continue; + + beam.Width = 1.2f; + beam.EndWidth = 1.2f; + + Utilities.SetStateChanged(beam, "CBeam", "m_fWidth"); + Utilities.SetStateChanged(beam, "CBeam", "m_fEndWidth"); + + ring.Beams.Add(beam.Index); + } + + if (end >= segments.Count) return; + + Server.NextFrame(() => SpawnBeamChunk(ring, segments, end, color)); + } + private static Vector PointOn(Vector center, float radius, float angle) { return new Vector( diff --git a/jRandomSkills - SRC Files/src/utils/EntityManager.cs b/jRandomSkills - SRC Files/src/utils/EntityManager.cs index 558b745..0581dfa 100644 --- a/jRandomSkills - SRC Files/src/utils/EntityManager.cs +++ b/jRandomSkills - SRC Files/src/utils/EntityManager.cs @@ -91,6 +91,21 @@ namespace src.utils return (trackedEntities.Count, trackedEntities.Values.Select(e => e.PlayerIndex).Distinct().Count()); } + public static string DescribeTracked(int topTypes = 4) + { + var byType = new Dictionary(); + foreach (var data in trackedEntities.Values) + { + string type = string.IsNullOrEmpty(data.EntityType) ? "?" : data.EntityType; + byType[type] = byType.TryGetValue(type, out int count) ? count + 1 : 1; + } + + if (byType.Count == 0) return string.Empty; + + var parts = byType.OrderByDescending(kvp => kvp.Value).Take(topTypes).Select(kvp => $"{kvp.Key}:{kvp.Value}"); + return " types=" + string.Join(",", parts); + } + public static CParticleSystem? CreateTrackedParticleSystem(uint playerIndex, string particleName, float? autoDestroySeconds = null) { diff --git a/jRandomSkills - SRC Files/src/utils/Localization.cs b/jRandomSkills - SRC Files/src/utils/Localization.cs index 6c257ac..e2359d6 100644 --- a/jRandomSkills - SRC Files/src/utils/Localization.cs +++ b/jRandomSkills - SRC Files/src/utils/Localization.cs @@ -18,6 +18,7 @@ namespace src.utils private static readonly string playersLanguageFileName = "playersLanguage.json"; private static readonly string configsFolderPath = Path.Combine(jRandomSkills.Instance.ModuleDirectory, "configs"); private static ConcurrentDictionary _playersLanguage = []; + private static string _lastSavedPlayersJson = string.Empty; private static readonly string geoliteFilePath = Path.Combine(jRandomSkills.Instance.ModuleDirectory, "packages", "GeoLite2-Country.mmdb"); private static string defaultLangCode = "en"; @@ -47,6 +48,7 @@ namespace src.utils { _translations.Clear(); _playersLanguage.Clear(); + _lastSavedPlayersJson = string.Empty; _skillNameCache.Clear(); _skillDescCache.Clear(); SetLangCode(); @@ -324,16 +326,78 @@ namespace src.utils return; var jsonText = File.ReadAllText(filePath); - _playersLanguage = JsonConvert.DeserializeObject>(jsonText) ?? []; + if (string.IsNullOrWhiteSpace(jsonText)) + return; + + bool legacyFormat = false; + var loaded = new ConcurrentDictionary(); + + try + { + var root = Newtonsoft.Json.Linq.JObject.Parse(jsonText); + foreach (var property in root.Properties()) + { + if (property.Value is Newtonsoft.Json.Linq.JArray steamIds) + { + foreach (var steamId in steamIds) + if (ulong.TryParse(steamId.ToString(), out ulong grouped) && grouped != 0) + loaded[grouped] = property.Name; + } + else + { + legacyFormat = true; + if (ulong.TryParse(property.Name, out ulong flat) && flat != 0) + loaded[flat] = property.Value.ToString(); + } + } + } + catch + { + Server.PrintToConsole($"[jRandomSkills] {playersLanguageFileName} could not be parsed, starting empty."); + return; + } + + _playersLanguage = loaded; + + if (legacyFormat) + { + Server.PrintToConsole($"[jRandomSkills] {playersLanguageFileName} converted to the grouped format ({_playersLanguage.Count} players)."); + SavePlayersLanguage(); + } + else + _lastSavedPlayersJson = BuildPlayersJson(); + } + + private static string BuildPlayersJson() + { + var grouped = new SortedDictionary>(StringComparer.Ordinal); + + foreach (var entry in _playersLanguage) + { + if (entry.Key == 0 || string.IsNullOrEmpty(entry.Value)) continue; + + if (!grouped.TryGetValue(entry.Value, out var steamIds)) + grouped[entry.Value] = steamIds = []; + + steamIds.Add(entry.Key.ToString()); + } + + foreach (var steamIds in grouped.Values) + steamIds.Sort(StringComparer.Ordinal); + + return JsonConvert.SerializeObject(grouped); } private static void SavePlayersLanguage() { + var json = BuildPlayersJson(); + + if (json == null || json == _lastSavedPlayersJson) + return; + Directory.CreateDirectory(configsFolderPath); - string filePath = Path.Combine(configsFolderPath, playersLanguageFileName); - var json = JsonConvert.SerializeObject(_playersLanguage); - if (json != null) - File.WriteAllText(filePath, json); + File.WriteAllText(Path.Combine(configsFolderPath, playersLanguageFileName), json); + _lastSavedPlayersJson = json; } private static string? GetLangCodeFromFile(ulong? playerSteamID) @@ -346,7 +410,9 @@ namespace src.utils public static void ChangePlayerLanguage(CCSPlayerController? player, string language) { - if (player == null || !player.IsValid) return; + if (player == null || !player.IsValid || player.SteamID == 0) return; + if (string.IsNullOrEmpty(language)) return; + _playersLanguage.AddOrUpdate(player.SteamID, language, (k, v) => language); SavePlayersLanguage(); } diff --git a/jRandomSkills - SRC Files/src/utils/PlayerManager.cs b/jRandomSkills - SRC Files/src/utils/PlayerManager.cs index 35ef3a0..6c87a5e 100644 --- a/jRandomSkills - SRC Files/src/utils/PlayerManager.cs +++ b/jRandomSkills - SRC Files/src/utils/PlayerManager.cs @@ -16,6 +16,35 @@ namespace src.utils private static int cachedBombTick = int.MinValue; private static CC4? cachedBomb; + private static int cachedIdleTick = int.MinValue; + private static bool cachedIdle = true; + + public static bool IsServerIdle() + { + int tick = Server.TickCount; + if (tick == cachedIdleTick) return cachedIdle; + cachedIdleTick = tick; + + cachedIdle = true; + foreach (var player in GetTickPlayers()) + { + if (player == null || !player.IsValid) continue; + if (player.IsBot || player.IsHLTV) continue; + + cachedIdle = false; + break; + } + + return cachedIdle; + } + + private static int cachedPawnMapTick = int.MinValue; + private static readonly Dictionary controllersByPawn = []; + + private static int cachedBotMapTick = int.MinValue; + private static readonly Dictionary botsByController = []; + private static readonly HashSet tickPlayerHandles = []; + private static void EnsureTickCache() { int tick = Server.TickCount; @@ -24,6 +53,28 @@ namespace src.utils cachedControllers = Utilities.GetPlayers(); } + private static void EnsureBotMapCache() + { + int tick = Server.TickCount; + if (tick == cachedBotMapTick) return; + cachedBotMapTick = tick; + + botsByController.Clear(); + tickPlayerHandles.Clear(); + + foreach (var player in GetTickPlayers()) + { + if (player == null || !player.IsValid) continue; + + tickPlayerHandles.Add(player.Handle); + if (!player.IsBot) continue; + + var original = player.OriginalControllerOfCurrentPawn.Value; + if (original != null) + botsByController[original.Handle] = player; + } + } + public static List GetTickPlayers() { EnsureTickCache(); @@ -69,12 +120,8 @@ namespace src.utils if (!player.ControllingBot) return player; - return GetTickPlayers().FirstOrDefault(p => - p != null && - p.IsValid && - p.IsBot && - p.OriginalControllerOfCurrentPawn.Value != null && p.OriginalControllerOfCurrentPawn.Value == player) - ?? player; + EnsureBotMapCache(); + return botsByController.TryGetValue(player.Handle, out var bot) ? bot : player; } public static CCSPlayerController? GetPlayerFromEvent(CCSPlayerController? player) @@ -85,12 +132,48 @@ namespace src.utils if (!player.IsBot) return player; - return GetTickPlayers().FirstOrDefault(p => - p != null && - p.IsValid && - !p.IsBot && - player.OriginalControllerOfCurrentPawn.Value != null && player.OriginalControllerOfCurrentPawn.Value == p) - ?? player; + var original = player.OriginalControllerOfCurrentPawn.Value; + if (original == null || !original.IsValid || original.IsBot) return player; + + EnsureBotMapCache(); + return tickPlayerHandles.Contains(original.Handle) ? original : player; + } + + public static CCSPlayerController? GetControllerByPawn(nint pawnHandle) + { + if (pawnHandle == nint.Zero) return null; + + int tick = Server.TickCount; + if (tick != cachedPawnMapTick) + { + cachedPawnMapTick = tick; + controllersByPawn.Clear(); + + foreach (var player in GetTickPlayers()) + { + if (player == null || !player.IsValid) continue; + + var handle = player.Pawn?.Value?.Handle; + if (handle != null && handle.Value != nint.Zero) + controllersByPawn[handle.Value] = player; + } + } + + return controllersByPawn.TryGetValue(pawnHandle, out var controller) ? controller : null; + } + + public static void FillSkillHolders(Skills skill, List buffer) + { + buffer.Clear(); + + foreach (var info in playersByIndex.Values) + { + if (info.Skill != skill) continue; + + var controller = Utilities.GetPlayerFromIndex((int)info.PlayerIndex); + if (controller != null && controller.IsValid) + buffer.Add(controller); + } } public static IEnumerable GetAllPlayers() diff --git a/jRandomSkills - SRC Files/src/utils/SkillUtils.cs b/jRandomSkills - SRC Files/src/utils/SkillUtils.cs index a4043e5..a7cde3f 100644 --- a/jRandomSkills - SRC Files/src/utils/SkillUtils.cs +++ b/jRandomSkills - SRC Files/src/utils/SkillUtils.cs @@ -220,27 +220,6 @@ namespace src.utils SnapViewAngles.Value?.Invoke(pawn, angle); } - public static void SetPlayerCollisions(CCSPlayerController? player, bool enable) - { - return; - - if (player == null || !player.IsValid) return; - - var pawn = player.PlayerPawn.Value; - if (pawn == null || !pawn.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE || pawn.CBodyComponent == null) return; - - var collision = pawn.Collision; - if (collision == null) return; - - var collisionGroup = (byte)(enable ? CollisionGroup.COLLISION_GROUP_PLAYER : CollisionGroup.COLLISION_GROUP_DEBRIS); - - collision.CollisionGroup = collisionGroup; - collision.CollisionAttribute.CollisionGroup = collisionGroup; - Utilities.SetStateChanged(pawn, "CCollisionProperty", "m_collisionAttribute"); - - // CollisionRulesChanged(pawn); - } - //public static void CollisionRulesChanged(CBaseEntity? entity) //{ // if (entity == null || !entity.IsValid || collisionRulesChangedOffset <= 0) return; @@ -367,6 +346,30 @@ namespace src.utils return !ff && !tae; // same team + FF off -> engine will zero this damage } + public static bool IsFriendlyFireBlocked(Skills skill, CTakeDamageInfo? info, CCSPlayerPawn? victimPawn) + { + if (!IsSameTeamHit(info, victimPawn)) return false; + if (!SkillsInfo.GetValue(skill, "friendlyFire")) return true; + + bool ff = ConVar.Find("mp_friendlyfire")?.GetPrimitiveValue() ?? false; + bool tae = ConVar.Find("mp_teammates_are_enemies")?.GetPrimitiveValue() ?? false; + return !ff && !tae; + } + + public static bool IsSameTeamHit(CTakeDamageInfo? info, CCSPlayerPawn? victimPawn) + { + if (info == null || victimPawn == null || !victimPawn.IsValid) return false; + + var attackerEnt = info.Attacker?.Value; + if (attackerEnt == null || !attackerEnt.IsValid) return false; + if (attackerEnt.Handle == victimPawn.Handle) return false; + + var attackerPawn = new CCSPlayerPawn(attackerEnt.Handle); + if (!attackerPawn.IsValid || attackerPawn.DesignerName != "player") return false; + + return attackerPawn.TeamNum == victimPawn.TeamNum; + } + public static float GetTeamDamageMultiplier(Skills skill) { float reduction = SkillsInfo.GetValue(skill, "dmgReductionForTeamates"); @@ -945,9 +948,17 @@ namespace src.utils { if (jRandomSkills.Instance.MenuManager == null) jRandomSkills.Instance.MenuManager = new WasdManager(); + + ApplyMenuVisibleItems(); return jRandomSkills.Instance.MenuManager; } + private static void ApplyMenuVisibleItems() + { + int visibleItems = Config.LoadedConfig.HtmlHudCustomisation.WSADMenuVisibleItems; + WASDMenuAPI.WasdMenuPlayer.DefaultVisibleOptions = visibleItems < 1 ? 3 : Math.Min(visibleItems, 10); + } + public static void CloseMenu(CCSPlayerController? player) { var manager = GetMenuManager(); @@ -1092,11 +1103,20 @@ namespace src.utils if (lastElement != null) { - string encodedLastElement = isIlliterate - ? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(lastElement.Value.Item1)!) - : System.Net.WebUtility.HtmlEncode(lastElement.Value.Item1); + string lastText = lastElement.Value.Item1; + string lastColor = string.Empty; - menu.Add($"\u202A{encodedLastElement}\u202C", (p, option) => + if (lastText.Length > 8 && lastText[0] == '#' && lastText[7] == '|') + { + lastColor = lastText[..8]; + lastText = lastText[8..]; + } + + string encodedLastElement = isIlliterate + ? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(lastText)!) + : System.Net.WebUtility.HtmlEncode(lastText); + + menu.Add($"{lastColor}\u202A{encodedLastElement}\u202C", (p, option) => { jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { lastElement.Value.Item2 }]); if (lastElement.Value.Item3) @@ -1131,7 +1151,7 @@ namespace src.utils TerminateRoundFunc.Value?.Invoke(jRandomSkills.Instance.GameRules.Handle, 5f, roundEndReason, 0, 0); } - public static void TerminateRound(CsTeam winnerTeam) + public static void TerminateRound(CsTeam winnerTeam, CCSPlayerController? bonusPlayer = null) { if (jRandomSkills.Instance == null || jRandomSkills.Instance.GameRules == null) return; var teams = Utilities.FindAllEntitiesByDesignerName("cs_team_manager"); @@ -1144,6 +1164,40 @@ namespace src.utils UpdateServerTeamScores(ctScore, tScore); jRandomSkills.Instance.GameRules?.TerminateRound(5f, winnerTeam == CsTeam.CounterTerrorist ? RoundEndReason.BombDefused : RoundEndReason.TargetBombed); + + AwardRoundEndMoney(winnerTeam, bonusPlayer); + } + + private static void AwardRoundEndMoney(CsTeam winnerTeam, CCSPlayerController? bonusPlayer = null) + { + int winnerReward = winnerTeam == CsTeam.CounterTerrorist + ? ConVar.Find("cash_team_win_by_defusing_bomb")?.GetPrimitiveValue() ?? 3500 + : ConVar.Find("cash_team_terrorist_win_bomb")?.GetPrimitiveValue() ?? 3500; + + if (winnerReward <= 0) return; + + int maxMoney = ConVar.Find("mp_maxmoney")?.GetPrimitiveValue() ?? 16000; + int personalBonus = bonusPlayer == null + ? 0 + : winnerTeam == CsTeam.CounterTerrorist + ? ConVar.Find("cash_player_defused_bomb")?.GetPrimitiveValue() ?? 300 + : ConVar.Find("cash_player_bomb_planted")?.GetPrimitiveValue() ?? 300; + + foreach (var player in PlayerManager.GetTickPlayers()) + { + if (player == null || !player.IsValid) continue; + if (player.Team != winnerTeam) continue; + + var moneyServices = player.InGameMoneyServices; + if (moneyServices == null) continue; + + int reward = winnerReward; + if (bonusPlayer != null && bonusPlayer.IsValid && player.Index == bonusPlayer.Index) + reward += personalBonus; + + moneyServices.Account = Math.Clamp(moneyServices.Account + reward, 0, maxMoney); + Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInGameMoneyServices"); + } } private static void UpdateServerTeamScores(short ctScore, short tScore) diff --git a/jRandomSkills - SRC Files/src/utils/SkillsInfo.cs b/jRandomSkills - SRC Files/src/utils/SkillsInfo.cs index d247a65..3045dd1 100644 --- a/jRandomSkills - SRC Files/src/utils/SkillsInfo.cs +++ b/jRandomSkills - SRC Files/src/utils/SkillsInfo.cs @@ -44,6 +44,9 @@ namespace src.utils json = sr.ReadToEnd(); var root = JsonConvert.DeserializeObject(json); + bool needsRewrite = root == null; + var present = new HashSet(StringComparer.Ordinal); + if (root != null) foreach (var skillObj in root) { @@ -51,8 +54,24 @@ namespace src.utils if (string.IsNullOrEmpty(name)) continue; var instance = newConfig.FirstOrDefault(x => x.Name == name.ToString()); - if (instance != null) JsonConvert.PopulateObject(skillObj.ToString(), instance); + if (instance == null) continue; + + present.Add(name); + + if (skillObj is JObject current && HasMissingKeys(current, instance)) + needsRewrite = true; + + JsonConvert.PopulateObject(skillObj.ToString(), instance); } + + if (newConfig.Any(s => !string.IsNullOrEmpty(s.Name) && !present.Contains(s.Name))) + needsRewrite = true; + + if (needsRewrite) + { + SaveConfig(newConfig); + Instance.Logger.LogInformation("skillsInfo.json was missing keys; rewritten with the defaults filled in."); + } } catch { @@ -63,6 +82,16 @@ namespace src.utils } } + private static bool HasMissingKeys(JObject current, DefaultSkillInfo expectedInstance) + { + var expected = JObject.FromObject(expectedInstance); + + foreach (var property in expected.Properties()) + if (current[property.Name] == null) return true; + + return false; + } + public static void SaveConfig(SkillsInfoModel config) { lock (fileLock) diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll b/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll index e80a256..de83998 100644 Binary files a/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll and b/jRandomSkills - Server Files/plugins/jRandomSkills/WASDMenuAPI.dll differ diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/configs/config.json b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/config.json index 6c87b8a..b771acc 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/configs/config.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/config.json @@ -126,6 +126,7 @@ "WSADMenuItemLineColor": "white", "WSADMenuItemHoverLineColor": "orange", "WSADMenuItemLineSize": "sm", + "WSADMenuVisibleItems": 3, "WSADMenuControllsLineSize": "sm", "WSADMenuControllsLineColor1": "cyan", "WSADMenuControllsLineColor2": "white", diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json index 2caa9b8..ec13255 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/configs/skillsInfo.json @@ -1,4 +1,4 @@ -[ +[ { "NeedsTeammates": false, "DisableOnFreezeTime": false, @@ -146,6 +146,7 @@ "TickCooldown": 17, "GrenadeLimit": 1, "DmgReductionForTeamates": 0.5, + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -321,6 +322,7 @@ { "ChanceFrom": 1.15, "ChanceTo": 1.35, + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -725,6 +727,7 @@ { "StorePercent": 0.5, "MaxStored": 100.0, + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -883,6 +886,7 @@ "Rarity": "Common" }, { + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -993,6 +997,7 @@ }, { "ExtraDamagePercent": 0.25, + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -1798,6 +1803,7 @@ "Rarity": "Epic" }, { + "Cooldown": 5.0, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -1832,6 +1838,7 @@ "ChanceFrom": 0.15, "ChanceTo": 0.3, "DmgReductionForTeamates": 0.5, + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -1851,6 +1858,7 @@ "ExplosionDamage": 50, "PropModel": "models/props/de_train/hr_t/barrel_a/barrel_a.vmdl", "DmgReductionForTeamates": 0.5, + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": true, "DisableOnPistolRound": false, @@ -2008,6 +2016,7 @@ "ExplosionRadius": 500.0, "ExplosionDamage": 999, "DmgReductionForTeamates": 0.5, + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -2103,6 +2112,7 @@ "Rarity": "Common" }, { + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -2243,6 +2253,7 @@ "DmgReductionForTeamates": 0.5, "Cooldown": 10.0, "Force": 1000.0, + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -2276,6 +2287,7 @@ { "MaxSpeedVelocity": 2.0, "MaxDamageVelocity": 2.0, + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -2356,6 +2368,7 @@ { "DamageMultiplier": 2.0, "ToleranceDeg": 45.0, + "FriendlyFire": true, "NeedsTeammates": false, "DisableOnFreezeTime": false, "DisableOnPistolRound": false, @@ -2475,4 +2488,4 @@ "MaxPerServer": -1, "Rarity": "Common" } -] \ No newline at end of file +] diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll b/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll index a766683..86d7a08 100644 Binary files a/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll and b/jRandomSkills - Server Files/plugins/jRandomSkills/jRandomSkills.dll differ diff --git a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json index 0bfd105..883f2b5 100644 --- a/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json +++ b/jRandomSkills - Server Files/plugins/jRandomSkills/languages/pl.json @@ -487,7 +487,7 @@ "returntosender": "Zwrot do Nadawcy", "returntosender_desc": "Pierwsze trafienie wroga powoduje, że wraca on na swój resp", - "rewind": "Cofnięcie", + "rewind": "Cofnięcietruearmor", "rewind_desc": "Naciśnij [css_useSkill], aby zostawić znacznik i wkrótce do niego wrócić", "rewind_pending_info": "POWRÓT: {0}",