commit
5fc3e8db66
34 changed files with 316 additions and 102 deletions
|
|
@ -26,6 +26,7 @@ namespace src
|
||||||
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).
|
// 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 static readonly ConcurrentDictionary<string, byte> ActiveSkillsThisRound = new();
|
||||||
|
public static readonly ConcurrentDictionary<string, byte> SkillsUsedThisMap = new();
|
||||||
|
|
||||||
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)";
|
||||||
|
|
@ -95,7 +96,10 @@ namespace src
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (methodName == "EnableSkill")
|
if (methodName == "EnableSkill")
|
||||||
|
{
|
||||||
ActiveSkillsThisRound.TryAdd(skill, 0);
|
ActiveSkillsThisRound.TryAdd(skill, 0);
|
||||||
|
SkillsUsedThisMap.TryAdd(skill, 0);
|
||||||
|
}
|
||||||
|
|
||||||
var method = _skillMethodCache.GetOrAdd((skill, methodName), key =>
|
var method = _skillMethodCache.GetOrAdd((skill, methodName), key =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -138,24 +138,56 @@ namespace src.player
|
||||||
return candidates[Random.Shared.Next(candidates.Count)];
|
return candidates[Random.Shared.Next(candidates.Count)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly Skills[] lateDamageSkills = [Skills.SecondLife];
|
||||||
|
|
||||||
|
private static readonly HashSet<Skills> tickFailuresLogged = [];
|
||||||
|
|
||||||
|
private static void InvokeSkill(Skills skill, string methodName, object[] args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Instance.SkillAction(skill.ToString(), methodName, args);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Server.PrintToConsole($"[jRandomSkills] {skill}.{methodName} failed: {ex.InnerException?.Message ?? ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void DispatchToActiveSkills(string methodName, params object[] args)
|
private static void DispatchToActiveSkills(string methodName, params object[] args)
|
||||||
{
|
{
|
||||||
var seen = new HashSet<Skills>();
|
var seen = new HashSet<Skills>();
|
||||||
foreach (var p in Instance.SkillPlayer)
|
foreach (var p in Instance.SkillPlayer)
|
||||||
{
|
{
|
||||||
if (p.IsDrawing || !seen.Add(p.Skill)) continue;
|
if (p.IsDrawing || !seen.Add(p.Skill)) continue;
|
||||||
try
|
InvokeSkill(p.Skill, methodName, args);
|
||||||
{
|
|
||||||
Instance.SkillAction(p.Skill.ToString(), methodName, args);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// One skill's failure must not break the dispatch chain.
|
|
||||||
Server.PrintToConsole($"[jRandomSkills] {p.Skill}.{methodName} failed: {ex.InnerException?.Message ?? ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void DispatchOnTakeDamage(DynamicHook h)
|
||||||
|
{
|
||||||
|
object[] args = [h];
|
||||||
|
var seen = new HashSet<Skills>();
|
||||||
|
List<Skills>? deferred = null;
|
||||||
|
|
||||||
|
foreach (var p in Instance.SkillPlayer)
|
||||||
|
{
|
||||||
|
if (p.IsDrawing || !seen.Add(p.Skill)) continue;
|
||||||
|
|
||||||
|
if (Array.IndexOf(lateDamageSkills, p.Skill) >= 0)
|
||||||
|
{
|
||||||
|
(deferred ??= []).Add(p.Skill);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
InvokeSkill(p.Skill, "OnTakeDamage", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deferred == null) return;
|
||||||
|
foreach (var skill in deferred)
|
||||||
|
InvokeSkill(skill, "OnTakeDamage", args);
|
||||||
|
}
|
||||||
|
|
||||||
private static HookResult PlayerMakeSound(UserMessage um)
|
private static HookResult PlayerMakeSound(UserMessage um)
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
|
|
@ -322,7 +354,7 @@ namespace src.player
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
{
|
{
|
||||||
DispatchToActiveSkills("OnTakeDamage", h);
|
DispatchOnTakeDamage(h);
|
||||||
|
|
||||||
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))
|
||||||
|
|
@ -468,7 +500,17 @@ namespace src.player
|
||||||
foreach (var skill in _activeSkillsList)
|
foreach (var skill in _activeSkillsList)
|
||||||
{
|
{
|
||||||
if (freeze && _freezeDisabledSkills.Contains(skill)) continue;
|
if (freeze && _freezeDisabledSkills.Contains(skill)) continue;
|
||||||
Instance.SkillAction(_skillNames[skill], "OnTick");
|
try
|
||||||
|
{
|
||||||
|
Instance.SkillAction(_skillNames[skill], "OnTick");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Without this one throwing skill cancels every later skill's tick, every frame.
|
||||||
|
// Logged once per skill per round; at 64 ticks a repeat would flood the console.
|
||||||
|
if (tickFailuresLogged.Add(skill))
|
||||||
|
Server.PrintToConsole($"[jRandomSkills] {skill}.OnTick failed: {ex.InnerException?.Message ?? ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PerfLog.Sample("OnTick(skills)", perfStart);
|
PerfLog.Sample("OnTick(skills)", perfStart);
|
||||||
|
|
@ -541,6 +583,10 @@ namespace src.player
|
||||||
|
|
||||||
Instance.SkillAction(skillPlayer.Skill.ToString(), "DisableSkill", [player]);
|
Instance.SkillAction(skillPlayer.Skill.ToString(), "DisableSkill", [player]);
|
||||||
|
|
||||||
|
uint leavingIndex = player.Index;
|
||||||
|
foreach (var skill in SkillData.Skills)
|
||||||
|
Instance.SkillAction(skill.Skill.ToString(), "PlayerDisconnect", [leavingIndex]);
|
||||||
|
|
||||||
PlayerManager.UnregisterPlayer(player.Index);
|
PlayerManager.UnregisterPlayer(player.Index);
|
||||||
EntityManager.DestroyPlayerEntities(player.Index);
|
EntityManager.DestroyPlayerEntities(player.Index);
|
||||||
|
|
||||||
|
|
@ -660,8 +706,12 @@ namespace src.player
|
||||||
if (playerInfo == null) continue;
|
if (playerInfo == null) continue;
|
||||||
|
|
||||||
ActiveSkillsThisRound.TryAdd(playerInfo.Skill.ToString(), 0);
|
ActiveSkillsThisRound.TryAdd(playerInfo.Skill.ToString(), 0);
|
||||||
|
SkillsUsedThisMap.TryAdd(playerInfo.Skill.ToString(), 0);
|
||||||
if (playerInfo.SpecialSkill != noneSkill.Skill)
|
if (playerInfo.SpecialSkill != noneSkill.Skill)
|
||||||
|
{
|
||||||
ActiveSkillsThisRound.TryAdd(playerInfo.SpecialSkill.ToString(), 0);
|
ActiveSkillsThisRound.TryAdd(playerInfo.SpecialSkill.ToString(), 0);
|
||||||
|
SkillsUsedThisMap.TryAdd(playerInfo.SpecialSkill.ToString(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
Instance.SkillAction(playerInfo.Skill.ToString(), "DisableSkill", [player]);
|
Instance.SkillAction(playerInfo.Skill.ToString(), "DisableSkill", [player]);
|
||||||
|
|
||||||
|
|
@ -674,9 +724,13 @@ namespace src.player
|
||||||
RestorePlayer(player);
|
RestorePlayer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var skillName in ActiveSkillsThisRound.Keys)
|
// Reset every skill used so far on this map, not only the ones held this round: a skill
|
||||||
|
// nobody drew now would otherwise never clear state left over from an earlier round.
|
||||||
|
// Skills that never ran cannot hold state, so they stay out of the sweep.
|
||||||
|
foreach (var skillName in SkillsUsedThisMap.Keys)
|
||||||
Instance.SkillAction(skillName, "NewRound");
|
Instance.SkillAction(skillName, "NewRound");
|
||||||
ActiveSkillsThisRound.Clear();
|
ActiveSkillsThisRound.Clear();
|
||||||
|
tickFailuresLogged.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -712,6 +766,7 @@ namespace src.player
|
||||||
EntityManager.SuppressKills = false;
|
EntityManager.SuppressKills = false;
|
||||||
|
|
||||||
ActiveSkillsThisRound.Clear();
|
ActiveSkillsThisRound.Clear();
|
||||||
|
SkillsUsedThisMap.Clear();
|
||||||
nextRoundPicks.Clear();
|
nextRoundPicks.Clear();
|
||||||
|
|
||||||
playersSkills.Clear();
|
playersSkills.Clear();
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,11 @@ namespace src.player.skills
|
||||||
Marshal.WriteInt32(hitGroupOffset, 56, hitGroup);
|
Marshal.WriteInt32(hitGroupOffset, 56, hitGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void NewRound()
|
||||||
|
{
|
||||||
|
hitGroups.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
public static void DisableSkill(CCSPlayerController _)
|
public static void DisableSkill(CCSPlayerController _)
|
||||||
{
|
{
|
||||||
foreach (var hit in hitGroups)
|
foreach (var hit in hitGroups)
|
||||||
|
|
@ -65,6 +70,8 @@ namespace src.player.skills
|
||||||
if (hit.Key != nint.Zero)
|
if (hit.Key != nint.Zero)
|
||||||
Marshal.WriteInt32(hit.Key, 56, hit.Value);
|
Marshal.WriteInt32(hit.Key, 56, hit.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hitGroups.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff0000", 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 = "#ff0000", 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)
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,10 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
if (Server.TickCount % 2 != 0) return;
|
if (Server.TickCount % 2 != 0) return;
|
||||||
|
|
||||||
|
var bomb = invisiblePlayers.IsEmpty
|
||||||
|
? null
|
||||||
|
: Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").FirstOrDefault();
|
||||||
|
|
||||||
foreach (var player in Utilities.GetPlayers())
|
foreach (var player in Utilities.GetPlayers())
|
||||||
{
|
{
|
||||||
if (player.PlayerPawn?.Value?.Health <= 0 && invisiblePlayers.ContainsKey(player.Index))
|
if (player.PlayerPawn?.Value?.Health <= 0 && invisiblePlayers.ContainsKey(player.Index))
|
||||||
|
|
@ -77,7 +81,7 @@ namespace src.player.skills
|
||||||
|
|
||||||
// CheckTransmit hides the model but the radar blip comes from spotted state, so clear it every tick.
|
// CheckTransmit hides the model but the radar blip comes from spotted state, so clear it every tick.
|
||||||
if (invisiblePlayers.ContainsKey(player.Index))
|
if (invisiblePlayers.ContainsKey(player.Index))
|
||||||
ClearSpottedState(player);
|
ClearSpottedState(player, bomb);
|
||||||
|
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||||
if (playerInfo?.Skill != skillName) continue;
|
if (playerInfo?.Skill != skillName) continue;
|
||||||
|
|
@ -99,6 +103,11 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
|
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
|
||||||
{
|
{
|
||||||
|
if (invisiblePlayers.IsEmpty) return;
|
||||||
|
|
||||||
|
var bomb = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").FirstOrDefault();
|
||||||
|
if (bomb != null && !bomb.IsValid) bomb = null;
|
||||||
|
|
||||||
foreach (var (info, player) in infoList)
|
foreach (var (info, player) in infoList)
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid || player.Team == CsTeam.Spectator) continue;
|
if (player == null || !player.IsValid || player.Team == CsTeam.Spectator) continue;
|
||||||
|
|
@ -127,14 +136,10 @@ namespace src.player.skills
|
||||||
if (info.TransmitEntities.Contains(entity.Index))
|
if (info.TransmitEntities.Contains(entity.Index))
|
||||||
info.TransmitEntities.Remove(entity.Index);
|
info.TransmitEntities.Remove(entity.Index);
|
||||||
|
|
||||||
var bombIndex = GetBombIndex();
|
if (bomb == null) continue;
|
||||||
if (bombIndex == null) continue;
|
|
||||||
|
|
||||||
var bombEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)bombIndex);
|
if (info.TransmitEntities.Contains(bomb.Index))
|
||||||
if (bombEntity == null || !bombEntity.IsValid) continue;
|
info.TransmitEntities.Remove(bomb.Index);
|
||||||
|
|
||||||
if (info.TransmitEntities.Contains(bombEntity.Index))
|
|
||||||
info.TransmitEntities.Remove(bombEntity.Index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -212,19 +217,8 @@ namespace src.player.skills
|
||||||
particle.AcceptInput("Start");
|
particle.AcceptInput("Start");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static uint? GetBombIndex()
|
|
||||||
{
|
|
||||||
var bombEntities = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").ToList();
|
|
||||||
if (bombEntities.Count == 0) return null;
|
|
||||||
|
|
||||||
var bomb = bombEntities.FirstOrDefault();
|
|
||||||
if (bomb == null) return null;
|
|
||||||
|
|
||||||
return bomb.Index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wipe spotted state (pawn + carried bomb) so a disguised carrier produces no radar blip.
|
// Wipe spotted state (pawn + carried bomb) so a disguised carrier produces no radar blip.
|
||||||
private static void ClearSpottedState(CCSPlayerController player)
|
private static void ClearSpottedState(CCSPlayerController player, CC4? bomb)
|
||||||
{
|
{
|
||||||
var pawn = player.PlayerPawn?.Value;
|
var pawn = player.PlayerPawn?.Value;
|
||||||
if (pawn != null && pawn.IsValid)
|
if (pawn != null && pawn.IsValid)
|
||||||
|
|
@ -234,7 +228,6 @@ namespace src.player.skills
|
||||||
pawn.EntitySpottedState.SpottedByMask[1] = 0;
|
pawn.EntitySpottedState.SpottedByMask[1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bomb = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").FirstOrDefault();
|
|
||||||
if (bomb != null && bomb.IsValid && bomb.OwnerEntity?.Index == player.Index)
|
if (bomb != null && bomb.IsValid && bomb.OwnerEntity?.Index == player.Index)
|
||||||
{
|
{
|
||||||
bomb.EntitySpottedState.Spotted = false;
|
bomb.EntitySpottedState.Spotted = false;
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,19 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
{
|
||||||
|
lock (setLock)
|
||||||
|
{
|
||||||
|
playersInDark.TryRemove(playerIndex, out _);
|
||||||
|
playersToTarget.TryRemove(playerIndex, out _);
|
||||||
|
|
||||||
|
foreach (var kvp in playersToTarget)
|
||||||
|
if (kvp.Value == playerIndex)
|
||||||
|
playersToTarget.TryRemove(kvp.Key, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,16 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
{
|
||||||
|
deafPlayers.TryRemove(playerIndex, out _);
|
||||||
|
playersToTarget.TryRemove(playerIndex, out _);
|
||||||
|
|
||||||
|
foreach (var kvp in playersToTarget)
|
||||||
|
if (kvp.Value == playerIndex)
|
||||||
|
playersToTarget.TryRemove(kvp.Key, out _);
|
||||||
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
foreach (var playerIndex in deafPlayers.Keys)
|
foreach (var playerIndex in deafPlayers.Keys)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
|
using CounterStrikeSharp.API.Modules.Cvars;
|
||||||
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;
|
||||||
|
|
@ -9,10 +10,23 @@ namespace src.player.skills
|
||||||
public class FriendlyFire : ISkill
|
public class FriendlyFire : ISkill
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.FriendlyFire;
|
private const Skills skillName = Skills.FriendlyFire;
|
||||||
|
private static bool defaultAutoKick = true;
|
||||||
|
private static bool autoKickOverridden;
|
||||||
|
|
||||||
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()
|
||||||
|
{
|
||||||
|
if (!autoKickOverridden) return;
|
||||||
|
|
||||||
|
autoKickOverridden = false;
|
||||||
|
Server.ExecuteCommand($"mp_autokick {(defaultAutoKick ? 1 : 0)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTakeDamage(DynamicHook h)
|
public static void OnTakeDamage(DynamicHook h)
|
||||||
|
|
@ -56,8 +70,12 @@ namespace src.player.skills
|
||||||
float damage = param2.Damage;
|
float damage = param2.Damage;
|
||||||
param2.Damage = 0;
|
param2.Damage = 0;
|
||||||
|
|
||||||
Server.ExecuteCommand("mp_autokick 0");
|
if (!autoKickOverridden)
|
||||||
|
{
|
||||||
|
autoKickOverridden = true;
|
||||||
|
Server.ExecuteCommand("mp_autokick 0");
|
||||||
|
}
|
||||||
|
|
||||||
SkillUtils.AddHealth(
|
SkillUtils.AddHealth(
|
||||||
victimPawn,
|
victimPawn,
|
||||||
(int)(damage * SkillsInfo.GetValue<float>(skillName, "healthDamageMultiplier")),
|
(int)(damage * SkillsInfo.GetValue<float>(skillName, "healthDamageMultiplier")),
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,11 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
|
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
|
||||||
{
|
{
|
||||||
|
if (invisiblePlayers.IsEmpty) return;
|
||||||
|
|
||||||
|
var bomb = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").FirstOrDefault();
|
||||||
|
uint? bombOwnerIndex = bomb != null && bomb.IsValid ? bomb.OwnerEntity?.Index : null;
|
||||||
|
|
||||||
foreach (var (info, player) in infoList)
|
foreach (var (info, player) in infoList)
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid || player.Team == CsTeam.Spectator) continue;
|
if (player == null || !player.IsValid || player.Team == CsTeam.Spectator) continue;
|
||||||
|
|
@ -84,14 +89,11 @@ namespace src.player.skills
|
||||||
if (info.TransmitEntities.Contains(entity.Index))
|
if (info.TransmitEntities.Contains(entity.Index))
|
||||||
info.TransmitEntities.Remove(entity.Index);
|
info.TransmitEntities.Remove(entity.Index);
|
||||||
|
|
||||||
var bombIndex = GetBombIndex(playerController);
|
// Hide the bomb as well, but only while this hidden player is the one holding it.
|
||||||
if (bombIndex == null) continue;
|
if (bomb == null || bombOwnerIndex != playerController.Index) continue;
|
||||||
|
|
||||||
var bombEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)bombIndex);
|
if (info.TransmitEntities.Contains(bomb.Index))
|
||||||
if (bombEntity == null || !bombEntity.IsValid) continue;
|
info.TransmitEntities.Remove(bomb.Index);
|
||||||
|
|
||||||
if (info.TransmitEntities.Contains(bombEntity.Index))
|
|
||||||
info.TransmitEntities.Remove(bombEntity.Index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -232,18 +234,6 @@ namespace src.player.skills
|
||||||
playerInfo.PrintHTML = $"<font color='#FF0000'>{player.GetTranslation("disabled_weapon")}</font>";
|
playerInfo.PrintHTML = $"<font color='#FF0000'>{player.GetTranslation("disabled_weapon")}</font>";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static uint? GetBombIndex(CCSPlayerController player)
|
|
||||||
{
|
|
||||||
var bombEntities = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").ToList();
|
|
||||||
if (bombEntities.Count == 0) return null;
|
|
||||||
|
|
||||||
var bomb = bombEntities.FirstOrDefault();
|
|
||||||
if (bomb == null) return null;
|
|
||||||
|
|
||||||
if (bomb.OwnerEntity.Index != player.Index) return null;
|
|
||||||
return bomb.Index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FFFFFF", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Epic) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FFFFFF", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, string requiredPermission = "", float? hudDuration = null, float? descriptionHudDuration = null, int maxPerServer = -1, Rarity rarity = Rarity.Epic) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,19 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
{
|
||||||
|
lock (setLock)
|
||||||
|
{
|
||||||
|
glitchedPlayers.TryRemove(playerIndex, out _);
|
||||||
|
playersToTarget.TryRemove(playerIndex, out _);
|
||||||
|
|
||||||
|
foreach (var kvp in playersToTarget)
|
||||||
|
if (kvp.Value == playerIndex)
|
||||||
|
playersToTarget.TryRemove(kvp.Key, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
int tick = SkillsInfo.GetValue<int>(skillName, "tickCooldown");
|
int tick = Math.Max(1, SkillsInfo.GetValue<int>(skillName, "tickCooldown"));
|
||||||
if (Server.TickCount % tick != 0) return;
|
if (Server.TickCount % tick != 0) return;
|
||||||
|
|
||||||
float smokeRadius = SkillsInfo.GetValue<float>(skillName, "smokeRadius");
|
float smokeRadius = SkillsInfo.GetValue<float>(skillName, "smokeRadius");
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
int cooldown = (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;
|
if (players.IsEmpty || jRandomSkills.Instance.GameRules?.FreezePeriod == true) return;
|
||||||
|
|
|
||||||
|
|
@ -340,6 +340,8 @@ 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 (PlayerManager.GetPlayerByIndex(victim.Index)?.Skill != skillName) return;
|
||||||
|
|
||||||
if (playersInfo.TryGetValue(victim.Index, out var playerSkill))
|
if (playersInfo.TryGetValue(victim.Index, out var playerSkill))
|
||||||
{
|
{
|
||||||
if (playerSkill.CloneProp != null)
|
if (playerSkill.CloneProp != null)
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ namespace src.player.skills
|
||||||
replica.Teleport(nexPos, null, null);
|
replica.Teleport(nexPos, null, null);
|
||||||
}, TimerFlags.REPEAT | TimerFlags.STOP_ON_MAPCHANGE);
|
}, TimerFlags.REPEAT | TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
ActiveTimers.TryAdd(replicaIndex, moveTimer);
|
ActiveTimers.TryAdd(replicaIndex, moveTimer);
|
||||||
});
|
}, TimerFlags.STOP_ON_MAPCHANGE);
|
||||||
|
|
||||||
float duration = SkillsInfo.GetValue<float>(skillName, ducking ? "durationCrouch" : "durationRun");
|
float duration = SkillsInfo.GetValue<float>(skillName, ducking ? "durationCrouch" : "durationRun");
|
||||||
Instance.AddTimer(duration, () =>
|
Instance.AddTimer(duration, () =>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,19 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"), false);
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
{
|
||||||
|
lock (setLock)
|
||||||
|
{
|
||||||
|
jammedPlayers.TryRemove(playerIndex, out _);
|
||||||
|
jammerToTarget.TryRemove(playerIndex, out _);
|
||||||
|
|
||||||
|
foreach (var kvp in jammerToTarget)
|
||||||
|
if (kvp.Value == playerIndex)
|
||||||
|
jammerToTarget.TryRemove(kvp.Key, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,16 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
{
|
||||||
|
if (!jesters.TryRemove(playerIndex, out var jester)) return;
|
||||||
|
|
||||||
|
jester.Generation++;
|
||||||
|
jester.Active = false;
|
||||||
|
jester.Timer?.Kill();
|
||||||
|
jester.Timer = null;
|
||||||
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
Server.NextWorldUpdate(() =>
|
Server.NextWorldUpdate(() =>
|
||||||
|
|
@ -37,7 +47,7 @@ namespace src.player.skills
|
||||||
SkillUtils.ResetPrintHTML(player);
|
SkillUtils.ResetPrintHTML(player);
|
||||||
|
|
||||||
var pawn = player.PlayerPawn.Value;
|
var pawn = player.PlayerPawn.Value;
|
||||||
if (pawn == null || !pawn.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return;
|
if (pawn == null || !pawn.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) continue;
|
||||||
|
|
||||||
var color = Color.FromArgb(255, 255, 255, 255);
|
var color = Color.FromArgb(255, 255, 255, 255);
|
||||||
pawn.Render = color;
|
pawn.Render = color;
|
||||||
|
|
@ -114,11 +124,10 @@ namespace src.player.skills
|
||||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||||
|
|
||||||
if (!Instance.IsPlayerValid(victim)) return;
|
if (!Instance.IsPlayerValid(victim)) return;
|
||||||
var jesterVictim = GetJesterInfo(victim!.Index);
|
|
||||||
|
|
||||||
if (!Instance.IsPlayerValid(attacker))
|
if (!Instance.IsPlayerValid(attacker))
|
||||||
{
|
{
|
||||||
if (jesterVictim != null && jesterVictim.Active)
|
if (IsActiveJester(victim!.Index))
|
||||||
{
|
{
|
||||||
SkillUtils.RestoreHealth(victim);
|
SkillUtils.RestoreHealth(victim);
|
||||||
RestoreArmor(victim, @event.DmgArmor);
|
RestoreArmor(victim, @event.DmgArmor);
|
||||||
|
|
@ -126,14 +135,19 @@ namespace src.player.skills
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var jesterAttacker = GetJesterInfo(attacker!.Index);
|
if (IsActiveJester(victim!.Index) || IsActiveJester(attacker!.Index))
|
||||||
if ((jesterVictim != null && jesterVictim.Active) || (jesterAttacker != null && jesterAttacker.Active))
|
|
||||||
{
|
{
|
||||||
SkillUtils.RestoreHealth(victim);
|
SkillUtils.RestoreHealth(victim);
|
||||||
RestoreArmor(victim, @event.DmgArmor);
|
RestoreArmor(victim, @event.DmgArmor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsActiveJester(uint playerIndex)
|
||||||
|
{
|
||||||
|
if (GetJesterInfo(playerIndex)?.Active != true) return false;
|
||||||
|
return PlayerManager.GetPlayerByIndex(playerIndex)?.Skill == skillName;
|
||||||
|
}
|
||||||
|
|
||||||
private static void RestoreArmor(CCSPlayerController? victim, int dmgArmor)
|
private static void RestoreArmor(CCSPlayerController? victim, int dmgArmor)
|
||||||
{
|
{
|
||||||
if (victim == null || !victim.IsValid || dmgArmor <= 0) return;
|
if (victim == null || !victim.IsValid || dmgArmor <= 0) return;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,16 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
{
|
||||||
|
bannedPlayers.TryRemove(playerIndex, out _);
|
||||||
|
playersToTarget.TryRemove(playerIndex, out _);
|
||||||
|
|
||||||
|
foreach (var kvp in playersToTarget)
|
||||||
|
if (kvp.Value == playerIndex)
|
||||||
|
playersToTarget.TryRemove(kvp.Key, out _);
|
||||||
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
bannedPlayers.Clear();
|
bannedPlayers.Clear();
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ namespace src.player.skills
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
playersInAction.Clear();
|
playersInAction.Clear();
|
||||||
|
|
||||||
|
if (!hooked) return;
|
||||||
|
|
||||||
hooked = false;
|
hooked = false;
|
||||||
Shoot_Secondary?.Unhook(ShootSecondary, HookMode.Pre);
|
Shoot_Secondary?.Unhook(ShootSecondary, HookMode.Pre);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,16 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
{
|
||||||
|
playersFOV.TryRemove(playerIndex, out _);
|
||||||
|
playersToTarget.TryRemove(playerIndex, out _);
|
||||||
|
|
||||||
|
foreach (var kvp in playersToTarget)
|
||||||
|
if (kvp.Value == playerIndex)
|
||||||
|
playersToTarget.TryRemove(kvp.Key, out _);
|
||||||
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
foreach (var playerIndex in playersFOV.Keys)
|
foreach (var playerIndex in playersFOV.Keys)
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,11 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
|
public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList)
|
||||||
{
|
{
|
||||||
|
if (invisiblePlayers.IsEmpty) return;
|
||||||
|
|
||||||
|
var bomb = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").FirstOrDefault();
|
||||||
|
uint? bombOwnerIndex = bomb != null && bomb.IsValid ? bomb.OwnerEntity?.Index : null;
|
||||||
|
|
||||||
foreach (var (info, player) in infoList)
|
foreach (var (info, player) in infoList)
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid || player.Team == CsTeam.Spectator) continue;
|
if (player == null || !player.IsValid || player.Team == CsTeam.Spectator) continue;
|
||||||
|
|
@ -86,13 +91,10 @@ namespace src.player.skills
|
||||||
if (info.TransmitEntities.Contains(entity.Index))
|
if (info.TransmitEntities.Contains(entity.Index))
|
||||||
info.TransmitEntities.Remove(entity.Index);
|
info.TransmitEntities.Remove(entity.Index);
|
||||||
|
|
||||||
var bombIndex = GetBombIndex(playerController);
|
if (bomb == null || bombOwnerIndex != playerController.Index) continue;
|
||||||
if (bombIndex == null) continue;
|
|
||||||
var bombEntity = Utilities.GetEntityFromIndex<CBaseEntity>((int)bombIndex);
|
|
||||||
if (bombEntity == null || !bombEntity.IsValid) continue;
|
|
||||||
|
|
||||||
if (info.TransmitEntities.Contains(bombEntity.Index))
|
if (info.TransmitEntities.Contains(bomb.Index))
|
||||||
info.TransmitEntities.Remove(bombEntity.Index);
|
info.TransmitEntities.Remove(bomb.Index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -229,18 +231,6 @@ namespace src.player.skills
|
||||||
particle.AcceptInput("Start");
|
particle.AcceptInput("Start");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static uint? GetBombIndex(CCSPlayerController player)
|
|
||||||
{
|
|
||||||
var bombEntities = Utilities.FindAllEntitiesByDesignerName<CC4>("weapon_c4").ToList();
|
|
||||||
if (bombEntities.Count == 0) return null;
|
|
||||||
|
|
||||||
var bomb = bombEntities.FirstOrDefault();
|
|
||||||
if (bomb == null) return null;
|
|
||||||
|
|
||||||
if (bomb.OwnerEntity.Index != player.Index) return null;
|
|
||||||
return bomb.Index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#dedede", 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 idlePercentInvisibility = .3f, float duckPercentInvisibility = .3f, float knifePercentInvisibility = .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 = "#dedede", 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 idlePercentInvisibility = .3f, float duckPercentInvisibility = .3f, float knifePercentInvisibility = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer, rarity)
|
||||||
{
|
{
|
||||||
public float IdlePercentInvisibility { get; set; } = idlePercentInvisibility;
|
public float IdlePercentInvisibility { get; set; } = idlePercentInvisibility;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -10,6 +11,9 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.NoRecoil;
|
private const Skills skillName = Skills.NoRecoil;
|
||||||
|
|
||||||
|
private static readonly ConcurrentDictionary<uint, byte> holders = [];
|
||||||
|
private static bool noSpreadActive;
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
|
|
@ -17,21 +21,33 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
var players = Utilities.GetPlayers();
|
holders.Clear();
|
||||||
foreach (var player in players)
|
ApplyNoSpread(false);
|
||||||
DisableSkill(player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EnableSkill(CCSPlayerController player)
|
public static void EnableSkill(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
Server.ExecuteCommand("weapon_accuracy_nospread 1");
|
|
||||||
|
holders.TryAdd(player.Index, 0);
|
||||||
|
ApplyNoSpread(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DisableSkill(CCSPlayerController player)
|
public static void DisableSkill(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) return;
|
||||||
Server.ExecuteCommand("weapon_accuracy_nospread 0");
|
|
||||||
|
holders.TryRemove(player.Index, out _);
|
||||||
|
if (holders.IsEmpty)
|
||||||
|
ApplyNoSpread(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplyNoSpread(bool enabled)
|
||||||
|
{
|
||||||
|
if (noSpreadActive == enabled) return;
|
||||||
|
|
||||||
|
noSpreadActive = enabled;
|
||||||
|
Server.ExecuteCommand($"weapon_accuracy_nospread {(enabled ? 1 : 0)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ namespace src.player.skills
|
||||||
private static readonly ConcurrentDictionary<uint, float> plantingPlayers = [];
|
private static readonly ConcurrentDictionary<uint, float> plantingPlayers = [];
|
||||||
// mp_c4timer is an Int32 cvar; captured at load so restore never picks up another skill's override.
|
// mp_c4timer is an Int32 cvar; captured at load so restore never picks up another skill's override.
|
||||||
private static int defaultC4Timer = 40;
|
private static int defaultC4Timer = 40;
|
||||||
|
private static bool c4TimerOverridden;
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -25,6 +26,7 @@ namespace src.player.skills
|
||||||
public static void EnableSkill(CCSPlayerController player)
|
public static void EnableSkill(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
// At round start (not at plant) so the client HUD/alert countdown is right before the plant completes.
|
// At round start (not at plant) so the client HUD/alert countdown is right before the plant completes.
|
||||||
|
c4TimerOverridden = true;
|
||||||
Server.ExecuteCommand($"mp_c4timer {SkillsInfo.GetValue<int>(skillName, "extraC4BlowTime")}");
|
Server.ExecuteCommand($"mp_c4timer {SkillsInfo.GetValue<int>(skillName, "extraC4BlowTime")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,6 +72,9 @@ namespace src.player.skills
|
||||||
DisableSkill(player);
|
DisableSkill(player);
|
||||||
plantingPlayers.Clear();
|
plantingPlayers.Clear();
|
||||||
|
|
||||||
|
if (!c4TimerOverridden) return;
|
||||||
|
|
||||||
|
c4TimerOverridden = false;
|
||||||
Server.ExecuteCommand($"mp_c4timer {defaultC4Timer}");
|
Server.ExecuteCommand($"mp_c4timer {defaultC4Timer}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,19 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"), false);
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
{
|
||||||
|
lock (setLock)
|
||||||
|
{
|
||||||
|
poisonedPlayers.TryRemove(playerIndex, out _);
|
||||||
|
playersToTarget.TryRemove(playerIndex, out _);
|
||||||
|
|
||||||
|
foreach (var kvp in playersToTarget)
|
||||||
|
if (kvp.Value == playerIndex)
|
||||||
|
playersToTarget.TryRemove(kvp.Key, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
|
|
@ -29,7 +42,7 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
int cooldown = (int)(64 * SkillsInfo.GetValue<float>(skillName, "Cooldown"));
|
int cooldown = Math.Max(1, (int)(64 * SkillsInfo.GetValue<float>(skillName, "Cooldown")));
|
||||||
|
|
||||||
if (Server.TickCount % cooldown == 0)
|
if (Server.TickCount % cooldown == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -43,7 +56,7 @@ namespace src.player.skills
|
||||||
var pawn = player.PlayerPawn.Value;
|
var pawn = player.PlayerPawn.Value;
|
||||||
if (pawn == null || !pawn.IsValid) continue;
|
if (pawn == null || !pawn.IsValid) continue;
|
||||||
|
|
||||||
if (Jester.GetJesterInfo(playerIndex)?.Active == true) continue;
|
if (Jester.IsActiveJester(playerIndex)) continue;
|
||||||
|
|
||||||
if (pawn.Health <= SkillsInfo.GetValue<int>(skillName, "MinHealth")) continue;
|
if (pawn.Health <= SkillsInfo.GetValue<int>(skillName, "MinHealth")) continue;
|
||||||
SkillUtils.TakeHealth(pawn, SkillsInfo.GetValue<int>(skillName, "Damage"));
|
SkillUtils.TakeHealth(pawn, SkillsInfo.GetValue<int>(skillName, "Damage"));
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,19 @@ namespace src.player.skills
|
||||||
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue<string>(skillName, "color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void PlayerDisconnect(uint playerIndex)
|
||||||
|
{
|
||||||
|
lock (setLock)
|
||||||
|
{
|
||||||
|
bannedPlayers.TryRemove(playerIndex, out _);
|
||||||
|
playersToTarget.TryRemove(playerIndex, out _);
|
||||||
|
|
||||||
|
foreach (var kvp in playersToTarget)
|
||||||
|
if (kvp.Value == playerIndex)
|
||||||
|
playersToTarget.TryRemove(kvp.Key, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ namespace src.player.skills
|
||||||
public class RadarHack : ISkill
|
public class RadarHack : ISkill
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.RadarHack;
|
private const Skills skillName = Skills.RadarHack;
|
||||||
|
private static readonly Skills[] hidingSkills = [Skills.Ghost, Skills.Ninja, Skills.C4Camouflage];
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -43,8 +44,9 @@ namespace src.player.skills
|
||||||
var enemyPawn = enemyEvent.PlayerPawn.Value;
|
var enemyPawn = enemyEvent.PlayerPawn.Value;
|
||||||
if (enemyPawn == null || !enemyPawn.IsValid) continue;
|
if (enemyPawn == null || !enemyPawn.IsValid) continue;
|
||||||
|
|
||||||
// Invisibility (low render alpha) beats the radar hack.
|
var enemyInfo = PlayerManager.GetPlayerByIndex(enemyEvent.Index);
|
||||||
if (enemyPawn.Render.A < 200) continue;
|
if (enemyInfo != null && Array.IndexOf(hidingSkills, enemyInfo.Skill) >= 0 && enemyPawn.Render.A < 200)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Only the observer's slot bit — the Spotted bool would reveal to the whole team.
|
// Only the observer's slot bit — the Spotted bool would reveal to the whole team.
|
||||||
enemyPawn.EntitySpottedState.SpottedByMask[0] |= (1u << (slot % 32));
|
enemyPawn.EntitySpottedState.SpottedByMask[0] |= (1u << (slot % 32));
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ namespace src.player.skills
|
||||||
if (attacker == null || !attacker.IsValid) return;
|
if (attacker == null || !attacker.IsValid) return;
|
||||||
if (victimEvent == null || !victimEvent.IsValid || !victim.PawnIsAlive) return;
|
if (victimEvent == null || !victimEvent.IsValid || !victim.PawnIsAlive) return;
|
||||||
|
|
||||||
|
if (PlayerManager.GetPlayerByIndex(victimEvent!.Index)?.Skill != skillName) return;
|
||||||
|
|
||||||
if (SkillPlayerInfo.TryGetValue(victimEvent!.Index, out var skillInfo))
|
if (SkillPlayerInfo.TryGetValue(victimEvent!.Index, out var skillInfo))
|
||||||
{
|
{
|
||||||
if (!skillInfo.CanUse) return;
|
if (!skillInfo.CanUse) return;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
if (Server.TickCount % (int)(64 * SkillsInfo.GetValue<float>(skillName, "cooldown")) != 0) return;
|
int cooldown = Math.Max(1, (int)(64 * SkillsInfo.GetValue<float>(skillName, "cooldown")));
|
||||||
|
if (Server.TickCount % cooldown != 0) return;
|
||||||
foreach (var player in Utilities.GetPlayers())
|
foreach (var player in Utilities.GetPlayers())
|
||||||
{
|
{
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace src.player.skills
|
||||||
public class SecondLife : ISkill
|
public class SecondLife : ISkill
|
||||||
{
|
{
|
||||||
private const Skills skillName = Skills.SecondLife;
|
private const Skills skillName = Skills.SecondLife;
|
||||||
private static readonly ConcurrentDictionary<nint, byte> usedThisRound = [];
|
private static readonly ConcurrentDictionary<nint, int> usedThisRound = [];
|
||||||
private static readonly object setLock = new();
|
private static readonly object setLock = new();
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
|
|
@ -45,7 +45,13 @@ namespace src.player.skills
|
||||||
if (victimInfo == null || victimInfo.Skill != skillName) return;
|
if (victimInfo == null || victimInfo.Skill != skillName) return;
|
||||||
|
|
||||||
if (info.Damage < victimPawn.Health) return;
|
if (info.Damage < victimPawn.Health) return;
|
||||||
if (usedThisRound.ContainsKey(victim.Handle)) return;
|
|
||||||
|
if (usedThisRound.TryGetValue(victim.Handle, out int savedTick))
|
||||||
|
{
|
||||||
|
if (savedTick == Server.TickCount)
|
||||||
|
info.Damage = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
{
|
{
|
||||||
|
|
@ -54,18 +60,18 @@ namespace src.player.skills
|
||||||
var spawnpoint = SkillUtils.GetSpawnPointVector(victim);
|
var spawnpoint = SkillUtils.GetSpawnPointVector(victim);
|
||||||
if (spawnpoint == null) return; // no clean respawn point -> let the normal death happen
|
if (spawnpoint == null) return; // no clean respawn point -> let the normal death happen
|
||||||
|
|
||||||
usedThisRound.TryAdd(victim.Handle, 0);
|
usedThisRound.TryAdd(victim.Handle, Server.TickCount);
|
||||||
info.Damage = 0;
|
info.Damage = 0;
|
||||||
|
|
||||||
int startHealth = SkillsInfo.GetValue<int>(skillName, "startHealth");
|
victimPawn.Health = SkillsInfo.GetValue<int>(skillName, "startHealth");
|
||||||
|
Utilities.SetStateChanged(victimPawn, "CBaseEntity", "m_iHealth");
|
||||||
|
|
||||||
Server.NextFrame(() =>
|
Server.NextFrame(() =>
|
||||||
{
|
{
|
||||||
if (victim == null || !victim.IsValid) return;
|
if (victim == null || !victim.IsValid) return;
|
||||||
var pawn = victim.PlayerPawn.Value;
|
var pawn = victim.PlayerPawn.Value;
|
||||||
if (pawn == null || !pawn.IsValid) return;
|
if (pawn == null || !pawn.IsValid) return;
|
||||||
|
|
||||||
pawn.Health = startHealth;
|
|
||||||
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth");
|
|
||||||
pawn.Teleport(spawnpoint, null, new Vector(0, 0, 0));
|
pawn.Teleport(spawnpoint, null, new Vector(0, 0, 0));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ namespace src.player.skills
|
||||||
private const Skills skillName = Skills.ShortBomb;
|
private const Skills skillName = Skills.ShortBomb;
|
||||||
// mp_c4timer is an Int32 cvar; captured at load so restore never picks up another skill's override.
|
// mp_c4timer is an Int32 cvar; captured at load so restore never picks up another skill's override.
|
||||||
private static int defaultC4Timer = 40;
|
private static int defaultC4Timer = 40;
|
||||||
|
private static bool c4TimerOverridden;
|
||||||
|
|
||||||
public static void LoadSkill()
|
public static void LoadSkill()
|
||||||
{
|
{
|
||||||
|
|
@ -22,11 +23,15 @@ namespace src.player.skills
|
||||||
public static void EnableSkill(CCSPlayerController player)
|
public static void EnableSkill(CCSPlayerController player)
|
||||||
{
|
{
|
||||||
// At round start (not at plant) so the client HUD/alert countdown is right before the plant completes.
|
// At round start (not at plant) so the client HUD/alert countdown is right before the plant completes.
|
||||||
|
c4TimerOverridden = true;
|
||||||
Server.ExecuteCommand($"mp_c4timer {SkillsInfo.GetValue<int>(skillName, "detonationTime")}");
|
Server.ExecuteCommand($"mp_c4timer {SkillsInfo.GetValue<int>(skillName, "detonationTime")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void NewRound()
|
public static void NewRound()
|
||||||
{
|
{
|
||||||
|
if (!c4TimerOverridden) return;
|
||||||
|
|
||||||
|
c4TimerOverridden = false;
|
||||||
Server.ExecuteCommand($"mp_c4timer {defaultC4Timer}");
|
Server.ExecuteCommand($"mp_c4timer {defaultC4Timer}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,20 +59,20 @@ namespace src.player.skills
|
||||||
foreach (var player in Utilities.GetPlayers())
|
foreach (var player in Utilities.GetPlayers())
|
||||||
if (cameras.TryGetValue(player.Index, out var cameraInfo) && cameraInfo.Item2 != 0)
|
if (cameras.TryGetValue(player.Index, out var cameraInfo) && cameraInfo.Item2 != 0)
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return;
|
if (player == null || !player.IsValid) continue;
|
||||||
|
|
||||||
var enemy = Utilities.GetPlayerFromIndex((int)cameraInfo.Item3);
|
var enemy = Utilities.GetPlayerFromIndex((int)cameraInfo.Item3);
|
||||||
if (enemy == null || !enemy.IsValid || enemy.PlayerPawn == null)
|
if (enemy == null || !enemy.IsValid || enemy.PlayerPawn == null)
|
||||||
{
|
{
|
||||||
ChangeCamera(player, true);
|
ChangeCamera(player, true);
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var enemyPawn = enemy.PlayerPawn.Value;
|
var enemyPawn = enemy.PlayerPawn.Value;
|
||||||
if (enemyPawn == null || !enemyPawn.IsValid)
|
if (enemyPawn == null || !enemyPawn.IsValid)
|
||||||
{
|
{
|
||||||
ChangeCamera(player, true);
|
ChangeCamera(player, true);
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enemyPawn.Health <= 0 || (player.PlayerPawn?.Value != null && player.PlayerPawn.Value.Health <= 0))
|
if (enemyPawn.Health <= 0 || (player.PlayerPawn?.Value != null && player.PlayerPawn.Value.Health <= 0))
|
||||||
|
|
@ -117,6 +117,10 @@ namespace src.player.skills
|
||||||
pawn.CameraServices.ViewEntity.Raw = orginalCameraRaw;
|
pawn.CameraServices.ViewEntity.Raw = orginalCameraRaw;
|
||||||
|
|
||||||
Utilities.SetStateChanged(pawn, "CBasePlayerPawn", "m_pCameraServices");
|
Utilities.SetStateChanged(pawn, "CBasePlayerPawn", "m_pCameraServices");
|
||||||
|
|
||||||
|
if (forceToDefault && cameras.TryGetValue(player.Index, out var current) && current.Item2 != 0)
|
||||||
|
cameras[player.Index] = (current.Item1, 0, current.Item3);
|
||||||
|
|
||||||
BlockWeapon(player, !defaultCam);
|
BlockWeapon(player, !defaultCam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ namespace src.player.skills
|
||||||
|
|
||||||
public static void OnTick()
|
public static void OnTick()
|
||||||
{
|
{
|
||||||
int tick = SkillsInfo.GetValue<int>(skillName, "tickCooldown");
|
int tick = Math.Max(1, SkillsInfo.GetValue<int>(skillName, "tickCooldown"));
|
||||||
if (Server.TickCount % tick != 0) return;
|
if (Server.TickCount % tick != 0) return;
|
||||||
|
|
||||||
float smokeRadius = SkillsInfo.GetValue<float>(skillName, "smokeRadius");
|
float smokeRadius = SkillsInfo.GetValue<float>(skillName, "smokeRadius");
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ namespace src.player.skills
|
||||||
{
|
{
|
||||||
lock (setLock)
|
lock (setLock)
|
||||||
{
|
{
|
||||||
|
if (infectedPlayers.IsEmpty && playersToTarget.IsEmpty) return;
|
||||||
|
|
||||||
foreach (var player in Utilities.GetPlayers())
|
foreach (var player in Utilities.GetPlayers())
|
||||||
DisableSkill(player);
|
DisableSkill(player);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -296,8 +296,8 @@ namespace src.utils
|
||||||
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||||
if (playerInfo == null) return false;
|
if (playerInfo == null) return false;
|
||||||
|
|
||||||
var jester = Jester.GetJesterInfo(player.Index);
|
if (playerInfo.Skill == Skills.Jester && Jester.GetJesterInfo(player.Index)?.Active == true)
|
||||||
if (jester?.Active == true) return false;
|
return false;
|
||||||
|
|
||||||
if (playerInfo.Skill == Skills.GodMode && GodMode.HaveHodMode(player.Index))
|
if (playerInfo.Skill == Skills.GodMode && GodMode.HaveHodMode(player.Index))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue