v1.2.2.b6
Performance & stability pass (issue #34 + audit) General: - DisableAll resets only skills active in the previous round instead of all loaded skills (~89ms -> ~5ms) - Removed per-tick LINQ/allocations, O(1) HUD skill lookups, translation substitutions moved to load time - ForceFullUpdate creates the network service once per broadcast; view-angle guard only skips a genuine (0,0,0) placeholder - Debug hooks/logging only active when DebugMode is enabled (single reused writer) - Player skill state unified to a single collection (fixes a rare disconnect race) - Gamedata signatures resolved lazily/isolated; hardened tick/transmit paths against invalid handles - Description HUD honours always-show / -1 duration consistently across all assignment paths Skills: - Jester: fixed the no-damage effect leaking after a mid-round skill change (Thief/Deactivator) - Jackal: fixed trail entities accumulating over a round - Falcon Eye / Third Eye / Spectator: camera state cleared on disable Also: added missing translation keys as untranslated markers across language files. Fixes #34
This commit is contained in:
parent
a0f37ba1a1
commit
25cd8eb989
31 changed files with 202 additions and 79 deletions
23
README.md
23
README.md
|
|
@ -374,6 +374,29 @@ This plugin uses content from the following projects:
|
||||||
|
|
||||||
## 📋 Changelog
|
## 📋 Changelog
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>v1.2.2.b6</b></summary>
|
||||||
|
|
||||||
|
- #### General:
|
||||||
|
- ###### Performance: much faster round changes - only skills that were actually active in the previous round are reset, instead of every loaded skill (`DisableAll` dropped from ~89ms to ~5ms on a full server). [by: [@ByDexterTR](https://github.com/ByDexterTR)]
|
||||||
|
- ###### Performance & memory: removed per-tick LINQ and skill-name allocations in the tick loop, made HUD skill lookups O(1), and moved translation color/button substitutions to load time. [by: [@ByDexterTR](https://github.com/ByDexterTR)]
|
||||||
|
- ###### `ForceFullUpdate` now creates the network service once per broadcast, and the round-start view-angle guard only skips a genuine (0, 0, 0) spawn placeholder instead of over-skipping valid angles. [by: [@ByDexterTR](https://github.com/ByDexterTR)]
|
||||||
|
- ###### Debug logging is only installed when `DebugMode` is enabled and writes through a single reused writer instead of reopening the file for every line. [by: [@ByDexterTR](https://github.com/ByDexterTR)]
|
||||||
|
- ###### Player skill state unified to a single collection (removed a duplicate list), closing a rare desync/race on player disconnect. [by: [@ByDexterTR](https://github.com/ByDexterTR)]
|
||||||
|
- ###### Gamedata signatures are now resolved lazily and in isolation, so one broken signature after a CS2 update no longer takes down the whole utility layer. [by: [@ByDexterTR](https://github.com/ByDexterTR)]
|
||||||
|
- ###### Hardened many tick/transmit code paths against invalid player handles to prevent rare crashes. [by: [@ByDexterTR](https://github.com/ByDexterTR)]
|
||||||
|
- ###### The skill description HUD honours "always show" / `-1` duration consistently across every assignment path. [by: [@ByDexterTR](https://github.com/ByDexterTR)]
|
||||||
|
|
||||||
|
- #### Skill improvements:
|
||||||
|
- ###### Jester (Joker):
|
||||||
|
- ###### Fixed the no-damage effect leaking onto a player after their skill was changed mid-round (e.g. stolen by Thief or removed by Deactivator) - the internal state is now cleared when the skill is disabled. [by: [@ByDexterTR](https://github.com/ByDexterTR)]
|
||||||
|
- ###### Jackal:
|
||||||
|
- ###### Fixed trail entities piling up over a round - the previous trail is now removed before a new one is spawned. [by: [@ByDexterTR](https://github.com/ByDexterTR)]
|
||||||
|
- ###### Falcon Eye / Third Eye / Spectator:
|
||||||
|
- ###### Camera state is now fully cleared when the skill is disabled. [by: [@ByDexterTR](https://github.com/ByDexterTR)]
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>v1.2.2.b5</b></summary>
|
<summary><b>v1.2.2.b5</b></summary>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ public class WASDMenuAPI
|
||||||
|
|
||||||
public static void LoadPlugin(BasePlugin basePlugin, bool hotReload)
|
public static void LoadPlugin(BasePlugin basePlugin, bool hotReload)
|
||||||
{
|
{
|
||||||
var wasdMenuManager = new WasdManager();
|
|
||||||
basePlugin.RegisterEventHandler<EventPlayerActivate>((@event, info) =>
|
basePlugin.RegisterEventHandler<EventPlayerActivate>((@event, info) =>
|
||||||
{
|
{
|
||||||
if (@event.Userid != null)
|
if (@event.Userid != null)
|
||||||
|
|
@ -46,7 +45,7 @@ public class WASDMenuAPI
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
foreach (var player in Players.Values.Where(p => p.MainMenu != null))
|
foreach (var player in Players.Values.Where(p => p.MainMenu != null && p.Player?.IsValid == true))
|
||||||
{
|
{
|
||||||
if ((player.Buttons & PlayerButtons.Forward) == 0 && (player.Player.Buttons & PlayerButtons.Forward) != 0)
|
if ((player.Buttons & PlayerButtons.Forward) == 0 && (player.Player.Buttons & PlayerButtons.Forward) != 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ namespace src.command
|
||||||
skillPlayer.Skill = skill.Skill;
|
skillPlayer.Skill = skill.Skill;
|
||||||
skillPlayer.SpecialSkill = Skills.None;
|
skillPlayer.SpecialSkill = Skills.None;
|
||||||
Instance.SkillAction(skill.Skill.ToString(), "EnableSkill", [targetPlayer]);
|
Instance.SkillAction(skill.Skill.ToString(), "EnableSkill", [targetPlayer]);
|
||||||
skillPlayer.SkillDescriptionHudExpired = DateTime.Now.AddSeconds(Config.LoadedConfig.SkillDescriptionDuration);
|
skillPlayer.SkillDescriptionHudExpired = Config.LoadedConfig.SkillDescriptionDuration == -1 ? DateTime.MaxValue : DateTime.Now.AddSeconds(Config.LoadedConfig.SkillDescriptionDuration);
|
||||||
|
|
||||||
if (player == null)
|
if (player == null)
|
||||||
{
|
{
|
||||||
|
|
@ -504,7 +504,7 @@ namespace src.command
|
||||||
Instance.SkillAction(skillPlayer.Skill.ToString(), "DisableSkill", [targetPlayer]);
|
Instance.SkillAction(skillPlayer.Skill.ToString(), "DisableSkill", [targetPlayer]);
|
||||||
skillPlayer.Skill = skill.Skill;
|
skillPlayer.Skill = skill.Skill;
|
||||||
skillPlayer.SpecialSkill = src.player.Skills.None;
|
skillPlayer.SpecialSkill = src.player.Skills.None;
|
||||||
skillPlayer.SkillDescriptionHudExpired = DateTime.Now.AddSeconds(Config.LoadedConfig.SkillDescriptionDuration);
|
skillPlayer.SkillDescriptionHudExpired = Config.LoadedConfig.SkillDescriptionDuration == -1 ? DateTime.MaxValue : DateTime.Now.AddSeconds(Config.LoadedConfig.SkillDescriptionDuration);
|
||||||
|
|
||||||
if (skill.Skill == src.player.Skills.None)
|
if (skill.Skill == src.player.Skills.None)
|
||||||
Event.staticSkills.TryRemove(targetPlayer.Index, out _);
|
Event.staticSkills.TryRemove(targetPlayer.Index, out _);
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,18 @@ namespace src
|
||||||
#pragma warning disable CS8618
|
#pragma warning disable CS8618
|
||||||
public static jRandomSkills Instance { get; private set; }
|
public static jRandomSkills Instance { get; private set; }
|
||||||
#pragma warning restore CS8618
|
#pragma warning restore CS8618
|
||||||
public ConcurrentBag<jSkill_PlayerInfo> SkillPlayer { get; set; } = [];
|
public IEnumerable<jSkill_PlayerInfo> SkillPlayer => PlayerManager.GetAllPlayers();
|
||||||
public Random Random { get; } = new Random();
|
public Random Random { get; } = new Random();
|
||||||
public CCSGameRules? GameRules { get; set; }
|
public CCSGameRules? GameRules { get; set; }
|
||||||
private ConcurrentBag<string> ManifestResources { get; set; } = ["models/sprays/spray_plane.vmdl"];
|
private ConcurrentBag<string> ManifestResources { get; set; } = ["models/sprays/spray_plane.vmdl"];
|
||||||
public IWasdMenuManager? MenuManager;
|
public IWasdMenuManager? MenuManager;
|
||||||
|
// Skills that were enabled at least once this round; used to reset only those on round change (not all 124).
|
||||||
|
public static readonly ConcurrentDictionary<string, byte> ActiveSkillsThisRound = new();
|
||||||
|
|
||||||
public override string ModuleName => "[CS2] [ jRandomSkills ]";
|
public override string ModuleName => "[CS2] [ jRandomSkills ]";
|
||||||
public override string ModuleAuthor => "D3X, Juzlus";
|
public override string ModuleAuthor => "D3X, Juzlus";
|
||||||
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.2.b5";
|
public override string ModuleVersion => "1.2.2.b6";
|
||||||
|
|
||||||
public override void Load(bool hotReload)
|
public override void Load(bool hotReload)
|
||||||
{
|
{
|
||||||
|
|
@ -85,6 +87,9 @@ namespace src
|
||||||
if (string.IsNullOrEmpty(skill))
|
if (string.IsNullOrEmpty(skill))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
if (methodName == "EnableSkill")
|
||||||
|
ActiveSkillsThisRound.TryAdd(skill, 0);
|
||||||
|
|
||||||
var method = _skillMethodCache.GetOrAdd((skill, methodName), key =>
|
var method = _skillMethodCache.GetOrAdd((skill, methodName), key =>
|
||||||
{
|
{
|
||||||
string className = $"src.player.skills.{key.Skill}";
|
string className = $"src.player.skills.{key.Skill}";
|
||||||
|
|
@ -292,12 +297,9 @@ namespace src
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a player info by index. O(n) lookup - consider caching if called frequently.
|
|
||||||
/// </summary>
|
|
||||||
internal jSkill_PlayerInfo? GetPlayerInfoByIndex(uint playerIndex)
|
internal jSkill_PlayerInfo? GetPlayerInfoByIndex(uint playerIndex)
|
||||||
{
|
{
|
||||||
return SkillPlayer.FirstOrDefault(p => p.PlayerIndex == playerIndex);
|
return PlayerManager.GetPlayerByIndex(playerIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -329,6 +331,23 @@ namespace src
|
||||||
public static class SkillData
|
public static class SkillData
|
||||||
{
|
{
|
||||||
public static ConcurrentBag<jSkill_SkillInfo> Skills { get; } = [];
|
public static ConcurrentBag<jSkill_SkillInfo> Skills { get; } = [];
|
||||||
|
|
||||||
|
private static Dictionary<Skills, jSkill_SkillInfo>? _bySkill;
|
||||||
|
|
||||||
|
public static jSkill_SkillInfo? GetInfo(Skills skill)
|
||||||
|
{
|
||||||
|
var map = _bySkill;
|
||||||
|
if (map == null)
|
||||||
|
{
|
||||||
|
map = new Dictionary<Skills, jSkill_SkillInfo>();
|
||||||
|
foreach (var s in Skills)
|
||||||
|
map[s.Skill] = s;
|
||||||
|
_bySkill = map;
|
||||||
|
}
|
||||||
|
return map.TryGetValue(skill, out var info) ? info : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Invalidate() => _bySkill = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CS2ConsoleColors
|
public enum CS2ConsoleColors
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,17 @@ namespace src.player
|
||||||
{
|
{
|
||||||
private static string sessionId = "00000";
|
private static string sessionId = "00000";
|
||||||
private static readonly string debugFolder = Path.Combine(Instance.ModuleDirectory, "logs");
|
private static readonly string debugFolder = Path.Combine(Instance.ModuleDirectory, "logs");
|
||||||
|
private static StreamWriter? _writer;
|
||||||
|
private static readonly object _writeLock = new();
|
||||||
|
|
||||||
public static void Load()
|
public static void Load()
|
||||||
{
|
{
|
||||||
sessionId = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}";
|
sessionId = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}";
|
||||||
|
lock (_writeLock) { _writer?.Dispose(); _writer = null; }
|
||||||
|
|
||||||
|
if (Config.LoadedConfig.DebugMode != true)
|
||||||
|
return;
|
||||||
|
|
||||||
Instance.RegisterEventHandler<EventPlayerConnectFull>((@event, info) =>
|
Instance.RegisterEventHandler<EventPlayerConnectFull>((@event, info) =>
|
||||||
{
|
{
|
||||||
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
var player = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||||
|
|
@ -131,13 +138,25 @@ namespace src.player
|
||||||
if (Config.LoadedConfig.DebugMode != true)
|
if (Config.LoadedConfig.DebugMode != true)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// GetAllEntityIndexes();
|
lock (_writeLock)
|
||||||
|
{
|
||||||
string filename = $"debug_{sessionId}.txt";
|
_writer ??= CreateWriter();
|
||||||
string path = Path.Combine(debugFolder, filename);
|
_writer?.WriteLine($"[{DateTime.Now:dd.MM.yyyy HH:mm:ss}] {message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StreamWriter? CreateWriter()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
Directory.CreateDirectory(debugFolder);
|
Directory.CreateDirectory(debugFolder);
|
||||||
File.AppendAllText(path, $"[{DateTime.Now:dd.MM.yyyy HH:mm:ss}] {message}{Environment.NewLine}", System.Text.Encoding.UTF8);
|
string path = Path.Combine(debugFolder, $"debug_{sessionId}.txt");
|
||||||
|
return new StreamWriter(path, append: true, System.Text.Encoding.UTF8) { AutoFlush = true };
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GetAllEntityIndexes()
|
private static void GetAllEntityIndexes()
|
||||||
|
|
|
||||||
|
|
@ -417,22 +417,50 @@ namespace src.player
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly Dictionary<Skills, string> _skillNames =
|
||||||
|
Enum.GetValues<Skills>().ToDictionary(s => s, s => s.ToString());
|
||||||
|
private static readonly HashSet<Skills> _activeSkillsSet = [];
|
||||||
|
private static readonly List<Skills> _activeSkillsList = [];
|
||||||
|
private static readonly Comparison<Skills> _tickOrderCmp = (a, b) => TickOrder(a).CompareTo(TickOrder(b));
|
||||||
|
private static HashSet<Skills>? _freezeDisabledSkills;
|
||||||
|
|
||||||
|
// AreaReaper and ChillOut depend on other skills' tick results, so they must tick last.
|
||||||
|
private static int TickOrder(Skills s) => s == Skills.AreaReaper ? 2 : s == Skills.ChillOut ? 1 : 0;
|
||||||
|
|
||||||
|
private static HashSet<Skills> BuildFreezeDisabledSkills()
|
||||||
|
{
|
||||||
|
var set = new HashSet<Skills>();
|
||||||
|
foreach (var s in SkillData.Skills)
|
||||||
|
if (SkillsInfo.GetValue<bool>(s.Skill, "disableOnFreezeTime"))
|
||||||
|
set.Add(s.Skill);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InvalidateFreezeDisabledCache() => _freezeDisabledSkills = null;
|
||||||
|
|
||||||
private static void OnTick()
|
private static void OnTick()
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
{
|
{
|
||||||
var activeSkills = Instance.SkillPlayer
|
_activeSkillsSet.Clear();
|
||||||
.Where(p => !p.IsDrawing)
|
_activeSkillsList.Clear();
|
||||||
.Select(p => p.Skill)
|
foreach (var p in Instance.SkillPlayer)
|
||||||
.Distinct()
|
{
|
||||||
.OrderBy(skill => skill.ToString() == "AreaReaper")
|
if (p.IsDrawing) continue;
|
||||||
.ThenBy(skill => skill.ToString() == "ChillOut");
|
if (_activeSkillsSet.Add(p.Skill))
|
||||||
|
_activeSkillsList.Add(p.Skill);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var skill in activeSkills)
|
_activeSkillsList.Sort(_tickOrderCmp);
|
||||||
if (SkillsInfo.GetValue<bool>(skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime())
|
|
||||||
continue;
|
bool freeze = SkillUtils.IsFreezeTime();
|
||||||
else
|
_freezeDisabledSkills ??= BuildFreezeDisabledSkills();
|
||||||
Instance.SkillAction(skill.ToString(), "OnTick");
|
|
||||||
|
foreach (var skill in _activeSkillsList)
|
||||||
|
{
|
||||||
|
if (freeze && _freezeDisabledSkills.Contains(skill)) continue;
|
||||||
|
Instance.SkillAction(_skillNames[skill], "OnTick");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -466,7 +494,6 @@ namespace src.player
|
||||||
DisplayHUD = true,
|
DisplayHUD = true,
|
||||||
SkillUsed = false,
|
SkillUsed = false,
|
||||||
};
|
};
|
||||||
Instance.SkillPlayer.Add(playerInfo);
|
|
||||||
PlayerManager.Register(playerInfo);
|
PlayerManager.Register(playerInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -502,8 +529,6 @@ namespace src.player
|
||||||
|
|
||||||
Instance.SkillAction(skillPlayer.Skill.ToString(), "DisableSkill", [player]);
|
Instance.SkillAction(skillPlayer.Skill.ToString(), "DisableSkill", [player]);
|
||||||
|
|
||||||
var items = Instance.SkillPlayer.ToList();
|
|
||||||
Instance.SkillPlayer = [.. items.Where(p => p.PlayerIndex != player.Index)];
|
|
||||||
PlayerManager.UnregisterPlayer(player.Index);
|
PlayerManager.UnregisterPlayer(player.Index);
|
||||||
EntityManager.DestroyPlayerEntities(player.Index);
|
EntityManager.DestroyPlayerEntities(player.Index);
|
||||||
|
|
||||||
|
|
@ -606,6 +631,10 @@ namespace src.player
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||||
if (playerInfo == null) continue;
|
if (playerInfo == null) continue;
|
||||||
|
|
||||||
|
ActiveSkillsThisRound.TryAdd(playerInfo.Skill.ToString(), 0);
|
||||||
|
if (playerInfo.SpecialSkill != noneSkill.Skill)
|
||||||
|
ActiveSkillsThisRound.TryAdd(playerInfo.SpecialSkill.ToString(), 0);
|
||||||
|
|
||||||
Instance.SkillAction(playerInfo.Skill.ToString(), "DisableSkill", [player]);
|
Instance.SkillAction(playerInfo.Skill.ToString(), "DisableSkill", [player]);
|
||||||
|
|
||||||
playerInfo.Skill = noneSkill.Skill;
|
playerInfo.Skill = noneSkill.Skill;
|
||||||
|
|
@ -617,8 +646,9 @@ namespace src.player
|
||||||
RestorePlayer(player);
|
RestorePlayer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var skill in SkillData.Skills)
|
foreach (var skillName in ActiveSkillsThisRound.Keys)
|
||||||
Instance.SkillAction(skill.Skill.ToString(), "NewRound");
|
Instance.SkillAction(skillName, "NewRound");
|
||||||
|
ActiveSkillsThisRound.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -649,6 +679,7 @@ namespace src.player
|
||||||
EntityManager.DestroyAllTracked();
|
EntityManager.DestroyAllTracked();
|
||||||
foreach (var skill in SkillData.Skills)
|
foreach (var skill in SkillData.Skills)
|
||||||
Instance.SkillAction(skill.Skill.ToString(), "NewRound");
|
Instance.SkillAction(skill.Skill.ToString(), "NewRound");
|
||||||
|
ActiveSkillsThisRound.Clear();
|
||||||
|
|
||||||
playersSkills.Clear();
|
playersSkills.Clear();
|
||||||
staticSkills.Clear();
|
staticSkills.Clear();
|
||||||
|
|
@ -657,7 +688,6 @@ namespace src.player
|
||||||
tSkill = noneSkill;
|
tSkill = noneSkill;
|
||||||
allSkill = noneSkill;
|
allSkill = noneSkill;
|
||||||
|
|
||||||
Instance.SkillPlayer.Clear();
|
|
||||||
PlayerManager.Clear();
|
PlayerManager.Clear();
|
||||||
|
|
||||||
ConVar.Find("sv_legacy_jump")?.SetValue("1");
|
ConVar.Find("sv_legacy_jump")?.SetValue("1");
|
||||||
|
|
@ -1113,7 +1143,7 @@ namespace src.player
|
||||||
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
|
|
||||||
Debug.WriteToDebug($"Player {skillPlayer.PlayerName} has got the skill \"{player.GetSkillName(randomSkill.Skill)}\".");
|
Debug.WriteToDebug($"Player {skillPlayer.PlayerName} has got the skill \"{player.GetSkillName(randomSkill.Skill)}\".");
|
||||||
skillPlayer.SkillDescriptionHudExpired = DateTime.Now.AddSeconds(Config.LoadedConfig.SkillDescriptionDuration);
|
skillPlayer.SkillDescriptionHudExpired = Config.LoadedConfig.SkillDescriptionDuration == -1 ? DateTime.MaxValue : DateTime.Now.AddSeconds(Config.LoadedConfig.SkillDescriptionDuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ namespace src.player
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid || player.IsBot) return;
|
if (player == null || !player.IsValid || player.IsBot) return;
|
||||||
|
|
||||||
var skillPlayer = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var skillPlayer = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index);
|
||||||
if (skillPlayer == null || !skillPlayer.DisplayHUD) return;
|
if (skillPlayer == null || !skillPlayer.DisplayHUD) return;
|
||||||
|
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
|
|
@ -72,7 +72,7 @@ namespace src.player
|
||||||
string skillLine = string.Empty;
|
string skillLine = string.Empty;
|
||||||
string remainingLine = string.Empty;
|
string remainingLine = string.Empty;
|
||||||
|
|
||||||
bool showDescriptionHUD = skillPlayer.SkillDescriptionHudExpired >= now;
|
bool showDescriptionHUD = skillPlayer.SkillDescriptionHudExpired >= now || Config.LoadedConfig.DisplayAlwaysDescription;
|
||||||
bool isDescription = true;
|
bool isDescription = true;
|
||||||
|
|
||||||
var skills = SkillData.Skills;
|
var skills = SkillData.Skills;
|
||||||
|
|
@ -99,7 +99,7 @@ namespace src.player
|
||||||
{
|
{
|
||||||
if (player.PawnIsAlive)
|
if (player.PawnIsAlive)
|
||||||
{
|
{
|
||||||
var skillInfo = skills.FirstOrDefault(s => s.Skill == skillPlayer.Skill);
|
var skillInfo = SkillData.GetInfo(skillPlayer.Skill);
|
||||||
|
|
||||||
if (skillInfo != null)
|
if (skillInfo != null)
|
||||||
{
|
{
|
||||||
|
|
@ -139,9 +139,9 @@ namespace src.player
|
||||||
var observedSkill = PlayerManager.GetPlayerByIndex(observedEvent.Index);
|
var observedSkill = PlayerManager.GetPlayerByIndex(observedEvent.Index);
|
||||||
if (observedSkill == null) return;
|
if (observedSkill == null) return;
|
||||||
|
|
||||||
var observedSkillInfo = skills.FirstOrDefault(s => s.Skill == observedSkill.Skill);
|
var observedSkillInfo = SkillData.GetInfo(observedSkill.Skill);
|
||||||
var observedSpecialInfo = observedSkill.SpecialSkill != Skills.None
|
var observedSpecialInfo = observedSkill.SpecialSkill != Skills.None
|
||||||
? skills.FirstOrDefault(s => s.Skill == observedSkill.SpecialSkill)
|
? SkillData.GetInfo(observedSkill.SpecialSkill)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
string primaryName = player.GetSkillName(observedSkill.Skill, observedSkill.SkillChance);
|
string primaryName = player.GetSkillName(observedSkill.Skill, observedSkill.SkillChance);
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ namespace src.player.skills
|
||||||
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
decoys.TryAdd(decoy.Index, 0);
|
decoys.TryAdd(decoy.Index, 0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) continue;
|
if (player == null || !player.IsValid) continue;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
if (playerInfo?.Skill == skillName)
|
if (playerInfo?.Skill == skillName)
|
||||||
GiveBunnyHop(player);
|
GiveBunnyHop(player);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) continue;
|
if (player == null || !player.IsValid) continue;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
if (playerInfo == null) continue;
|
if (playerInfo == null) continue;
|
||||||
|
|
||||||
var pawn = player.PlayerPawn.Value;
|
var pawn = player.PlayerPawn.Value;
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ namespace src.player.skills
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
ChangeCamera(player, true);
|
ChangeCamera(player, true);
|
||||||
EntityManager.DestroyPlayerEntities(player.Index);
|
EntityManager.DestroyPlayerEntities(player.Index);
|
||||||
|
cameras.TryRemove(player.Index, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ namespace src.player.skills
|
||||||
foreach (var (info, player) in infoList)
|
foreach (var (info, player) in infoList)
|
||||||
{
|
{
|
||||||
if (player == null) continue;
|
if (player == null) continue;
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
|
|
||||||
var observedPlayer = Utilities.GetPlayers().FirstOrDefault(p => p?.Pawn?.Value?.Handle == player?.Pawn?.Value?.ObserverServices?.ObserverTarget?.Value?.Handle);
|
var observedPlayer = Utilities.GetPlayers().FirstOrDefault(p => p?.Pawn?.Value?.Handle == player?.Pawn?.Value?.ObserverServices?.ObserverTarget?.Value?.Handle);
|
||||||
var observerInfo = observedPlayer != null ? PlayerManager.GetPlayerByIndex(observedPlayer.Index) : null;
|
var observerInfo = observedPlayer != null ? PlayerManager.GetPlayerByIndex(observedPlayer.Index) : null;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ namespace src.player.skills
|
||||||
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
grenade.Bounces = 555;
|
grenade.Bounces = 555;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ namespace src.player.skills
|
||||||
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
|
|
||||||
Server.NextFrame(() =>
|
Server.NextFrame(() =>
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ namespace src.player.skills
|
||||||
if (pawn.Controller.Value == null || !pawn.Controller.Value.IsValid) return;
|
if (pawn.Controller.Value == null || !pawn.Controller.Value.IsValid) return;
|
||||||
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
|
|
||||||
Vector pos = new(grenade.AbsOrigin?.X, grenade.AbsOrigin?.Y, grenade.AbsOrigin?.Z);
|
Vector pos = new(grenade.AbsOrigin?.X, grenade.AbsOrigin?.Y, grenade.AbsOrigin?.Z);
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) continue;
|
if (player == null || !player.IsValid) continue;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
bool isJackalOwner = playerInfo?.Skill == skillName;
|
bool isJackalOwner = playerInfo?.Skill == skillName;
|
||||||
|
|
||||||
if (!isJackalOwner)
|
if (!isJackalOwner)
|
||||||
|
|
@ -90,6 +90,11 @@ namespace src.player.skills
|
||||||
if (playerPawn == null || !playerPawn.IsValid || playerPawn.AbsOrigin == null
|
if (playerPawn == null || !playerPawn.IsValid || playerPawn.AbsOrigin == null
|
||||||
|| playerPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || playerPawn.Health <= 0) return; ;
|
|| playerPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || playerPawn.Health <= 0) return; ;
|
||||||
|
|
||||||
|
foreach (var old in EntityManager.GetPlayerEntities(player.Index, "prop_physics_multiplayer"))
|
||||||
|
EntityManager.DestroyEntity(old);
|
||||||
|
foreach (var old in EntityManager.GetPlayerEntities(player.Index, "particle_system"))
|
||||||
|
EntityManager.DestroyEntity(old);
|
||||||
|
|
||||||
var relay = EntityManager.CreateTrackedPhysicsProp(player.Index);
|
var relay = EntityManager.CreateTrackedPhysicsProp(player.Index);
|
||||||
if (relay == null || !relay.IsValid) return;
|
if (relay == null || !relay.IsValid) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,16 +53,18 @@ namespace src.player.skills
|
||||||
var jester = GetJesterInfo(player.Index);
|
var jester = GetJesterInfo(player.Index);
|
||||||
if (jester == null) return;
|
if (jester == null) return;
|
||||||
|
|
||||||
Server.NextWorldUpdate(() =>
|
jester.Active = false;
|
||||||
{
|
|
||||||
if (jester.Timer != null)
|
if (jester.Timer != null)
|
||||||
{
|
{
|
||||||
jester.Timer?.Kill();
|
jester.Timer.Kill();
|
||||||
jester.Timer = null;
|
jester.Timer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Server.NextWorldUpdate(() =>
|
||||||
|
{
|
||||||
SkillUtils.ResetPrintHTML(player);
|
SkillUtils.ResetPrintHTML(player);
|
||||||
SetPlayerColor(player, true);
|
SetPlayerColor(player, true);
|
||||||
|
jesters.TryRemove(player.Index, out _);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ namespace src.player.skills
|
||||||
if (pawn.Controller.Value == null || !pawn.Controller.Value.IsValid) return;
|
if (pawn.Controller.Value == null || !pawn.Controller.Value.IsValid) return;
|
||||||
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
|
|
||||||
nades.TryAdd(grenade.Index, 0);
|
nades.TryAdd(grenade.Index, 0);
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ namespace src.player.skills
|
||||||
var player = @event.Userid;
|
var player = @event.Userid;
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
|
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
UpdateNinja(player);
|
UpdateNinja(player);
|
||||||
|
|
@ -50,7 +50,7 @@ namespace src.player.skills
|
||||||
var player = @event.Userid;
|
var player = @event.Userid;
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
|
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
UpdateNinja(player);
|
UpdateNinja(player);
|
||||||
|
|
@ -118,7 +118,7 @@ namespace src.player.skills
|
||||||
|
|
||||||
UpdateNinja(PlayerManager.GetPlayerFromEvent(player));
|
UpdateNinja(PlayerManager.GetPlayerFromEvent(player));
|
||||||
|
|
||||||
var props = EntityManager.GetPlayerEntities(PlayerManager.GetPlayerEvent(player)!.Index, "empty_prop");
|
var props = EntityManager.GetPlayerEntities((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index), "empty_prop");
|
||||||
if (props.Count == 0) continue;
|
if (props.Count == 0) continue;
|
||||||
|
|
||||||
var prop = Utilities.GetEntityFromIndex<CDynamicProp>((int)props[0]);
|
var prop = Utilities.GetEntityFromIndex<CDynamicProp>((int)props[0]);
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace src.player.skills
|
||||||
var player = Utilities.GetPlayers().FirstOrDefault(p => p.Pawn?.Value != null && p.Pawn.Value.IsValid && p.Pawn.Value.Index == userIndex);
|
var player = Utilities.GetPlayers().FirstOrDefault(p => p.Pawn?.Value != null && p.Pawn.Value.IsValid && p.Pawn.Value.Index == userIndex);
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
|
|
||||||
um.Recipients.Clear();
|
um.Recipients.Clear();
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ namespace src.player.skills
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
ChangeCamera(player, true);
|
ChangeCamera(player, true);
|
||||||
EntityManager.DestroyPlayerEntities(player.Index);
|
EntityManager.DestroyPlayerEntities(player.Index);
|
||||||
|
cameras.TryRemove(player.Index, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ namespace src.player.skills
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
ChangeCamera(player, true);
|
ChangeCamera(player, true);
|
||||||
EntityManager.DestroyPlayerEntities(player.Index);
|
EntityManager.DestroyPlayerEntities(player.Index);
|
||||||
|
cameras.TryRemove(player.Index, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
|
|
|
||||||
|
|
@ -388,7 +388,7 @@ namespace src.player.skills
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isOwner = knifeOwnerIndex == PlayerManager.GetPlayerEvent(player)!.Index;
|
bool isOwner = knifeOwnerIndex == (PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index);
|
||||||
bool isObservingOwner = observedPlayerIndex.HasValue && observedPlayerIndex.Value == knifeOwnerIndex;
|
bool isObservingOwner = observedPlayerIndex.HasValue && observedPlayerIndex.Value == knifeOwnerIndex;
|
||||||
|
|
||||||
if (!isOwner && !isObservingOwner)
|
if (!isOwner && !isObservingOwner)
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ namespace src.player.skills
|
||||||
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
|
|
||||||
Server.NextFrame(() =>
|
Server.NextFrame(() =>
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) continue;
|
if (player == null || !player.IsValid) continue;
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
var observedPlayer = Utilities.GetPlayers().FirstOrDefault(p => p?.Pawn?.Value?.Handle == player?.Pawn?.Value?.ObserverServices?.ObserverTarget?.Value?.Handle);
|
var observedPlayer = Utilities.GetPlayers().FirstOrDefault(p => p?.Pawn?.Value?.Handle == player?.Pawn?.Value?.ObserverServices?.ObserverTarget?.Value?.Handle);
|
||||||
var observerInfo = observedPlayer != null ? PlayerManager.GetPlayerByIndex(observedPlayer.Index) : null;
|
var observerInfo = observedPlayer != null ? PlayerManager.GetPlayerByIndex(observedPlayer.Index) : null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ namespace src.player.skills
|
||||||
if (pawn == null || !pawn.IsValid || pawn.Controller == null || !pawn.Controller.IsValid || pawn.Controller.Value == null || !pawn.Controller.Value.IsValid) return;
|
if (pawn == null || !pawn.IsValid || pawn.Controller == null || !pawn.Controller.IsValid || pawn.Controller.Value == null || !pawn.Controller.Value.IsValid) return;
|
||||||
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
if (playerInfo?.Skill != skillName || Instance.GameRules == null) return;
|
if (playerInfo?.Skill != skillName || Instance.GameRules == null) return;
|
||||||
|
|
||||||
var roundTime = SkillsInfo.GetValue<int>(skillName, "changeRoundTime");
|
var roundTime = SkillsInfo.GetValue<int>(skillName, "changeRoundTime");
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ namespace src.player.skills
|
||||||
if (pawn.Controller.Value == null || !pawn.Controller.Value.IsValid) return;
|
if (pawn.Controller.Value == null || !pawn.Controller.Value.IsValid) return;
|
||||||
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
var player = pawn.Controller.Value.As<CCSPlayerController>();
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(PlayerManager.GetPlayerEvent(player)!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex((PlayerManager.GetPlayerEvent(player)?.Index ?? player.Index));
|
||||||
if (playerInfo?.Skill != skillName) return;
|
if (playerInfo?.Skill != skillName) return;
|
||||||
|
|
||||||
Server.NextFrame(() => {
|
Server.NextFrame(() => {
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,19 @@ namespace src.utils
|
||||||
var translations = JsonConvert.DeserializeObject<ConcurrentDictionary<string, string>>(jsonText);
|
var translations = JsonConvert.DeserializeObject<ConcurrentDictionary<string, string>>(jsonText);
|
||||||
|
|
||||||
if (translations != null)
|
if (translations != null)
|
||||||
|
{
|
||||||
|
string redColor = ChatColors.Red.ToString();
|
||||||
|
string? altButton = Config.LoadedConfig.AlternativeSkillButton;
|
||||||
|
foreach (var tkey in translations.Keys.ToList())
|
||||||
|
{
|
||||||
|
var val = translations[tkey].Replace("CHATCOLORS.RED", redColor);
|
||||||
|
if (!string.IsNullOrEmpty(altButton))
|
||||||
|
val = val.Replace("css_useSkill", $"css_useSkill/{altButton}");
|
||||||
|
translations[tkey] = val;
|
||||||
|
}
|
||||||
_translations.AddOrUpdate(code, translations, (k, v) => translations);
|
_translations.AddOrUpdate(code, translations, (k, v) => translations);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static bool HasTranslation(string code)
|
public static bool HasTranslation(string code)
|
||||||
{
|
{
|
||||||
|
|
@ -150,9 +161,6 @@ namespace src.utils
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string output = args.Length == 0 ? translation : string.Format(translation, args);
|
string output = args.Length == 0 ? translation : string.Format(translation, args);
|
||||||
output = output.Replace("CHATCOLORS.RED", ChatColors.Red.ToString());
|
|
||||||
if (Config.LoadedConfig.AlternativeSkillButton != null)
|
|
||||||
output = output.Replace("css_useSkill", $"css_useSkill/{Config.LoadedConfig.AlternativeSkillButton}");
|
|
||||||
|
|
||||||
if (Illiterate.CheckIlliterateSkill(player))
|
if (Illiterate.CheckIlliterateSkill(player))
|
||||||
return Illiterate.GetRandomText(output)!;
|
return Illiterate.GetRandomText(output)!;
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,21 @@ namespace src.utils
|
||||||
{
|
{
|
||||||
public static class SkillUtils
|
public static class SkillUtils
|
||||||
{
|
{
|
||||||
private static readonly MemoryFunctionWithReturn<IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, int> HEGrenadeProjectile_CreateFunc = new(GameData.GetSignature("HEGrenadeProjectile_CreateFunc"));
|
private static Lazy<T?> LazySig<T>(string name, Func<string, T> factory) where T : class =>
|
||||||
private static readonly MemoryFunctionWithReturn<IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, int, int, CSmokeGrenadeProjectile> SmokeGrenadeProjectile_CreateFunc = new(GameData.GetSignature("SmokeGrenadeProjectile_CreateFunc"));
|
new(() =>
|
||||||
private static readonly MemoryFunctionVoid<nint, float, RoundEndReason, nint, nint> TerminateRoundFunc = new(GameData.GetSignature("CCSGameRules_TerminateRound"));
|
{
|
||||||
private static readonly MemoryFunctionVoid<CBasePlayerPawn, QAngle> SnapViewAngles = new(GameData.GetSignature("SnapViewAngles"));
|
try { return factory(GameData.GetSignature(name)); }
|
||||||
|
catch (Exception ex) { Server.PrintToConsole($"[jRandomSkills] gamedata signature '{name}' could not be resolved: {ex.Message}"); return null; }
|
||||||
|
});
|
||||||
|
|
||||||
|
private static readonly Lazy<MemoryFunctionWithReturn<IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, int>?> HEGrenadeProjectile_CreateFunc =
|
||||||
|
LazySig<MemoryFunctionWithReturn<IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, int>>("HEGrenadeProjectile_CreateFunc", s => new(s));
|
||||||
|
private static readonly Lazy<MemoryFunctionWithReturn<IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, int, int, CSmokeGrenadeProjectile>?> SmokeGrenadeProjectile_CreateFunc =
|
||||||
|
LazySig<MemoryFunctionWithReturn<IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, int, int, CSmokeGrenadeProjectile>>("SmokeGrenadeProjectile_CreateFunc", s => new(s));
|
||||||
|
private static readonly Lazy<MemoryFunctionVoid<nint, float, RoundEndReason, nint, nint>?> TerminateRoundFunc =
|
||||||
|
LazySig<MemoryFunctionVoid<nint, float, RoundEndReason, nint, nint>>("CCSGameRules_TerminateRound", s => new(s));
|
||||||
|
private static readonly Lazy<MemoryFunctionVoid<CBasePlayerPawn, QAngle>?> SnapViewAngles =
|
||||||
|
LazySig<MemoryFunctionVoid<CBasePlayerPawn, QAngle>>("SnapViewAngles", s => new(s));
|
||||||
// private static readonly int collisionRulesChangedOffset = GameData.GetOffset("CBaseEntity_CollisionRulesChanged");
|
// private static readonly int collisionRulesChangedOffset = GameData.GetOffset("CBaseEntity_CollisionRulesChanged");
|
||||||
|
|
||||||
public static void PrintToChat(CCSPlayerController player, string? msg, string border = "tb", string? title = null)
|
public static void PrintToChat(CCSPlayerController player, string? msg, string border = "tb", string? title = null)
|
||||||
|
|
@ -74,6 +85,8 @@ namespace src.utils
|
||||||
weapon.Value.Clip1 = ammo;
|
weapon.Value.Clip1 = ammo;
|
||||||
Utilities.SetStateChanged(weapon.Value, "CBasePlayerWeapon", "m_iClip1");
|
Utilities.SetStateChanged(weapon.Value, "CBasePlayerWeapon", "m_iClip1");
|
||||||
|
|
||||||
|
if (ammo == 1) return;
|
||||||
|
|
||||||
jRandomSkills.Instance.AddTimer(.1f, () =>
|
jRandomSkills.Instance.AddTimer(.1f, () =>
|
||||||
{
|
{
|
||||||
if (weapon == null || !weapon.IsValid || weapon.Value == null || !weapon.Value.IsValid) return;
|
if (weapon == null || !weapon.IsValid || weapon.Value == null || !weapon.Value.IsValid) return;
|
||||||
|
|
@ -169,7 +182,7 @@ namespace src.utils
|
||||||
public static void Look(this CBasePlayerPawn pawn, QAngle angle)
|
public static void Look(this CBasePlayerPawn pawn, QAngle angle)
|
||||||
{
|
{
|
||||||
if (pawn == null || !pawn.IsValid) return;
|
if (pawn == null || !pawn.IsValid) return;
|
||||||
SnapViewAngles.Invoke(pawn, angle);
|
SnapViewAngles.Value?.Invoke(pawn, angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CBeam? CreateLine(Vector start, Vector end, Color color, uint ownerPlayerIndex = EntityManager.SystemOwnerIndex)
|
public static CBeam? CreateLine(Vector start, Vector end, Color color, uint ownerPlayerIndex = EntityManager.SystemOwnerIndex)
|
||||||
|
|
@ -264,12 +277,12 @@ namespace src.utils
|
||||||
|
|
||||||
public static void CreateHEGrenadeProjectile(Vector pos, QAngle angle, Vector vel, int teamNum)
|
public static void CreateHEGrenadeProjectile(Vector pos, QAngle angle, Vector vel, int teamNum)
|
||||||
{
|
{
|
||||||
HEGrenadeProjectile_CreateFunc.Invoke(pos.Handle, angle.Handle, vel.Handle, vel.Handle, IntPtr.Zero, 44, teamNum);
|
HEGrenadeProjectile_CreateFunc.Value?.Invoke(pos.Handle, angle.Handle, vel.Handle, vel.Handle, IntPtr.Zero, 44, teamNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void CreateSmokeGrenadeProjectile(Vector pos, QAngle angle, Vector vel, int teamNum)
|
public static void CreateSmokeGrenadeProjectile(Vector pos, QAngle angle, Vector vel, int teamNum)
|
||||||
{
|
{
|
||||||
SmokeGrenadeProjectile_CreateFunc.Invoke(pos.Handle, angle.Handle, vel.Handle, vel.Handle, IntPtr.Zero, 45, teamNum);
|
SmokeGrenadeProjectile_CreateFunc.Value?.Invoke(pos.Handle, angle.Handle, vel.Handle, vel.Handle, IntPtr.Zero, 45, teamNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TakeHealth(CCSPlayerPawn? pawn, int damage)
|
public static bool TakeHealth(CCSPlayerPawn? pawn, int damage)
|
||||||
|
|
@ -320,7 +333,7 @@ namespace src.utils
|
||||||
return EntityManager.CreateTrackedTrigger(ownerPlayerIndex, name, radius, pos);
|
return EntityManager.CreateTrackedTrigger(ownerPlayerIndex, name, radius, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ForceFullUpdate(CCSPlayerController player, List<(uint PlayerIndex, QAngle LastAngle)>? batchList = null)
|
public static void ForceFullUpdate(CCSPlayerController player, List<(uint PlayerIndex, QAngle LastAngle)>? batchList = null, INetworkGameServer? networkGameServer = null)
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
|
|
||||||
|
|
@ -329,14 +342,15 @@ namespace src.utils
|
||||||
|
|
||||||
QAngle lastAngle = new(pawn.V_angle.X, pawn.V_angle.Y, pawn.V_angle.Z);
|
QAngle lastAngle = new(pawn.V_angle.X, pawn.V_angle.Y, pawn.V_angle.Z);
|
||||||
|
|
||||||
INetworkServerService networkServerService = new();
|
networkGameServer ??= new INetworkServerService().GetIGameServer();
|
||||||
INetworkGameServer networkGameServer = networkServerService.GetIGameServer();
|
|
||||||
|
|
||||||
var client = networkGameServer.GetClientBySlot(player.Slot);
|
var client = networkGameServer.GetClientBySlot(player.Slot);
|
||||||
if (client == null) return;
|
if (client == null) return;
|
||||||
|
|
||||||
client.ForceFullUpdate();
|
client.ForceFullUpdate();
|
||||||
if (lastAngle.Y == 0) return;
|
// Only skip the angle restore when the captured view is a spawn-time (0,0,0) placeholder;
|
||||||
|
// a genuine angle with a single zero component (e.g. yaw exactly 0) must still be restored.
|
||||||
|
if (lastAngle.X == 0 && lastAngle.Y == 0 && lastAngle.Z == 0) return;
|
||||||
|
|
||||||
uint playerIndex = player.Index;
|
uint playerIndex = player.Index;
|
||||||
|
|
||||||
|
|
@ -368,8 +382,9 @@ namespace src.utils
|
||||||
lastForceFullUpdateAll = tickCount;
|
lastForceFullUpdateAll = tickCount;
|
||||||
var playersToRestore = new List<(uint PlayerIndex, QAngle LastAngle)>();
|
var playersToRestore = new List<(uint PlayerIndex, QAngle LastAngle)>();
|
||||||
|
|
||||||
|
INetworkGameServer networkGameServer = new INetworkServerService().GetIGameServer();
|
||||||
foreach (var player in Utilities.GetPlayers())
|
foreach (var player in Utilities.GetPlayers())
|
||||||
ForceFullUpdate(player, playersToRestore);
|
ForceFullUpdate(player, playersToRestore, networkGameServer);
|
||||||
|
|
||||||
if (playersToRestore.Count <= 0) return;
|
if (playersToRestore.Count <= 0) return;
|
||||||
|
|
||||||
|
|
@ -639,7 +654,7 @@ namespace src.utils
|
||||||
{
|
{
|
||||||
if (jRandomSkills.Instance == null || jRandomSkills.Instance.GameRules == null) return;
|
if (jRandomSkills.Instance == null || jRandomSkills.Instance.GameRules == null) return;
|
||||||
UpdateServerTeamScores(ctScore, tScore);
|
UpdateServerTeamScores(ctScore, tScore);
|
||||||
TerminateRoundFunc.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)
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue