using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using Cs2SuperPowers.Players; using Cs2SuperPowers.Players.PlayerValidations; using Cs2SuperPowers.Utils; namespace Cs2SuperPowers.Powers; public class NotSoDecoy(IPlayerHud playerHud) : BasePower(playerHud) { public override int Id => 24; public override string Name => "Not So Decoy"; public override string Description => "Decoys thrown by you spawn 20 grenades when thrown (5 second cooldown)."; public override char ChatColor => ChatColors.Red; public override string HtmlColor => "#FF0000"; private readonly Dictionary _lastUse = new(); protected override void OnInitialize() { RegisterEventHandler(OnRoundStart); RegisterEventHandler(OnDecoyStarted); } private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo _) { _lastUse.Clear(); foreach (var player in Utilities.GetPlayers()) { if (!Ensure.Player(player).IsValid().IsAlive().Check() || !IsAssignedTo(player!)) { continue; } Server.NextFrame(() => { player.GiveNamedItem("weapon_decoy"); }); } return HookResult.Continue; } private HookResult OnDecoyStarted(EventDecoyStarted @event, GameEventInfo _) { var player = @event.Userid; if (!Ensure.Player(player).IsValid().Check() || !IsAssignedTo(player!)) { return HookResult.Continue; } // Check cooldown (skip if nodelay is enabled) var now = DateTime.UtcNow; DateTime lastUseTime = default; bool isOnCooldown = !GlobalConfig.NoDelayEnabled && _lastUse.TryGetValue(player!, out lastUseTime) && (now - lastUseTime).TotalSeconds < 5; // Always give a new decoy, regardless of cooldown! player!.GiveNamedItem("weapon_decoy"); if (isOnCooldown) { double secondsRemaining = Math.Ceiling(5 - (now - lastUseTime).TotalSeconds); player.PrintToCenterAlert($"Not So Decoy is on cooldown! {ChatColors.DarkRed}{secondsRemaining}{ChatColors.Default}s remaining"); return HookResult.Continue; } _lastUse[player!] = now; var pos = new Vector(@event.X, @event.Y, @event.Z); // Spawn grenades (with boost level) in a loop with random angles and velocities var random = new Random(); int grenadeCount = GlobalConfig.GetNotSoDecoyCount(20); for (int i = 0; i < grenadeCount; i++) { // Random pitch between -45 and 45 degrees, yaw between 0 and 360 float pitch = (float)(random.NextDouble() * 90.0 - 45.0); float yaw = (float)(random.NextDouble() * 360.0); var ang = new QAngle(pitch, yaw, 0); // Random speed between 400 and 900 units float speed = (float)(random.NextDouble() * 500.0 + 400.0); // Convert angles to a forward vector float pitchRad = pitch * MathF.PI / 180f; float yawRad = yaw * MathF.PI / 180f; var forward = new Vector( MathF.Cos(yawRad) * MathF.Cos(pitchRad), MathF.Sin(yawRad) * MathF.Cos(pitchRad), -MathF.Sin(pitchRad)); // Add some random upward velocity for the "jump" float upward = (float)(random.NextDouble() * 200.0 + 200.0); var velocity = new Vector( forward.X * speed, forward.Y * speed, forward.Z * speed + upward); var createdGrenade = GrenadeSpawner.CreateGrenade(pos, ang, velocity, player.PlayerPawn.Value); } return HookResult.Continue; } }