jRandomSkills/jRandomSkills - SRC Files/src/player/EntityEvents.cs
ByDexter b0b50a224f QoE Update
## Added

- **`CurseSkillPerPlayer`** (config.json) — limits how many curse skills (Darkness, JumpBan, PrimaryBan, Bankrupt and 14 others) can be stacked on a single player per round. `null` = unlimited (default), `1` = one curse per player, `2` = two. When the limit is reached the target is rejected and the curser gets a chat warning. Enforced centrally in `SkillAction`, so no per-skill code is needed.
- **Tripwire cooldown mode** — replaces the fixed wire limit with a Replicator-style cooldown. Players can place as many wires as they want as long as the cooldown is up; remaining time is shown on the HUD. Configurable via `Cooldown` in skillsInfo.json (default 20s).
- **Tripwire team colors** — T wires red (255, 64, 64), CT wires blue (64, 128, 255).

## Fixed

- **ExpensiveAmmo** also charged money for knife swings and grenade throws. It now only charges when a bullet-firing weapon is fired.
- **Knockback** also triggered on grenade throws; bound to the same allowlist.

## Removed

- Dead code: `Earthquake.cs` and `HealingChicken.cs` (neither was registered in the `Skills` enum, so neither ever ran), `EntityManager.CreateTrackedEnvShake`, `EntityManager.CreateTrackedChicken`, `PlayerManager.UpdatePlayerSkill`.

## Changed

- **Tripwire wires are now removed when the owner dies or loses the skill.** Previously they stayed on the map until the end of the round.
- `PlayerEvents.cs` split into three partial files: player events in `PlayerEvents.cs`, round and skill dispatch in `RoundEvents.cs`, entity and weapon hooks in `EntityEvents.cs`. Behaviour is unchanged; all members stay in the same `Event` class.
- 28 copy-pasted enemy-gathering LINQ chains reduced to the shared helper (Bankrupt, CarefulBullets, Darkness, Deactivator, Deaf, ExpensiveAmmo, Giant, Glitch, Jammer, JumpBan, JumpCurse, LifeSwap, Magnifier, MoneySwap, Nightmare, Poison, PrimaryBan, WildThrow).
2026-07-30 20:52:07 +03:00

249 lines
8.8 KiB
C#

using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Events;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
using CounterStrikeSharp.API.Modules.UserMessages;
using CounterStrikeSharp.API.Modules.Utils;
using RayTraceAPI;
using src.player.skills;
using src.utils;
using System.Collections.Concurrent;
using static CounterStrikeSharp.API.Core.Listeners;
using static src.jRandomSkills;
using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
namespace src.player
{
public static partial class Event
{
private static HookResult BombBeginplant(EventBombBeginplant @event, GameEventInfo info)
{
lock (setLock)
{
DispatchToActiveSkills("BombBeginplant", @event);
return HookResult.Continue;
}
}
private static HookResult BombAbortplant(EventBombAbortplant @event, GameEventInfo info)
{
lock (setLock)
{
DispatchToActiveSkills("BombAbortplant", @event);
return HookResult.Continue;
}
}
private static HookResult BombPlanted(EventBombPlanted @event, GameEventInfo info)
{
lock (setLock)
{
DispatchToActiveSkills("BombPlanted", @event);
return HookResult.Continue;
}
}
private static HookResult BombBegindefuse(EventBombBegindefuse @event, GameEventInfo info)
{
lock (setLock)
{
DispatchToActiveSkills("BombBegindefuse", @event);
return HookResult.Continue;
}
}
private static HookResult DecoyStarted(EventDecoyStarted @event, GameEventInfo info)
{
lock (setLock)
{
DispatchToActiveSkills("DecoyStarted", @event);
return HookResult.Continue;
}
}
private static HookResult DecoyDetonate(EventDecoyDetonate @event, GameEventInfo info)
{
lock (setLock)
{
DispatchToActiveSkills("DecoyDetonate", @event);
return HookResult.Continue;
}
}
private static HookResult SmokegrenadeDetonate(EventSmokegrenadeDetonate @event, GameEventInfo info)
{
lock (setLock)
{
DispatchToActiveSkills("SmokegrenadeDetonate", @event);
return HookResult.Continue;
}
}
private static HookResult SmokegrenadeExpired(EventSmokegrenadeExpired @event, GameEventInfo info)
{
lock (setLock)
{
DispatchToActiveSkills("SmokegrenadeExpired", @event);
return HookResult.Continue;
}
}
private static HookResult OnTakeDamage(DynamicHook h)
{
lock (setLock)
{
DispatchOnTakeDamage(h);
if (Fortnite.skillInThisRound == true &&
!Instance.SkillPlayer.Any(p => !p.IsDrawing && p.Skill == Skills.Fortnite))
Instance.SkillAction("Fortnite", "OnTakeDamage", [h]);
return HookResult.Continue;
}
}
private static HookResult OnTriggerEnter(DynamicHook hook)
{
lock (setLock)
{
CBaseTrigger trigger = hook.GetParam<CBaseTrigger>(0);
CBaseEntity entity = hook.GetParam<CBaseEntity>(1);
DispatchToActiveSkills("OnTriggerEnter", trigger, entity);
return HookResult.Continue;
}
}
private static HookResult OnTriggerExit(DynamicHook hook)
{
lock (setLock)
{
CBaseTrigger trigger = hook.GetParam<CBaseTrigger>(0);
CBaseEntity entity = hook.GetParam<CBaseEntity>(1);
DispatchToActiveSkills("OnTriggerExit", trigger, entity);
return HookResult.Continue;
}
}
private static HookResult OnWeaponCanAcquire(DynamicHook hook)
{
lock (setLock)
{
CCSPlayer_ItemServices itemServices = hook.GetParam<CCSPlayer_ItemServices>(0);
if (itemServices == null || itemServices.Pawn.Value == null || !itemServices.Pawn.Value.IsValid) return HookResult.Continue;
CEconItemView econItem = hook.GetParam<CEconItemView>(1);
if (econItem == null) return HookResult.Continue;
CBasePlayerPawn pawn = itemServices.Pawn.Value;
if (pawn == null || !pawn.IsValid || pawn.Controller.Value == null || !pawn.Controller.Value.IsValid) return HookResult.Continue;
CCSPlayerController player = pawn.Controller.Value.As<CCSPlayerController>();
if (player == null || !player.IsValid) return HookResult.Continue;
var playerInfo = PlayerManager.GetPlayerByIndex(player.Index);
if (playerInfo == null) return HookResult.Continue;
CCSWeaponBaseVData vdata = VirtualFunctions.GetCSWeaponDataFromKeyFunc.Invoke(-1, econItem.ItemDefinitionIndex.ToString());
if (vdata == null || vdata.Handle == IntPtr.Zero) return HookResult.Continue;
var activeSkills = Instance.SkillPlayer
.Where(p => !p.IsDrawing)
.Select(p => p.Skill.ToString())
.Distinct();
bool block = false;
foreach (string skillName in activeSkills)
{
bool? result = (bool?)Instance.SkillAction(skillName, "OnWeaponCanAcquire", [hook, player, econItem, vdata]);
if (result == true)
{
block = true;
break;
}
}
return block ? HookResult.Handled : HookResult.Continue;
}
}
private static HookResult WeaponDrop(DynamicHook hook)
{
lock (setLock)
{
CCSPlayerController player = hook.GetParam<CCSPlayerController>(0);
if (player == null || !player.IsValid)
return HookResult.Continue;
var activeSkills = Instance.SkillPlayer
.Where(p => !p.IsDrawing)
.Select(p => p.Skill.ToString())
.Distinct();
bool block = false;
foreach (string skillName in activeSkills)
{
bool? result = (bool?)Instance.SkillAction(skillName, "WeaponDrop", [hook, player]);
if (result == true)
{
block = true;
break;
}
}
return block ? HookResult.Handled : HookResult.Continue;
}
}
private static void EntitySpawned(CEntityInstance entity)
{
lock (setLock)
{
DispatchToActiveSkills("OnEntitySpawned", entity);
}
}
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
{
long perfStart = PerfLog.Start();
lock (setLock)
{
try
{
// Keep dying entities out of snapshots until the engine processes the kill.
var dying = EntityManager.GetRecentlyDestroyedSnapshot();
if (dying.Count > 0)
{
foreach (var (info, player) in infoList)
{
if (player == null || !player.IsValid) continue;
foreach (var entityIndex in dying)
if (info.TransmitEntities.Contains(entityIndex))
info.TransmitEntities.Remove(entityIndex);
}
}
}
catch (Exception ex)
{
Server.PrintToConsole($"[jRandomSkills] CheckTransmit dying-filter failed: {ex.Message}");
}
DispatchToActiveSkills("CheckTransmit", infoList);
}
PerfLog.Sample("CheckTransmit", perfStart);
}
public static void EnableTransmit()
{
if (!isTransmitRegistered)
{
Instance?.RegisterListener<CheckTransmit>(CheckTransmit);
isTransmitRegistered = true;
}
}
}
}