commit
427b8e6d93
88 changed files with 1251 additions and 582 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -30,6 +30,7 @@ project.fragment.lock.json
|
||||||
*.userosscache
|
*.userosscache
|
||||||
|
|
||||||
# Logs, caches, temp
|
# Logs, caches, temp
|
||||||
|
log/
|
||||||
*.log
|
*.log
|
||||||
debug_*.txt
|
debug_*.txt
|
||||||
perf_*.txt
|
perf_*.txt
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using WASDSharedAPI;
|
using WASDSharedAPI;
|
||||||
|
|
||||||
namespace WASDMenuAPI.Classes;
|
namespace WASDMenuAPI.Classes;
|
||||||
|
|
||||||
public class WasdMenu : IWasdMenu
|
public class WasdMenu : IWasdMenu
|
||||||
{
|
{
|
||||||
public string Title { get; set; } = "";
|
[AllowNull] public string Title { get; set; } = "";
|
||||||
public string ItemText { get; set; } = "";
|
[AllowNull] public string ItemText { get; set; } = "";
|
||||||
public string ItemHoverText { get; set; } = "";
|
[AllowNull] public string ItemHoverText { get; set; } = "";
|
||||||
public string ControlText { get; set; } = "";
|
[AllowNull] public string ControlText { get; set; } = "";
|
||||||
public LinkedList<IWasdMenuOption>? Options { get; set; } = new();
|
public LinkedList<IWasdMenuOption>? Options { get; set; } = new();
|
||||||
public LinkedListNode<IWasdMenuOption>? Prev { get; set; } = null;
|
public LinkedListNode<IWasdMenuOption>? Prev { get; set; } = null;
|
||||||
public LinkedListNode<IWasdMenuOption> Add(string display, Action<CCSPlayerController, IWasdMenuOption> onChoice)
|
public LinkedListNode<IWasdMenuOption> Add(string display, Action<CCSPlayerController, IWasdMenuOption> onChoice)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ public class WasdMenuPlayer
|
||||||
public LinkedListNode<IWasdMenuOption>? CurrentChoice = null;
|
public LinkedListNode<IWasdMenuOption>? CurrentChoice = null;
|
||||||
public LinkedListNode<IWasdMenuOption>? MenuStart = null;
|
public LinkedListNode<IWasdMenuOption>? MenuStart = null;
|
||||||
public string CenterHtml = "";
|
public string CenterHtml = "";
|
||||||
public int VisibleOptions = 3;
|
public static int DefaultVisibleOptions = 3;
|
||||||
|
public int VisibleOptions => DefaultVisibleOptions;
|
||||||
public PlayerButtons Buttons { get; set; }
|
public PlayerButtons Buttons { get; set; }
|
||||||
public bool Paused = false;
|
public bool Paused = false;
|
||||||
|
|
||||||
|
|
@ -245,10 +246,11 @@ public class WasdMenuPlayer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MainMenu == null || MainMenu.Options == null)
|
var options = MainMenu?.Options;
|
||||||
|
if (MainMenu == null || options == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MainMenu.Options.Clear();
|
options.Clear();
|
||||||
bool choiceExists = false;
|
bool choiceExists = false;
|
||||||
|
|
||||||
foreach (var item in list)
|
foreach (var item in list)
|
||||||
|
|
@ -257,19 +259,19 @@ public class WasdMenuPlayer
|
||||||
{
|
{
|
||||||
OptionDisplay = item.Key,
|
OptionDisplay = item.Key,
|
||||||
OnChoose = item.Value,
|
OnChoose = item.Value,
|
||||||
Index = MainMenu.Options.Count,
|
Index = options.Count,
|
||||||
Parent = MainMenu
|
Parent = MainMenu
|
||||||
};
|
};
|
||||||
MainMenu.Options.AddLast(newOption);
|
options.AddLast(newOption);
|
||||||
|
|
||||||
if (CurrentChoice?.Value?.OptionDisplay == item.Key)
|
if (CurrentChoice?.Value?.OptionDisplay == item.Key)
|
||||||
{
|
{
|
||||||
CurrentChoice = MainMenu.Options?.Last;
|
CurrentChoice = options.Last;
|
||||||
choiceExists = true;
|
choiceExists = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!choiceExists)
|
if (!choiceExists)
|
||||||
CurrentChoice = MainMenu.Options?.First;
|
CurrentChoice = options.First;
|
||||||
|
|
||||||
if (CurrentChoice != null)
|
if (CurrentChoice != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,9 @@ namespace src.command
|
||||||
foreach (var commandPair in commands)
|
foreach (var commandPair in commands)
|
||||||
foreach (var command in commandPair.Key)
|
foreach (var command in commandPair.Key)
|
||||||
{
|
{
|
||||||
Instance.AddCommand($"css_{command}", commandPair.Value.description, commandPair.Value.handler);
|
string name = $"css_{command}";
|
||||||
oldCommands.TryAdd($"css_{command}", commandPair.Value.handler);
|
if (!oldCommands.TryAdd(name, commandPair.Value.handler)) continue;
|
||||||
|
Instance.AddCommand(name, commandPair.Value.description, commandPair.Value.handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ namespace src
|
||||||
public override string ModuleName => "[CS2] [ jRandomSkills ]";
|
public override string ModuleName => "[CS2] [ jRandomSkills ]";
|
||||||
public override string ModuleAuthor => "D3X (Original), Juzlus (Modifier), ByDexterTR (Contributor)";
|
public override string ModuleAuthor => "D3X (Original), Juzlus (Modifier), ByDexterTR (Contributor)";
|
||||||
public override string ModuleDescription => "Plugin adds random skills every round for CS2 by D3X. Modified by Juzlus.";
|
public override string ModuleDescription => "Plugin adds random skills every round for CS2 by D3X. Modified by Juzlus.";
|
||||||
public override string ModuleVersion => "1.2.3.b7";
|
public override string ModuleVersion => "1.2.3.b8";
|
||||||
|
|
||||||
public override void Load(bool hotReload)
|
public override void Load(bool hotReload)
|
||||||
{
|
{
|
||||||
|
|
@ -165,7 +165,7 @@ namespace src
|
||||||
|
|
||||||
long perfStart = PerfLog.Start();
|
long perfStart = PerfLog.Start();
|
||||||
var result = method.Invoke(null, param);
|
var result = method.Invoke(null, param);
|
||||||
PerfLog.End($"SkillAction {skill}.{methodName}", perfStart, 2.0);
|
PerfLog.Sample($"SkillAction {skill}.{methodName}", perfStart, 30.0, 1.0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,7 +176,7 @@ namespace src
|
||||||
CommandManager.RegisterCommand(definition);
|
CommandManager.RegisterCommand(definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool IsPlayerValid(CCSPlayerController? player)
|
internal bool IsPlayerValid([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] CCSPlayerController? player)
|
||||||
{
|
{
|
||||||
return player != null && player.IsValid && player.PlayerPawn?.Value != null && player.PlayerPawn.Value.IsValid && player.PlayerPawn.Value.LifeState == (byte)LifeState_t.LIFE_ALIVE;
|
return player != null && player.IsValid && player.PlayerPawn?.Value != null && player.PlayerPawn.Value.IsValid && player.PlayerPawn.Value.LifeState == (byte)LifeState_t.LIFE_ALIVE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ namespace src.player
|
||||||
{
|
{
|
||||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||||
if (player == null || !player.IsValid) return HookResult.Continue;
|
if (player == null || !player.IsValid) return HookResult.Continue;
|
||||||
WriteToDebug($"{(player.IsBot ? "Bot" : "Player")} {player.PlayerName} joined the game.", DebugCategory.Round);
|
WriteToDebug($"{(player.IsBot ? "Bot" : "Player")} {player.PlayerName} joined the game.", DebugCategory.Round, ignoreIdle: true);
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ namespace src.player
|
||||||
{
|
{
|
||||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||||
if (player == null || !player.IsValid) return HookResult.Continue;
|
if (player == null || !player.IsValid) return HookResult.Continue;
|
||||||
WriteToDebug($"{(player.IsBot ? "Bot" : "Player")} {player.PlayerName} disconnected.", DebugCategory.Round);
|
WriteToDebug($"{(player.IsBot ? "Bot" : "Player")} {player.PlayerName} disconnected.", DebugCategory.Round, ignoreIdle: true);
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -101,7 +101,7 @@ namespace src.player
|
||||||
|
|
||||||
if (Config.DebugEnabled(DebugCategory.Damage))
|
if (Config.DebugEnabled(DebugCategory.Damage))
|
||||||
{
|
{
|
||||||
VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Hook(OnTakeDamage, HookMode.Pre);
|
Instance.RegisterListener<OnEntityTakeDamagePre>(OnTakeDamage);
|
||||||
damageHooked = true;
|
damageHooked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +110,7 @@ namespace src.player
|
||||||
{
|
{
|
||||||
if (damageHooked)
|
if (damageHooked)
|
||||||
{
|
{
|
||||||
try { VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Unhook(OnTakeDamage, HookMode.Pre); }
|
try { Instance.RemoveListener<OnEntityTakeDamagePre>(OnTakeDamage); }
|
||||||
catch { }
|
catch { }
|
||||||
damageHooked = false;
|
damageHooked = false;
|
||||||
}
|
}
|
||||||
|
|
@ -118,16 +118,13 @@ namespace src.player
|
||||||
lock (_writeLock) { _writer?.Dispose(); _writer = null; }
|
lock (_writeLock) { _writer?.Dispose(); _writer = null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HookResult OnTakeDamage(DynamicHook h)
|
private static HookResult OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
|
|
||||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle);
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
|
|
||||||
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
|
|
@ -141,10 +138,10 @@ namespace src.player
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex(attacker!.Index);
|
||||||
if (playerInfo == null) return HookResult.Continue;
|
if (playerInfo == null) return HookResult.Continue;
|
||||||
|
|
||||||
var nativeHitGroup = SkillUtils.GetHitGroup(param2);
|
var nativeHitGroup = SkillUtils.GetHitGroup(damageInfo);
|
||||||
|
|
||||||
WriteToDebug($"{(victim.IsBot ? "Bot" : "Player")} {victim.PlayerName} took damage from {(attacker.IsBot ? "bot" : "player")} {attacker.PlayerName}. " +
|
WriteToDebug($"{(victim.IsBot ? "Bot" : "Player")} {victim.PlayerName} took damage from {(attacker.IsBot ? "bot" : "player")} {attacker.PlayerName}. " +
|
||||||
$"[dmg={param2.Damage:0.#} hp={victimPawn.Health}/{victimPawn.MaxHealth} armor={victimPawn.ArmorValue} hitgroup={nativeHitGroup} " +
|
$"[dmg={damageInfo.Damage:0.#} hp={victimPawn.Health}/{victimPawn.MaxHealth} armor={victimPawn.ArmorValue} hitgroup={nativeHitGroup} " +
|
||||||
$"takes={victimPawn.TakesDamage} vskill={PlayerManager.GetPlayerByIndex(victim.Index)?.Skill} askill={playerInfo.Skill}]", DebugCategory.Damage);
|
$"takes={victimPawn.TakesDamage} vskill={PlayerManager.GetPlayerByIndex(victim.Index)?.Skill} askill={playerInfo.Skill}]", DebugCategory.Damage);
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
}
|
}
|
||||||
|
|
@ -163,11 +160,14 @@ namespace src.player
|
||||||
return gameRules.WarmupPeriod ? " [WARMUP]" : string.Empty;
|
return gameRules.WarmupPeriod ? " [WARMUP]" : string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteToDebug(string message, DebugCategory category = DebugCategory.Core)
|
public static void WriteToDebug(string message, DebugCategory category = DebugCategory.Core, bool ignoreIdle = false)
|
||||||
{
|
{
|
||||||
if (!Config.DebugEnabled(category))
|
if (!Config.DebugEnabled(category))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!ignoreIdle && category != DebugCategory.Core && PlayerManager.IsServerIdle())
|
||||||
|
return;
|
||||||
|
|
||||||
lock (_writeLock)
|
lock (_writeLock)
|
||||||
{
|
{
|
||||||
_writer ??= CreateWriter();
|
_writer ??= CreateWriter();
|
||||||
|
|
|
||||||
|
|
@ -84,26 +84,58 @@ namespace src.player
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HookResult OnTakeDamage(DynamicHook h)
|
private static HookResult OnEntityTakeDamagePre(CBaseEntity entity, CTakeDamageInfo info)
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
{
|
{
|
||||||
DispatchOnTakeDamage(h);
|
CountDamageResolution(info);
|
||||||
|
|
||||||
|
object[] args = [entity, info];
|
||||||
|
DispatchOnTakeDamage(entity, info, args);
|
||||||
|
|
||||||
if (Fortnite.skillInThisRound == true &&
|
if (Fortnite.skillInThisRound == true &&
|
||||||
!Instance.SkillPlayer.Any(p => !p.IsDrawing && p.Skill == Skills.Fortnite))
|
!Instance.SkillPlayer.Any(p => !p.IsDrawing && p.Skill == Skills.Fortnite))
|
||||||
Instance.SkillAction("Fortnite", "OnTakeDamage", [h]);
|
Instance.SkillAction("Fortnite", "OnTakeDamage", args);
|
||||||
|
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HookResult OnTakeDamagePost(DynamicHook h)
|
private static int dmgTotal, dmgAttackerOk, dmgAbilityOk, dmgSameTeam;
|
||||||
|
private static DateTime dmgWindowStart = DateTime.Now;
|
||||||
|
|
||||||
|
private static void CountDamageResolution(CTakeDamageInfo info)
|
||||||
|
{
|
||||||
|
if (!PerfLog.Enabled || info == null) return;
|
||||||
|
|
||||||
|
dmgTotal++;
|
||||||
|
|
||||||
|
var attacker = info.Attacker?.Value;
|
||||||
|
if (attacker != null && attacker.IsValid)
|
||||||
|
{
|
||||||
|
dmgAttackerOk++;
|
||||||
|
|
||||||
|
var attackerPawn = new CCSPlayerPawn(attacker.Handle);
|
||||||
|
if (attackerPawn.IsValid && attackerPawn.DesignerName == "player") dmgSameTeam++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ability = info.Ability?.Value;
|
||||||
|
if (ability != null && ability.IsValid) dmgAbilityOk++;
|
||||||
|
|
||||||
|
if ((DateTime.Now - dmgWindowStart).TotalSeconds < 60) return;
|
||||||
|
|
||||||
|
if (dmgTotal > 0)
|
||||||
|
PerfLog.Info($"DMGHOOK total={dmgTotal} attacker={dmgAttackerOk} ability={dmgAbilityOk} playerAttacker={dmgSameTeam}");
|
||||||
|
|
||||||
|
dmgTotal = dmgAttackerOk = dmgAbilityOk = dmgSameTeam = 0;
|
||||||
|
dmgWindowStart = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnEntityTakeDamagePost(CBaseEntity entity, CTakeDamageInfo info, CTakeDamageResult result)
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
{
|
{
|
||||||
DispatchOnTakeDamage(h, true);
|
DispatchOnTakeDamage(entity, info, [entity, info, result], true);
|
||||||
return HookResult.Continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ public interface ISkill
|
||||||
public static void UseSkill(CCSPlayerController _) { }
|
public static void UseSkill(CCSPlayerController _) { }
|
||||||
public static void TypeSkill(CCSPlayerController _, string[] __) { }
|
public static void TypeSkill(CCSPlayerController _, string[] __) { }
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook _) { }
|
public static void OnTakeDamage(CBaseEntity _, CTakeDamageInfo __) { }
|
||||||
public static void OnTakeDamagePost(DynamicHook _) { }
|
public static void OnTakeDamagePost(CBaseEntity _, CTakeDamageInfo __, CTakeDamageResult ___) { }
|
||||||
public static void OnEntitySpawned(CEntityInstance _) { }
|
public static void OnEntitySpawned(CEntityInstance _) { }
|
||||||
public static void OnTick() { }
|
public static void OnTick() { }
|
||||||
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList _) { }
|
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList _) { }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using src.utils;
|
using src.utils;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using static src.jRandomSkills;
|
using static src.jRandomSkills;
|
||||||
|
|
@ -47,39 +47,137 @@ namespace src.player
|
||||||
Write($"{label} took {ms:F2}ms");
|
Write($"{label} took {ms:F2}ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly double[] bucketBoundsMs = [0.05, 0.1, 0.25, 0.5, 1, 2, 4, 8, 16, 32, double.MaxValue];
|
||||||
|
|
||||||
|
private const int maxRawSamples = 4096;
|
||||||
|
|
||||||
private sealed class Aggregate
|
private sealed class Aggregate
|
||||||
{
|
{
|
||||||
public double TotalMs;
|
public double TotalMs;
|
||||||
public double MaxMs;
|
public double MaxMs;
|
||||||
public int Count;
|
public int Count;
|
||||||
|
public readonly int[] Buckets = new int[bucketBoundsMs.Length];
|
||||||
|
public readonly List<double> Raw = [];
|
||||||
public DateTime WindowStart = DateTime.Now;
|
public DateTime WindowStart = DateTime.Now;
|
||||||
|
|
||||||
|
private bool rawSorted;
|
||||||
|
|
||||||
|
public void Add(double ms)
|
||||||
|
{
|
||||||
|
TotalMs += ms;
|
||||||
|
Count++;
|
||||||
|
if (ms > MaxMs) MaxMs = ms;
|
||||||
|
|
||||||
|
if (Raw.Count < maxRawSamples) Raw.Add(ms);
|
||||||
|
|
||||||
|
for (int i = 0; i < bucketBoundsMs.Length; i++)
|
||||||
|
if (ms <= bucketBoundsMs[i]) { Buckets[i]++; break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public double Percentile(double fraction)
|
||||||
|
{
|
||||||
|
if (Count == 0) return 0;
|
||||||
|
|
||||||
|
double value = Raw.Count == Count ? ExactPercentile(fraction) : BucketPercentile(fraction);
|
||||||
|
return value > MaxMs ? MaxMs : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double ExactPercentile(double fraction)
|
||||||
|
{
|
||||||
|
if (!rawSorted)
|
||||||
|
{
|
||||||
|
Raw.Sort();
|
||||||
|
rawSorted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = (int)Math.Ceiling(Raw.Count * fraction) - 1;
|
||||||
|
if (index < 0) index = 0;
|
||||||
|
if (index >= Raw.Count) index = Raw.Count - 1;
|
||||||
|
return Raw[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
private double BucketPercentile(double fraction)
|
||||||
|
{
|
||||||
|
int target = (int)Math.Ceiling(Count * fraction);
|
||||||
|
if (target < 1) target = 1;
|
||||||
|
|
||||||
|
int seen = 0;
|
||||||
|
double lower = 0;
|
||||||
|
for (int i = 0; i < bucketBoundsMs.Length; i++)
|
||||||
|
{
|
||||||
|
double upper = bucketBoundsMs[i] == double.MaxValue || bucketBoundsMs[i] > MaxMs ? MaxMs : bucketBoundsMs[i];
|
||||||
|
|
||||||
|
if (seen + Buckets[i] >= target)
|
||||||
|
{
|
||||||
|
if (Buckets[i] <= 0) return upper;
|
||||||
|
return lower + (upper - lower) * ((target - seen) / (double)Buckets[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
seen += Buckets[i];
|
||||||
|
lower = upper;
|
||||||
|
}
|
||||||
|
return MaxMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
TotalMs = 0;
|
||||||
|
MaxMs = 0;
|
||||||
|
Count = 0;
|
||||||
|
Array.Clear(Buckets);
|
||||||
|
Raw.Clear();
|
||||||
|
rawSorted = false;
|
||||||
|
WindowStart = DateTime.Now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<string, Aggregate> _aggregates = new();
|
private static readonly ConcurrentDictionary<string, Aggregate> _aggregates = new();
|
||||||
|
|
||||||
// Per-tick measurement: accumulates and logs an avg/max summary every few seconds,
|
// Per-tick measurement: accumulates and logs an avg/percentile summary every few seconds,
|
||||||
// so tick paths do not produce one log line per tick.
|
// so tick paths do not produce one log line per tick.
|
||||||
public static void Sample(string label, long startTimestamp, double reportSeconds = 5.0, double maxThresholdMs = 0.5)
|
public static void Sample(string label, long startTimestamp, double reportSeconds = 5.0, double maxThresholdMs = 0.5)
|
||||||
{
|
{
|
||||||
if (startTimestamp == 0 || !Enabled) return;
|
if (startTimestamp == 0 || !Enabled) return;
|
||||||
|
|
||||||
double ms = (Stopwatch.GetTimestamp() - startTimestamp) * 1000.0 / Stopwatch.Frequency;
|
|
||||||
var agg = _aggregates.GetOrAdd(label, _ => new Aggregate());
|
var agg = _aggregates.GetOrAdd(label, _ => new Aggregate());
|
||||||
|
|
||||||
|
if (PlayerManager.IsServerIdle())
|
||||||
|
{
|
||||||
|
lock (agg) agg.Reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ms = (Stopwatch.GetTimestamp() - startTimestamp) * 1000.0 / Stopwatch.Frequency;
|
||||||
lock (agg)
|
lock (agg)
|
||||||
{
|
{
|
||||||
agg.TotalMs += ms;
|
agg.Add(ms);
|
||||||
agg.Count++;
|
|
||||||
if (ms > agg.MaxMs) agg.MaxMs = ms;
|
|
||||||
|
|
||||||
if ((DateTime.Now - agg.WindowStart).TotalSeconds < reportSeconds) return;
|
if ((DateTime.Now - agg.WindowStart).TotalSeconds < reportSeconds) return;
|
||||||
|
|
||||||
if (agg.MaxMs >= maxThresholdMs)
|
if (agg.MaxMs >= maxThresholdMs)
|
||||||
Write($"{label} avg={agg.TotalMs / agg.Count:F2}ms max={agg.MaxMs:F2}ms samples={agg.Count}");
|
Write($"{label} avg={agg.TotalMs / agg.Count:F2}ms p50={agg.Percentile(.5):F2}ms " +
|
||||||
|
$"p95={agg.Percentile(.95):F2}ms p99={agg.Percentile(.99):F2}ms max={agg.MaxMs:F2}ms " +
|
||||||
|
$"samples={agg.Count}{ContextSuffix()}");
|
||||||
|
|
||||||
agg.TotalMs = 0;
|
agg.Reset();
|
||||||
agg.MaxMs = 0;
|
}
|
||||||
agg.Count = 0;
|
}
|
||||||
agg.WindowStart = DateTime.Now;
|
|
||||||
|
public static Func<string>? ContextProvider;
|
||||||
|
|
||||||
|
private static string ContextSuffix()
|
||||||
|
{
|
||||||
|
var provider = ContextProvider;
|
||||||
|
if (provider == null) return string.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string context = provider();
|
||||||
|
return string.IsNullOrEmpty(context) ? string.Empty : $" {context}";
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Cvars;
|
using CounterStrikeSharp.API.Modules.Cvars;
|
||||||
using CounterStrikeSharp.API.Modules.Events;
|
using CounterStrikeSharp.API.Modules.Events;
|
||||||
|
|
@ -80,8 +80,10 @@ namespace src.player
|
||||||
Instance.HookUserMessage(208, PlayerMakeSound);
|
Instance.HookUserMessage(208, PlayerMakeSound);
|
||||||
Instance.HookUserMessage(207, GetPrintToCenterHtml);
|
Instance.HookUserMessage(207, GetPrintToCenterHtml);
|
||||||
|
|
||||||
VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Hook(OnTakeDamage, HookMode.Pre);
|
Instance.RegisterListener<OnEntityTakeDamagePre>(OnEntityTakeDamagePre);
|
||||||
VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Hook(OnTakeDamagePost, HookMode.Post);
|
Instance.RegisterListener<OnEntityTakeDamagePost>(OnEntityTakeDamagePost);
|
||||||
|
|
||||||
|
PerfLog.ContextProvider = PerfContext;
|
||||||
|
|
||||||
Instance.RegisterEventHandler<EventBulletImpact>(BulletImpact);
|
Instance.RegisterEventHandler<EventBulletImpact>(BulletImpact);
|
||||||
|
|
||||||
|
|
@ -96,8 +98,8 @@ namespace src.player
|
||||||
|
|
||||||
public static void Unload()
|
public static void Unload()
|
||||||
{
|
{
|
||||||
TryUnhook(() => VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Unhook(OnTakeDamage, HookMode.Pre));
|
TryUnhook(() => Instance.RemoveListener<OnEntityTakeDamagePre>(OnEntityTakeDamagePre));
|
||||||
TryUnhook(() => VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Unhook(OnTakeDamagePost, HookMode.Post));
|
TryUnhook(() => Instance.RemoveListener<OnEntityTakeDamagePost>(OnEntityTakeDamagePost));
|
||||||
TryUnhook(() => VirtualFunctions.CBaseTrigger_StartTouchFunc.Unhook(OnTriggerEnter, HookMode.Post));
|
TryUnhook(() => VirtualFunctions.CBaseTrigger_StartTouchFunc.Unhook(OnTriggerEnter, HookMode.Post));
|
||||||
TryUnhook(() => VirtualFunctions.CBaseTrigger_EndTouchFunc.Unhook(OnTriggerExit, HookMode.Pre));
|
TryUnhook(() => VirtualFunctions.CBaseTrigger_EndTouchFunc.Unhook(OnTriggerExit, HookMode.Pre));
|
||||||
TryUnhook(() => VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Unhook(OnWeaponCanAcquire, HookMode.Pre));
|
TryUnhook(() => VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Unhook(OnWeaponCanAcquire, HookMode.Pre));
|
||||||
|
|
@ -170,9 +172,8 @@ namespace src.player
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DispatchOnTakeDamage(DynamicHook h, bool post = false)
|
private static void DispatchOnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, object[] args, bool post = false)
|
||||||
{
|
{
|
||||||
object[] args = [h];
|
|
||||||
var seen = new HashSet<Skills>();
|
var seen = new HashSet<Skills>();
|
||||||
List<Skills>? deferred = null;
|
List<Skills>? deferred = null;
|
||||||
|
|
||||||
|
|
@ -186,15 +187,15 @@ namespace src.player
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
InvokeOnTakeDamage(p.Skill, h, args, post);
|
InvokeOnTakeDamage(p.Skill, damagedEntity, damageInfo, args, post);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deferred == null) return;
|
if (deferred == null) return;
|
||||||
foreach (var skill in deferred)
|
foreach (var skill in deferred)
|
||||||
InvokeOnTakeDamage(skill, h, args, post);
|
InvokeOnTakeDamage(skill, damagedEntity, damageInfo, args, post);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InvokeOnTakeDamage(Skills skill, DynamicHook h, object[] args, bool post)
|
private static void InvokeOnTakeDamage(Skills skill, CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, object[] args, bool post)
|
||||||
{
|
{
|
||||||
if (!Config.DebugEnabled(DebugCategory.Damage))
|
if (!Config.DebugEnabled(DebugCategory.Damage))
|
||||||
{
|
{
|
||||||
|
|
@ -202,24 +203,22 @@ namespace src.player
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var info = h.GetParam<CTakeDamageInfo>(1);
|
float before = damageInfo == null ? 0f : damageInfo.Damage;
|
||||||
float before = info == null ? 0f : info.Damage;
|
|
||||||
|
|
||||||
InvokeSkill(skill, post ? "OnTakeDamagePost" : "OnTakeDamage", args);
|
InvokeSkill(skill, post ? "OnTakeDamagePost" : "OnTakeDamage", args);
|
||||||
|
|
||||||
float after = info == null ? 0f : info.Damage;
|
float after = damageInfo == null ? 0f : damageInfo.Damage;
|
||||||
if (Math.Abs(before - after) > 0.01f)
|
if (Math.Abs(before - after) > 0.01f)
|
||||||
Debug.WriteToDebug($"[DMG] {skill} changed damage {before:0.#} -> {after:0.#}{DescribeDamageTarget(h)}", DebugCategory.Damage);
|
Debug.WriteToDebug($"[DMG] {skill} changed damage {before:0.#} -> {after:0.#}{DescribeDamageTarget(damagedEntity)}", DebugCategory.Damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string DescribeDamageTarget(DynamicHook h)
|
private static string DescribeDamageTarget(CBaseEntity damagedEntity)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var victimEntity = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || !damagedEntity.IsValid) return string.Empty;
|
||||||
if (victimEntity == null || !victimEntity.IsValid) return string.Empty;
|
|
||||||
|
|
||||||
var pawn = victimEntity.As<CCSPlayerPawn>();
|
var pawn = damagedEntity.As<CCSPlayerPawn>();
|
||||||
if (pawn == null || !pawn.IsValid || pawn.DesignerName != "player") return string.Empty;
|
if (pawn == null || !pawn.IsValid || pawn.DesignerName != "player") return string.Empty;
|
||||||
|
|
||||||
var controller = pawn.Controller.Value?.As<CCSPlayerController>();
|
var controller = pawn.Controller.Value?.As<CCSPlayerController>();
|
||||||
|
|
@ -445,8 +444,61 @@ namespace src.player
|
||||||
|
|
||||||
public static void InvalidateFreezeDisabledCache() => _freezeDisabledSkills = null;
|
public static void InvalidateFreezeDisabledCache() => _freezeDisabledSkills = null;
|
||||||
|
|
||||||
|
private const double stallThresholdMs = 100.0;
|
||||||
|
private static long lastTickTimestamp;
|
||||||
|
|
||||||
|
private static void ReportStall()
|
||||||
|
{
|
||||||
|
if (!PerfLog.Enabled)
|
||||||
|
{
|
||||||
|
lastTickTimestamp = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long now = System.Diagnostics.Stopwatch.GetTimestamp();
|
||||||
|
long previous = lastTickTimestamp;
|
||||||
|
lastTickTimestamp = now;
|
||||||
|
|
||||||
|
if (previous == 0) return;
|
||||||
|
|
||||||
|
double gapMs = (now - previous) * 1000.0 / System.Diagnostics.Stopwatch.Frequency;
|
||||||
|
if (gapMs >= stallThresholdMs && !PlayerManager.IsServerIdle())
|
||||||
|
{
|
||||||
|
var (tracked, owners) = EntityManager.GetStatistics();
|
||||||
|
PerfLog.Info($"STALL gap={gapMs:F2}ms tick={Server.TickCount} tracked={tracked} owners={owners}{PerfContext()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string PerfContext()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int players = 0, alive = 0, bots = 0;
|
||||||
|
foreach (var player in PlayerManager.GetTickPlayers())
|
||||||
|
{
|
||||||
|
if (player == null || !player.IsValid || player.IsHLTV) continue;
|
||||||
|
if (player.Team != CsTeam.Terrorist && player.Team != CsTeam.CounterTerrorist) continue;
|
||||||
|
|
||||||
|
players++;
|
||||||
|
if (player.IsBot) bots++;
|
||||||
|
if (player.PawnIsAlive) alive++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gameRules = jRandomSkills.Instance?.GameRules;
|
||||||
|
int round = gameRules == null || gameRules.Handle == IntPtr.Zero ? -1 : gameRules.TotalRoundsPlayed;
|
||||||
|
|
||||||
|
return $" players={players} alive={alive} bots={bots} skills={_activeSkillsList.Count} round={round}";
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void OnTick()
|
private static void OnTick()
|
||||||
{
|
{
|
||||||
|
ReportStall();
|
||||||
|
|
||||||
long perfStart = PerfLog.Start();
|
long perfStart = PerfLog.Start();
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@ namespace src.player
|
||||||
UpdateGameRules();
|
UpdateGameRules();
|
||||||
if (!SkillUtils.IsHudFrame()) return;
|
if (!SkillUtils.IsHudFrame()) return;
|
||||||
|
|
||||||
if (PerfLog.Enabled && Server.TickCount % 1920 == 0)
|
if (PerfLog.Enabled && Server.TickCount % 1920 == 0 && !PlayerManager.IsServerIdle())
|
||||||
{
|
{
|
||||||
int server = Utilities.GetAllEntities().Count(e => e != null && e.IsValid);
|
int server = Utilities.GetAllEntities().Count(e => e != null && e.IsValid);
|
||||||
var (tracked, owners) = EntityManager.GetStatistics();
|
var (tracked, owners) = EntityManager.GetStatistics();
|
||||||
PerfLog.Info($"ENTITIES server={server} tracked={tracked} owners={owners}");
|
PerfLog.Info($"ENTITIES server={server} tracked={tracked} owners={owners}{EntityManager.DescribeTracked()}{Event.PerfContext()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
long perfStart = PerfLog.Start();
|
long perfStart = PerfLog.Start();
|
||||||
|
|
@ -42,6 +42,7 @@ namespace src.player
|
||||||
|
|
||||||
private static void OnMapStart(string mapName)
|
private static void OnMapStart(string mapName)
|
||||||
{
|
{
|
||||||
|
PlayerManager.SetServerActive(true);
|
||||||
Instance.GameRules = null;
|
Instance.GameRules = null;
|
||||||
Event.OnMapChange();
|
Event.OnMapChange();
|
||||||
BotManager.Initialize();
|
BotManager.Initialize();
|
||||||
|
|
@ -52,6 +53,7 @@ namespace src.player
|
||||||
PerfLog.Info("===== MAP END (clean map change) =====");
|
PerfLog.Info("===== MAP END (clean map change) =====");
|
||||||
Debug.WriteToDebug("===== MAP END (clean map change) =====");
|
Debug.WriteToDebug("===== MAP END (clean map change) =====");
|
||||||
BotManager.Stop();
|
BotManager.Stop();
|
||||||
|
PlayerManager.SetServerActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void InitializeGameRules()
|
public static void InitializeGameRules()
|
||||||
|
|
@ -108,7 +110,7 @@ namespace src.player
|
||||||
}
|
}
|
||||||
else if (skillPlayer.IsDrawing && player.PawnIsAlive)
|
else if (skillPlayer.IsDrawing && player.PawnIsAlive)
|
||||||
{
|
{
|
||||||
var randomSkill = skills[Instance.Random.Next(skills.Length)];
|
var randomSkill = skills[Random.Shared.Next(skills.Length)];
|
||||||
|
|
||||||
infoLine = player.GetTranslationWithoutIlliterate("drawing_skill");
|
infoLine = player.GetTranslationWithoutIlliterate("drawing_skill");
|
||||||
skillLine = $"<font color='{randomSkill.Color}'>{player.GetSkillName(randomSkill.Skill)}</font>";
|
skillLine = $"<font color='{randomSkill.Color}'>{player.GetSkillName(randomSkill.Skill)}</font>";
|
||||||
|
|
@ -148,9 +150,7 @@ namespace src.player
|
||||||
var observerTarget = pawn.ObserverServices.ObserverTarget?.Value;
|
var observerTarget = pawn.ObserverServices.ObserverTarget?.Value;
|
||||||
if (observerTarget == null || !observerTarget.IsValid) return;
|
if (observerTarget == null || !observerTarget.IsValid) return;
|
||||||
|
|
||||||
var observedPlayer = PlayerManager.GetTickPlayers().FirstOrDefault(p =>
|
var observedPlayer = PlayerManager.GetControllerByPawn(observerTarget.Handle);
|
||||||
p != null && p.IsValid && p.Pawn?.Value?.Handle == observerTarget.Handle);
|
|
||||||
|
|
||||||
if (observedPlayer == null) return;
|
if (observedPlayer == null) return;
|
||||||
|
|
||||||
var observedEvent = PlayerManager.GetPlayerEvent(observedPlayer);
|
var observedEvent = PlayerManager.GetPlayerEvent(observedPlayer);
|
||||||
|
|
|
||||||
|
|
@ -550,10 +550,6 @@ namespace src.player
|
||||||
var playerTarget = Utilities.GetPlayerFromIndex((int)playerIndex);
|
var playerTarget = Utilities.GetPlayerFromIndex((int)playerIndex);
|
||||||
if (playerTarget == null || !playerTarget.IsValid) return;
|
if (playerTarget == null || !playerTarget.IsValid) return;
|
||||||
|
|
||||||
if (randomSkill.Display)
|
|
||||||
SkillUtils.PrintToChat(playerTarget, $"{ChatColors.DarkRed}{playerTarget.GetSkillName(randomSkill.Skill)}{ChatColors.Lime}: {playerTarget.GetSkillDescription(randomSkill.Skill)}",
|
|
||||||
border: !Utilities.GetPlayers().Any(p => p != null && p.IsValid && p.Team == playerTarget.Team && p != playerTarget) ? "tb" : "t");
|
|
||||||
|
|
||||||
if (SkillsInfo.GetValue<bool>(randomSkill.Skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime())
|
if (SkillsInfo.GetValue<bool>(randomSkill.Skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime())
|
||||||
Instance?.AddTimer(Config.LoadedConfig.SkillTimeBeforeStart, () =>
|
Instance?.AddTimer(Config.LoadedConfig.SkillTimeBeforeStart, () =>
|
||||||
{
|
{
|
||||||
|
|
@ -575,9 +571,18 @@ namespace src.player
|
||||||
Debug.WriteToDebug($"Player {skillPlayer.PlayerName} has got the skill \"{SkillNames.Get(randomSkill.Skill)}\".", DebugCategory.Skill);
|
Debug.WriteToDebug($"Player {skillPlayer.PlayerName} has got the skill \"{SkillNames.Get(randomSkill.Skill)}\".", DebugCategory.Skill);
|
||||||
UpdateSkillHudExpired(skillPlayer, randomSkill.Skill);
|
UpdateSkillHudExpired(skillPlayer, randomSkill.Skill);
|
||||||
|
|
||||||
|
if (randomSkill.Display)
|
||||||
|
Instance?.AddTimer(.6f, () =>
|
||||||
|
{
|
||||||
|
var descTarget = Utilities.GetPlayerFromIndex((int)playerIndex);
|
||||||
|
if (descTarget == null || !descTarget.IsValid) return;
|
||||||
|
|
||||||
|
SkillUtils.PrintToChat(descTarget, $"{ChatColors.DarkRed}{descTarget.GetSkillName(randomSkill.Skill)}{ChatColors.Lime}: {descTarget.GetSkillDescription(randomSkill.Skill)}", border: "tb");
|
||||||
|
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
|
|
||||||
if (Config.LoadedConfig.TeamMateSkillChatInfo)
|
if (Config.LoadedConfig.TeamMateSkillChatInfo)
|
||||||
{
|
{
|
||||||
Instance?.AddTimer(.6f, () =>
|
Instance?.AddTimer(.2f, () =>
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
|
|
@ -597,6 +602,7 @@ namespace src.player
|
||||||
foreach (string text in teammateSkills.Split("\n"))
|
foreach (string text in teammateSkills.Split("\n"))
|
||||||
if (!string.IsNullOrEmpty(text))
|
if (!string.IsNullOrEmpty(text))
|
||||||
SkillUtils.PrintToChat(player, text, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: "");
|
SkillUtils.PrintToChat(player, text, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: "");
|
||||||
|
if (!randomSkill.Display)
|
||||||
SkillUtils.PrintToChat(player, string.Empty, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: "b");
|
SkillUtils.PrintToChat(player, string.Empty, title: player.GetTranslationWithoutIlliterate("teammate_skills"), border: "b");
|
||||||
}
|
}
|
||||||
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
|
|
@ -700,8 +706,7 @@ namespace src.player
|
||||||
skillPlayer.SpecialSkill = Skills.None;
|
skillPlayer.SpecialSkill = Skills.None;
|
||||||
|
|
||||||
if (randomSkill.Display && Config.LoadedConfig.YourSkillChatInfo)
|
if (randomSkill.Display && Config.LoadedConfig.YourSkillChatInfo)
|
||||||
SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(randomSkill.Skill)}{ChatColors.Lime}: {player.GetSkillDescription(randomSkill.Skill)}",
|
SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(randomSkill.Skill)}{ChatColors.Lime}: {player.GetSkillDescription(randomSkill.Skill)}", border: "tb");
|
||||||
border: !Utilities.GetPlayers().Any(p => p != null && p.IsValid && p.Team == player.Team && p != player) ? "tb" : "t");
|
|
||||||
|
|
||||||
if (randomSkill.Skill == Skills.Illiterate)
|
if (randomSkill.Skill == Skills.Illiterate)
|
||||||
Illiterate.Enable();
|
Illiterate.Enable();
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,12 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null || damageInfo.Handle == nint.Zero) return;
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
if (damageInfo.Attacker == null || !damageInfo.Attacker.IsValid || damageInfo.Attacker.Value == null) return;
|
||||||
|
|
||||||
if (param == null || !param.IsValid || param2 == null || param2.Handle == nint.Zero) return;
|
var attackerEnt = damageInfo.Attacker.Value;
|
||||||
if (param2.Attacker == null || !param2.Attacker.IsValid || param2.Attacker.Value == null) return;
|
|
||||||
|
|
||||||
var attackerEnt = param2.Attacker.Value;
|
|
||||||
if (attackerEnt == null || !attackerEnt.IsValid) return;
|
if (attackerEnt == null || !attackerEnt.IsValid) return;
|
||||||
|
|
||||||
var attackerPawn = attackerEnt.As<CCSPlayerPawn>();
|
var attackerPawn = attackerEnt.As<CCSPlayerPawn>();
|
||||||
|
|
@ -40,12 +37,12 @@ namespace src.player.skills
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(attacker.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex(attacker.Index);
|
||||||
if (playerInfo == null || playerInfo.Skill != skillName) return;
|
if (playerInfo == null || playerInfo.Skill != skillName) return;
|
||||||
|
|
||||||
if (!SkillUtils.IsBulletDamage(param2)) return;
|
if (!SkillUtils.IsBulletDamage(damageInfo)) return;
|
||||||
|
|
||||||
int offset = GameData.GetOffset("CTakeDamageInfo_HitGroup");
|
int offset = GameData.GetOffset("CTakeDamageInfo_HitGroup");
|
||||||
if (offset <= 0) return;
|
if (offset <= 0) return;
|
||||||
|
|
||||||
nint hitGroupPointer = Marshal.ReadIntPtr(param2.Handle, offset);
|
nint hitGroupPointer = Marshal.ReadIntPtr(damageInfo.Handle, offset);
|
||||||
if (hitGroupPointer == nint.Zero) return;
|
if (hitGroupPointer == nint.Zero) return;
|
||||||
|
|
||||||
nint hitGroupData = Marshal.ReadIntPtr(hitGroupPointer, 16);
|
nint hitGroupData = Marshal.ReadIntPtr(hitGroupPointer, 16);
|
||||||
|
|
@ -60,14 +57,12 @@ namespace src.player.skills
|
||||||
Marshal.WriteInt32(address, (int)HitGroup_t.HITGROUP_HEAD);
|
Marshal.WriteInt32(address, (int)HitGroup_t.HITGROUP_HEAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamagePost(DynamicHook h)
|
public static void OnTakeDamagePost(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, CTakeDamageResult damageResult)
|
||||||
{
|
{
|
||||||
var info = h.GetParam<CTakeDamageInfo>(1);
|
if (damageInfo == null || damageInfo.Handle == nint.Zero)
|
||||||
|
|
||||||
if (info == null || info.Handle == nint.Zero)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!SkillUtils.IsBulletDamage(info)) return;
|
if (!SkillUtils.IsBulletDamage(damageInfo)) return;
|
||||||
|
|
||||||
if (_restoreStack.Value!.Count > 0)
|
if (_restoreStack.Value!.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -31,14 +31,11 @@ namespace src.player.skills
|
||||||
border: !PlayerManager.GetTickPlayers().Any(p => p.IsValid && p.Team == player.Team && p != player) ? "tb" : "t");
|
border: !PlayerManager.GetTickPlayers().Any(p => p.IsValid && p.Team == player.Team && p != player) ? "tb" : "t");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return;
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || !param.IsValid || param2 == null) return;
|
var victimPawn = damagedEntity.As<CCSPlayerPawn>();
|
||||||
|
|
||||||
var victimPawn = param.As<CCSPlayerPawn>();
|
|
||||||
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
||||||
|
|
||||||
var victimController = victimPawn.Controller.Value;
|
var victimController = victimPawn.Controller.Value;
|
||||||
|
|
@ -53,7 +50,7 @@ namespace src.player.skills
|
||||||
if (playerInfo.Skill == skillName && victim.PawnIsAlive)
|
if (playerInfo.Skill == skillName && victim.PawnIsAlive)
|
||||||
{
|
{
|
||||||
float? skillChance = playerInfo.SkillChance;
|
float? skillChance = playerInfo.SkillChance;
|
||||||
param2.Damage *= skillChance ?? 1f;
|
damageInfo.Damage *= skillChance ?? 1f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
using src.utils;
|
using src.utils;
|
||||||
|
|
@ -17,20 +17,19 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle);
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
|
|
||||||
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return;
|
||||||
|
|
||||||
if (attackerPawn.Controller?.Value == null || victimPawn.Controller?.Value == null)
|
if (attackerPawn.Controller?.Value == null || victimPawn.Controller?.Value == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -42,12 +41,12 @@ namespace src.player.skills
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(attacker.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex(attacker.Index);
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
|
|
||||||
var weapon = param2.Ability?.Value;
|
var weapon = damageInfo.Ability?.Value;
|
||||||
if (weapon == null || !weapon.IsValid) return;
|
if (weapon == null || !weapon.IsValid) return;
|
||||||
if (IsNade(weapon.DesignerName)) return;
|
if (IsNade(weapon.DesignerName)) return;
|
||||||
|
|
||||||
if (IsBehind(attacker, victim))
|
if (IsBehind(attacker, victim))
|
||||||
param2.Damage *= SkillsInfo.GetValue<float>(skillName, "damageMultiplier");
|
damageInfo.Damage *= SkillsInfo.GetValue<float>(skillName, "damageMultiplier");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsBehind(CCSPlayerController attacker, CCSPlayerController victim)
|
private static bool IsBehind(CCSPlayerController attacker, CCSPlayerController victim)
|
||||||
|
|
@ -81,10 +80,11 @@ namespace src.player.skills
|
||||||
return (target >= a || target <= b);
|
return (target >= a || target <= b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d9d9d9", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float damageMultiplier = 2f, float toleranceDeg = 45f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d9d9d9", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float damageMultiplier = 2f, float toleranceDeg = 45f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float DamageMultiplier { get; set; } = damageMultiplier;
|
public float DamageMultiplier { get; set; } = damageMultiplier;
|
||||||
public float ToleranceDeg { get; set; } = toleranceDeg;
|
public float ToleranceDeg { get; set; } = toleranceDeg;
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.Berserker;
|
private const Skills skillName = Skills.Berserker;
|
||||||
public static readonly ConcurrentDictionary<uint, int> jumpedPlayers = [];
|
public static readonly ConcurrentDictionary<uint, int> jumpedPlayers = [];
|
||||||
|
private static readonly List<CCSPlayerController> holderBuffer = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -35,20 +36,19 @@ namespace src.player.skills
|
||||||
return Math.Max(1, Math.Min(maxValue, newValue));
|
return Math.Max(1, Math.Min(maxValue, newValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle);
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
|
|
||||||
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return;
|
||||||
|
|
||||||
if (attackerPawn == null || attackerPawn.Controller?.Value == null || victimPawn == null || victimPawn.Controller?.Value == null)
|
if (attackerPawn == null || attackerPawn.Controller?.Value == null || victimPawn == null || victimPawn.Controller?.Value == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ namespace src.player.skills
|
||||||
if (playerInfo.Skill == skillName)
|
if (playerInfo.Skill == skillName)
|
||||||
{
|
{
|
||||||
float damageMultiplier = CalculateNewVelocity(attackerPawn, SkillsInfo.GetValue<float>(skillName, "maxDamageVelocity"));
|
float damageMultiplier = CalculateNewVelocity(attackerPawn, SkillsInfo.GetValue<float>(skillName, "maxDamageVelocity"));
|
||||||
param2.Damage *= damageMultiplier;
|
damageInfo.Damage *= damageMultiplier;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,18 +90,20 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
PlayerManager.FillSkillHolders(skillName, holderBuffer);
|
||||||
|
if (holderBuffer.Count == 0) return;
|
||||||
|
|
||||||
|
float maxSpeedVelocity = SkillsInfo.GetValue<float>(skillName, "maxSpeedVelocity");
|
||||||
|
|
||||||
|
foreach (var player in holderBuffer)
|
||||||
{
|
{
|
||||||
if (!Instance.IsPlayerValid(player)) continue;
|
if (!Instance.IsPlayerValid(player)) continue;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
|
||||||
if (playerInfo?.Skill != skillName) continue;
|
|
||||||
|
|
||||||
var playerPawn = player.PlayerPawn?.Value;
|
var playerPawn = player.PlayerPawn?.Value;
|
||||||
if (playerPawn == null || playerPawn.VelocityModifier == 0) continue;
|
if (playerPawn == null || playerPawn.VelocityModifier == 0) continue;
|
||||||
|
|
||||||
var buttons = player.Buttons;
|
var buttons = player.Buttons;
|
||||||
float newSpeedVelocity = CalculateNewVelocity(playerPawn, SkillsInfo.GetValue<float>(skillName, "maxSpeedVelocity"));
|
float newSpeedVelocity = CalculateNewVelocity(playerPawn, maxSpeedVelocity);
|
||||||
|
|
||||||
if (buttons.HasFlag(PlayerButtons.Moveleft) || buttons.HasFlag(PlayerButtons.Moveright) || buttons.HasFlag(PlayerButtons.Forward) || buttons.HasFlag(PlayerButtons.Back))
|
if (buttons.HasFlag(PlayerButtons.Moveleft) || buttons.HasFlag(PlayerButtons.Moveright) || buttons.HasFlag(PlayerButtons.Forward) || buttons.HasFlag(PlayerButtons.Back))
|
||||||
playerPawn.VelocityModifier = newSpeedVelocity;
|
playerPawn.VelocityModifier = newSpeedVelocity;
|
||||||
|
|
@ -109,15 +111,18 @@ namespace src.player.skills
|
||||||
if (jumpedPlayers.TryGetValue(player.Index, out var time) && time > Server.TickCount)
|
if (jumpedPlayers.TryGetValue(player.Index, out var time) && time > Server.TickCount)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (playerPawn.MoveType == MoveType_t.MOVETYPE_NOCLIP) continue;
|
||||||
|
|
||||||
if (!((PlayerFlags)player.Flags).HasFlag(PlayerFlags.FL_ONGROUND))
|
if (!((PlayerFlags)player.Flags).HasFlag(PlayerFlags.FL_ONGROUND))
|
||||||
playerPawn.AbsVelocity.Z = Math.Min(playerPawn.AbsVelocity.Z, 10);
|
playerPawn.AbsVelocity.Z = Math.Min(playerPawn.AbsVelocity.Z, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#cc2929", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float maxSpeedVelocity = 2f, float maxDamageVelocity = 2f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#cc2929", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float maxSpeedVelocity = 2f, float maxDamageVelocity = 2f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float MaxSpeedVelocity { get; set; } = maxSpeedVelocity;
|
public float MaxSpeedVelocity { get; set; } = maxSpeedVelocity;
|
||||||
public float MaxDamageVelocity { get; set; } = maxDamageVelocity;
|
public float MaxDamageVelocity { get; set; } = maxDamageVelocity;
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.BladeMaster;
|
private const Skills skillName = Skills.BladeMaster;
|
||||||
private static readonly string[] noReflectionWeapon = ["inferno", "flashbang", "smokegrenade", "decoy", "hegrenade", "knife", "taser", "bayonet"];
|
private static readonly string[] noReflectionWeapon = ["inferno", "flashbang", "smokegrenade", "decoy", "hegrenade", "knife", "taser", "bayonet"];
|
||||||
|
private static readonly List<CCSPlayerController> holderBuffer = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -20,13 +21,11 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
var modifier = SkillsInfo.GetValue<float>(skillName, "velocityModifier");
|
var modifier = SkillsInfo.GetValue<float>(skillName, "velocityModifier");
|
||||||
|
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
PlayerManager.FillSkillHolders(skillName, holderBuffer);
|
||||||
|
if (holderBuffer.Count == 0) return;
|
||||||
|
|
||||||
|
foreach (var player in holderBuffer)
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) continue;
|
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
|
||||||
if (playerInfo?.Skill != skillName) continue;
|
|
||||||
|
|
||||||
var playerPawn = player.PlayerPawn?.Value;
|
var playerPawn = player.PlayerPawn?.Value;
|
||||||
if (playerPawn == null || !playerPawn.IsValid || playerPawn.VelocityModifier == 0) continue;
|
if (playerPawn == null || !playerPawn.IsValid || playerPawn.VelocityModifier == 0) continue;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (SkillPlayerInfo.IsEmpty) return;
|
||||||
|
|
||||||
float cooldown = SkillsInfo.GetValue<float>(skillName, "cooldown");
|
float cooldown = SkillsInfo.GetValue<float>(skillName, "cooldown");
|
||||||
|
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
foreach (var player in PlayerManager.GetTickPlayers())
|
||||||
|
|
@ -140,14 +142,11 @@ namespace src.player.skills
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null) return;
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null) return;
|
var nade = damageInfo.Attacker.Value;
|
||||||
|
|
||||||
var nade = param2.Attacker.Value;
|
|
||||||
if (nade == null || !nade.IsValid) return;
|
if (nade == null || !nade.IsValid) return;
|
||||||
|
|
||||||
if (nade.DesignerName != "hegrenade_projectile") return;
|
if (nade.DesignerName != "hegrenade_projectile") return;
|
||||||
|
|
@ -158,7 +157,7 @@ namespace src.player.skills
|
||||||
if (!int.TryParse(parts[2], out int nadeTeam)) return;
|
if (!int.TryParse(parts[2], out int nadeTeam)) return;
|
||||||
if (!uint.TryParse(parts[3], out uint ownerIndex)) return;
|
if (!uint.TryParse(parts[3], out uint ownerIndex)) return;
|
||||||
|
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
|
|
||||||
if (victimPawn.DesignerName != "player") return;
|
if (victimPawn.DesignerName != "player") return;
|
||||||
if (victimPawn.Controller?.Value == null) return;
|
if (victimPawn.Controller?.Value == null) return;
|
||||||
|
|
@ -170,9 +169,17 @@ namespace src.player.skills
|
||||||
if (owner != null && !owner.IsValid) owner = null;
|
if (owner != null && !owner.IsValid) owner = null;
|
||||||
|
|
||||||
if (victimPawn.TeamNum == nadeTeam)
|
if (victimPawn.TeamNum == nadeTeam)
|
||||||
param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
|
{
|
||||||
|
if (!SkillsInfo.GetValue<bool>(skillName, "friendlyFire"))
|
||||||
|
{
|
||||||
|
damageInfo.Damage = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (owner != null && param2.Damage >= victimPawn.Health)
|
damageInfo.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (owner != null && damageInfo.Damage >= victimPawn.Health)
|
||||||
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,13 +192,14 @@ namespace src.player.skills
|
||||||
public DateTime Cooldown { get; set; }
|
public DateTime Cooldown { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7740c9", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float explosionRadius = 400.0f, int explosionDamage = 60, float dmgReductionForTeamates = 0.5f, float cooldown = 10f, float force = 1000f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7740c9", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float explosionRadius = 400.0f, int explosionDamage = 60, float dmgReductionForTeamates = 0.5f, float cooldown = 10f, float force = 1000f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float ExplosionRadius { get; set; } = explosionRadius;
|
public float ExplosionRadius { get; set; } = explosionRadius;
|
||||||
public int ExplosionDamage { get; set; } = explosionDamage;
|
public int ExplosionDamage { get; set; } = explosionDamage;
|
||||||
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
|
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
|
||||||
public float Cooldown { get; set; } = cooldown;
|
public float Cooldown { get; set; } = cooldown;
|
||||||
public float Force { get; set; } = force;
|
public float Force { get; set; } = force;
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.BunnyHop;
|
private const Skills skillName = Skills.BunnyHop;
|
||||||
private static readonly ConcurrentDictionary<uint, int> playersLastJump = [];
|
private static readonly ConcurrentDictionary<uint, int> playersLastJump = [];
|
||||||
|
private static readonly List<CCSPlayerController> holderBuffer = [];
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
|
|
@ -28,12 +29,14 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
PlayerManager.FillSkillHolders(skillName, holderBuffer);
|
||||||
|
if (holderBuffer.Count == 0) return;
|
||||||
|
|
||||||
|
foreach (var playerEvent in holderBuffer)
|
||||||
{
|
{
|
||||||
|
var player = PlayerManager.GetPlayerFromEvent(playerEvent) ?? playerEvent;
|
||||||
if (player == null || !player.IsValid) continue;
|
if (player == null || !player.IsValid) continue;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
|
||||||
if (playerInfo?.Skill == skillName)
|
|
||||||
GiveBunnyHop(player);
|
GiveBunnyHop(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Core.Attributes;
|
using CounterStrikeSharp.API.Core.Attributes;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -170,7 +170,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index);
|
var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index, trackAs: "empty_prop");
|
||||||
if (emptyProp == null || !emptyProp.IsValid) return;
|
if (emptyProp == null || !emptyProp.IsValid) return;
|
||||||
|
|
||||||
var playerPawn = player.PlayerPawn?.Value;
|
var playerPawn = player.PlayerPawn?.Value;
|
||||||
|
|
@ -188,7 +188,6 @@ namespace src.player.skills
|
||||||
|
|
||||||
Utilities.SetStateChanged(emptyProp, "CBaseEntity", "m_CBodyComponent");
|
Utilities.SetStateChanged(emptyProp, "CBaseEntity", "m_CBodyComponent");
|
||||||
|
|
||||||
EntityManager.RegisterExisting(emptyProp, player.Index, "empty_prop");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PlayerHurt(EventPlayerHurt @event)
|
public static void PlayerHurt(EventPlayerHurt @event)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -174,16 +174,13 @@ namespace src.player.skills
|
||||||
return owner != null && owner.IsValid ? owner : null;
|
return owner != null && owner.IsValid ? owner : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle);
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
|
|
||||||
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (chickens.IsEmpty) return;
|
||||||
|
|
||||||
var pairs = chickens.ToArray();
|
var pairs = chickens.ToArray();
|
||||||
foreach (var valuePair in pairs)
|
foreach (var valuePair in pairs)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,20 +14,19 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle);
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
|
|
||||||
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return;
|
||||||
|
|
||||||
if (attackerPawn.Controller?.Value == null || victimPawn.Controller?.Value == null)
|
if (attackerPawn.Controller?.Value == null || victimPawn.Controller?.Value == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -38,15 +37,16 @@ namespace src.player.skills
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(attacker.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex(attacker.Index);
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
|
|
||||||
var weapon = param2.Ability?.Value;
|
var weapon = damageInfo.Ability?.Value;
|
||||||
if (weapon == null || !weapon.IsValid) return;
|
if (weapon == null || !weapon.IsValid) return;
|
||||||
if (weapon.DesignerName != "weapon_knife" && weapon.DesignerName != "weapon_bayonet") return;
|
if (weapon.DesignerName != "weapon_knife" && weapon.DesignerName != "weapon_bayonet") return;
|
||||||
|
|
||||||
param2.Damage = 9999f;
|
damageInfo.Damage = 9999f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#88a31a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#88a31a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
using CounterStrikeSharp.API.Modules.Commands.Targeting;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
|
|
@ -292,18 +292,15 @@ namespace src.player.skills
|
||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(param.Entity.Name)) return;
|
if (string.IsNullOrEmpty(damagedEntity.Entity.Name)) return;
|
||||||
if (!param.Entity.Name.StartsWith("CypherCamera_")) return;
|
if (!damagedEntity.Entity.Name.StartsWith("CypherCamera_")) return;
|
||||||
|
|
||||||
var nameParams = param.Entity.Name.Split('_')[2];
|
var nameParams = damagedEntity.Entity.Name.Split('_')[2];
|
||||||
_ = uint.TryParse(nameParams, out uint userIndex);
|
_ = uint.TryParse(nameParams, out uint userIndex);
|
||||||
if (userIndex == 0) return;
|
if (userIndex == 0) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (SkillPlayerInfo.IsEmpty) return;
|
||||||
|
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
foreach (var player in PlayerManager.GetTickPlayers())
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ namespace src.player.skills
|
||||||
|
|
||||||
Server.NextWorldUpdate(() =>
|
Server.NextWorldUpdate(() =>
|
||||||
{
|
{
|
||||||
|
|
||||||
if (player == null || !player.IsValid || player.Team != lastTeam) return;
|
if (player == null || !player.IsValid || player.Team != lastTeam) return;
|
||||||
|
|
||||||
var pawn = player.PlayerPawn.Value;
|
var pawn = player.PlayerPawn.Value;
|
||||||
|
|
@ -92,14 +91,11 @@ namespace src.player.skills
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null) return;
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null) return;
|
var nade = damageInfo.Attacker.Value;
|
||||||
|
|
||||||
var nade = param2.Attacker.Value;
|
|
||||||
if (nade == null || !nade.IsValid) return;
|
if (nade == null || !nade.IsValid) return;
|
||||||
|
|
||||||
if (nade.DesignerName != "hegrenade_projectile") return;
|
if (nade.DesignerName != "hegrenade_projectile") return;
|
||||||
|
|
@ -110,7 +106,7 @@ namespace src.player.skills
|
||||||
if (!int.TryParse(parts[2], out int nadeTeam)) return;
|
if (!int.TryParse(parts[2], out int nadeTeam)) return;
|
||||||
if (!uint.TryParse(parts[3], out uint ownerIndex)) return;
|
if (!uint.TryParse(parts[3], out uint ownerIndex)) return;
|
||||||
|
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
|
|
||||||
if (victimPawn.DesignerName != "player") return;
|
if (victimPawn.DesignerName != "player") return;
|
||||||
if (victimPawn.Controller?.Value == null) return;
|
if (victimPawn.Controller?.Value == null) return;
|
||||||
|
|
@ -122,9 +118,17 @@ namespace src.player.skills
|
||||||
if (owner != null && !owner.IsValid) owner = null;
|
if (owner != null && !owner.IsValid) owner = null;
|
||||||
|
|
||||||
if (victimPawn.TeamNum == nadeTeam)
|
if (victimPawn.TeamNum == nadeTeam)
|
||||||
param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
|
{
|
||||||
|
if (!SkillsInfo.GetValue<bool>(skillName, "friendlyFire"))
|
||||||
|
{
|
||||||
|
damageInfo.Damage = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (owner != null && param2.Damage >= victimPawn.Health)
|
damageInfo.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (owner != null && damageInfo.Damage >= victimPawn.Health)
|
||||||
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,12 +139,13 @@ namespace src.player.skills
|
||||||
return player != null && player.IsValid && player.PlayerPawn?.Value != null;
|
return player != null && player.IsValid && player.PlayerPawn?.Value != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#F5CB42", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float explosionRadius = 500.0f, int explosionDamage = 999, float dmgReductionForTeamates = 0.5f, float soundVolume = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#F5CB42", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float explosionRadius = 500.0f, int explosionDamage = 999, float dmgReductionForTeamates = 0.5f, float soundVolume = 1f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float SoundVolume { get; set; } = soundVolume;
|
public float SoundVolume { get; set; } = soundVolume;
|
||||||
public float ExplosionRadius { get; set; } = explosionRadius;
|
public float ExplosionRadius { get; set; } = explosionRadius;
|
||||||
public int ExplosionDamage { get; set; } = explosionDamage;
|
public int ExplosionDamage { get; set; } = explosionDamage;
|
||||||
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
|
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.DemonEye;
|
private const Skills skillName = Skills.DemonEye;
|
||||||
private static readonly Skills[] hidingSkills = [Skills.Ghost, Skills.Ninja, Skills.C4Camouflage];
|
private static readonly Skills[] hidingSkills = [Skills.Ghost, Skills.Ninja, Skills.C4Camouflage];
|
||||||
|
private static readonly List<CCSPlayerController> holderBuffer = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -24,13 +25,13 @@ namespace src.player.skills
|
||||||
|
|
||||||
int damage = SkillsInfo.GetValue<int>(skillName, "damage");
|
int damage = SkillsInfo.GetValue<int>(skillName, "damage");
|
||||||
|
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
PlayerManager.FillSkillHolders(skillName, holderBuffer);
|
||||||
|
if (holderBuffer.Count == 0) return;
|
||||||
|
|
||||||
|
foreach (var playerEvent in holderBuffer)
|
||||||
{
|
{
|
||||||
var playerEvent = PlayerManager.GetPlayerEvent(player);
|
|
||||||
if (!Instance.IsPlayerValid(playerEvent)) continue;
|
if (!Instance.IsPlayerValid(playerEvent)) continue;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(playerEvent!.Index);
|
|
||||||
if (playerInfo?.Skill == skillName)
|
|
||||||
HitEnemies(playerEvent, damage);
|
HitEnemies(playerEvent, damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,11 @@ namespace src.player.skills
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
RestoreAll();
|
RestoreAll();
|
||||||
jammedUntilTick.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RoundEnd()
|
public static void RoundEnd()
|
||||||
{
|
{
|
||||||
RestoreAll();
|
RestoreAll();
|
||||||
jammedUntilTick.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PlayerDisconnect(uint playerIndex)
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
|
@ -89,6 +87,8 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
foreach (var playerIndex in jammedUntilTick.Keys)
|
foreach (var playerIndex in jammedUntilTick.Keys)
|
||||||
SetHud(playerIndex, false);
|
SetHud(playerIndex, false);
|
||||||
|
|
||||||
|
jammedUntilTick.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetHud(uint playerIndex, bool jam)
|
private static void SetHud(uint playerIndex, bool jam)
|
||||||
|
|
@ -142,6 +142,10 @@ namespace src.player.skills
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
playersWithSkill.TryRemove(player.Index, out _);
|
playersWithSkill.TryRemove(player.Index, out _);
|
||||||
|
|
||||||
|
if (playersWithSkill.IsEmpty)
|
||||||
|
RestoreAll();
|
||||||
|
|
||||||
SkillUtils.UpdateGrenadeCount(player, CsItem.HEGrenade, 1);
|
SkillUtils.UpdateGrenadeCount(player, CsItem.HEGrenade, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,12 +130,16 @@ namespace src.player.skills
|
||||||
barrel.Entity!.Name = barrel.Globalname = $"Barrel_{Server.TickCount}_{player.TeamNum}_{player.Index}";
|
barrel.Entity!.Name = barrel.Globalname = $"Barrel_{Server.TickCount}_{player.TeamNum}_{player.Index}";
|
||||||
barrel.Collision.SolidType = SolidType_t.SOLID_VPHYSICS;
|
barrel.Collision.SolidType = SolidType_t.SOLID_VPHYSICS;
|
||||||
barrel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(barrel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2));
|
barrel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(barrel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2));
|
||||||
barrel.DispatchSpawn();
|
|
||||||
|
using (var keys = new CEntityKeyValues())
|
||||||
|
{
|
||||||
|
keys.SetString("model", SkillsInfo.GetValue<string>(skillName, "propModel"));
|
||||||
|
barrel.DispatchSpawn(keys);
|
||||||
|
}
|
||||||
|
|
||||||
Server.NextFrame(() =>
|
Server.NextFrame(() =>
|
||||||
{
|
{
|
||||||
if (barrel == null || !barrel.IsValid) return;
|
if (barrel == null || !barrel.IsValid) return;
|
||||||
barrel.SetModel(SkillsInfo.GetValue<string>(skillName, "propModel"));
|
|
||||||
barrel.Teleport(pos, rotation, null);
|
barrel.Teleport(pos, rotation, null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -165,27 +169,24 @@ namespace src.player.skills
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null) return;
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null) return;
|
if (HandleBarrelHit(damagedEntity)) return;
|
||||||
|
HandleNadeDamage(damagedEntity, damageInfo);
|
||||||
if (HandleBarrelHit(param)) return;
|
|
||||||
HandleNadeDamage(param, param2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HandleBarrelHit(CEntityInstance param)
|
private static bool HandleBarrelHit(CEntityInstance damagedEntity)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(param.Entity!.Name)) return false;
|
if (string.IsNullOrEmpty(damagedEntity.Entity!.Name)) return false;
|
||||||
if (!param.Entity.Name.StartsWith("Barrel_")) return false;
|
if (!damagedEntity.Entity.Name.StartsWith("Barrel_")) return false;
|
||||||
|
|
||||||
var barrel = param.As<CDynamicProp>();
|
var barrel = damagedEntity.As<CDynamicProp>();
|
||||||
if (barrel == null || !barrel.IsValid) return true;
|
if (barrel == null || !barrel.IsValid) return true;
|
||||||
if (!consumedBarrels.TryAdd(barrel.Index, 0)) return true;
|
if (!consumedBarrels.TryAdd(barrel.Index, 0)) return true;
|
||||||
|
|
||||||
var nameParts = param.Entity.Name.Split('_');
|
var nameParts = damagedEntity.Entity.Name.Split('_');
|
||||||
if (nameParts.Length < 4) return true;
|
if (nameParts.Length < 4) return true;
|
||||||
if (!int.TryParse(nameParts[2], out int teamNum)) return true;
|
if (!int.TryParse(nameParts[2], out int teamNum)) return true;
|
||||||
if (!uint.TryParse(nameParts[3], out uint ownerIndex)) return true;
|
if (!uint.TryParse(nameParts[3], out uint ownerIndex)) return true;
|
||||||
|
|
@ -207,9 +208,9 @@ namespace src.player.skills
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleNadeDamage(CEntityInstance param, CTakeDamageInfo param2)
|
private static void HandleNadeDamage(CEntityInstance damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
var nade = param2.Attacker?.Value;
|
var nade = damageInfo.Attacker?.Value;
|
||||||
if (nade == null || !nade.IsValid) return;
|
if (nade == null || !nade.IsValid) return;
|
||||||
if (nade.DesignerName != "hegrenade_projectile") return;
|
if (nade.DesignerName != "hegrenade_projectile") return;
|
||||||
if (string.IsNullOrEmpty(nade.Globalname) || !nade.Globalname.StartsWith("barrel_team_")) return;
|
if (string.IsNullOrEmpty(nade.Globalname) || !nade.Globalname.StartsWith("barrel_team_")) return;
|
||||||
|
|
@ -219,7 +220,7 @@ namespace src.player.skills
|
||||||
if (!int.TryParse(parts[2], out int nadeTeam)) return;
|
if (!int.TryParse(parts[2], out int nadeTeam)) return;
|
||||||
if (!uint.TryParse(parts[3], out uint ownerIndex)) return;
|
if (!uint.TryParse(parts[3], out uint ownerIndex)) return;
|
||||||
|
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
if (victimPawn.DesignerName != "player" || victimPawn.Controller?.Value == null) return;
|
if (victimPawn.DesignerName != "player" || victimPawn.Controller?.Value == null) return;
|
||||||
|
|
||||||
var victim = victimPawn.Controller.Value.As<CCSPlayerController>();
|
var victim = victimPawn.Controller.Value.As<CCSPlayerController>();
|
||||||
|
|
@ -229,9 +230,17 @@ namespace src.player.skills
|
||||||
if (owner != null && !owner.IsValid) owner = null;
|
if (owner != null && !owner.IsValid) owner = null;
|
||||||
|
|
||||||
if (victimPawn.TeamNum == nadeTeam)
|
if (victimPawn.TeamNum == nadeTeam)
|
||||||
param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
|
{
|
||||||
|
if (!SkillsInfo.GetValue<bool>(skillName, "friendlyFire"))
|
||||||
|
{
|
||||||
|
damageInfo.Damage = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (owner != null && param2.Damage >= victimPawn.Health)
|
damageInfo.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (owner != null && damageInfo.Damage >= victimPawn.Health)
|
||||||
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,13 +253,14 @@ namespace src.player.skills
|
||||||
public DateTime Cooldown { get; set; }
|
public DateTime Cooldown { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c0392b", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Common, float cooldown = 20f, float explosionRadius = 600f, int explosionDamage = 50, string propModel = "models/props/de_train/hr_t/barrel_a/barrel_a.vmdl", float dmgReductionForTeamates = 0.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c0392b", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Common, float cooldown = 20f, float explosionRadius = 600f, int explosionDamage = 50, string propModel = "models/props/de_train/hr_t/barrel_a/barrel_a.vmdl", float dmgReductionForTeamates = 0.5f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float Cooldown { get; set; } = cooldown;
|
public float Cooldown { get; set; } = cooldown;
|
||||||
public float ExplosionRadius { get; set; } = explosionRadius;
|
public float ExplosionRadius { get; set; } = explosionRadius;
|
||||||
public int ExplosionDamage { get; set; } = explosionDamage;
|
public int ExplosionDamage { get; set; } = explosionDamage;
|
||||||
public string PropModel { get; set; } = propModel;
|
public string PropModel { get; set; } = propModel;
|
||||||
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
|
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,14 +75,11 @@ namespace src.player.skills
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null) return;
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null) return;
|
var nade = damageInfo.Attacker?.Value;
|
||||||
|
|
||||||
var nade = param2.Attacker?.Value;
|
|
||||||
if (nade == null || !nade.IsValid) return;
|
if (nade == null || !nade.IsValid) return;
|
||||||
|
|
||||||
if (nade.DesignerName != "hegrenade_projectile") return;
|
if (nade.DesignerName != "hegrenade_projectile") return;
|
||||||
|
|
@ -93,7 +90,7 @@ namespace src.player.skills
|
||||||
if (!int.TryParse(parts[2], out int nadeTeam)) return;
|
if (!int.TryParse(parts[2], out int nadeTeam)) return;
|
||||||
if (!uint.TryParse(parts[3], out uint ownerIndex)) return;
|
if (!uint.TryParse(parts[3], out uint ownerIndex)) return;
|
||||||
|
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
|
|
||||||
if (victimPawn.DesignerName != "player") return;
|
if (victimPawn.DesignerName != "player") return;
|
||||||
if (victimPawn.Controller?.Value == null) return;
|
if (victimPawn.Controller?.Value == null) return;
|
||||||
|
|
@ -102,12 +99,20 @@ namespace src.player.skills
|
||||||
if (victim == null || !victim.IsValid || victim.Index == ownerIndex) return;
|
if (victim == null || !victim.IsValid || victim.Index == ownerIndex) return;
|
||||||
|
|
||||||
if (victimPawn.TeamNum == nadeTeam)
|
if (victimPawn.TeamNum == nadeTeam)
|
||||||
param2.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
|
{
|
||||||
|
if (!SkillsInfo.GetValue<bool>(skillName, "friendlyFire"))
|
||||||
|
{
|
||||||
|
damageInfo.Damage = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
damageInfo.Damage *= SkillUtils.GetTeamDamageMultiplier(skillName);
|
||||||
|
}
|
||||||
|
|
||||||
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
|
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
|
||||||
if (owner != null && !owner.IsValid) owner = null;
|
if (owner != null && !owner.IsValid) owner = null;
|
||||||
|
|
||||||
if (owner != null && param2.Damage >= victimPawn.Health)
|
if (owner != null && damageInfo.Damage >= victimPawn.Health)
|
||||||
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
SkillUtils.RegisterKillCredit(victim.Index, owner.Index, KillfeedIcons.Explosion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,13 +134,14 @@ namespace src.player.skills
|
||||||
SpawnExplosion(pos, player);
|
SpawnExplosion(pos, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9c0000", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float damage = 25f, float damageRadius = 210f, float chanceFrom = .15f, float chanceTo = .3f, float dmgReductionForTeamates = 0.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9c0000", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float damage = 25f, float damageRadius = 210f, float chanceFrom = .15f, float chanceTo = .3f, float dmgReductionForTeamates = 0.5f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float Damage { get; set; } = damage;
|
public float Damage { get; set; } = damage;
|
||||||
public float DamageRadius { get; set; } = damageRadius;
|
public float DamageRadius { get; set; } = damageRadius;
|
||||||
public float ChanceFrom { get; set; } = chanceFrom;
|
public float ChanceFrom { get; set; } = chanceFrom;
|
||||||
public float ChanceTo { get; set; } = chanceTo;
|
public float ChanceTo { get; set; } = chanceTo;
|
||||||
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
|
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,6 +60,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (cameras.IsEmpty) return;
|
||||||
|
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
foreach (var player in PlayerManager.GetTickPlayers())
|
||||||
if (cameras.TryGetValue(player.Index, out var cameraInfo) && cameraInfo.Item2 != 0)
|
if (cameras.TryGetValue(player.Index, out var cameraInfo) && cameraInfo.Item2 != 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
using src.utils;
|
using src.utils;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using static src.jRandomSkills;
|
using static src.jRandomSkills;
|
||||||
|
|
||||||
namespace src.player.skills
|
namespace src.player.skills
|
||||||
|
|
@ -9,12 +10,75 @@ namespace src.player.skills
|
||||||
public class FastReload : ISkill
|
public class FastReload : ISkill
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.FastReload;
|
private const Skills skillName = Skills.FastReload;
|
||||||
|
private static readonly ConcurrentDictionary<uint, PlayerSkillInfo> SkillPlayerInfo = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void NewRound()
|
||||||
|
{
|
||||||
|
SkillPlayerInfo.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnableSkill(CCSPlayerController player)
|
||||||
|
{
|
||||||
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
|
SkillPlayerInfo[player.Index] = new PlayerSkillInfo
|
||||||
|
{
|
||||||
|
SteamID = player.Index,
|
||||||
|
CanUse = true,
|
||||||
|
Cooldown = DateTime.MinValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DisableSkill(CCSPlayerController player)
|
||||||
|
{
|
||||||
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
|
SkillPlayerInfo.TryRemove(player.Index, out _);
|
||||||
|
SkillUtils.ResetPrintHTML(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
{
|
||||||
|
SkillPlayerInfo.TryRemove(playerIndex, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OnTick()
|
||||||
|
{
|
||||||
|
if (SkillPlayerInfo.IsEmpty) return;
|
||||||
|
if (!SkillUtils.IsHudFrame()) return;
|
||||||
|
if (SkillUtils.IsFreezeTime()) return;
|
||||||
|
|
||||||
|
foreach (var player in PlayerManager.GetTickPlayers())
|
||||||
|
{
|
||||||
|
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||||
|
if (playerInfo?.Skill != skillName) continue;
|
||||||
|
if (!SkillPlayerInfo.TryGetValue(player.Index, out var skillInfo)) continue;
|
||||||
|
|
||||||
|
UpdateHUD(player, skillInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo)
|
||||||
|
{
|
||||||
|
float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue<float>(skillName, "cooldown")) - DateTime.Now).TotalSeconds);
|
||||||
|
float cooldown = Math.Max(time, 0);
|
||||||
|
|
||||||
|
if (cooldown == 0 && !skillInfo.CanUse)
|
||||||
|
skillInfo.CanUse = true;
|
||||||
|
|
||||||
|
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||||
|
if (playerInfo == null) return;
|
||||||
|
|
||||||
|
playerInfo.PrintHTML = cooldown != 0
|
||||||
|
? $"{player.GetTranslation("hud_info", $"<font color='#FF0000'>{cooldown}</font>")}"
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
public static void UseSkill(CCSPlayerController player)
|
public static void UseSkill(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
var playerPawn = player.PlayerPawn.Value;
|
var playerPawn = player.PlayerPawn.Value;
|
||||||
|
|
@ -24,24 +88,40 @@ namespace src.player.skills
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
if (!player.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
|
if (!player.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
|
||||||
|
|
||||||
InstaReload(playerPawn);
|
if (!SkillPlayerInfo.TryGetValue(player.Index, out var skillInfo)) return;
|
||||||
|
if (!skillInfo.CanUse) return;
|
||||||
|
if (skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue<float>(skillName, "cooldown")) > DateTime.Now) return;
|
||||||
|
|
||||||
|
if (!InstaReload(playerPawn)) return;
|
||||||
|
|
||||||
|
skillInfo.CanUse = false;
|
||||||
|
skillInfo.Cooldown = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InstaReload(CCSPlayerPawn pawn)
|
private static bool InstaReload(CCSPlayerPawn pawn)
|
||||||
{
|
{
|
||||||
if (pawn == null || !pawn.IsValid) return;
|
if (pawn == null || !pawn.IsValid) return false;
|
||||||
var weaponServices = pawn.WeaponServices;
|
var weaponServices = pawn.WeaponServices;
|
||||||
if (weaponServices == null || weaponServices.ActiveWeapon == null || !weaponServices.ActiveWeapon.IsValid) return;
|
if (weaponServices == null || weaponServices.ActiveWeapon == null || !weaponServices.ActiveWeapon.IsValid) return false;
|
||||||
|
|
||||||
var activeWeapon = weaponServices.ActiveWeapon.Value;
|
var activeWeapon = weaponServices.ActiveWeapon.Value;
|
||||||
if (activeWeapon == null || !activeWeapon.IsValid || activeWeapon.VData == null) return;
|
if (activeWeapon == null || !activeWeapon.IsValid || activeWeapon.VData == null) return false;
|
||||||
|
|
||||||
activeWeapon.Clip1 = activeWeapon.VData.MaxClip1;
|
activeWeapon.Clip1 = activeWeapon.VData.MaxClip1;
|
||||||
Utilities.SetStateChanged(activeWeapon, "CBasePlayerWeapon", "m_iClip1");
|
Utilities.SetStateChanged(activeWeapon, "CBasePlayerWeapon", "m_iClip1");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffc061", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class PlayerSkillInfo
|
||||||
{
|
{
|
||||||
|
public ulong SteamID { get; set; }
|
||||||
|
public bool CanUse { get; set; }
|
||||||
|
public DateTime Cooldown { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffc061", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float cooldown = 5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
|
{
|
||||||
|
public float Cooldown { get; set; } = cooldown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.Flash;
|
private const Skills skillName = Skills.Flash;
|
||||||
public static readonly ConcurrentDictionary<uint, int> jumpedPlayers = [];
|
public static readonly ConcurrentDictionary<uint, int> jumpedPlayers = [];
|
||||||
|
private static readonly List<CCSPlayerController> holderBuffer = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -75,13 +76,14 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
PlayerManager.FillSkillHolders(skillName, holderBuffer);
|
||||||
|
if (holderBuffer.Count == 0) return;
|
||||||
|
|
||||||
|
foreach (var player in holderBuffer)
|
||||||
{
|
{
|
||||||
if (!Instance.IsPlayerValid(player)) continue;
|
if (!Instance.IsPlayerValid(player)) continue;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||||
if (playerInfo?.Skill != skillName) continue;
|
|
||||||
|
|
||||||
var playerPawn = player.PlayerPawn?.Value;
|
var playerPawn = player.PlayerPawn?.Value;
|
||||||
if (playerPawn == null || playerPawn.VelocityModifier == 0) continue;
|
if (playerPawn == null || playerPawn.VelocityModifier == 0) continue;
|
||||||
|
|
||||||
|
|
@ -93,6 +95,8 @@ namespace src.player.skills
|
||||||
if (jumpedPlayers.TryGetValue(player.Index, out var time) && time > Server.TickCount)
|
if (jumpedPlayers.TryGetValue(player.Index, out var time) && time > Server.TickCount)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (playerPawn.MoveType == MoveType_t.MOVETYPE_NOCLIP) continue;
|
||||||
|
|
||||||
if (!((PlayerFlags)player.Flags).HasFlag(PlayerFlags.FL_ONGROUND))
|
if (!((PlayerFlags)player.Flags).HasFlag(PlayerFlags.FL_ONGROUND))
|
||||||
playerPawn.AbsVelocity.Z = Math.Min(playerPawn.AbsVelocity.Z, 10);
|
playerPawn.AbsVelocity.Z = Math.Min(playerPawn.AbsVelocity.Z, 10);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (SkillPlayerInfo.IsEmpty) return;
|
||||||
|
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
foreach (var player in PlayerManager.GetTickPlayers())
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid)
|
if (player == null || !player.IsValid)
|
||||||
|
|
@ -288,10 +290,14 @@ namespace src.player.skills
|
||||||
if (needEnemySees && !enemySeesPlayer)
|
if (needEnemySees && !enemySeesPlayer)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
var targetOrigin = targetPawn.AbsOrigin;
|
||||||
|
if (targetOrigin == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
Vector targetEyePos = new(
|
Vector targetEyePos = new(
|
||||||
targetPawn.AbsOrigin.X,
|
targetOrigin.X,
|
||||||
targetPawn.AbsOrigin.Y,
|
targetOrigin.Y,
|
||||||
targetPawn.AbsOrigin.Z + targetPawn.ViewOffset.Z
|
targetOrigin.Z + targetPawn.ViewOffset.Z
|
||||||
);
|
);
|
||||||
|
|
||||||
Vector toTarget = new(
|
Vector toTarget = new(
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -141,24 +141,21 @@ namespace src.player.skills
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(param.Entity.Name)) return;
|
if (string.IsNullOrEmpty(damagedEntity.Entity.Name)) return;
|
||||||
if (!param.Entity.Name.StartsWith("FortniteWall")) return;
|
if (!damagedEntity.Entity.Name.StartsWith("FortniteWall")) return;
|
||||||
|
|
||||||
var box = param.As<CDynamicProp>();
|
var box = damagedEntity.As<CDynamicProp>();
|
||||||
if (box == null || !box.IsValid) return;
|
if (box == null || !box.IsValid) return;
|
||||||
box.EmitSound("Wood_Plank.BulletImpact", volume: SkillsInfo.GetValue<float>(skillName, "soundVolume"));
|
box.EmitSound("Wood_Plank.BulletImpact", volume: SkillsInfo.GetValue<float>(skillName, "soundVolume"));
|
||||||
|
|
||||||
if (barricades.TryGetValue(box.Index, out int health))
|
if (barricades.TryGetValue(box.Index, out int health))
|
||||||
{
|
{
|
||||||
int newHealth = health - (int)param2.Damage;
|
int newHealth = health - (int)damageInfo.Damage;
|
||||||
|
|
||||||
if (newHealth <= 0)
|
if (newHealth <= 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Cvars;
|
using CounterStrikeSharp.API.Modules.Cvars;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
|
|
@ -16,9 +16,6 @@ namespace src.player.skills
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
|
|
||||||
try { defaultAutoKick = ConVar.Find("mp_autokick")?.GetPrimitiveValue<bool>() ?? true; }
|
|
||||||
catch { defaultAutoKick = true; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
|
|
@ -26,22 +23,36 @@ namespace src.player.skills
|
||||||
if (!autoKickOverridden) return;
|
if (!autoKickOverridden) return;
|
||||||
|
|
||||||
autoKickOverridden = false;
|
autoKickOverridden = false;
|
||||||
Server.ExecuteCommand($"mp_autokick {(defaultAutoKick ? 1 : 0)}");
|
if (defaultAutoKick) Server.ExecuteCommand("mp_autokick 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
private static bool TrySuppressAutoKick()
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (autoKickOverridden) return true;
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
if (!SkillsInfo.GetValue<bool>(skillName, "manageAutoKick")) return false;
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
bool live;
|
||||||
|
try { live = ConVar.Find("mp_autokick")?.GetPrimitiveValue<bool>() ?? false; }
|
||||||
|
catch { return false; }
|
||||||
|
|
||||||
|
if (!live) return false;
|
||||||
|
|
||||||
|
defaultAutoKick = true;
|
||||||
|
autoKickOverridden = true;
|
||||||
|
Server.ExecuteCommand("mp_autokick 0");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
|
{
|
||||||
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (param2.AmmoType == 255)
|
if (damageInfo.AmmoType == 255)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle);
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
|
|
||||||
if (attackerPawn == null || !attackerPawn.IsValid || victimPawn == null || !victimPawn.IsValid)
|
if (attackerPawn == null || !attackerPawn.IsValid || victimPawn == null || !victimPawn.IsValid)
|
||||||
return;
|
return;
|
||||||
|
|
@ -67,14 +78,10 @@ namespace src.player.skills
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(attacker)?.Index ?? attacker.Index));
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(attacker)?.Index ?? attacker.Index));
|
||||||
if (playerInfo?.Skill != skillName || attacker!.Team != victim!.Team) return;
|
if (playerInfo?.Skill != skillName || attacker!.Team != victim!.Team) return;
|
||||||
|
|
||||||
float damage = param2.Damage;
|
float damage = damageInfo.Damage;
|
||||||
param2.Damage = 0;
|
damageInfo.Damage = 0;
|
||||||
|
|
||||||
if (!autoKickOverridden)
|
TrySuppressAutoKick();
|
||||||
{
|
|
||||||
autoKickOverridden = true;
|
|
||||||
Server.ExecuteCommand("mp_autokick 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
SkillUtils.AddHealth(
|
SkillUtils.AddHealth(
|
||||||
victimPawn,
|
victimPawn,
|
||||||
|
|
@ -83,9 +90,10 @@ namespace src.player.skills
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff0000", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = true, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float healthDamageMultiplier = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff0000", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = true, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float healthDamageMultiplier = .3f, bool manageAutoKick = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float HealthDamageMultiplier { get; set; } = healthDamageMultiplier;
|
public float HealthDamageMultiplier { get; set; } = healthDamageMultiplier;
|
||||||
|
public bool ManageAutoKick { get; set; } = manageAutoKick;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -46,12 +46,18 @@ namespace src.player.skills
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skill.Skill == skillName && !TakeMoney(player))
|
if (skill.Skill == skillName)
|
||||||
|
{
|
||||||
|
if (!TakeMoney(player))
|
||||||
{
|
{
|
||||||
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("gambler_no_money"));
|
playerEvent.PrintToChat($" {ChatColors.Red}" + playerEvent.GetTranslation("gambler_no_money"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EnableSkill(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Instance.AddTimer(.1f, () =>
|
Instance.AddTimer(.1f, () =>
|
||||||
{
|
{
|
||||||
playerInfo.Skill = skill.Skill;
|
playerInfo.Skill = skill.Skill;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Core.Attributes;
|
using CounterStrikeSharp.API.Core.Attributes;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -114,7 +114,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index);
|
var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index, trackAs: "empty_prop");
|
||||||
if (emptyProp == null || !emptyProp.IsValid) return;
|
if (emptyProp == null || !emptyProp.IsValid) return;
|
||||||
|
|
||||||
var playerPawn = player.PlayerPawn?.Value;
|
var playerPawn = player.PlayerPawn?.Value;
|
||||||
|
|
@ -132,7 +132,6 @@ namespace src.player.skills
|
||||||
|
|
||||||
Utilities.SetStateChanged(emptyProp, "CBaseEntity", "m_CBodyComponent");
|
Utilities.SetStateChanged(emptyProp, "CBaseEntity", "m_CBodyComponent");
|
||||||
|
|
||||||
EntityManager.RegisterExisting(emptyProp, player.Index, "empty_prop");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DisableSkill(CCSPlayerController player)
|
public static void DisableSkill(CCSPlayerController player)
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,9 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.GravityDecoy;
|
private const Skills skillName = Skills.GravityDecoy;
|
||||||
|
|
||||||
private const float defaultGravity = 1f;
|
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<uint, int> playersWithSkill = [];
|
private static readonly ConcurrentDictionary<uint, int> playersWithSkill = [];
|
||||||
private static readonly ConcurrentDictionary<uint, byte> affected = [];
|
private static readonly ConcurrentDictionary<uint, float> affected = [];
|
||||||
private static readonly ConcurrentDictionary<uint, byte> restoreOnRespawn = [];
|
private static readonly ConcurrentDictionary<uint, float> restoreOnRespawn = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -78,8 +76,8 @@ namespace src.player.skills
|
||||||
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) continue;
|
if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) continue;
|
||||||
if (pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) continue;
|
if (pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) continue;
|
||||||
|
|
||||||
if (restoreOnRespawn.TryRemove(eventPlayer.Index, out _))
|
if (restoreOnRespawn.TryRemove(eventPlayer.Index, out float respawnGravity))
|
||||||
pawn.ActualGravityScale = defaultGravity;
|
pawn.ActualGravityScale = respawnGravity;
|
||||||
|
|
||||||
bool inside = false;
|
bool inside = false;
|
||||||
foreach (var decoyPos in decoyPositions)
|
foreach (var decoyPos in decoyPositions)
|
||||||
|
|
@ -87,11 +85,11 @@ namespace src.player.skills
|
||||||
|
|
||||||
if (inside)
|
if (inside)
|
||||||
{
|
{
|
||||||
if (!affected.TryAdd(eventPlayer.Index, 0)) continue;
|
if (!affected.TryAdd(eventPlayer.Index, pawn.ActualGravityScale)) continue;
|
||||||
pawn.ActualGravityScale = gravity;
|
pawn.ActualGravityScale = gravity;
|
||||||
}
|
}
|
||||||
else if (affected.TryRemove(eventPlayer.Index, out _))
|
else if (affected.TryRemove(eventPlayer.Index, out float ownGravity))
|
||||||
pawn.ActualGravityScale = defaultGravity;
|
pawn.ActualGravityScale = ownGravity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,10 +98,10 @@ namespace src.player.skills
|
||||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||||
if (victim == null || !victim.IsValid) return;
|
if (victim == null || !victim.IsValid) return;
|
||||||
|
|
||||||
if (!affected.TryRemove(victim.Index, out _)) return;
|
if (!affected.TryRemove(victim.Index, out float ownGravity)) return;
|
||||||
|
|
||||||
Restore(victim.Index);
|
Restore(victim.Index, ownGravity);
|
||||||
restoreOnRespawn[victim.Index] = 0;
|
restoreOnRespawn[victim.Index] = ownGravity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PlayerDisconnect(uint playerIndex)
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
|
@ -115,23 +113,23 @@ namespace src.player.skills
|
||||||
|
|
||||||
private static void RestoreAll()
|
private static void RestoreAll()
|
||||||
{
|
{
|
||||||
foreach (uint playerIndex in affected.Keys)
|
foreach (var entry in affected)
|
||||||
Restore(playerIndex);
|
Restore(entry.Key, entry.Value);
|
||||||
|
|
||||||
foreach (uint playerIndex in restoreOnRespawn.Keys)
|
foreach (var entry in restoreOnRespawn)
|
||||||
Restore(playerIndex);
|
Restore(entry.Key, entry.Value);
|
||||||
|
|
||||||
affected.Clear();
|
affected.Clear();
|
||||||
restoreOnRespawn.Clear();
|
restoreOnRespawn.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Restore(uint playerIndex)
|
private static void Restore(uint playerIndex, float gravity)
|
||||||
{
|
{
|
||||||
var player = Utilities.GetPlayerFromIndex((int)playerIndex);
|
var player = Utilities.GetPlayerFromIndex((int)playerIndex);
|
||||||
var pawn = player?.PlayerPawn?.Value;
|
var pawn = player?.PlayerPawn?.Value;
|
||||||
|
|
||||||
if (pawn != null && pawn.IsValid)
|
if (pawn != null && pawn.IsValid)
|
||||||
pawn.ActualGravityScale = defaultGravity;
|
pawn.ActualGravityScale = gravity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void GrenadeThrown(EventGrenadeThrown @event)
|
public static void GrenadeThrown(EventGrenadeThrown @event)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -35,21 +35,18 @@ namespace src.player.skills
|
||||||
chickenOwners.Clear();
|
chickenOwners.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damageInfo == null) return;
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param2 == null) return;
|
if (!chickenOwners.TryGetValue(damagedEntity.Index, out var ownerData)) return;
|
||||||
|
|
||||||
if (!chickenOwners.TryGetValue(param.Index, out var ownerData)) return;
|
|
||||||
uint ownerIndex = ownerData.ownerIndex;
|
uint ownerIndex = ownerData.ownerIndex;
|
||||||
int health = ownerData.health;
|
int health = ownerData.health;
|
||||||
|
|
||||||
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
|
var owner = Utilities.GetPlayerFromIndex((int)ownerIndex);
|
||||||
if (owner == null || !owner.IsValid) return;
|
if (owner == null || !owner.IsValid) return;
|
||||||
|
|
||||||
var attackerEnt = param2.Attacker?.Value;
|
var attackerEnt = damageInfo.Attacker?.Value;
|
||||||
if (attackerEnt == null || !attackerEnt.IsValid) return;
|
if (attackerEnt == null || !attackerEnt.IsValid) return;
|
||||||
|
|
||||||
var attackerPawn = new CCSPlayerPawn(attackerEnt.Handle);
|
var attackerPawn = new CCSPlayerPawn(attackerEnt.Handle);
|
||||||
|
|
@ -60,15 +57,15 @@ namespace src.player.skills
|
||||||
|
|
||||||
if (attackerPawn.Index != ownerPawn.Index && attackerPawn.TeamNum == owner.TeamNum)
|
if (attackerPawn.Index != ownerPawn.Index && attackerPawn.TeamNum == owner.TeamNum)
|
||||||
{
|
{
|
||||||
param2.Damage = 0;
|
damageInfo.Damage = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int newHealth = Math.Max(0, health - (int)param2.Damage);
|
int newHealth = Math.Max(0, health - (int)damageInfo.Damage);
|
||||||
chickenOwners[param.Index] = (ownerIndex, newHealth);
|
chickenOwners[damagedEntity.Index] = (ownerIndex, newHealth);
|
||||||
|
|
||||||
if (newHealth > 0)
|
if (newHealth > 0)
|
||||||
CreateHitParticles(param.Index);
|
CreateHitParticles(damagedEntity.Index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CreateHitParticles(uint chickenIndex)
|
private static void CreateHitParticles(uint chickenIndex)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,14 @@ namespace src.player.skills
|
||||||
public class HealingSmoke : ISkill
|
public class HealingSmoke : ISkill
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.HealingSmoke;
|
private const Skills skillName = Skills.HealingSmoke;
|
||||||
private static readonly ConcurrentDictionary<Vector, byte> smokes = [];
|
private static readonly ConcurrentDictionary<Vector, uint> smokes = [];
|
||||||
|
|
||||||
|
private static void RemoveOwnerSmokes(uint ownerIndex)
|
||||||
|
{
|
||||||
|
foreach (var smoke in smokes)
|
||||||
|
if (smoke.Value == ownerIndex)
|
||||||
|
smokes.TryRemove(smoke.Key, out _);
|
||||||
|
}
|
||||||
private readonly static ConcurrentDictionary<uint, int> playersWithSkill = [];
|
private readonly static ConcurrentDictionary<uint, int> playersWithSkill = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
|
|
@ -31,7 +38,7 @@ namespace src.player.skills
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
|
|
||||||
smokes.TryAdd(new Vector(@event.X, @event.Y, @event.Z), 0);
|
smokes.TryAdd(new Vector(@event.X, @event.Y, @event.Z), player.Index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SmokegrenadeExpired(EventSmokegrenadeExpired @event)
|
public static void SmokegrenadeExpired(EventSmokegrenadeExpired @event)
|
||||||
|
|
@ -39,9 +46,6 @@ namespace src.player.skills
|
||||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
|
||||||
if (playerInfo?.Skill != skillName) return;
|
|
||||||
|
|
||||||
foreach (var smoke in smokes.Keys.Where(v => v.X == @event.X && v.Y == @event.Y && v.Z == @event.Z))
|
foreach (var smoke in smokes.Keys.Where(v => v.X == @event.X && v.Y == @event.Y && v.Z == @event.Z))
|
||||||
smokes.TryRemove(smoke, out _);
|
smokes.TryRemove(smoke, out _);
|
||||||
}
|
}
|
||||||
|
|
@ -153,6 +157,7 @@ namespace src.player.skills
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
playersWithSkill.TryRemove(player.Index, out _);
|
playersWithSkill.TryRemove(player.Index, out _);
|
||||||
|
RemoveOwnerSmokes(player.Index);
|
||||||
SkillUtils.UpdateGrenadeCount(player, CsItem.SmokeGrenade, 1);
|
SkillUtils.UpdateGrenadeCount(player, CsItem.SmokeGrenade, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,10 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (players.IsEmpty || jRandomSkills.Instance.GameRules?.FreezePeriod == true) return;
|
||||||
|
|
||||||
int cooldown = Math.Max(1, (int)(SkillsInfo.GetValue<float>(skillName, "cooldown") * 64));
|
int cooldown = Math.Max(1, (int)(SkillsInfo.GetValue<float>(skillName, "cooldown") * 64));
|
||||||
if (Server.TickCount % cooldown != 0) return;
|
if (Server.TickCount % cooldown != 0) return;
|
||||||
|
|
||||||
if (players.IsEmpty || jRandomSkills.Instance.GameRules?.FreezePeriod == true) return;
|
|
||||||
int damage = SkillsInfo.GetValue<int>(skillName, "damage");
|
int damage = SkillsInfo.GetValue<int>(skillName, "damage");
|
||||||
|
|
||||||
var owner = GetSkillOwner();
|
var owner = GetSkillOwner();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Timers;
|
using CounterStrikeSharp.API.Modules.Timers;
|
||||||
|
|
@ -65,7 +65,6 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
UpdateWeapons(player, playerSkill);
|
UpdateWeapons(player, playerSkill);
|
||||||
CreateClone(playerSkill);
|
CreateClone(playerSkill);
|
||||||
SkillUtils.SetPlayerCollisions(player, false);
|
|
||||||
}
|
}
|
||||||
else if (playerSkill.CloneProp != null)
|
else if (playerSkill.CloneProp != null)
|
||||||
KillClone(playerSkill);
|
KillClone(playerSkill);
|
||||||
|
|
@ -138,8 +137,6 @@ namespace src.player.skills
|
||||||
if (eventTarget != null && eventTarget.IsValid)
|
if (eventTarget != null && eventTarget.IsValid)
|
||||||
SkillUtils.ApplyScreenColor(eventTarget, 0, 0, 0, 0, 10, 0, 2);
|
SkillUtils.ApplyScreenColor(eventTarget, 0, 0, 0, 0, 10, 0, 2);
|
||||||
|
|
||||||
SkillUtils.SetPlayerCollisions(player, true);
|
|
||||||
|
|
||||||
BlockWeapon(player, false);
|
BlockWeapon(player, false);
|
||||||
playerSkill.NextUse = Server.TickCount + SkillsInfo.GetValue<float>(skillName, "Cooldown") * 64;
|
playerSkill.NextUse = Server.TickCount + SkillsInfo.GetValue<float>(skillName, "Cooldown") * 64;
|
||||||
playerSkill.UseTime = 0;
|
playerSkill.UseTime = 0;
|
||||||
|
|
@ -305,25 +302,22 @@ namespace src.player.skills
|
||||||
return shooter != null && shooter.IsValid ? shooter : null;
|
return shooter != null && shooter.IsValid ? shooter : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(param.Entity.Name)) return;
|
if (string.IsNullOrEmpty(damagedEntity.Entity.Name)) return;
|
||||||
if (!param.Entity.Name.StartsWith("IanaClone_")) return;
|
if (!damagedEntity.Entity.Name.StartsWith("IanaClone_")) return;
|
||||||
|
|
||||||
var nameParams = param.Entity.Name.Split('_')[2];
|
var nameParams = damagedEntity.Entity.Name.Split('_')[2];
|
||||||
_ = uint.TryParse(nameParams, out uint userIndex);
|
_ = uint.TryParse(nameParams, out uint userIndex);
|
||||||
if (userIndex == 0) return;
|
if (userIndex == 0) return;
|
||||||
|
|
||||||
var player = Utilities.GetPlayerFromIndex((int)userIndex);
|
var player = Utilities.GetPlayerFromIndex((int)userIndex);
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
float dealDamage = param2.Damage;
|
float dealDamage = damageInfo.Damage;
|
||||||
|
|
||||||
if (playersInfo.TryGetValue(player.Index, out var playerSkill))
|
if (playersInfo.TryGetValue(player.Index, out var playerSkill))
|
||||||
{
|
{
|
||||||
|
|
@ -333,7 +327,7 @@ namespace src.player.skills
|
||||||
|
|
||||||
if (cloneProp != null && cloneProp.IsValid && cloneProp.AbsOrigin != null)
|
if (cloneProp != null && cloneProp.IsValid && cloneProp.AbsOrigin != null)
|
||||||
{
|
{
|
||||||
Vector pos = param2.DamagePosition;
|
Vector pos = damageInfo.DamagePosition;
|
||||||
Vector posCl = cloneProp.AbsOrigin;
|
Vector posCl = cloneProp.AbsOrigin;
|
||||||
|
|
||||||
bool isHead = false;
|
bool isHead = false;
|
||||||
|
|
@ -343,8 +337,8 @@ namespace src.player.skills
|
||||||
if (pos.Z >= posCl.Z + viewOffset - diff)
|
if (pos.Z >= posCl.Z + viewOffset - diff)
|
||||||
isHead = true;
|
isHead = true;
|
||||||
|
|
||||||
float damage = param2.Damage;
|
float damage = damageInfo.Damage;
|
||||||
string? weapon = param2.Ability?.Value?.DesignerName;
|
string? weapon = damageInfo.Ability?.Value?.DesignerName;
|
||||||
if (weapon != null)
|
if (weapon != null)
|
||||||
dealDamage = CalculateDamage(player, weapon, damage, isHead);
|
dealDamage = CalculateDamage(player, weapon, damage, isHead);
|
||||||
}
|
}
|
||||||
|
|
@ -357,8 +351,8 @@ namespace src.player.skills
|
||||||
var playerPawn = player.PlayerPawn.Value;
|
var playerPawn = player.PlayerPawn.Value;
|
||||||
if (playerPawn != null)
|
if (playerPawn != null)
|
||||||
{
|
{
|
||||||
KillfeedIcons? killfeed = KillfeedIconsExtensions.FromWeaponName(param2.Ability?.Value?.DesignerName);
|
KillfeedIcons? killfeed = KillfeedIconsExtensions.FromWeaponName(damageInfo.Ability?.Value?.DesignerName);
|
||||||
SkillUtils.TakeHealth(playerPawn, (int)dealDamage, GetShooter(param2), killfeed ?? KillfeedIcons.Leg);
|
SkillUtils.TakeHealth(playerPawn, (int)dealDamage, GetShooter(damageInfo), killfeed ?? KillfeedIcons.Leg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Timers;
|
using CounterStrikeSharp.API.Modules.Timers;
|
||||||
|
|
@ -183,18 +183,15 @@ namespace src.player.skills
|
||||||
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(param.Entity.Name)) return;
|
if (string.IsNullOrEmpty(damagedEntity.Entity.Name)) return;
|
||||||
if (!param.Entity.Name.StartsWith("Illusionist_")) return;
|
if (!damagedEntity.Entity.Name.StartsWith("Illusionist_")) return;
|
||||||
|
|
||||||
var replica = param.As<CDynamicProp>();
|
var replica = damagedEntity.As<CDynamicProp>();
|
||||||
if (replica == null || !replica.IsValid) return;
|
if (replica == null || !replica.IsValid) return;
|
||||||
|
|
||||||
if (!consumedReplicas.TryAdd(replica.Index, 0)) return;
|
if (!consumedReplicas.TryAdd(replica.Index, 0)) return;
|
||||||
|
|
@ -205,7 +202,7 @@ namespace src.player.skills
|
||||||
var owner = GetReplicaOwner(replica.Index);
|
var owner = GetReplicaOwner(replica.Index);
|
||||||
EntityManager.DestroyEntity(replica.Index);
|
EntityManager.DestroyEntity(replica.Index);
|
||||||
|
|
||||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle);
|
||||||
if (attackerPawn.DesignerName != "player") return;
|
if (attackerPawn.DesignerName != "player") return;
|
||||||
|
|
||||||
var attackerTeam = attackerPawn.TeamNum;
|
var attackerTeam = attackerPawn.TeamNum;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
using src.utils;
|
using src.utils;
|
||||||
|
|
@ -58,7 +58,7 @@ namespace src.player.skills
|
||||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
||||||
if (!SkillUtils.HasMenu(player)) continue;
|
if (!SkillUtils.HasMenu(player)) continue;
|
||||||
|
|
||||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).OfType<CCSPlayerController>().ToArray();
|
||||||
|
|
||||||
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString())));
|
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString())));
|
||||||
SkillUtils.UpdateMenu(player, menuItems);
|
SkillUtils.UpdateMenu(player, menuItems);
|
||||||
|
|
@ -117,7 +117,7 @@ namespace src.player.skills
|
||||||
var playerEvent = PlayerManager.GetPlayerFromEvent(player);
|
var playerEvent = PlayerManager.GetPlayerFromEvent(player);
|
||||||
if (playerEvent == null || !playerEvent.IsValid) return;
|
if (playerEvent == null || !playerEvent.IsValid) return;
|
||||||
|
|
||||||
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray();
|
var enemies = PlayerManager.GetTickPlayers().Where(p => p != null && p.IsValid).Select(p => PlayerManager.GetPlayerEvent(p)).Where(p => p != null && p.IsValid && p.Team != player.Team && p.PlayerPawn?.Value != null && p.PlayerPawn.Value.IsValid && p.PlayerPawn.Value.Health > 0 && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).OfType<CCSPlayerController>().ToArray();
|
||||||
|
|
||||||
if (enemies.Length > 0)
|
if (enemies.Length > 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (bannedPlayers.IsEmpty) return;
|
||||||
|
|
||||||
foreach (var item in bannedPlayers)
|
foreach (var item in bannedPlayers)
|
||||||
{
|
{
|
||||||
var playerIndex = item.Key;
|
var playerIndex = item.Key;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ namespace src.player.skills
|
||||||
|
|
||||||
var enemies = SkillUtils.GetSelectableEnemies(player, true);
|
var enemies = SkillUtils.GetSelectableEnemies(player, true);
|
||||||
|
|
||||||
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"\u202A{e.PlayerName}\u202C : {e?.PlayerPawn?.Value?.Health ?? 0} HP", e.Index.ToString())));
|
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"\u202A{e.PlayerName}\u202C : {e.PlayerPawn?.Value?.Health ?? 0} HP", e.Index.ToString())));
|
||||||
SkillUtils.UpdateMenu(player, menuItems);
|
SkillUtils.UpdateMenu(player, menuItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,15 +125,13 @@ namespace src.player.skills
|
||||||
enemyEvent.PrintToChat($" {ChatColors.Red}{enemyEvent.GetTranslation("nemesis_enemy_info")}");
|
enemyEvent.PrintToChat($" {ChatColors.Red}{enemyEvent.GetTranslation("nemesis_enemy_info")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
if (markedPlayers.IsEmpty) return;
|
if (markedPlayers.IsEmpty) return;
|
||||||
|
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return;
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
if (param == null || !param.IsValid || param2 == null) return;
|
|
||||||
|
|
||||||
var victimPawn = param.As<CCSPlayerPawn>();
|
var victimPawn = damagedEntity.As<CCSPlayerPawn>();
|
||||||
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
||||||
|
|
||||||
var victimController = victimPawn.Controller.Value;
|
var victimController = victimPawn.Controller.Value;
|
||||||
|
|
@ -145,12 +143,15 @@ namespace src.player.skills
|
||||||
uint victimIndex = PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index;
|
uint victimIndex = PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index;
|
||||||
if (!markedPlayers.ContainsKey(victimIndex)) return;
|
if (!markedPlayers.ContainsKey(victimIndex)) return;
|
||||||
|
|
||||||
param2.Damage *= 1f + SkillsInfo.GetValue<float>(skillName, "extraDamagePercent");
|
if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return;
|
||||||
|
|
||||||
|
damageInfo.Damage *= 1f + SkillsInfo.GetValue<float>(skillName, "extraDamagePercent");
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#b8003a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Rare, float extraDamagePercent = .25f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#b8003a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = 2, Rarity rarity = Rarity.Rare, float extraDamagePercent = .25f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float ExtraDamagePercent { get; set; } = extraDamagePercent;
|
public float ExtraDamagePercent { get; set; } = extraDamagePercent;
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Core.Attributes;
|
using CounterStrikeSharp.API.Core.Attributes;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -143,7 +143,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index);
|
var emptyProp = EntityManager.CreateTrackedDynamicProp(player.Index, trackAs: "empty_prop");
|
||||||
if (emptyProp == null || !emptyProp.IsValid) return;
|
if (emptyProp == null || !emptyProp.IsValid) return;
|
||||||
|
|
||||||
var playerPawn = player.PlayerPawn?.Value;
|
var playerPawn = player.PlayerPawn?.Value;
|
||||||
|
|
@ -161,7 +161,6 @@ namespace src.player.skills
|
||||||
|
|
||||||
Utilities.SetStateChanged(emptyProp, "CBaseEntity", "m_CBodyComponent");
|
Utilities.SetStateChanged(emptyProp, "CBaseEntity", "m_CBodyComponent");
|
||||||
|
|
||||||
EntityManager.RegisterExisting(emptyProp, player.Index, "empty_prop");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateNinja(CCSPlayerController? player)
|
private static void UpdateNinja(CCSPlayerController? player)
|
||||||
|
|
|
||||||
|
|
@ -15,20 +15,19 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle);
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
|
|
||||||
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return;
|
||||||
|
|
||||||
if (attackerPawn == null || attackerPawn.Controller?.Value == null || victimPawn == null || victimPawn.Controller?.Value == null)
|
if (attackerPawn == null || attackerPawn.Controller?.Value == null || victimPawn == null || victimPawn.Controller?.Value == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -39,11 +38,12 @@ namespace src.player.skills
|
||||||
if (playerInfo == null) return;
|
if (playerInfo == null) return;
|
||||||
|
|
||||||
if (playerInfo.Skill == skillName)
|
if (playerInfo.Skill == skillName)
|
||||||
param2.Damage = 1000f;
|
damageInfo.Damage = 1000f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5CD9", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5CD9", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -20,10 +20,12 @@ namespace src.player.skills
|
||||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||||
|
|
||||||
if (!Instance.IsPlayerValid(attacker) || !Instance.IsPlayerValid(victim)) return false;
|
if (!Instance.IsPlayerValid(victim)) return false;
|
||||||
if ((HitGroup_t)@event.Hitgroup == HitGroup_t.HITGROUP_HEAD) return false;
|
|
||||||
if (PlayerManager.GetPlayerByIndex(victim!.Index)?.Skill != skillName) return false;
|
if (PlayerManager.GetPlayerByIndex(victim!.Index)?.Skill != skillName) return false;
|
||||||
|
|
||||||
|
bool worldDamage = !Instance.IsPlayerValid(attacker) || attacker!.Index == victim.Index;
|
||||||
|
if (!worldDamage && (HitGroup_t)@event.Hitgroup == HitGroup_t.HITGROUP_HEAD) return false;
|
||||||
|
|
||||||
SkillUtils.RestoreHealth(victim);
|
SkillUtils.RestoreHealth(victim);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.PawelJumper;
|
private const Skills skillName = Skills.PawelJumper;
|
||||||
private const string jumpSound = "Default.WalkJump";
|
private const string jumpSound = "Default.WalkJump";
|
||||||
|
private static readonly List<CCSPlayerController> holderBuffer = [];
|
||||||
private static readonly int?[] J = new int?[64];
|
private static readonly int?[] J = new int?[64];
|
||||||
private static readonly PlayerButtons[] LB = new PlayerButtons[64];
|
private static readonly PlayerButtons[] LB = new PlayerButtons[64];
|
||||||
|
|
||||||
|
|
@ -20,13 +21,14 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
PlayerManager.FillSkillHolders(skillName, holderBuffer);
|
||||||
{
|
if (holderBuffer.Count == 0) return;
|
||||||
if (player == null || !player.IsValid) return;
|
|
||||||
var playerEvent = PlayerManager.GetPlayerEvent(player);
|
foreach (var playerEvent in holderBuffer)
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(playerEvent!.Index);
|
{
|
||||||
|
var player = PlayerManager.GetPlayerFromEvent(playerEvent) ?? playerEvent;
|
||||||
|
if (player == null || !player.IsValid) continue;
|
||||||
|
|
||||||
if (playerInfo?.Skill == skillName)
|
|
||||||
GiveAdditionalJump(player, playerEvent);
|
GiveAdditionalJump(player, playerEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -35,13 +35,11 @@ namespace src.player.skills
|
||||||
phoenixTicks.TryRemove(playerIndex, out _);
|
phoenixTicks.TryRemove(playerIndex, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
var victimEntity = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return;
|
||||||
var info = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
if (victimEntity == null || !victimEntity.IsValid || info == null) return;
|
|
||||||
|
|
||||||
var victimPawn = victimEntity.As<CCSPlayerPawn>();
|
var victimPawn = damagedEntity.As<CCSPlayerPawn>();
|
||||||
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
||||||
|
|
||||||
var victimController = victimPawn.Controller.Value;
|
var victimController = victimPawn.Controller.Value;
|
||||||
|
|
@ -53,16 +51,16 @@ namespace src.player.skills
|
||||||
var victimInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index);
|
var victimInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index);
|
||||||
if (victimInfo == null || victimInfo.Skill != skillName) return;
|
if (victimInfo == null || victimInfo.Skill != skillName) return;
|
||||||
|
|
||||||
if (SkillUtils.IsFriendlyFireBlocked(info, victimPawn)) return;
|
if (SkillUtils.IsFriendlyFireBlocked(damageInfo, victimPawn)) return;
|
||||||
|
|
||||||
float effectiveDamage = info.Damage;
|
float effectiveDamage = damageInfo.Damage;
|
||||||
if (SkillUtils.GetHitGroup(info) == HitGroup_t.HITGROUP_HEAD)
|
if (SkillUtils.GetHitGroup(damageInfo) == HitGroup_t.HITGROUP_HEAD)
|
||||||
effectiveDamage *= GetHeadshotMultiplier(info);
|
effectiveDamage *= GetHeadshotMultiplier(damageInfo);
|
||||||
|
|
||||||
if (effectiveDamage < victimPawn.Health) return;
|
if (effectiveDamage < victimPawn.Health) return;
|
||||||
|
|
||||||
if (TryConsumeRevive(victim, victimPawn))
|
if (TryConsumeRevive(victim, victimPawn))
|
||||||
info.Damage = 0;
|
damageInfo.Damage = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryConsumeRevive(CCSPlayerController? victim, CCSPlayerPawn? victimPawn)
|
public static bool TryConsumeRevive(CCSPlayerController? victim, CCSPlayerPawn? victimPawn)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.Pilot;
|
private const Skills skillName = Skills.Pilot;
|
||||||
private static readonly ConcurrentDictionary<uint, Pilot_PlayerInfo> PlayerPilotInfo = [];
|
private static readonly ConcurrentDictionary<uint, Pilot_PlayerInfo> PlayerPilotInfo = [];
|
||||||
|
private static readonly List<CCSPlayerController> holderBuffer = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -40,13 +41,12 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
PlayerManager.FillSkillHolders(skillName, holderBuffer);
|
||||||
{
|
if (holderBuffer.Count == 0) return;
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
|
||||||
if (playerInfo?.Skill == skillName)
|
foreach (var player in holderBuffer)
|
||||||
HandlePilot(player);
|
HandlePilot(player);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static void EnableSkill(CCSPlayerController player)
|
public static void EnableSkill(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ namespace src.player.skills
|
||||||
private static readonly ConcurrentDictionary<uint, uint> playersToTarget = [];
|
private static readonly ConcurrentDictionary<uint, uint> playersToTarget = [];
|
||||||
private static readonly ConcurrentDictionary<uint, uint> targetToPlayer = [];
|
private static readonly ConcurrentDictionary<uint, uint> targetToPlayer = [];
|
||||||
private static readonly object setLock = new();
|
private static readonly object setLock = new();
|
||||||
|
private static readonly List<CCSPlayerController> holderBuffer = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -49,9 +50,9 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
int cooldown = Math.Max(1, (int)(64 * SkillsInfo.GetValue<float>(skillName, "Cooldown")));
|
int cooldown = poisonedPlayers.IsEmpty ? 0 : Math.Max(1, (int)(64 * SkillsInfo.GetValue<float>(skillName, "Cooldown")));
|
||||||
|
|
||||||
if (Server.TickCount % cooldown == 0)
|
if (cooldown != 0 && Server.TickCount % cooldown == 0)
|
||||||
{
|
{
|
||||||
int cooldown2 = cooldown * 2;
|
int cooldown2 = cooldown * 2;
|
||||||
|
|
||||||
|
|
@ -75,11 +76,12 @@ namespace src.player.skills
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Server.TickCount % 32 != 0) return;
|
if (Server.TickCount % 32 != 0) return;
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
|
||||||
{
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
|
||||||
|
|
||||||
if (playerInfo == null || playerInfo.Skill != skillName) continue;
|
PlayerManager.FillSkillHolders(skillName, holderBuffer);
|
||||||
|
if (holderBuffer.Count == 0) return;
|
||||||
|
|
||||||
|
foreach (var player in holderBuffer)
|
||||||
|
{
|
||||||
if (!SkillUtils.HasMenu(player)) continue;
|
if (!SkillUtils.HasMenu(player)) continue;
|
||||||
|
|
||||||
var enemies = SkillUtils.GetSelectableEnemies(player, true);
|
var enemies = SkillUtils.GetSelectableEnemies(player, true);
|
||||||
|
|
|
||||||
|
|
@ -52,10 +52,19 @@ namespace src.player.skills
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void BombPlanted(EventBombPlanted _)
|
public static void BombPlanted(EventBombPlanted _)
|
||||||
|
{
|
||||||
|
ResolveBombLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ResolveBombLocation()
|
||||||
{
|
{
|
||||||
var plantedBomb = Utilities.FindAllEntitiesByDesignerName<CPlantedC4>("planted_c4").FirstOrDefault();
|
var plantedBomb = Utilities.FindAllEntitiesByDesignerName<CPlantedC4>("planted_c4").FirstOrDefault();
|
||||||
if (plantedBomb != null)
|
if (plantedBomb == null || !plantedBomb.IsValid || !plantedBomb.BombTicking || plantedBomb.BombDefused) return;
|
||||||
bombLocation = plantedBomb.AbsOrigin;
|
|
||||||
|
var origin = plantedBomb.AbsOrigin;
|
||||||
|
if (origin == null) return;
|
||||||
|
|
||||||
|
bombLocation = new Vector(origin.X, origin.Y, origin.Z);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
|
|
@ -92,7 +101,7 @@ namespace src.player.skills
|
||||||
if (plantedBomb != null && plantedBomb.IsValid && plantedBomb.BombTicking && !plantedBomb.BombDefused)
|
if (plantedBomb != null && plantedBomb.IsValid && plantedBomb.BombTicking && !plantedBomb.BombDefused)
|
||||||
{
|
{
|
||||||
plantedBomb.AddEntityIOEvent("Kill", plantedBomb, delay: 0.1f);
|
plantedBomb.AddEntityIOEvent("Kill", plantedBomb, delay: 0.1f);
|
||||||
SkillUtils.TerminateRound(CsTeam.CounterTerrorist);
|
SkillUtils.TerminateRound(CsTeam.CounterTerrorist, player);
|
||||||
}
|
}
|
||||||
SkillUtils.ResetPrintHTML(player);
|
SkillUtils.ResetPrintHTML(player);
|
||||||
SkillPlayerInfo.Clear();
|
SkillPlayerInfo.Clear();
|
||||||
|
|
@ -105,8 +114,7 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void EnableSkill(CCSPlayerController player)
|
public static void EnableSkill(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
var pawn = player.PlayerPawn.Value;
|
if (player == null || !player.IsValid) return;
|
||||||
if (pawn == null || !pawn.IsValid) return;
|
|
||||||
|
|
||||||
SkillPlayerInfo.TryAdd(player.Index, new PlayerSkillInfo
|
SkillPlayerInfo.TryAdd(player.Index, new PlayerSkillInfo
|
||||||
{
|
{
|
||||||
|
|
@ -114,6 +122,9 @@ namespace src.player.skills
|
||||||
Defusing = false,
|
Defusing = false,
|
||||||
DefusingTime = SkillsInfo.GetValue<float>(skillName, "defusingTime"),
|
DefusingTime = SkillsInfo.GetValue<float>(skillName, "defusingTime"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!roundEnded && bombLocation == null)
|
||||||
|
ResolveBombLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DisableSkill(CCSPlayerController player)
|
public static void DisableSkill(CCSPlayerController player)
|
||||||
|
|
|
||||||
|
|
@ -50,15 +50,13 @@ namespace src.player.skills
|
||||||
storedDamage[victim.Index] = 0f;
|
storedDamage[victim.Index] = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
if (storedDamage.IsEmpty) return;
|
if (storedDamage.IsEmpty) return;
|
||||||
|
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return;
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
if (param == null || !param.IsValid || param2 == null) return;
|
|
||||||
|
|
||||||
var victimPawn = param.As<CCSPlayerPawn>();
|
var victimPawn = damagedEntity.As<CCSPlayerPawn>();
|
||||||
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
||||||
|
|
||||||
var victimController = victimPawn.Controller.Value;
|
var victimController = victimPawn.Controller.Value;
|
||||||
|
|
@ -67,9 +65,11 @@ namespace src.player.skills
|
||||||
var victim = victimController.As<CCSPlayerController>();
|
var victim = victimController.As<CCSPlayerController>();
|
||||||
if (victim == null || !victim.IsValid) return;
|
if (victim == null || !victim.IsValid) return;
|
||||||
|
|
||||||
|
if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return;
|
||||||
|
|
||||||
uint victimIndex = PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index;
|
uint victimIndex = PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index;
|
||||||
|
|
||||||
var attacker = param2.Attacker?.Value?.As<CCSPlayerPawn>();
|
var attacker = damageInfo.Attacker?.Value?.As<CCSPlayerPawn>();
|
||||||
var attackerController = attacker?.Controller?.Value?.As<CCSPlayerController>();
|
var attackerController = attacker?.Controller?.Value?.As<CCSPlayerController>();
|
||||||
uint? attackerIndex = attackerController != null && attackerController.IsValid
|
uint? attackerIndex = attackerController != null && attackerController.IsValid
|
||||||
? PlayerManager.GetPlayerEvent(attackerController)?.Index ?? attackerController.Index
|
? PlayerManager.GetPlayerEvent(attackerController)?.Index ?? attackerController.Index
|
||||||
|
|
@ -78,7 +78,7 @@ namespace src.player.skills
|
||||||
if (attackerIndex.HasValue && attackerIndex.Value != victimIndex && storedDamage.TryGetValue(attackerIndex.Value, out float banked) && banked > 0f)
|
if (attackerIndex.HasValue && attackerIndex.Value != victimIndex && storedDamage.TryGetValue(attackerIndex.Value, out float banked) && banked > 0f)
|
||||||
{
|
{
|
||||||
storedDamage[attackerIndex.Value] = 0f;
|
storedDamage[attackerIndex.Value] = 0f;
|
||||||
param2.Damage += banked;
|
damageInfo.Damage += banked;
|
||||||
|
|
||||||
var puncher = CounterStrikeSharp.API.Utilities.GetPlayerFromIndex((int)attackerIndex.Value);
|
var puncher = CounterStrikeSharp.API.Utilities.GetPlayerFromIndex((int)attackerIndex.Value);
|
||||||
var puncherPawn = puncher?.PlayerPawn?.Value;
|
var puncherPawn = puncher?.PlayerPawn?.Value;
|
||||||
|
|
@ -97,14 +97,15 @@ namespace src.player.skills
|
||||||
float ratio = SkillsInfo.GetValue<float>(skillName, "storePercent");
|
float ratio = SkillsInfo.GetValue<float>(skillName, "storePercent");
|
||||||
float cap = SkillsInfo.GetValue<float>(skillName, "maxStored");
|
float cap = SkillsInfo.GetValue<float>(skillName, "maxStored");
|
||||||
|
|
||||||
float stored = storedDamage[victimIndex] + (param2.Damage * ratio);
|
float stored = storedDamage[victimIndex] + (damageInfo.Damage * ratio);
|
||||||
storedDamage[victimIndex] = MathF.Min(stored, cap);
|
storedDamage[victimIndex] = MathF.Min(stored, cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffb347", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float storePercent = .5f, float maxStored = 100f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffb347", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float storePercent = .5f, float maxStored = 100f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float StorePercent { get; set; } = storePercent;
|
public float StorePercent { get; set; } = storePercent;
|
||||||
public float MaxStored { get; set; } = maxStored;
|
public float MaxStored { get; set; } = maxStored;
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory;
|
using CounterStrikeSharp.API.Modules.Memory;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
using src.utils;
|
using src.utils;
|
||||||
|
|
@ -9,6 +10,7 @@ namespace src.player.skills
|
||||||
public class QuickShot : ISkill
|
public class QuickShot : ISkill
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.QuickShot;
|
private const Skills skillName = Skills.QuickShot;
|
||||||
|
private static readonly List<CCSPlayerController> holderBuffer = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -17,14 +19,16 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
PlayerManager.FillSkillHolders(skillName, holderBuffer);
|
||||||
{
|
if (holderBuffer.Count == 0) return;
|
||||||
if (player == null) continue;
|
|
||||||
|
|
||||||
if (PlayerManager.GetPlayerByIndex(player.Index)?.Skill != skillName) continue;
|
foreach (var player in holderBuffer)
|
||||||
|
{
|
||||||
if (!Instance.IsPlayerValid(player)) continue;
|
if (!Instance.IsPlayerValid(player)) continue;
|
||||||
|
|
||||||
var pawn = player.PlayerPawn.Value!;
|
var pawn = player.PlayerPawn.Value;
|
||||||
|
if (pawn == null || !pawn.IsValid) continue;
|
||||||
|
|
||||||
var weaponServices = pawn.WeaponServices;
|
var weaponServices = pawn.WeaponServices;
|
||||||
if (weaponServices == null || weaponServices.ActiveWeapon == null || !weaponServices.ActiveWeapon.IsValid) continue;
|
if (weaponServices == null || weaponServices.ActiveWeapon == null || !weaponServices.ActiveWeapon.IsValid) continue;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.RadarHack;
|
private const Skills skillName = Skills.RadarHack;
|
||||||
private static readonly Skills[] hidingSkills = [Skills.Ghost, Skills.Ninja, Skills.C4Camouflage];
|
private static readonly Skills[] hidingSkills = [Skills.Ghost, Skills.Ninja, Skills.C4Camouflage];
|
||||||
|
private static readonly List<CCSPlayerController> holderBuffer = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -18,14 +19,14 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
PlayerManager.FillSkillHolders(skillName, holderBuffer);
|
||||||
{
|
if (holderBuffer.Count == 0) return;
|
||||||
var playerEvent = PlayerManager.GetPlayerEvent(player);
|
|
||||||
if (playerEvent == null) continue;
|
|
||||||
|
|
||||||
if (PlayerManager.GetPlayerByIndex(playerEvent.Index)?.Skill != skillName) continue;
|
foreach (var playerEvent in holderBuffer)
|
||||||
|
{
|
||||||
if (!Instance.IsPlayerValid(playerEvent)) continue;
|
if (!Instance.IsPlayerValid(playerEvent)) continue;
|
||||||
|
|
||||||
|
var player = PlayerManager.GetPlayerFromEvent(playerEvent) ?? playerEvent;
|
||||||
SetEnemiesVisibleOnRadar(player);
|
SetEnemiesVisibleOnRadar(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -69,13 +69,11 @@ namespace src.player.skills
|
||||||
zombies.Clear();
|
zombies.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
var victimEntity = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return;
|
||||||
var info = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
if (victimEntity == null || !victimEntity.IsValid || info == null) return;
|
|
||||||
|
|
||||||
var victimPawn = victimEntity.As<CCSPlayerPawn>();
|
var victimPawn = damagedEntity.As<CCSPlayerPawn>();
|
||||||
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
||||||
|
|
||||||
var victimController = victimPawn.Controller.Value;
|
var victimController = victimPawn.Controller.Value;
|
||||||
|
|
@ -88,16 +86,16 @@ namespace src.player.skills
|
||||||
if (victimInfo == null || victimInfo.Skill != skillName) return;
|
if (victimInfo == null || victimInfo.Skill != skillName) return;
|
||||||
|
|
||||||
// Friendly-fire-off teammate hit deals 0 damage; don't zombify over a hit that never lands.
|
// Friendly-fire-off teammate hit deals 0 damage; don't zombify over a hit that never lands.
|
||||||
if (SkillUtils.IsFriendlyFireBlocked(info, victimPawn)) return;
|
if (SkillUtils.IsFriendlyFireBlocked(damageInfo, victimPawn)) return;
|
||||||
|
|
||||||
float effectiveDamage = info.Damage;
|
float effectiveDamage = damageInfo.Damage;
|
||||||
if (SkillUtils.GetHitGroup(info) == HitGroup_t.HITGROUP_HEAD)
|
if (SkillUtils.GetHitGroup(damageInfo) == HitGroup_t.HITGROUP_HEAD)
|
||||||
effectiveDamage *= GetHeadshotMultiplier(info);
|
effectiveDamage *= GetHeadshotMultiplier(damageInfo);
|
||||||
|
|
||||||
if (effectiveDamage < victimPawn.Health) return; // survivable, let it through
|
if (effectiveDamage < victimPawn.Health) return; // survivable, let it through
|
||||||
|
|
||||||
if (TryBecomeZombie(victim, victimPawn))
|
if (TryBecomeZombie(victim, victimPawn))
|
||||||
info.Damage = 0;
|
damageInfo.Damage = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryBecomeZombie(CCSPlayerController? victim, CCSPlayerPawn? victimPawn)
|
public static bool TryBecomeZombie(CCSPlayerController? victim, CCSPlayerPawn? victimPawn)
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,6 @@ namespace src.player.skills
|
||||||
var victim = @event.Userid;
|
var victim = @event.Userid;
|
||||||
if (victim == null || !victim.IsValid || !victim.PawnIsAlive) return false;
|
if (victim == null || !victim.IsValid || !victim.PawnIsAlive) return false;
|
||||||
|
|
||||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
|
||||||
if (attacker == null || !attacker.IsValid) return false;
|
|
||||||
|
|
||||||
var victimEvent = PlayerManager.GetPlayerEvent(victim);
|
var victimEvent = PlayerManager.GetPlayerEvent(victim);
|
||||||
if (victimEvent == null || !victimEvent.IsValid) return false;
|
if (victimEvent == null || !victimEvent.IsValid) return false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
using src.utils;
|
using src.utils;
|
||||||
using static src.jRandomSkills;
|
using static src.jRandomSkills;
|
||||||
|
|
@ -8,6 +9,7 @@ namespace src.player.skills
|
||||||
public class Regeneration : ISkill
|
public class Regeneration : ISkill
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.Regeneration;
|
private const Skills skillName = Skills.Regeneration;
|
||||||
|
private static readonly List<CCSPlayerController> holderBuffer = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -18,11 +20,11 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
int cooldown = Math.Max(1, (int)(64 * SkillsInfo.GetValue<float>(skillName, "cooldown")));
|
int cooldown = Math.Max(1, (int)(64 * SkillsInfo.GetValue<float>(skillName, "cooldown")));
|
||||||
if (Server.TickCount % cooldown != 0) return;
|
if (Server.TickCount % cooldown != 0) return;
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
PlayerManager.FillSkillHolders(skillName, holderBuffer);
|
||||||
{
|
if (holderBuffer.Count == 0) return;
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
|
||||||
if (playerInfo?.Skill != skillName) continue;
|
|
||||||
|
|
||||||
|
foreach (var player in holderBuffer)
|
||||||
|
{
|
||||||
var pawn = player.PlayerPawn.Value;
|
var pawn = player.PlayerPawn.Value;
|
||||||
if (pawn == null || !pawn.IsValid) continue;
|
if (pawn == null || !pawn.IsValid) continue;
|
||||||
SkillUtils.AddHealth(pawn, SkillsInfo.GetValue<int>(skillName, "healthToAdd"));
|
SkillUtils.AddHealth(pawn, SkillsInfo.GetValue<int>(skillName, "healthToAdd"));
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -136,18 +136,15 @@ namespace src.player.skills
|
||||||
replica.DispatchSpawn();
|
replica.DispatchSpawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(param.Entity.Name)) return;
|
if (string.IsNullOrEmpty(damagedEntity.Entity.Name)) return;
|
||||||
if (!param.Entity.Name.StartsWith("Replica_")) return;
|
if (!damagedEntity.Entity.Name.StartsWith("Replica_")) return;
|
||||||
|
|
||||||
var replica = param.As<CPhysicsPropMultiplayer>();
|
var replica = damagedEntity.As<CPhysicsPropMultiplayer>();
|
||||||
if (replica == null || !replica.IsValid) return;
|
if (replica == null || !replica.IsValid) return;
|
||||||
|
|
||||||
if (!consumedReplicas.TryAdd(replica.Index, 0)) return;
|
if (!consumedReplicas.TryAdd(replica.Index, 0)) return;
|
||||||
|
|
@ -157,7 +154,7 @@ namespace src.player.skills
|
||||||
var owner = GetReplicaOwner(replica.Index);
|
var owner = GetReplicaOwner(replica.Index);
|
||||||
EntityManager.DestroyEntity(replica.Index);
|
EntityManager.DestroyEntity(replica.Index);
|
||||||
|
|
||||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle);
|
||||||
if (attackerPawn.DesignerName != "player")
|
if (attackerPawn.DesignerName != "player")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (playersToSlow.IsEmpty) return;
|
||||||
|
|
||||||
foreach (var item in playersToSlow)
|
foreach (var item in playersToSlow)
|
||||||
{
|
{
|
||||||
var playerIndex = item.Key;
|
var playerIndex = item.Key;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -27,13 +27,11 @@ namespace src.player.skills
|
||||||
|
|
||||||
// Must intercept pre-damage: player_hurt fires after the death is committed, so a
|
// Must intercept pre-damage: player_hurt fires after the death is committed, so a
|
||||||
// lethal hit (esp. fall damage) can't be undone there. Cancelling the blow here keeps the respawn clean.
|
// lethal hit (esp. fall damage) can't be undone there. Cancelling the blow here keeps the respawn clean.
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
var victimEntity = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return;
|
||||||
var info = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
if (victimEntity == null || !victimEntity.IsValid || info == null) return;
|
|
||||||
|
|
||||||
var victimPawn = victimEntity.As<CCSPlayerPawn>();
|
var victimPawn = damagedEntity.As<CCSPlayerPawn>();
|
||||||
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return;
|
||||||
|
|
||||||
var victimController = victimPawn.Controller.Value;
|
var victimController = victimPawn.Controller.Value;
|
||||||
|
|
@ -45,23 +43,23 @@ namespace src.player.skills
|
||||||
var victimInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index));
|
var victimInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(victim)?.Index ?? victim.Index));
|
||||||
if (victimInfo == null || victimInfo.Skill != skillName) return;
|
if (victimInfo == null || victimInfo.Skill != skillName) return;
|
||||||
|
|
||||||
if (SkillUtils.IsFriendlyFireBlocked(info, victimPawn)) return;
|
if (SkillUtils.IsFriendlyFireBlocked(damageInfo, victimPawn)) return;
|
||||||
|
|
||||||
float effectiveDamage = info.Damage;
|
float effectiveDamage = damageInfo.Damage;
|
||||||
if (SkillUtils.GetHitGroup(info) == HitGroup_t.HITGROUP_HEAD)
|
if (SkillUtils.GetHitGroup(damageInfo) == HitGroup_t.HITGROUP_HEAD)
|
||||||
effectiveDamage *= GetHeadshotMultiplier(info);
|
effectiveDamage *= GetHeadshotMultiplier(damageInfo);
|
||||||
|
|
||||||
if (effectiveDamage < victimPawn.Health) return;
|
if (effectiveDamage < victimPawn.Health) return;
|
||||||
|
|
||||||
if (usedThisRound.TryGetValue(victim.Handle, out int savedTick))
|
if (usedThisRound.TryGetValue(victim.Handle, out int savedTick))
|
||||||
{
|
{
|
||||||
if (savedTick == Server.TickCount)
|
if (savedTick == Server.TickCount)
|
||||||
info.Damage = 0;
|
damageInfo.Damage = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryConsumeRevive(victim, victimPawn))
|
if (TryConsumeRevive(victim, victimPawn))
|
||||||
info.Damage = 0;
|
damageInfo.Damage = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float GetHeadshotMultiplier(CTakeDamageInfo info)
|
private static float GetHeadshotMultiplier(CTakeDamageInfo info)
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (noSpace.IsEmpty) return;
|
||||||
|
|
||||||
foreach (var (playerIndex, time) in noSpace)
|
foreach (var (playerIndex, time) in noSpace)
|
||||||
{
|
{
|
||||||
var player = Utilities.GetPlayerFromIndex((int)playerIndex);
|
var player = Utilities.GetPlayerFromIndex((int)playerIndex);
|
||||||
|
|
|
||||||
|
|
@ -29,20 +29,19 @@ namespace src.player.skills
|
||||||
border: !PlayerManager.GetTickPlayers().Any(p => p.Team == player.Team && p != player) ? "tb" : "t");
|
border: !PlayerManager.GetTickPlayers().Any(p => p.Team == player.Team && p != player) ? "tb" : "t");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || damagedEntity.Entity == null || damageInfo == null || damageInfo.Attacker == null || damageInfo.Attacker.Value == null)
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
|
|
||||||
if (param == null || param.Entity == null || param2 == null || param2.Attacker == null || param2.Attacker.Value == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CCSPlayerPawn attackerPawn = new(param2.Attacker.Value.Handle);
|
CCSPlayerPawn attackerPawn = new(damageInfo.Attacker.Value.Handle);
|
||||||
CCSPlayerPawn victimPawn = new(param.Handle);
|
CCSPlayerPawn victimPawn = new(damagedEntity.Handle);
|
||||||
|
|
||||||
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
if (attackerPawn.DesignerName != "player" || victimPawn.DesignerName != "player")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (SkillUtils.IsFriendlyFireBlocked(skillName, damageInfo, victimPawn)) return;
|
||||||
|
|
||||||
if (attackerPawn == null || attackerPawn.Controller?.Value == null || victimPawn == null || victimPawn.Controller?.Value == null)
|
if (attackerPawn == null || attackerPawn.Controller?.Value == null || victimPawn == null || victimPawn.Controller?.Value == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -54,14 +53,15 @@ namespace src.player.skills
|
||||||
if (playerInfo.Skill == skillName)
|
if (playerInfo.Skill == skillName)
|
||||||
{
|
{
|
||||||
float? skillChance = playerInfo.SkillChance;
|
float? skillChance = playerInfo.SkillChance;
|
||||||
param2.Damage *= skillChance ?? 1f;
|
damageInfo.Damage *= skillChance ?? 1f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#09ba00", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float chanceFrom = 1.15f, float chanceTo = 1.35f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#09ba00", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, float chanceFrom = 1.15f, float chanceTo = 1.35f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float ChanceFrom { get; set; } = chanceFrom;
|
public float ChanceFrom { get; set; } = chanceFrom;
|
||||||
public float ChanceTo { get; set; } = chanceTo;
|
public float ChanceTo { get; set; } = chanceTo;
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -74,6 +74,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (cameras.IsEmpty) return;
|
||||||
|
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
foreach (var player in PlayerManager.GetTickPlayers())
|
||||||
if (cameras.TryGetValue(player.Index, out var cameraInfo) && cameraInfo.Item2 != 0)
|
if (cameras.TryGetValue(player.Index, out var cameraInfo) && cameraInfo.Item2 != 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (SkillPlayerInfo.IsEmpty) return;
|
||||||
|
|
||||||
var players = PlayerManager.GetTickPlayers().ToArray();
|
var players = PlayerManager.GetTickPlayers().ToArray();
|
||||||
foreach (var player in players)
|
foreach (var player in players)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (SkillPlayerInfo.IsEmpty) return;
|
||||||
|
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
foreach (var player in PlayerManager.GetTickPlayers())
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) continue;
|
if (player == null || !player.IsValid) continue;
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (cameras.IsEmpty) return;
|
||||||
|
|
||||||
foreach (var player in PlayerManager.GetTickPlayers())
|
foreach (var player in PlayerManager.GetTickPlayers())
|
||||||
if (cameras.TryGetValue(player.Index, out var cameraInfo) && cameraInfo.Item2 != 0)
|
if (cameras.TryGetValue(player.Index, out var cameraInfo) && cameraInfo.Item2 != 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,13 @@ namespace src.player.skills
|
||||||
public static void DisableSkill(CCSPlayerController player)
|
public static void DisableSkill(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
if (knivesInfo.TryRemove(player.Index, out KnifeInfo? knifeInfo) && knifeInfo != null)
|
if (knivesInfo.TryRemove(player.Index, out KnifeInfo? knifeInfo) && knifeInfo != null)
|
||||||
|
{
|
||||||
|
bool wasDropped = knifeInfo.IsDropped;
|
||||||
DisableKnifeSkill(knifeInfo);
|
DisableKnifeSkill(knifeInfo);
|
||||||
|
|
||||||
|
if (wasDropped && player != null && player.IsValid && player.PawnIsAlive)
|
||||||
|
player.GiveNamedItem("weapon_knife");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PlayerDisconnect(uint playerIndex)
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,13 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.ToxicSmoke;
|
private const Skills skillName = Skills.ToxicSmoke;
|
||||||
private static readonly ConcurrentDictionary<Vector, uint> smokes = [];
|
private static readonly ConcurrentDictionary<Vector, uint> smokes = [];
|
||||||
|
|
||||||
|
private static void RemoveOwnerSmokes(uint ownerIndex)
|
||||||
|
{
|
||||||
|
foreach (var smoke in smokes)
|
||||||
|
if (smoke.Value == ownerIndex)
|
||||||
|
smokes.TryRemove(smoke.Key, out _);
|
||||||
|
}
|
||||||
private readonly static ConcurrentDictionary<uint, int> playersWithSkill = [];
|
private readonly static ConcurrentDictionary<uint, int> playersWithSkill = [];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
|
|
@ -40,9 +47,6 @@ namespace src.player.skills
|
||||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
|
||||||
if (playerInfo?.Skill != skillName) return;
|
|
||||||
|
|
||||||
foreach (var smoke in smokes.Keys.Where(v => v.X == @event.X && v.Y == @event.Y && v.Z == @event.Z))
|
foreach (var smoke in smokes.Keys.Where(v => v.X == @event.X && v.Y == @event.Y && v.Z == @event.Z))
|
||||||
smokes.TryRemove(smoke, out _);
|
smokes.TryRemove(smoke, out _);
|
||||||
}
|
}
|
||||||
|
|
@ -100,6 +104,8 @@ namespace src.player.skills
|
||||||
int damage = smokeDamage;
|
int damage = smokeDamage;
|
||||||
if (thrower != null && eventPlayer.Index != thrower.Index && pawn.TeamNum == thrower.TeamNum)
|
if (thrower != null && eventPlayer.Index != thrower.Index && pawn.TeamNum == thrower.TeamNum)
|
||||||
{
|
{
|
||||||
|
if (!SkillsInfo.GetValue<bool>(skillName, "friendlyFire")) continue;
|
||||||
|
|
||||||
damage = (int)MathF.Round(smokeDamage * SkillUtils.GetTeamDamageMultiplier(skillName));
|
damage = (int)MathF.Round(smokeDamage * SkillUtils.GetTeamDamageMultiplier(skillName));
|
||||||
if (damage <= 0) continue;
|
if (damage <= 0) continue;
|
||||||
}
|
}
|
||||||
|
|
@ -167,10 +173,11 @@ namespace src.player.skills
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
playersWithSkill.TryRemove(player.Index, out _);
|
playersWithSkill.TryRemove(player.Index, out _);
|
||||||
|
RemoveOwnerSmokes(player.Index);
|
||||||
SkillUtils.UpdateGrenadeCount(player, CsItem.SmokeGrenade, 1);
|
SkillUtils.UpdateGrenadeCount(player, CsItem.SmokeGrenade, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int smokeDamage = 2, float smokeRadius = 180, int tickCooldown = 17, int grenadeLimit = 1, float dmgReductionForTeamates = 0.5f, float soundVolume = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Common, int smokeDamage = 2, float smokeRadius = 180, int tickCooldown = 17, int grenadeLimit = 1, float dmgReductionForTeamates = 0.5f, float soundVolume = .3f, bool friendlyFire = true) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float SoundVolume { get; set; } = soundVolume;
|
public float SoundVolume { get; set; } = soundVolume;
|
||||||
public int SmokeDamage { get; set; } = smokeDamage;
|
public int SmokeDamage { get; set; } = smokeDamage;
|
||||||
|
|
@ -178,6 +185,7 @@ namespace src.player.skills
|
||||||
public int TickCooldown { get; set; } = tickCooldown;
|
public int TickCooldown { get; set; } = tickCooldown;
|
||||||
public int GrenadeLimit { get; set; } = grenadeLimit;
|
public int GrenadeLimit { get; set; } = grenadeLimit;
|
||||||
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
|
public float DmgReductionForTeamates { get; set; } = dmgReductionForTeamates;
|
||||||
|
public bool FriendlyFire { get; set; } = friendlyFire;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -43,11 +43,11 @@ namespace src.player.skills
|
||||||
pending.TryRemove(playerIndex, out _);
|
pending.TryRemove(playerIndex, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo)
|
||||||
{
|
{
|
||||||
if (holders.IsEmpty) return;
|
if (holders.IsEmpty) return;
|
||||||
|
|
||||||
if (!TryResolveVictim(h, out var victim, out var victimPawn, out var info)) return;
|
if (!TryResolveVictim(damagedEntity, damageInfo, out var victim, out var victimPawn, out var info)) return;
|
||||||
if (!holders.ContainsKey(victim!.Index)) return;
|
if (!holders.ContainsKey(victim!.Index)) return;
|
||||||
|
|
||||||
int armor = victimPawn!.ArmorValue;
|
int armor = victimPawn!.ArmorValue;
|
||||||
|
|
@ -71,28 +71,26 @@ namespace src.player.skills
|
||||||
$"passed={info.Damage:0.#} armor={armor}->{pending[victim.Index]} hp={victimPawn.Health}", DebugCategory.Damage);
|
$"passed={info.Damage:0.#} armor={armor}->{pending[victim.Index]} hp={victimPawn.Health}", DebugCategory.Damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamagePost(DynamicHook h)
|
public static void OnTakeDamagePost(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, CTakeDamageResult damageResult)
|
||||||
{
|
{
|
||||||
if (pending.IsEmpty) return;
|
if (pending.IsEmpty) return;
|
||||||
|
|
||||||
if (!TryResolveVictim(h, out var victim, out var victimPawn, out _)) return;
|
if (!TryResolveVictim(damagedEntity, damageInfo, out var victim, out var victimPawn, out _)) return;
|
||||||
if (!pending.TryRemove(victim!.Index, out int restored)) return;
|
if (!pending.TryRemove(victim!.Index, out int restored)) return;
|
||||||
|
|
||||||
victimPawn!.ArmorValue = restored;
|
victimPawn!.ArmorValue = restored;
|
||||||
Utilities.SetStateChanged(victimPawn, "CCSPlayerPawn", "m_ArmorValue");
|
Utilities.SetStateChanged(victimPawn, "CCSPlayerPawn", "m_ArmorValue");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryResolveVictim(DynamicHook h, out CCSPlayerController? victim, out CCSPlayerPawn? pawn, out CTakeDamageInfo? info)
|
private static bool TryResolveVictim(CBaseEntity damagedEntity, CTakeDamageInfo damageInfo, out CCSPlayerController? victim, out CCSPlayerPawn? pawn, out CTakeDamageInfo? info)
|
||||||
{
|
{
|
||||||
victim = null;
|
victim = null;
|
||||||
pawn = null;
|
pawn = null;
|
||||||
info = null;
|
info = null;
|
||||||
|
|
||||||
CEntityInstance param = h.GetParam<CEntityInstance>(0);
|
if (damagedEntity == null || !damagedEntity.IsValid || damageInfo == null) return false;
|
||||||
CTakeDamageInfo param2 = h.GetParam<CTakeDamageInfo>(1);
|
|
||||||
if (param == null || !param.IsValid || param2 == null) return false;
|
|
||||||
|
|
||||||
var victimPawn = param.As<CCSPlayerPawn>();
|
var victimPawn = damagedEntity.As<CCSPlayerPawn>();
|
||||||
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return false;
|
if (victimPawn == null || !victimPawn.IsValid || victimPawn.DesignerName != "player") return false;
|
||||||
|
|
||||||
var controller = victimPawn.Controller.Value;
|
var controller = victimPawn.Controller.Value;
|
||||||
|
|
@ -104,7 +102,7 @@ namespace src.player.skills
|
||||||
var routed = PlayerManager.GetPlayerEvent(resolved);
|
var routed = PlayerManager.GetPlayerEvent(resolved);
|
||||||
victim = routed != null && routed.IsValid ? routed : resolved;
|
victim = routed != null && routed.IsValid ? routed : resolved;
|
||||||
pawn = victimPawn;
|
pawn = victimPawn;
|
||||||
info = param2;
|
info = damageInfo;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
|
if (SkillPlayerInfo.IsEmpty) return;
|
||||||
|
|
||||||
var players = PlayerManager.GetTickPlayers().ToArray();
|
var players = PlayerManager.GetTickPlayers().ToArray();
|
||||||
foreach (var player in players)
|
foreach (var player in players)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ namespace src.player.skills
|
||||||
!p.IsHLTV &&
|
!p.IsHLTV &&
|
||||||
p.Team != CsTeam.Spectator
|
p.Team != CsTeam.Spectator
|
||||||
&& p.Team != CsTeam.None
|
&& p.Team != CsTeam.None
|
||||||
).ToArray();
|
).OfType<CCSPlayerController>().ToArray();
|
||||||
|
|
||||||
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString())));
|
ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString())));
|
||||||
SkillUtils.UpdateMenu(player, menuItems);
|
SkillUtils.UpdateMenu(player, menuItems);
|
||||||
|
|
|
||||||
|
|
@ -63,10 +63,7 @@ namespace src.utils
|
||||||
var current = Newtonsoft.Json.Linq.JObject.Parse(json);
|
var current = Newtonsoft.Json.Linq.JObject.Parse(json);
|
||||||
var expected = Newtonsoft.Json.Linq.JObject.FromObject(new SettingsModel());
|
var expected = Newtonsoft.Json.Linq.JObject.FromObject(new SettingsModel());
|
||||||
|
|
||||||
foreach (var property in expected.Properties())
|
return HasMissingKeys(current, expected);
|
||||||
if (current[property.Name] == null) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
@ -74,6 +71,21 @@ namespace src.utils
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool HasMissingKeys(Newtonsoft.Json.Linq.JObject current, Newtonsoft.Json.Linq.JObject expected)
|
||||||
|
{
|
||||||
|
foreach (var property in expected.Properties())
|
||||||
|
{
|
||||||
|
var currentValue = current[property.Name];
|
||||||
|
if (currentValue == null) return true;
|
||||||
|
|
||||||
|
if (property.Value is Newtonsoft.Json.Linq.JObject expectedChild
|
||||||
|
&& currentValue is Newtonsoft.Json.Linq.JObject currentChild
|
||||||
|
&& HasMissingKeys(currentChild, expectedChild)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool IsLegacyDebugMode(string json)
|
private static bool IsLegacyDebugMode(string json)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -227,6 +239,7 @@ namespace src.utils
|
||||||
WSADMenuItemLineColor = "white",
|
WSADMenuItemLineColor = "white",
|
||||||
WSADMenuItemHoverLineColor = "orange",
|
WSADMenuItemHoverLineColor = "orange",
|
||||||
WSADMenuItemLineSize = "sm",
|
WSADMenuItemLineSize = "sm",
|
||||||
|
WSADMenuVisibleItems = 3,
|
||||||
WSADMenuControllsLineSize = "sm",
|
WSADMenuControllsLineSize = "sm",
|
||||||
WSADMenuControllsLineColor1 = "cyan",
|
WSADMenuControllsLineColor1 = "cyan",
|
||||||
WSADMenuControllsLineColor2 = "white",
|
WSADMenuControllsLineColor2 = "white",
|
||||||
|
|
@ -260,13 +273,13 @@ namespace src.utils
|
||||||
ChangeLanguageCommand = new NormalCommand("lang, language, changelang, change_lang, jezyk, język", ""),
|
ChangeLanguageCommand = new NormalCommand("lang, language, changelang, change_lang, jezyk, język", ""),
|
||||||
ReloadCommand = new NormalCommand("reload, refresh", "@jRandomSkills/admin"),
|
ReloadCommand = new NormalCommand("reload, refresh", "@jRandomSkills/admin"),
|
||||||
NextCommand = new NormalCommand("next_skill", "@jRandomSkills/admin"),
|
NextCommand = new NormalCommand("next_skill", "@jRandomSkills/admin"),
|
||||||
CheckEntityCommand = new NormalCommand("ent, entity, checkentity, check_entity, sprawdzencje, checkent, check_ent, 检查实体, 检查实体", "@jRandomSkills/owner"),
|
CheckEntityCommand = new NormalCommand("ent, entity, checkentity, check_entity, sprawdzencje, checkent, check_ent, 检查实体", "@jRandomSkills/owner"),
|
||||||
};
|
};
|
||||||
|
|
||||||
VotingCommands = new VotingCommands
|
VotingCommands = new VotingCommands
|
||||||
{
|
{
|
||||||
StartGameCommand = new StartGameCommand(true, "start, go, começar, iniciar, 开始, 启动", "@jRandomSkills/admin", "mp_freezetime 15; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 0", "mp_freezetime 0; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 1", 15, 60, 15, 500, 2),
|
StartGameCommand = new StartGameCommand(true, "start, go, começar, iniciar, 开始, 启动", "@jRandomSkills/admin", "mp_freezetime 15; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 0", "mp_freezetime 0; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 1", 15, 60, 15, 500, 2),
|
||||||
ChangeMapCommand = new VotingCommand(true, "map, mapa, changemap, zmienmape, zmienmape, mudarMapa, trocarMapa, 更换地图, 更改地图", "@jRandomSkills/admin", 25, 90, 15, 500, 2),
|
ChangeMapCommand = new VotingCommand(true, "map, mapa, changemap, zmienmape, mudarMapa, trocarMapa, 更换地图, 更改地图", "@jRandomSkills/admin", 25, 90, 15, 500, 2),
|
||||||
SwapCommand = new VotingCommand(true, "swap, zmiana, trocar, 交换, 切换", "@jRandomSkills/admin", 15, 90, 15, 20, 2),
|
SwapCommand = new VotingCommand(true, "swap, zmiana, trocar, 交换, 切换", "@jRandomSkills/admin", 15, 90, 15, 20, 2),
|
||||||
ShuffleCommand = new VotingCommand(true, "shuffle, embaralhar, 随机排序, 洗牌", "@jRandomSkills/admin", 15, 90, 15, 20, 2),
|
ShuffleCommand = new VotingCommand(true, "shuffle, embaralhar, 随机排序, 洗牌", "@jRandomSkills/admin", 15, 90, 15, 20, 2),
|
||||||
PauseCommand = new VotingCommand(true, "pause, unpause, pausar, despausar, 暂停, 恢复", "@jRandomSkills/admin", 15, 60, 15, 2, 2),
|
PauseCommand = new VotingCommand(true, "pause, unpause, pausar, despausar, 暂停, 恢复", "@jRandomSkills/admin", 15, 60, 15, 2, 2),
|
||||||
|
|
@ -308,6 +321,7 @@ namespace src.utils
|
||||||
public required string WSADMenuItemLineColor { get; set; }
|
public required string WSADMenuItemLineColor { get; set; }
|
||||||
public required string WSADMenuItemHoverLineColor { get; set; }
|
public required string WSADMenuItemHoverLineColor { get; set; }
|
||||||
public required string WSADMenuItemLineSize { get; set; }
|
public required string WSADMenuItemLineSize { get; set; }
|
||||||
|
public required int WSADMenuVisibleItems { get; set; }
|
||||||
public required string WSADMenuControllsLineSize { get; set; }
|
public required string WSADMenuControllsLineSize { get; set; }
|
||||||
public required string WSADMenuControllsLineColor1 { get; set; }
|
public required string WSADMenuControllsLineColor1 { get; set; }
|
||||||
public required string WSADMenuControllsLineColor2 { get; set; }
|
public required string WSADMenuControllsLineColor2 { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,15 @@ namespace src.utils
|
||||||
|
|
||||||
private const float maxLifetime = 25f;
|
private const float maxLifetime = 25f;
|
||||||
|
|
||||||
|
private const int spawnFrames = 4;
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<string, Ring> rings = [];
|
private static readonly ConcurrentDictionary<string, Ring> rings = [];
|
||||||
|
|
||||||
private sealed class Ring
|
private sealed class Ring
|
||||||
{
|
{
|
||||||
public uint? Disc { get; set; }
|
public uint? Disc { get; set; }
|
||||||
public List<uint> Beams { get; } = [];
|
public List<uint> Beams { get; } = [];
|
||||||
|
public bool Cancelled { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PreloadAssets()
|
public static void PreloadAssets()
|
||||||
|
|
@ -48,9 +51,11 @@ namespace src.utils
|
||||||
|
|
||||||
if (!EntityManager.OverBudget())
|
if (!EntityManager.OverBudget())
|
||||||
{
|
{
|
||||||
Color color = GetSkillColor(skill);
|
var segments = new List<(Vector From, Vector To)>(outerSegments + innerSegments);
|
||||||
SpawnRing(center, radius, outerSegments, color, ring);
|
CollectRing(center, radius, outerSegments, segments);
|
||||||
SpawnRing(center, radius * .5f, innerSegments, color, ring);
|
CollectRing(center, radius * .5f, innerSegments, segments);
|
||||||
|
|
||||||
|
SpawnBeamChunk(ring, segments, 0, GetSkillColor(skill));
|
||||||
}
|
}
|
||||||
|
|
||||||
jRandomSkills.Instance.AddTimer(maxLifetime, () => Hide(skill, id), TimerFlags.STOP_ON_MAPCHANGE);
|
jRandomSkills.Instance.AddTimer(maxLifetime, () => Hide(skill, id), TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
|
|
@ -77,6 +82,8 @@ namespace src.utils
|
||||||
|
|
||||||
private static void Destroy(Ring ring)
|
private static void Destroy(Ring ring)
|
||||||
{
|
{
|
||||||
|
ring.Cancelled = true;
|
||||||
|
|
||||||
foreach (uint beamIndex in ring.Beams)
|
foreach (uint beamIndex in ring.Beams)
|
||||||
EntityManager.DestroyBeam(beamIndex);
|
EntityManager.DestroyBeam(beamIndex);
|
||||||
|
|
||||||
|
|
@ -166,7 +173,7 @@ namespace src.utils
|
||||||
attribute.InteractsExclude = 0;
|
attribute.InteractsExclude = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SpawnRing(Vector center, float radius, int segments, Color color, Ring ring)
|
private static void CollectRing(Vector center, float radius, int segments, List<(Vector From, Vector To)> output)
|
||||||
{
|
{
|
||||||
if (segments < 3 || radius <= 0) return;
|
if (segments < 3 || radius <= 0) return;
|
||||||
|
|
||||||
|
|
@ -176,10 +183,23 @@ namespace src.utils
|
||||||
for (int i = 1; i <= segments; i++)
|
for (int i = 1; i <= segments; i++)
|
||||||
{
|
{
|
||||||
Vector next = PointOn(center, radius, step * i);
|
Vector next = PointOn(center, radius, step * i);
|
||||||
|
output.Add((previous, next));
|
||||||
|
previous = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var beam = EntityManager.CreateTrackedBeam(EntityManager.SystemOwnerIndex, previous, next, color);
|
private static void SpawnBeamChunk(Ring ring, List<(Vector From, Vector To)> segments, int start, Color color)
|
||||||
if (beam != null && beam.IsValid)
|
|
||||||
{
|
{
|
||||||
|
if (ring.Cancelled || start >= segments.Count) return;
|
||||||
|
|
||||||
|
int perFrame = (segments.Count + spawnFrames - 1) / spawnFrames;
|
||||||
|
int end = Math.Min(start + perFrame, segments.Count);
|
||||||
|
|
||||||
|
for (int i = start; i < end; i++)
|
||||||
|
{
|
||||||
|
var beam = EntityManager.CreateTrackedBeam(EntityManager.SystemOwnerIndex, segments[i].From, segments[i].To, color);
|
||||||
|
if (beam == null || !beam.IsValid) continue;
|
||||||
|
|
||||||
beam.Width = 1.2f;
|
beam.Width = 1.2f;
|
||||||
beam.EndWidth = 1.2f;
|
beam.EndWidth = 1.2f;
|
||||||
|
|
||||||
|
|
@ -189,8 +209,9 @@ namespace src.utils
|
||||||
ring.Beams.Add(beam.Index);
|
ring.Beams.Add(beam.Index);
|
||||||
}
|
}
|
||||||
|
|
||||||
previous = next;
|
if (end >= segments.Count) return;
|
||||||
}
|
|
||||||
|
Server.NextFrame(() => SpawnBeamChunk(ring, segments, end, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Vector PointOn(Vector center, float radius, float angle)
|
private static Vector PointOn(Vector center, float radius, float angle)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||||
using CounterStrikeSharp.API.Modules.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
|
|
@ -42,6 +42,8 @@ namespace src.utils
|
||||||
{
|
{
|
||||||
if (entityIndex == 0) return;
|
if (entityIndex == 0) return;
|
||||||
|
|
||||||
|
bool alreadyTracked = trackedEntities.TryGetValue(entityIndex, out var previous);
|
||||||
|
|
||||||
trackedEntities[entityIndex] = new EntityData
|
trackedEntities[entityIndex] = new EntityData
|
||||||
{
|
{
|
||||||
EntityIndex = entityIndex,
|
EntityIndex = entityIndex,
|
||||||
|
|
@ -50,8 +52,12 @@ namespace src.utils
|
||||||
CreatedAt = DateTime.UtcNow
|
CreatedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Config.DebugEnabled(DebugCategory.Entity))
|
if (!Config.DebugEnabled(DebugCategory.Entity)) return;
|
||||||
|
|
||||||
|
if (!alreadyTracked)
|
||||||
Debug.WriteToDebug($"[Entity] + {entityType} #{entityIndex} owner={DescribeOwner(playerIndex)} tracked={trackedEntities.Count}", DebugCategory.Entity);
|
Debug.WriteToDebug($"[Entity] + {entityType} #{entityIndex} owner={DescribeOwner(playerIndex)} tracked={trackedEntities.Count}", DebugCategory.Entity);
|
||||||
|
else if (previous.EntityType != entityType)
|
||||||
|
Debug.WriteToDebug($"[Entity] ~ {previous.EntityType} -> {entityType} #{entityIndex} owner={DescribeOwner(playerIndex)} tracked={trackedEntities.Count}", DebugCategory.Entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string DescribeOwner(uint playerIndex)
|
private static string DescribeOwner(uint playerIndex)
|
||||||
|
|
@ -91,6 +97,21 @@ namespace src.utils
|
||||||
return (trackedEntities.Count, trackedEntities.Values.Select(e => e.PlayerIndex).Distinct().Count());
|
return (trackedEntities.Count, trackedEntities.Values.Select(e => e.PlayerIndex).Distinct().Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string DescribeTracked(int topTypes = 4)
|
||||||
|
{
|
||||||
|
var byType = new Dictionary<string, int>();
|
||||||
|
foreach (var data in trackedEntities.Values)
|
||||||
|
{
|
||||||
|
string type = string.IsNullOrEmpty(data.EntityType) ? "?" : data.EntityType;
|
||||||
|
byType[type] = byType.TryGetValue(type, out int count) ? count + 1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byType.Count == 0) return string.Empty;
|
||||||
|
|
||||||
|
var parts = byType.OrderByDescending(kvp => kvp.Value).Take(topTypes).Select(kvp => $"{kvp.Key}:{kvp.Value}");
|
||||||
|
return " types=" + string.Join(",", parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static CParticleSystem? CreateTrackedParticleSystem(uint playerIndex, string particleName, float? autoDestroySeconds = null)
|
public static CParticleSystem? CreateTrackedParticleSystem(uint playerIndex, string particleName, float? autoDestroySeconds = null)
|
||||||
{
|
{
|
||||||
|
|
@ -115,7 +136,7 @@ namespace src.utils
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CDynamicProp? CreateTrackedDynamicProp(uint playerIndex, string designerName = "prop_dynamic")
|
public static CDynamicProp? CreateTrackedDynamicProp(uint playerIndex, string designerName = "prop_dynamic", string? trackAs = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -123,7 +144,7 @@ namespace src.utils
|
||||||
var prop = Utilities.CreateEntityByName<CDynamicProp>(designerName);
|
var prop = Utilities.CreateEntityByName<CDynamicProp>(designerName);
|
||||||
if (prop == null || !prop.IsValid) return null;
|
if (prop == null || !prop.IsValid) return null;
|
||||||
|
|
||||||
RegisterEntity(prop.Index, playerIndex, designerName);
|
RegisterEntity(prop.Index, playerIndex, trackAs ?? designerName);
|
||||||
return prop;
|
return prop;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ namespace src.utils
|
||||||
private static readonly string playersLanguageFileName = "playersLanguage.json";
|
private static readonly string playersLanguageFileName = "playersLanguage.json";
|
||||||
private static readonly string configsFolderPath = Path.Combine(jRandomSkills.Instance.ModuleDirectory, "configs");
|
private static readonly string configsFolderPath = Path.Combine(jRandomSkills.Instance.ModuleDirectory, "configs");
|
||||||
private static ConcurrentDictionary<ulong, string> _playersLanguage = [];
|
private static ConcurrentDictionary<ulong, string> _playersLanguage = [];
|
||||||
|
private static string _lastSavedPlayersJson = string.Empty;
|
||||||
private static readonly string geoliteFilePath = Path.Combine(jRandomSkills.Instance.ModuleDirectory, "packages", "GeoLite2-Country.mmdb");
|
private static readonly string geoliteFilePath = Path.Combine(jRandomSkills.Instance.ModuleDirectory, "packages", "GeoLite2-Country.mmdb");
|
||||||
|
|
||||||
private static string defaultLangCode = "en";
|
private static string defaultLangCode = "en";
|
||||||
|
|
@ -47,6 +48,7 @@ namespace src.utils
|
||||||
{
|
{
|
||||||
_translations.Clear();
|
_translations.Clear();
|
||||||
_playersLanguage.Clear();
|
_playersLanguage.Clear();
|
||||||
|
_lastSavedPlayersJson = string.Empty;
|
||||||
_skillNameCache.Clear();
|
_skillNameCache.Clear();
|
||||||
_skillDescCache.Clear();
|
_skillDescCache.Clear();
|
||||||
SetLangCode();
|
SetLangCode();
|
||||||
|
|
@ -324,16 +326,78 @@ namespace src.utils
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var jsonText = File.ReadAllText(filePath);
|
var jsonText = File.ReadAllText(filePath);
|
||||||
_playersLanguage = JsonConvert.DeserializeObject<ConcurrentDictionary<ulong, string>>(jsonText) ?? [];
|
if (string.IsNullOrWhiteSpace(jsonText))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool legacyFormat = false;
|
||||||
|
var loaded = new ConcurrentDictionary<ulong, string>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var root = Newtonsoft.Json.Linq.JObject.Parse(jsonText);
|
||||||
|
foreach (var property in root.Properties())
|
||||||
|
{
|
||||||
|
if (property.Value is Newtonsoft.Json.Linq.JArray steamIds)
|
||||||
|
{
|
||||||
|
foreach (var steamId in steamIds)
|
||||||
|
if (ulong.TryParse(steamId.ToString(), out ulong grouped) && grouped != 0)
|
||||||
|
loaded[grouped] = property.Name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
legacyFormat = true;
|
||||||
|
if (ulong.TryParse(property.Name, out ulong flat) && flat != 0)
|
||||||
|
loaded[flat] = property.Value.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Server.PrintToConsole($"[jRandomSkills] {playersLanguageFileName} could not be parsed, starting empty.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_playersLanguage = loaded;
|
||||||
|
|
||||||
|
if (legacyFormat)
|
||||||
|
{
|
||||||
|
Server.PrintToConsole($"[jRandomSkills] {playersLanguageFileName} converted to the grouped format ({_playersLanguage.Count} players).");
|
||||||
|
SavePlayersLanguage();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_lastSavedPlayersJson = BuildPlayersJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildPlayersJson()
|
||||||
|
{
|
||||||
|
var grouped = new SortedDictionary<string, List<string>>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
foreach (var entry in _playersLanguage)
|
||||||
|
{
|
||||||
|
if (entry.Key == 0 || string.IsNullOrEmpty(entry.Value)) continue;
|
||||||
|
|
||||||
|
if (!grouped.TryGetValue(entry.Value, out var steamIds))
|
||||||
|
grouped[entry.Value] = steamIds = [];
|
||||||
|
|
||||||
|
steamIds.Add(entry.Key.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var steamIds in grouped.Values)
|
||||||
|
steamIds.Sort(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(grouped);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SavePlayersLanguage()
|
private static void SavePlayersLanguage()
|
||||||
{
|
{
|
||||||
|
var json = BuildPlayersJson();
|
||||||
|
|
||||||
|
if (json == null || json == _lastSavedPlayersJson)
|
||||||
|
return;
|
||||||
|
|
||||||
Directory.CreateDirectory(configsFolderPath);
|
Directory.CreateDirectory(configsFolderPath);
|
||||||
string filePath = Path.Combine(configsFolderPath, playersLanguageFileName);
|
File.WriteAllText(Path.Combine(configsFolderPath, playersLanguageFileName), json);
|
||||||
var json = JsonConvert.SerializeObject(_playersLanguage);
|
_lastSavedPlayersJson = json;
|
||||||
if (json != null)
|
|
||||||
File.WriteAllText(filePath, json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string? GetLangCodeFromFile(ulong? playerSteamID)
|
private static string? GetLangCodeFromFile(ulong? playerSteamID)
|
||||||
|
|
@ -346,7 +410,9 @@ namespace src.utils
|
||||||
|
|
||||||
public static void ChangePlayerLanguage(CCSPlayerController? player, string language)
|
public static void ChangePlayerLanguage(CCSPlayerController? player, string language)
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid || player.SteamID == 0) return;
|
||||||
|
if (string.IsNullOrEmpty(language)) return;
|
||||||
|
|
||||||
_playersLanguage.AddOrUpdate(player.SteamID, language, (k, v) => language);
|
_playersLanguage.AddOrUpdate(player.SteamID, language, (k, v) => language);
|
||||||
SavePlayersLanguage();
|
SavePlayersLanguage();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,41 @@ namespace src.utils
|
||||||
private static int cachedBombTick = int.MinValue;
|
private static int cachedBombTick = int.MinValue;
|
||||||
private static CC4? cachedBomb;
|
private static CC4? cachedBomb;
|
||||||
|
|
||||||
|
private static int cachedIdleTick = int.MinValue;
|
||||||
|
private static bool cachedIdle = true;
|
||||||
|
|
||||||
|
private static bool serverActive;
|
||||||
|
|
||||||
|
public static void SetServerActive(bool active) => serverActive = active;
|
||||||
|
|
||||||
|
public static bool IsServerIdle()
|
||||||
|
{
|
||||||
|
if (!serverActive) return true;
|
||||||
|
|
||||||
|
int tick = Server.TickCount;
|
||||||
|
if (tick == cachedIdleTick) return cachedIdle;
|
||||||
|
cachedIdleTick = tick;
|
||||||
|
|
||||||
|
cachedIdle = true;
|
||||||
|
foreach (var player in GetTickPlayers())
|
||||||
|
{
|
||||||
|
if (player == null || !player.IsValid) continue;
|
||||||
|
if (player.IsBot || player.IsHLTV) continue;
|
||||||
|
|
||||||
|
cachedIdle = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedIdle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int cachedPawnMapTick = int.MinValue;
|
||||||
|
private static readonly Dictionary<nint, CCSPlayerController> controllersByPawn = [];
|
||||||
|
|
||||||
|
private static int cachedBotMapTick = int.MinValue;
|
||||||
|
private static readonly Dictionary<nint, CCSPlayerController> botsByController = [];
|
||||||
|
private static readonly HashSet<nint> tickPlayerHandles = [];
|
||||||
|
|
||||||
private static void EnsureTickCache()
|
private static void EnsureTickCache()
|
||||||
{
|
{
|
||||||
int tick = Server.TickCount;
|
int tick = Server.TickCount;
|
||||||
|
|
@ -24,14 +59,40 @@ namespace src.utils
|
||||||
cachedControllers = Utilities.GetPlayers();
|
cachedControllers = Utilities.GetPlayers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EnsureBotMapCache()
|
||||||
|
{
|
||||||
|
int tick = Server.TickCount;
|
||||||
|
if (tick == cachedBotMapTick) return;
|
||||||
|
cachedBotMapTick = tick;
|
||||||
|
|
||||||
|
botsByController.Clear();
|
||||||
|
tickPlayerHandles.Clear();
|
||||||
|
|
||||||
|
foreach (var player in GetTickPlayers())
|
||||||
|
{
|
||||||
|
if (player == null || !player.IsValid) continue;
|
||||||
|
|
||||||
|
tickPlayerHandles.Add(player.Handle);
|
||||||
|
if (!player.IsBot) continue;
|
||||||
|
|
||||||
|
var original = player.OriginalControllerOfCurrentPawn.Value;
|
||||||
|
if (original != null)
|
||||||
|
botsByController[original.Handle] = player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static List<CCSPlayerController> GetTickPlayers()
|
public static List<CCSPlayerController> GetTickPlayers()
|
||||||
{
|
{
|
||||||
|
if (!serverActive) return [];
|
||||||
|
|
||||||
EnsureTickCache();
|
EnsureTickCache();
|
||||||
return cachedControllers;
|
return cachedControllers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CC4? GetTickBomb()
|
public static CC4? GetTickBomb()
|
||||||
{
|
{
|
||||||
|
if (!serverActive) return null;
|
||||||
|
|
||||||
int tick = Server.TickCount;
|
int tick = Server.TickCount;
|
||||||
if (tick != cachedBombTick)
|
if (tick != cachedBombTick)
|
||||||
{
|
{
|
||||||
|
|
@ -69,12 +130,8 @@ namespace src.utils
|
||||||
if (!player.ControllingBot)
|
if (!player.ControllingBot)
|
||||||
return player;
|
return player;
|
||||||
|
|
||||||
return GetTickPlayers().FirstOrDefault(p =>
|
EnsureBotMapCache();
|
||||||
p != null &&
|
return botsByController.TryGetValue(player.Handle, out var bot) ? bot : player;
|
||||||
p.IsValid &&
|
|
||||||
p.IsBot &&
|
|
||||||
p.OriginalControllerOfCurrentPawn.Value != null && p.OriginalControllerOfCurrentPawn.Value == player)
|
|
||||||
?? player;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CCSPlayerController? GetPlayerFromEvent(CCSPlayerController? player)
|
public static CCSPlayerController? GetPlayerFromEvent(CCSPlayerController? player)
|
||||||
|
|
@ -85,12 +142,48 @@ namespace src.utils
|
||||||
if (!player.IsBot)
|
if (!player.IsBot)
|
||||||
return player;
|
return player;
|
||||||
|
|
||||||
return GetTickPlayers().FirstOrDefault(p =>
|
var original = player.OriginalControllerOfCurrentPawn.Value;
|
||||||
p != null &&
|
if (original == null || !original.IsValid || original.IsBot) return player;
|
||||||
p.IsValid &&
|
|
||||||
!p.IsBot &&
|
EnsureBotMapCache();
|
||||||
player.OriginalControllerOfCurrentPawn.Value != null && player.OriginalControllerOfCurrentPawn.Value == p)
|
return tickPlayerHandles.Contains(original.Handle) ? original : player;
|
||||||
?? player;
|
}
|
||||||
|
|
||||||
|
public static CCSPlayerController? GetControllerByPawn(nint pawnHandle)
|
||||||
|
{
|
||||||
|
if (pawnHandle == nint.Zero) return null;
|
||||||
|
|
||||||
|
int tick = Server.TickCount;
|
||||||
|
if (tick != cachedPawnMapTick)
|
||||||
|
{
|
||||||
|
cachedPawnMapTick = tick;
|
||||||
|
controllersByPawn.Clear();
|
||||||
|
|
||||||
|
foreach (var player in GetTickPlayers())
|
||||||
|
{
|
||||||
|
if (player == null || !player.IsValid) continue;
|
||||||
|
|
||||||
|
var handle = player.Pawn?.Value?.Handle;
|
||||||
|
if (handle != null && handle.Value != nint.Zero)
|
||||||
|
controllersByPawn[handle.Value] = player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return controllersByPawn.TryGetValue(pawnHandle, out var controller) ? controller : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void FillSkillHolders(Skills skill, List<CCSPlayerController> buffer)
|
||||||
|
{
|
||||||
|
buffer.Clear();
|
||||||
|
|
||||||
|
foreach (var info in playersByIndex.Values)
|
||||||
|
{
|
||||||
|
if (info.Skill != skill) continue;
|
||||||
|
|
||||||
|
var controller = Utilities.GetPlayerFromIndex((int)info.PlayerIndex);
|
||||||
|
if (controller != null && controller.IsValid)
|
||||||
|
buffer.Add(controller);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<jSkill_PlayerInfo> GetAllPlayers()
|
public static IEnumerable<jSkill_PlayerInfo> GetAllPlayers()
|
||||||
|
|
|
||||||
|
|
@ -220,27 +220,6 @@ namespace src.utils
|
||||||
SnapViewAngles.Value?.Invoke(pawn, angle);
|
SnapViewAngles.Value?.Invoke(pawn, angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetPlayerCollisions(CCSPlayerController? player, bool enable)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (player == null || !player.IsValid) return;
|
|
||||||
|
|
||||||
var pawn = player.PlayerPawn.Value;
|
|
||||||
if (pawn == null || !pawn.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE || pawn.CBodyComponent == null) return;
|
|
||||||
|
|
||||||
var collision = pawn.Collision;
|
|
||||||
if (collision == null) return;
|
|
||||||
|
|
||||||
var collisionGroup = (byte)(enable ? CollisionGroup.COLLISION_GROUP_PLAYER : CollisionGroup.COLLISION_GROUP_DEBRIS);
|
|
||||||
|
|
||||||
collision.CollisionGroup = collisionGroup;
|
|
||||||
collision.CollisionAttribute.CollisionGroup = collisionGroup;
|
|
||||||
Utilities.SetStateChanged(pawn, "CCollisionProperty", "m_collisionAttribute");
|
|
||||||
|
|
||||||
// CollisionRulesChanged(pawn);
|
|
||||||
}
|
|
||||||
|
|
||||||
//public static void CollisionRulesChanged(CBaseEntity? entity)
|
//public static void CollisionRulesChanged(CBaseEntity? entity)
|
||||||
//{
|
//{
|
||||||
// if (entity == null || !entity.IsValid || collisionRulesChangedOffset <= 0) return;
|
// if (entity == null || !entity.IsValid || collisionRulesChangedOffset <= 0) return;
|
||||||
|
|
@ -367,6 +346,30 @@ namespace src.utils
|
||||||
return !ff && !tae; // same team + FF off -> engine will zero this damage
|
return !ff && !tae; // same team + FF off -> engine will zero this damage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsFriendlyFireBlocked(Skills skill, CTakeDamageInfo? info, CCSPlayerPawn? victimPawn)
|
||||||
|
{
|
||||||
|
if (!IsSameTeamHit(info, victimPawn)) return false;
|
||||||
|
if (!SkillsInfo.GetValue<bool>(skill, "friendlyFire")) return true;
|
||||||
|
|
||||||
|
bool ff = ConVar.Find("mp_friendlyfire")?.GetPrimitiveValue<bool>() ?? false;
|
||||||
|
bool tae = ConVar.Find("mp_teammates_are_enemies")?.GetPrimitiveValue<bool>() ?? false;
|
||||||
|
return !ff && !tae;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSameTeamHit(CTakeDamageInfo? info, CCSPlayerPawn? victimPawn)
|
||||||
|
{
|
||||||
|
if (info == null || victimPawn == null || !victimPawn.IsValid) return false;
|
||||||
|
|
||||||
|
var attackerEnt = info.Attacker?.Value;
|
||||||
|
if (attackerEnt == null || !attackerEnt.IsValid) return false;
|
||||||
|
if (attackerEnt.Handle == victimPawn.Handle) return false;
|
||||||
|
|
||||||
|
var attackerPawn = new CCSPlayerPawn(attackerEnt.Handle);
|
||||||
|
if (!attackerPawn.IsValid || attackerPawn.DesignerName != "player") return false;
|
||||||
|
|
||||||
|
return attackerPawn.TeamNum == victimPawn.TeamNum;
|
||||||
|
}
|
||||||
|
|
||||||
public static float GetTeamDamageMultiplier(Skills skill)
|
public static float GetTeamDamageMultiplier(Skills skill)
|
||||||
{
|
{
|
||||||
float reduction = SkillsInfo.GetValue<float>(skill, "dmgReductionForTeamates");
|
float reduction = SkillsInfo.GetValue<float>(skill, "dmgReductionForTeamates");
|
||||||
|
|
@ -945,9 +948,17 @@ namespace src.utils
|
||||||
{
|
{
|
||||||
if (jRandomSkills.Instance.MenuManager == null)
|
if (jRandomSkills.Instance.MenuManager == null)
|
||||||
jRandomSkills.Instance.MenuManager = new WasdManager();
|
jRandomSkills.Instance.MenuManager = new WasdManager();
|
||||||
|
|
||||||
|
ApplyMenuVisibleItems();
|
||||||
return jRandomSkills.Instance.MenuManager;
|
return jRandomSkills.Instance.MenuManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ApplyMenuVisibleItems()
|
||||||
|
{
|
||||||
|
int visibleItems = Config.LoadedConfig.HtmlHudCustomisation.WSADMenuVisibleItems;
|
||||||
|
WASDMenuAPI.WasdMenuPlayer.DefaultVisibleOptions = visibleItems < 1 ? 3 : Math.Min(visibleItems, 10);
|
||||||
|
}
|
||||||
|
|
||||||
public static void CloseMenu(CCSPlayerController? player)
|
public static void CloseMenu(CCSPlayerController? player)
|
||||||
{
|
{
|
||||||
var manager = GetMenuManager();
|
var manager = GetMenuManager();
|
||||||
|
|
@ -1092,11 +1103,20 @@ namespace src.utils
|
||||||
|
|
||||||
if (lastElement != null)
|
if (lastElement != null)
|
||||||
{
|
{
|
||||||
string encodedLastElement = isIlliterate
|
string lastText = lastElement.Value.Item1;
|
||||||
? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(lastElement.Value.Item1)!)
|
string lastColor = string.Empty;
|
||||||
: System.Net.WebUtility.HtmlEncode(lastElement.Value.Item1);
|
|
||||||
|
|
||||||
menu.Add($"\u202A{encodedLastElement}\u202C", (p, option) =>
|
if (lastText.Length > 8 && lastText[0] == '#' && lastText[7] == '|')
|
||||||
|
{
|
||||||
|
lastColor = lastText[..8];
|
||||||
|
lastText = lastText[8..];
|
||||||
|
}
|
||||||
|
|
||||||
|
string encodedLastElement = isIlliterate
|
||||||
|
? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(lastText)!)
|
||||||
|
: System.Net.WebUtility.HtmlEncode(lastText);
|
||||||
|
|
||||||
|
menu.Add($"{lastColor}\u202A{encodedLastElement}\u202C", (p, option) =>
|
||||||
{
|
{
|
||||||
jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { lastElement.Value.Item2 }]);
|
jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { lastElement.Value.Item2 }]);
|
||||||
if (lastElement.Value.Item3)
|
if (lastElement.Value.Item3)
|
||||||
|
|
@ -1131,7 +1151,7 @@ namespace src.utils
|
||||||
TerminateRoundFunc.Value?.Invoke(jRandomSkills.Instance.GameRules.Handle, 5f, roundEndReason, 0, 0);
|
TerminateRoundFunc.Value?.Invoke(jRandomSkills.Instance.GameRules.Handle, 5f, roundEndReason, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void TerminateRound(CsTeam winnerTeam)
|
public static void TerminateRound(CsTeam winnerTeam, CCSPlayerController? bonusPlayer = null)
|
||||||
{
|
{
|
||||||
if (jRandomSkills.Instance == null || jRandomSkills.Instance.GameRules == null) return;
|
if (jRandomSkills.Instance == null || jRandomSkills.Instance.GameRules == null) return;
|
||||||
var teams = Utilities.FindAllEntitiesByDesignerName<CCSTeam>("cs_team_manager");
|
var teams = Utilities.FindAllEntitiesByDesignerName<CCSTeam>("cs_team_manager");
|
||||||
|
|
@ -1144,6 +1164,40 @@ namespace src.utils
|
||||||
|
|
||||||
UpdateServerTeamScores(ctScore, tScore);
|
UpdateServerTeamScores(ctScore, tScore);
|
||||||
jRandomSkills.Instance.GameRules?.TerminateRound(5f, winnerTeam == CsTeam.CounterTerrorist ? RoundEndReason.BombDefused : RoundEndReason.TargetBombed);
|
jRandomSkills.Instance.GameRules?.TerminateRound(5f, winnerTeam == CsTeam.CounterTerrorist ? RoundEndReason.BombDefused : RoundEndReason.TargetBombed);
|
||||||
|
|
||||||
|
AwardRoundEndMoney(winnerTeam, bonusPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AwardRoundEndMoney(CsTeam winnerTeam, CCSPlayerController? bonusPlayer = null)
|
||||||
|
{
|
||||||
|
int winnerReward = winnerTeam == CsTeam.CounterTerrorist
|
||||||
|
? ConVar.Find("cash_team_win_by_defusing_bomb")?.GetPrimitiveValue<int>() ?? 3500
|
||||||
|
: ConVar.Find("cash_team_terrorist_win_bomb")?.GetPrimitiveValue<int>() ?? 3500;
|
||||||
|
|
||||||
|
if (winnerReward <= 0) return;
|
||||||
|
|
||||||
|
int maxMoney = ConVar.Find("mp_maxmoney")?.GetPrimitiveValue<int>() ?? 16000;
|
||||||
|
int personalBonus = bonusPlayer == null
|
||||||
|
? 0
|
||||||
|
: winnerTeam == CsTeam.CounterTerrorist
|
||||||
|
? ConVar.Find("cash_player_defused_bomb")?.GetPrimitiveValue<int>() ?? 300
|
||||||
|
: ConVar.Find("cash_player_bomb_planted")?.GetPrimitiveValue<int>() ?? 300;
|
||||||
|
|
||||||
|
foreach (var player in PlayerManager.GetTickPlayers())
|
||||||
|
{
|
||||||
|
if (player == null || !player.IsValid) continue;
|
||||||
|
if (player.Team != winnerTeam) continue;
|
||||||
|
|
||||||
|
var moneyServices = player.InGameMoneyServices;
|
||||||
|
if (moneyServices == null) continue;
|
||||||
|
|
||||||
|
int reward = winnerReward;
|
||||||
|
if (bonusPlayer != null && bonusPlayer.IsValid && player.Index == bonusPlayer.Index)
|
||||||
|
reward += personalBonus;
|
||||||
|
|
||||||
|
moneyServices.Account = Math.Clamp(moneyServices.Account + reward, 0, maxMoney);
|
||||||
|
Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInGameMoneyServices");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateServerTeamScores(short ctScore, short tScore)
|
private static void UpdateServerTeamScores(short ctScore, short tScore)
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,9 @@ namespace src.utils
|
||||||
json = sr.ReadToEnd();
|
json = sr.ReadToEnd();
|
||||||
|
|
||||||
var root = JsonConvert.DeserializeObject<JArray>(json);
|
var root = JsonConvert.DeserializeObject<JArray>(json);
|
||||||
|
bool needsRewrite = root == null;
|
||||||
|
var present = new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
|
||||||
if (root != null)
|
if (root != null)
|
||||||
foreach (var skillObj in root)
|
foreach (var skillObj in root)
|
||||||
{
|
{
|
||||||
|
|
@ -51,7 +54,23 @@ namespace src.utils
|
||||||
if (string.IsNullOrEmpty(name)) continue;
|
if (string.IsNullOrEmpty(name)) continue;
|
||||||
|
|
||||||
var instance = newConfig.FirstOrDefault(x => x.Name == name.ToString());
|
var instance = newConfig.FirstOrDefault(x => x.Name == name.ToString());
|
||||||
if (instance != null) JsonConvert.PopulateObject(skillObj.ToString(), instance);
|
if (instance == null) continue;
|
||||||
|
|
||||||
|
present.Add(name);
|
||||||
|
|
||||||
|
if (skillObj is JObject current && HasMissingKeys(current, instance))
|
||||||
|
needsRewrite = true;
|
||||||
|
|
||||||
|
JsonConvert.PopulateObject(skillObj.ToString(), instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newConfig.Any(s => !string.IsNullOrEmpty(s.Name) && !present.Contains(s.Name)))
|
||||||
|
needsRewrite = true;
|
||||||
|
|
||||||
|
if (needsRewrite)
|
||||||
|
{
|
||||||
|
SaveConfig(newConfig);
|
||||||
|
Instance.Logger.LogInformation("skillsInfo.json was missing keys; rewritten with the defaults filled in.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|
@ -63,6 +82,16 @@ namespace src.utils
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool HasMissingKeys(JObject current, DefaultSkillInfo expectedInstance)
|
||||||
|
{
|
||||||
|
var expected = JObject.FromObject(expectedInstance);
|
||||||
|
|
||||||
|
foreach (var property in expected.Properties())
|
||||||
|
if (current[property.Name] == null) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static void SaveConfig(SkillsInfoModel config)
|
public static void SaveConfig(SkillsInfoModel config)
|
||||||
{
|
{
|
||||||
lock (fileLock)
|
lock (fileLock)
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"ConfigName": "Default",
|
"ConfigName": "Default",
|
||||||
"GameMode": 3,
|
"GameMode": 3,
|
||||||
"YourSkillChatInfo": true,
|
"YourSkillChatInfo": true,
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
"WSADMenuItemLineColor": "white",
|
"WSADMenuItemLineColor": "white",
|
||||||
"WSADMenuItemHoverLineColor": "orange",
|
"WSADMenuItemHoverLineColor": "orange",
|
||||||
"WSADMenuItemLineSize": "sm",
|
"WSADMenuItemLineSize": "sm",
|
||||||
|
"WSADMenuVisibleItems": 3,
|
||||||
"WSADMenuControllsLineSize": "sm",
|
"WSADMenuControllsLineSize": "sm",
|
||||||
"WSADMenuControllsLineColor1": "cyan",
|
"WSADMenuControllsLineColor1": "cyan",
|
||||||
"WSADMenuControllsLineColor2": "white",
|
"WSADMenuControllsLineColor2": "white",
|
||||||
|
|
@ -195,7 +196,7 @@
|
||||||
"Permissions": "@jRandomSkills/admin"
|
"Permissions": "@jRandomSkills/admin"
|
||||||
},
|
},
|
||||||
"CheckEntityCommand": {
|
"CheckEntityCommand": {
|
||||||
"Alias": "ent, entity, checkentity, check_entity, sprawdzencje, checkent, check_ent, 检查实体, 检查实体",
|
"Alias": "ent, entity, checkentity, check_entity, sprawdzencje, checkent, check_ent, 检查实体",
|
||||||
"Permissions": "@jRandomSkills/owner"
|
"Permissions": "@jRandomSkills/owner"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -219,7 +220,7 @@
|
||||||
"TimeToNextVoting": 15.0,
|
"TimeToNextVoting": 15.0,
|
||||||
"TimeToNextSameVoting": 500.0,
|
"TimeToNextSameVoting": 500.0,
|
||||||
"MinimumPlayersToStartVoting": 2,
|
"MinimumPlayersToStartVoting": 2,
|
||||||
"Alias": "map, mapa, changemap, zmienmape, zmienmape, mudarMapa, trocarMapa, 更换地图, 更改地图",
|
"Alias": "map, mapa, changemap, zmienmape, mudarMapa, trocarMapa, 更换地图, 更改地图",
|
||||||
"Permissions": "@jRandomSkills/admin"
|
"Permissions": "@jRandomSkills/admin"
|
||||||
},
|
},
|
||||||
"SwapCommand": {
|
"SwapCommand": {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
|
|
@ -146,6 +146,7 @@
|
||||||
"TickCooldown": 17,
|
"TickCooldown": 17,
|
||||||
"GrenadeLimit": 1,
|
"GrenadeLimit": 1,
|
||||||
"DmgReductionForTeamates": 0.5,
|
"DmgReductionForTeamates": 0.5,
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -321,6 +322,7 @@
|
||||||
{
|
{
|
||||||
"ChanceFrom": 1.15,
|
"ChanceFrom": 1.15,
|
||||||
"ChanceTo": 1.35,
|
"ChanceTo": 1.35,
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -725,6 +727,7 @@
|
||||||
{
|
{
|
||||||
"StorePercent": 0.5,
|
"StorePercent": 0.5,
|
||||||
"MaxStored": 100.0,
|
"MaxStored": 100.0,
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -883,6 +886,7 @@
|
||||||
"Rarity": "Common"
|
"Rarity": "Common"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -993,6 +997,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ExtraDamagePercent": 0.25,
|
"ExtraDamagePercent": 0.25,
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -1697,6 +1702,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"HealthDamageMultiplier": 0.3,
|
"HealthDamageMultiplier": 0.3,
|
||||||
|
"ManageAutoKick": true,
|
||||||
"NeedsTeammates": true,
|
"NeedsTeammates": true,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -1798,6 +1804,7 @@
|
||||||
"Rarity": "Epic"
|
"Rarity": "Epic"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"Cooldown": 5.0,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -1832,6 +1839,7 @@
|
||||||
"ChanceFrom": 0.15,
|
"ChanceFrom": 0.15,
|
||||||
"ChanceTo": 0.3,
|
"ChanceTo": 0.3,
|
||||||
"DmgReductionForTeamates": 0.5,
|
"DmgReductionForTeamates": 0.5,
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -1851,6 +1859,7 @@
|
||||||
"ExplosionDamage": 50,
|
"ExplosionDamage": 50,
|
||||||
"PropModel": "models/props/de_train/hr_t/barrel_a/barrel_a.vmdl",
|
"PropModel": "models/props/de_train/hr_t/barrel_a/barrel_a.vmdl",
|
||||||
"DmgReductionForTeamates": 0.5,
|
"DmgReductionForTeamates": 0.5,
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": true,
|
"DisableOnFreezeTime": true,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -2008,6 +2017,7 @@
|
||||||
"ExplosionRadius": 500.0,
|
"ExplosionRadius": 500.0,
|
||||||
"ExplosionDamage": 999,
|
"ExplosionDamage": 999,
|
||||||
"DmgReductionForTeamates": 0.5,
|
"DmgReductionForTeamates": 0.5,
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -2103,6 +2113,7 @@
|
||||||
"Rarity": "Common"
|
"Rarity": "Common"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -2243,6 +2254,7 @@
|
||||||
"DmgReductionForTeamates": 0.5,
|
"DmgReductionForTeamates": 0.5,
|
||||||
"Cooldown": 10.0,
|
"Cooldown": 10.0,
|
||||||
"Force": 1000.0,
|
"Force": 1000.0,
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -2276,6 +2288,7 @@
|
||||||
{
|
{
|
||||||
"MaxSpeedVelocity": 2.0,
|
"MaxSpeedVelocity": 2.0,
|
||||||
"MaxDamageVelocity": 2.0,
|
"MaxDamageVelocity": 2.0,
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
@ -2356,6 +2369,7 @@
|
||||||
{
|
{
|
||||||
"DamageMultiplier": 2.0,
|
"DamageMultiplier": 2.0,
|
||||||
"ToleranceDeg": 45.0,
|
"ToleranceDeg": 45.0,
|
||||||
|
"FriendlyFire": true,
|
||||||
"NeedsTeammates": false,
|
"NeedsTeammates": false,
|
||||||
"DisableOnFreezeTime": false,
|
"DisableOnFreezeTime": false,
|
||||||
"DisableOnPistolRound": false,
|
"DisableOnPistolRound": false,
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -487,7 +487,7 @@
|
||||||
"returntosender": "Zwrot do Nadawcy",
|
"returntosender": "Zwrot do Nadawcy",
|
||||||
"returntosender_desc": "Pierwsze trafienie wroga powoduje, że wraca on na swój resp",
|
"returntosender_desc": "Pierwsze trafienie wroga powoduje, że wraca on na swój resp",
|
||||||
|
|
||||||
"rewind": "Cofnięcie",
|
"rewind": "Cofnięcietruearmor",
|
||||||
"rewind_desc": "Naciśnij [css_useSkill], aby zostawić znacznik i wkrótce do niego wrócić",
|
"rewind_desc": "Naciśnij [css_useSkill], aby zostawić znacznik i wkrótce do niego wrócić",
|
||||||
"rewind_pending_info": "POWRÓT: {0}",
|
"rewind_pending_info": "POWRÓT: {0}",
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue