jRandomSkills/jRandomSkills - SRC Files/src/player/skills/ChillOut.cs
ByDexter 25cd8eb989 v1.2.2.b6
Performance & stability pass (issue #34 + audit)

General:
- DisableAll resets only skills active in the previous round instead of all loaded skills (~89ms -> ~5ms)
- Removed per-tick LINQ/allocations, O(1) HUD skill lookups, translation substitutions moved to load time
- ForceFullUpdate creates the network service once per broadcast; view-angle guard only skips a genuine (0,0,0) placeholder
- Debug hooks/logging only active when DebugMode is enabled (single reused writer)
- Player skill state unified to a single collection (fixes a rare disconnect race)
- Gamedata signatures resolved lazily/isolated; hardened tick/transmit paths against invalid handles
- Description HUD honours always-show / -1 duration consistently across all assignment paths

Skills:
- Jester: fixed the no-damage effect leaking after a mid-round skill change (Thief/Deactivator)
- Jackal: fixed trail entities accumulating over a round
- Falcon Eye / Third Eye / Spectator: camera state cleared on disable

Also: added missing translation keys as untranslated markers across language files.

Fixes #34
2026-07-05 21:48:29 +03:00

103 lines
No EOL
4.2 KiB
C#

using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Utils;
using Microsoft.Extensions.Logging;
using src.utils;
using System.Collections.Concurrent;
using static src.jRandomSkills;
namespace src.player.skills
{
public class ChillOut : ISkill
{
private const Skills skillName = Skills.ChillOut;
private static readonly ConcurrentDictionary<uint, float> plantingPlayers = [];
public static void LoadSkill()
{
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
}
public static void NewRound()
{
plantingPlayers.Clear();
}
public static void DisableSkill(CCSPlayerController player)
{
if (player == null || !player.IsValid) return;
plantingPlayers.TryRemove(player.Index, out _);
SkillUtils.ResetPrintHTML(player);
}
public static void BombAbortplant(EventBombAbortplant @event)
{
var user = PlayerManager.GetPlayerEvent(@event.Userid);
if (!Instance.IsPlayerValid(user)) return;
plantingPlayers.TryRemove(user.Index, out _);
SkillUtils.ResetPrintHTML(user);
}
public static void BombBeginplant(EventBombBeginplant @event)
{
var player = PlayerManager.GetPlayerEvent(@event.Userid);
if (!Instance.IsPlayerValid(player)) return;
plantingPlayers.TryAdd(player!.Index, Server.CurrentTime);
var anyChillOut = PlayerManager.GetAllPlayers().FirstOrDefault(p => p.Skill == skillName);
if (anyChillOut != null)
{
var bombEntities = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").ToList();
if (bombEntities.Count != 0)
{
var bomb = bombEntities.FirstOrDefault();
if (bomb != null)
bomb.ArmedTime = Server.CurrentTime + SkillsInfo.GetValue<float>(skillName, "bombArmedTime");
}
}
}
public static void BombPlanted(EventBombPlanted @event)
{
var player = PlayerManager.GetPlayerEvent(@event.Userid);
if (!Instance.IsPlayerValid(player)) return;
plantingPlayers.TryRemove(player!.Index, out _);
SkillUtils.ResetPrintHTML(player);
}
public static void OnTick()
{
float currentTime = Server.CurrentTime;
float extraTime = SkillsInfo.GetValue<float>(skillName, "bombArmedTime");
foreach (var player in Utilities.GetPlayers().Where(p => p.Team == CsTeam.Terrorist))
{
if (player == null || !player.IsValid) continue;
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
if (playerInfo == null) continue;
var pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid) continue;
if (pawn.WeaponServices == null) continue;
var activeWeapon = pawn.WeaponServices.ActiveWeapon.Value;
if (activeWeapon == null || !activeWeapon.IsValid || activeWeapon.DesignerName != "weapon_c4") continue;
if (plantingPlayers.TryGetValue(player.Index, out float plantTime))
{
float remaining = plantTime + extraTime - currentTime;
playerInfo.PrintHTML = $"{player.GetTranslation("planter_planting", $"<font color='#00FF00'>{Math.Max(0, remaining):0.0}s</font>")}";
}
}
}
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#343deb", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", int maxPerServer = 1, Rarity rarity = Rarity.Common, float bombArmedTime = 10f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, maxPerServer, rarity)
{
public float BombArmedTime { get; set; } = bombArmedTime;
}
}
}