122 lines
4.2 KiB
C#
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;
|
|
}
|
|
}
|