using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Cvars; using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.UserMessages; using CounterStrikeSharp.API.Modules.Utils; using src.player; using src.player.skills; using System.Collections.Concurrent; using System.Drawing; using System.Globalization; using System.Runtime.InteropServices; using WASDMenuAPI.Classes; using WASDSharedAPI; namespace src.utils { public static class SkillUtils { private static readonly MemoryFunctionWithReturn HEGrenadeProjectile_CreateFunc = new(GameData.GetSignature("HEGrenadeProjectile_CreateFunc")); private static readonly MemoryFunctionWithReturn SmokeGrenadeProjectile_CreateFunc = new(GameData.GetSignature("SmokeGrenadeProjectile_CreateFunc")); private static readonly MemoryFunctionVoid TerminateRoundFunc = new(GameData.GetSignature("CCSGameRules_TerminateRound")); private static readonly MemoryFunctionVoid SnapViewAngles = new(GameData.GetSignature("SnapViewAngles")); public static void PrintToChat(CCSPlayerController player, string? msg, string border = "tb", string? title = null) { if (!player.IsValid) return; var config = Config.LoadedConfig.ChatMessage; float maxWidth = config.MaxWidth; char symbol = config.LineSymbol; if (string.IsNullOrEmpty(title)) title = player.GetTranslation("jRandomSkills"); if (Illiterate.CheckIlliterateSkill(player)) msg = Illiterate.GetRandomText(msg); if (border.Contains('t') && config.LineShow) player.PrintToChat($" {MeansureString.GetTextDashed($"{(config.TagFormat.Contains("{TAG}") ? config.TagFormat.Replace("{TAG}", title) : $"\u0002◢◆◤ {title} ◥◆◣")}", maxWidth, symbol, config.LineColor)}"); if (!string.IsNullOrEmpty(msg) && config.InfoMessageShow) player.PrintToChat($" {config.InfoSkillColor} {msg.Replace("\x02", config.InfoPlayerNameColor).Replace("\x06", config.InfoSkillColor)}"); if (border.Contains('b') && config.LineShow) player.PrintToChat($" {MeansureString.GetTextDashed("", maxWidth, symbol, config.LineColor)}"); } public static bool IsFreezeTime() { return jRandomSkills.Instance?.GameRules?.FreezePeriod == true; } public static void RegisterSkill(Skills skill, string color, bool display = true) { if (!SkillData.Skills.Any(s => s.Skill == skill)) SkillData.Skills.Add(new jSkill_SkillInfo(skill, color, display)); } public static void TryGiveWeapon(CCSPlayerController player, CsItem item, int count = 1) { string? itemString = EnumUtils.GetEnumMemberAttributeValue(item); if (string.IsNullOrWhiteSpace(itemString)) return; if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) return; if (player.PlayerPawn.Value.WeaponServices == null) return; var exists = player.PlayerPawn.Value.WeaponServices.MyWeapons .FirstOrDefault(w => w != null && w.IsValid && w.Value != null && w.Value.IsValid && w.Value.DesignerName == itemString); if (exists == null) for (int i = 0; i < count; i++) player.GiveNamedItem(item); } public static double GetDistance(Vector vector1, Vector vector2) { return Math.Sqrt(Math.Pow(vector2.X - vector1.X, 2) + Math.Pow(vector2.Y - vector1.Y, 2) + Math.Pow(vector2.Z - vector1.Z, 2)); } public static float Distance(this Vector vector1, Vector vector2) { return (float)GetDistance(vector1, vector2); } public static float Dot(this Vector vector1, Vector vector2) { return (vector1.X * vector2.X) + (vector1.Y * vector2.Y) + (vector1.Z * vector2.Z); } public static Vector Normalize(this Vector vector) { float length = vector.Length(); if (length > 0) return new Vector(vector.X / length, vector.Y / length, vector.Z / length); return Vector.Zero; } public static string SecondsToTimer(int totalSeconds) { if (totalSeconds <= 0) return "00:00"; int minutes = totalSeconds / 60; int seconds = totalSeconds % 60; return $"{minutes:D2}:{seconds:D2}"; } public static Vector GetForwardVector(QAngle angles) { float pitch = -angles.X * (float)(Math.PI / 180); float yaw = angles.Y * (float)(Math.PI / 180); float x = (float)(Math.Cos(pitch) * Math.Cos(yaw)); float y = (float)(Math.Cos(pitch) * Math.Sin(yaw)); float z = (float)Math.Sin(pitch); return new Vector(x, y, z); } public static void Look(this CBasePlayerPawn pawn, QAngle angle) { if (pawn == null || !pawn.IsValid) return; SnapViewAngles.Invoke(pawn, angle); } public static CBeam? CreateLine(Vector start, Vector end, Color color) { CBeam beam = Utilities.CreateEntityByName("beam")!; if (beam == null) return null; beam.Render = color; beam.Width = 2.0f; beam.EndWidth = 2.0f; beam.Teleport(start); beam.EndPos.X = end.X; beam.EndPos.Y = end.Y; beam.EndPos.Z = end.Z; beam.DispatchSpawn(); return beam; } public static void ApplyScreenColor(CCSPlayerController? player, int r, int g, int b, int a, int duration, int holdTime, int flags = 1) { if (player == null || !player.IsValid) return; using var msg = UserMessage.FromPartialName("Fade"); if (msg == null) return; int packageColor = (a << 24) | (b << 16) | (g << 8) | r; msg.SetInt("duration", duration); msg.SetInt("hold_time", holdTime); msg.SetInt("flags", flags); msg.SetInt("color", packageColor); msg.Send(player); } public static void ChangePlayerScale(CCSPlayerController? player, float scale) { if (player == null || !player.IsValid) return; var playerPawn = player.PlayerPawn.Value; if (playerPawn == null || !playerPawn.IsValid || playerPawn.CBodyComponent == null || playerPawn.CBodyComponent.SceneNode == null) return; var skeleton = playerPawn.CBodyComponent.SceneNode.GetSkeletonInstance(); if (scale <= 0 || skeleton == null) return; skeleton.Scale = scale; playerPawn.AcceptInput("SetScale", null, null, scale.ToString(CultureInfo.InvariantCulture)); Server.NextWorldUpdate(() => { if (playerPawn == null || !playerPawn.IsValid) return; Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_CBodyComponent"); }); } public static Vector? GetSpawnPointVector(CCSPlayerController player, bool enemySpawn = false) { if (player == null || player.Team is CsTeam.None or CsTeam.Spectator) return null; CsTeam targetTeam = enemySpawn ? (player.Team == CsTeam.CounterTerrorist ? CsTeam.Terrorist : CsTeam.CounterTerrorist) : player.Team; string spawnPointName = targetTeam == CsTeam.CounterTerrorist ? "info_player_counterterrorist" : "info_player_terrorist"; var spawns = Utilities.FindAllEntitiesByDesignerName(spawnPointName).Where(s => s.IsValid && s.Enabled).ToList(); if (spawns.Count != 0) { var randomSpawn = spawns[jRandomSkills.Instance.Random.Next(spawns.Count)]; if (randomSpawn != null && randomSpawn.IsValid && randomSpawn.AbsOrigin != null) return new Vector(randomSpawn.AbsOrigin.X, randomSpawn.AbsOrigin.Y, randomSpawn.AbsOrigin.Z); } return null; } 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); } 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); } public static void TakeHealth(CCSPlayerPawn? pawn, int damage) { if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return; var player = pawn.Controller.Value; if (player != null && player.IsValid && player.SteamID != 0) { ulong steamId = player.SteamID; var playerInfo = jRandomSkills.Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == steamId); if (playerInfo == null) return; var jester = Jester.GetJesterInfo(steamId); if (jester?.Active == true) return; } int newHealth = (int)(pawn.Health - damage); pawn.Health = newHealth; Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth"); if (pawn.Health <= 0) Server.NextFrame(() => { if (pawn == null || !pawn.IsValid) return; pawn?.CommitSuicide(false, true); }); } public static void ResetPrintHTML(CCSPlayerController? player) { var playerInfo = jRandomSkills.Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); if (playerInfo == null) return; playerInfo.PrintHTML = null; } public static CTriggerMultiple? CreateTrigger(string name, float radius, Vector pos) { if (pos == null) return null; var trigger = Utilities.CreateEntityByName("trigger_multiple"); if (trigger == null || trigger.AbsOrigin == null) return null; trigger.Collision.SolidType = SolidType_t.SOLID_CAPSULE; trigger.Collision.SolidFlags = 0; trigger.Spawnflags = 1; trigger.Globalname = $"{name}_{trigger.Index}"; trigger.Collision.SolidFlags = 1; trigger.AbsOrigin.X = pos.X; trigger.AbsOrigin.Y = pos.Y; trigger.AbsOrigin.Z = pos.Z; trigger.Collision.CapsuleRadius = radius; trigger.Collision.BoundingRadius = radius; trigger.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_TRIGGER; trigger.Collision.EnablePhysics = 1; trigger.Collision.TriggerBloat = 0; trigger.Collision.SurroundType = SurroundingBoundsType_t.USE_OBB_COLLISION_BOUNDS; trigger.Collision.CollisionAttribute.CollisionFunctionMask = 39; trigger.Collision.CollisionAttribute.CollisionGroup = 2; trigger.DispatchSpawn(); return trigger; } public static void AddHealth(CCSPlayerPawn? pawn, int extraHealth, int? maxHealth = null) { if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return; maxHealth ??= pawn.MaxHealth; int newHealth = (int)(pawn.Health + extraHealth); pawn.Health = Math.Min(newHealth, (int)maxHealth); Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth"); pawn.MaxHealth = (int)maxHealth; Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iMaxHealth"); } public static void RestoreHealth(CCSPlayerController? player) { if (player == null || !player.IsValid || player.PlayerPawn == null) return; CBasePlayerPawn? pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return; pawn.Health = (int)player.PawnHealth; Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth"); } public static string GetDesignerName(CBasePlayerWeapon? weapon) { if (weapon == null || !weapon.IsValid) return string.Empty; string designerName = weapon.DesignerName; ushort index = weapon.AttributeManager.Item.ItemDefinitionIndex; designerName = (designerName, index) switch { var (name, _) when name.Contains("bayonet") => "weapon_knife", ("weapon_m4a1", 60) => "weapon_m4a1_silencer", ("weapon_hkp2000", 61) => "weapon_usp_silencer", ("weapon_deagle", 64) => "weapon_revolver", _ => designerName }; return designerName; } private static IWasdMenuManager? GetMenuManager() { if (jRandomSkills.Instance.MenuManager == null) jRandomSkills.Instance.MenuManager = new WasdManager(); return jRandomSkills.Instance.MenuManager; } public static void CloseMenu(CCSPlayerController? player) { var manager = GetMenuManager(); if (manager == null) return; manager.CloseMenu(player); } public static bool HasMenu(CCSPlayerController? player) { var manager = GetMenuManager(); if (manager == null) return false; return manager.HasMenu(player); } public static void UpdateMenu(CCSPlayerController? player, ConcurrentBag<(string, string)> items) { if (player == null) return; var manager = GetMenuManager(); if (manager == null) return; var playerInfo = jRandomSkills.Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null) return; bool isIlliterate = Illiterate.CheckIlliterateSkill(player); Dictionary> list = []; foreach (var item in items) list.TryAdd(isIlliterate ? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(item.Item1)!) : System.Net.WebUtility.HtmlEncode(item.Item1), (p, option) => { jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { item.Item2 }]); manager.CloseMenu(p); }); manager.UpdateActiveMenu(player, list); } public static void CreateMenu(CCSPlayerController? player, ConcurrentBag<(string, string)> enemies, (string, string, bool)? lastElement = null) { if (player == null || !player.IsValid) return; var playerInfo = jRandomSkills.Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null || !playerInfo.DisplayHUD) return; var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == playerInfo.Skill); if (skillData == null) return; var manager = GetMenuManager(); if (manager == null) return; var config = Config.LoadedConfig.HtmlHudCustomisation; var your_skill = player.GetTranslation("your_skill"); var emptySymbol = $" "; string infoLine = string.IsNullOrEmpty(your_skill) ? "" : $"{your_skill}:
"; string skillLine = Illiterate.CheckIlliterateSkill(player) ? $"{Illiterate.GetRandomText(player.GetSkillName(skillData.Skill))}
" : $"{player.GetSkillName(skillData.Skill)}
"; var skill_select_info = player.GetTranslation($"{playerInfo.Skill.ToString().ToLower()}_select_info"); string remainingLine = string.IsNullOrWhiteSpace(skill_select_info) ? "" : $"{skill_select_info}
"; var hudContent = infoLine + skillLine + remainingLine; string controllsLine = $"{emptySymbol}{player.GetTranslation($"menu_controlls_scroll")}" + $"{player.GetTranslation($"menu_controlls_padding")}" + $"{player.GetTranslation($"menu_controlls_select")}{emptySymbol}"; string itemText = $"{{0}}
"; string itemHoverText = $"[ {{0}} ]
"; bool isIlliterate = Illiterate.CheckIlliterateSkill(player); IWasdMenu menu = manager.CreateMenu(hudContent, itemText, itemHoverText, controllsLine); foreach (var enemy in enemies) menu.Add(isIlliterate ? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(enemy.Item1)!) : System.Net.WebUtility.HtmlEncode(enemy.Item1), (p, option) => { jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { enemy.Item2 }]); manager.CloseMenu(p); }); if (lastElement != null) menu.Add(isIlliterate ? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(lastElement.Value.Item1)!) : System.Net.WebUtility.HtmlEncode(lastElement.Value.Item1), (p, option) => { jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { lastElement.Value.Item2 }]); if (lastElement.Value.Item3) manager.CloseMenu(p); }); manager.OpenMainMenu(player, menu); } public static void SetTeamScores(short ctScore, short tScore, RoundEndReason roundEndReason) { if (jRandomSkills.Instance == null || jRandomSkills.Instance.GameRules == null) return; UpdateServerTeamScores(ctScore, tScore); TerminateRoundFunc.Invoke(jRandomSkills.Instance.GameRules.Handle, 5f, roundEndReason, 0, 0); } public static void TerminateRound(CsTeam winnerTeam) { if (jRandomSkills.Instance == null || jRandomSkills.Instance.GameRules == null) return; var teams = Utilities.FindAllEntitiesByDesignerName("cs_team_manager"); var ctTeam = teams.First(t => t.IsValid && (CsTeam)t.TeamNum == CsTeam.CounterTerrorist); var tTeams = teams.First(t => t.IsValid && (CsTeam)t.TeamNum == CsTeam.Terrorist); if (ctTeam == null || tTeams == null) return; short ctScore = (short)(winnerTeam == CsTeam.CounterTerrorist ? ctTeam.Score + 1 : ctTeam.Score); short tScore = (short)(winnerTeam == CsTeam.Terrorist ? tTeams.Score + 1 : tTeams.Score); UpdateServerTeamScores(ctScore, tScore); jRandomSkills.Instance.GameRules?.TerminateRound(5f, winnerTeam == CsTeam.CounterTerrorist ? RoundEndReason.BombDefused : RoundEndReason.TargetBombed); // TerminateRoundFunc.Invoke(jRandomSkills.Instance.GameRules.Handle, 5f, winnerTeam == CsTeam.CounterTerrorist ? RoundEndReason.BombDefused : RoundEndReason.TargetBombed, 0, 0); } private static void UpdateServerTeamScores(short ctScore, short tScore) { if (jRandomSkills.Instance == null || jRandomSkills.Instance.GameRules == null) return; int totalRoundsPlayed = ctScore + tScore; int maxRounds = ConVar.Find("mp_maxrounds")?.GetPrimitiveValue() ?? 24; int halfRounds = maxRounds / 2; int overtimeMaxRounds = ConVar.Find("mp_overtime_maxrounds")?.GetPrimitiveValue() ?? 6; int overtimeLimit = ConVar.Find("mp_overtime_limit")?.GetPrimitiveValue() ?? 1; var gameRulesProxy = jRandomSkills.Instance.GameRules; gameRulesProxy.TotalRoundsPlayed = totalRoundsPlayed; gameRulesProxy.ITotalRoundsPlayed = totalRoundsPlayed; gameRulesProxy.RoundsPlayedThisPhase = totalRoundsPlayed; gameRulesProxy.TeamIntroPeriod = false; if (gameRulesProxy.GamePhase == 1 && totalRoundsPlayed < halfRounds) { gameRulesProxy.GamePhase = 0; gameRulesProxy.SwapTeamsOnRestart = true; gameRulesProxy.SwitchingTeamsAtRoundReset = true; gameRulesProxy.RoundsPlayedThisPhase = 0; gameRulesProxy.TeamIntroPeriod = true; } if (totalRoundsPlayed < halfRounds) gameRulesProxy.GamePhase = 0; else if (gameRulesProxy.GamePhase == 0) { gameRulesProxy.GamePhase = 1; gameRulesProxy.SwapTeamsOnRestart = true; gameRulesProxy.SwitchingTeamsAtRoundReset = true; gameRulesProxy.RoundsPlayedThisPhase = 0; gameRulesProxy.TeamIntroPeriod = true; } var structOffset = jRandomSkills.Instance.GameRules.Handle + Schema.GetSchemaOffset("CCSGameRules", "m_bMapHasBombZone") + 0x02; var matchStruct = Marshal.PtrToStructure(structOffset); matchStruct.m_totalScore = (short)totalRoundsPlayed; matchStruct.m_actualRoundsPlayed = (short)totalRoundsPlayed; gameRulesProxy.MatchInfoDecidedTime = Server.CurrentTime; matchStruct.m_ctScoreTotal = ctScore; gameRulesProxy.AccountCT = ctScore; matchStruct.m_terroristScoreTotal = tScore; gameRulesProxy.AccountTerrorist = tScore; if (gameRulesProxy.GamePhase == 0) { matchStruct.m_ctScoreFirstHalf = ctScore; matchStruct.m_terroristScoreFirstHalf = tScore; } else { matchStruct.m_ctScoreSecondHalf = ctScore; matchStruct.m_terroristScoreSecondHalf = tScore; } if (totalRoundsPlayed >= maxRounds) { if (gameRulesProxy.OvertimePlaying == 0) { gameRulesProxy.OvertimePlaying = 1; gameRulesProxy.SwapTeamsOnRestart = true; gameRulesProxy.SwitchingTeamsAtRoundReset = true; } else { int roundsInOvertime = totalRoundsPlayed - maxRounds; if (roundsInOvertime % overtimeMaxRounds == 0) { int currentOvertime = roundsInOvertime / overtimeMaxRounds; if ( currentOvertime < overtimeLimit) { gameRulesProxy.SwapTeamsOnRestart = true; gameRulesProxy.SwitchingTeamsAtRoundReset = true; } } } } gameRulesProxy.OvertimePlaying = 0; Marshal.StructureToPtr(matchStruct, structOffset, true); UpdateClientTeamScores(matchStruct); } private static void UpdateClientTeamScores(MCCSMatch match) { var teams = Utilities.FindAllEntitiesByDesignerName("cs_team_manager"); var ctTeam = teams.First(t => t.IsValid && (CsTeam)t.TeamNum == CsTeam.CounterTerrorist); var tTeams = teams.First(t => t.IsValid && (CsTeam)t.TeamNum == CsTeam.Terrorist); if (ctTeam != null && tTeams != null) { ctTeam.Score = match.m_ctScoreTotal; ctTeam.ScoreFirstHalf = match.m_ctScoreFirstHalf; ctTeam.ScoreSecondHalf = match.m_ctScoreSecondHalf; ctTeam.ScoreOvertime = match.m_ctScoreOvertime; Utilities.SetStateChanged(ctTeam, "CTeam", "m_iScore"); Utilities.SetStateChanged(ctTeam, "CCSTeam", "m_scoreFirstHalf"); Utilities.SetStateChanged(ctTeam, "CCSTeam", "m_scoreSecondHalf"); Utilities.SetStateChanged(ctTeam, "CCSTeam", "m_scoreOvertime"); tTeams.Score = match.m_terroristScoreTotal; tTeams.ScoreFirstHalf = match.m_terroristScoreFirstHalf; tTeams.ScoreSecondHalf = match.m_terroristScoreSecondHalf; tTeams.ScoreOvertime = match.m_terroristScoreOvertime; Utilities.SetStateChanged(tTeams, "CTeam", "m_iScore"); Utilities.SetStateChanged(tTeams, "CCSTeam", "m_scoreFirstHalf"); Utilities.SetStateChanged(tTeams, "CCSTeam", "m_scoreSecondHalf"); Utilities.SetStateChanged(tTeams, "CCSTeam", "m_scoreOvertime"); } } } [StructLayout(LayoutKind.Sequential)] public struct MCCSMatch { public short m_totalScore; public short m_actualRoundsPlayed; public short m_nOvertimePlaying; public short m_ctScoreFirstHalf; public short m_ctScoreSecondHalf; public short m_ctScoreOvertime; public short m_ctScoreTotal; public short m_terroristScoreFirstHalf; public short m_terroristScoreSecondHalf; public short m_terroristScoreOvertime; public short m_terroristScoreTotal; public short unknown; public int m_phase; } }