Files
2026-07-26 23:47:38 +02:00

122 lines
4.2 KiB
C#

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 GrenadeLauncher(IPlayerHud playerHud) : BasePower(playerHud)
{
public override int Id => 11;
public override string Name => "Grenade Launcher";
public override string Description => "Every shot from your pistol launches a grenade (0.5s cooldown).";
public override char ChatColor => ChatColors.Red;
public override string HtmlColor => "#FF0000";
private readonly Dictionary<CCSPlayerController, DateTime> _lastUse = new();
protected override void OnInitialize()
{
RegisterEventHandler<EventRoundStart>(OnRoundStart);
RegisterEventHandler<EventWeaponFire>(OnWeaponFire);
}
private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo _)
{
_lastUse.Clear();
return HookResult.Continue;
}
private HookResult OnWeaponFire(EventWeaponFire @event, GameEventInfo info)
{
var controller = @event.Userid;
if (!Ensure.Player(controller).IsValid().Check() || !IsAssignedTo(controller!))
return HookResult.Continue;
// Only pistols
var weapon = controller!.PlayerPawn.Value?.WeaponServices?.ActiveWeapon.Value;
if (weapon == null)
return HookResult.Continue;
// List of pistol weapon names (classnames)
var pistolClassNames = new[]
{
"weapon_glock", "weapon_usp_silencer", "weapon_hkp2000", "weapon_p250",
"weapon_fiveseven", "weapon_tec9", "weapon_cz75a", "weapon_deagle",
"weapon_revolver", "weapon_elite"
};
if (!pistolClassNames.Contains(weapon.DesignerName))
return HookResult.Continue;
// Check cooldown (skip if nodelay is enabled)
var now = DateTime.UtcNow;
if (!GlobalConfig.NoDelayEnabled && _lastUse.TryGetValue(controller!, out var lastUseTime) && (now - lastUseTime).TotalSeconds < 0.5)
return HookResult.Continue;
_lastUse[controller!] = now;
var pawn = controller!.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid)
return HookResult.Continue;
var pos = pawn.AbsOrigin;
if (pos == null) return HookResult.Continue;
pos = new Vector(pos.X, pos.Y, pos.Z + 64f);
// Forward vector from the player's view angles
var ang = pawn.V_angle;
float yaw = ang.Y * MathF.PI / 180f;
float pitch = ang.X * MathF.PI / 180f;
var forward = new Vector(
MathF.Cos(yaw) * MathF.Cos(pitch),
MathF.Sin(yaw) * MathF.Cos(pitch),
-MathF.Sin(pitch)
);
// Spawn farther in front of the player so the grenade does not collide with the shooter.
const float spawnAhead = 72f;
pos = new Vector(
pos.X + forward.X * spawnAhead,
pos.Y + forward.Y * spawnAhead,
pos.Z + forward.Z * 24f
);
const float speed = 1100f;
int grenadeCount = GlobalConfig.GetGrenadeLauncherCount(1);
for (int i = 0; i < grenadeCount; i++)
{
// Add slight random spread for multiple grenades
var random = new Random();
var spreadAng = new QAngle(
ang.X + (float)(random.NextDouble() * 10.0 - 5.0), // ±5° pitch
ang.Y + (float)(random.NextDouble() * 10.0 - 5.0), // ±5° yaw
ang.Z
);
float spreadYaw = spreadAng.Y * MathF.PI / 180f;
float spreadPitch = spreadAng.X * MathF.PI / 180f;
var spreadForward = new Vector(
MathF.Cos(spreadYaw) * MathF.Cos(spreadPitch),
MathF.Sin(spreadYaw) * MathF.Cos(spreadPitch),
-MathF.Sin(spreadPitch)
);
var velocity = new Vector(
spreadForward.X * speed,
spreadForward.Y * speed,
spreadForward.Z * speed + 100f
);
GrenadeSpawner.CreateGrenade(pos, spreadAng, velocity, pawn);
}
return HookResult.Continue;
}
}