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.Timers; using CounterStrikeSharp.API.Modules.UserMessages; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.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 Lazy LazySig(string name, Func factory) where T : class => new(() => { 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?> HEGrenadeProjectile_CreateFunc = LazySig>("HEGrenadeProjectile_CreateFunc", s => new(s)); private static readonly Lazy?> SmokeGrenadeProjectile_CreateFunc = LazySig>("SmokeGrenadeProjectile_CreateFunc", s => new(s)); private static readonly Lazy?> TerminateRoundFunc = LazySig>("CCSGameRules_TerminateRound", s => new(s)); private static readonly Lazy?> SnapViewAngles = LazySig>("SnapViewAngles", s => new(s)); // private static readonly int collisionRulesChangedOffset = GameData.GetOffset("CBaseEntity_CollisionRulesChanged"); 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 UpdateGrenadeCount(CCSPlayerController player, CsItem item, int ammo) { 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 weapon = player.PlayerPawn.Value.WeaponServices.MyWeapons .FirstOrDefault(w => w != null && w.IsValid && w.Value != null && w.Value.IsValid && !string.IsNullOrEmpty(w.Value.DesignerName) && w.Value.DesignerName == itemString); if (weapon == null || !weapon.IsValid || weapon.Value == null || !weapon.Value.IsValid) return; weapon.Value.Clip1 = ammo; Utilities.SetStateChanged(weapon.Value, "CBasePlayerWeapon", "m_iClip1"); if (ammo == 1) return; jRandomSkills.Instance.AddTimer(.1f, () => { if (weapon == null || !weapon.IsValid || weapon.Value == null || !weapon.Value.IsValid) return; weapon.Value.Clip1 = 1; }, TimerFlags.STOP_ON_MAPCHANGE); } public static void TryGiveWeapon(CCSPlayerController player, CsItem item, int count = 1, bool existValidator = true) { 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 || !existValidator) 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 void SafeKillEntity(uint? index) where T : CBaseEntity { if (index == null) return; EntityManager.DestroyEntity(index.Value); } public static bool IsValid(this CHandle? handle) where T : NativeEntity { return handle != null && handle.IsValid && handle.Value != null; } public static bool IsValid(this CBaseEntity? ent) { return ent != null && ent.IsValid; } public static bool CheckPlayer(this CCSPlayerController? player) { return player != null && player.IsValid && player.PlayerPawn?.Value?.IsValid() == true && player.PawnIsAlive && (player?.Team is CsTeam.CounterTerrorist or CsTeam.Terrorist); } 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.Value?.Invoke(pawn, angle); } public static CBeam? CreateLine(Vector start, Vector end, Color color, uint ownerPlayerIndex = EntityManager.SystemOwnerIndex) { return EntityManager.CreateTrackedBeam(ownerPlayerIndex, start, end, color); } public static void SetPlayerCollisions(CCSPlayerController? player, bool enable) { return; if (player == null || !player.IsValid) return; var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE || pawn.CBodyComponent == null) return; var collision = pawn.Collision; if (collision == null) return; var collisionGroup = (byte)(enable ? CollisionGroup.COLLISION_GROUP_PLAYER : CollisionGroup.COLLISION_GROUP_DEBRIS); collision.CollisionGroup = collisionGroup; collision.CollisionAttribute.CollisionGroup = collisionGroup; Utilities.SetStateChanged(pawn, "CCollisionProperty", "m_collisionAttribute"); // CollisionRulesChanged(pawn); } //public static void CollisionRulesChanged(CBaseEntity? entity) //{ // if (entity == null || !entity.IsValid || collisionRulesChangedOffset <= 0) return; // var collisionRulesChanged = new VirtualFunctionVoid(entity.Handle, collisionRulesChangedOffset); // collisionRulesChanged.Invoke(entity.Handle); //} 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.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) { SmokeGrenadeProjectile_CreateFunc.Value?.Invoke(pos.Handle, angle.Handle, vel.Handle, vel.Handle, IntPtr.Zero, 45, teamNum); } public static bool TakeHealth(CCSPlayerPawn? pawn, int damage) { if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return false; var player = pawn.Controller.Value; if (player != null && player.IsValid && player.SteamID != 0) { var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); if (playerInfo == null) return false; var jester = Jester.GetJesterInfo(player.Index); if (jester?.Active == true) return false; if (playerInfo.Skill == Skills.GodMode && GodMode.HaveHodMode(player.Index)) return false; } 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); }); return false; } return true; } public static void ResetPrintHTML(CCSPlayerController? player) { if (player == null || !player.IsValid) return; var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); if (playerInfo == null) return; playerInfo.PrintHTML = null; } public static CTriggerMultiple? CreateTrigger(string name, float radius, Vector pos, uint ownerPlayerIndex = EntityManager.SystemOwnerIndex) { return EntityManager.CreateTrackedTrigger(ownerPlayerIndex, name, radius, pos); } public static void ForceFullUpdate(CCSPlayerController player, List<(uint PlayerIndex, QAngle LastAngle)>? batchList = null, INetworkGameServer? networkGameServer = null) { if (!Config.LoadedConfig.EnableFullForceUpdate) return; if (player == null || !player.IsValid) return; var pawn = player.PlayerPawn?.Value; if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return; QAngle lastAngle = new(pawn.V_angle.X, pawn.V_angle.Y, pawn.V_angle.Z); networkGameServer ??= new INetworkServerService().GetIGameServer(); var client = networkGameServer.GetClientBySlot(player.Slot); if (client == null) return; client.ForceFullUpdate(); // 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; if (batchList != null) { batchList.Add((playerIndex, lastAngle)); return; } jRandomSkills.Instance.AddTickTimer(3, () => { var target = Utilities.GetPlayerFromIndex((int)playerIndex); if (target == null || !target.IsValid) return; var targetPawn = target.PlayerPawn?.Value; if (targetPawn == null || !targetPawn.IsValid || targetPawn.AbsOrigin == null) return; targetPawn.Look(lastAngle); }); } private static int lastForceFullUpdateAll = int.MinValue; public static void ForceFullUpdateToAll() { if (!Config.LoadedConfig.EnableFullForceUpdate) return; int tickCount = Server.TickCount; if (tickCount == lastForceFullUpdateAll) return; lastForceFullUpdateAll = tickCount; var playersToRestore = new List<(uint PlayerIndex, QAngle LastAngle)>(); INetworkGameServer networkGameServer = new INetworkServerService().GetIGameServer(); foreach (var player in Utilities.GetPlayers()) ForceFullUpdate(player, playersToRestore, networkGameServer); if (playersToRestore.Count <= 0) return; jRandomSkills.Instance.AddTickTimer(3, () => { foreach (var item in playersToRestore) { var target = Utilities.GetPlayerFromIndex((int)item.PlayerIndex); if (target == null || !target.IsValid) continue; var targetPawn = target.PlayerPawn?.Value; if (targetPawn == null || !targetPawn.IsValid || targetPawn.AbsOrigin == null) continue; targetPawn.Look(item.LastAngle); } }); } public static bool SetHealth(CCSPlayerPawn? pawn, int newHealth, int? maxHealth = null) { if (pawn == null || !pawn.IsValid) return false; maxHealth ??= pawn.MaxHealth; if (pawn.Health == maxHealth) return false; pawn.Health = Math.Min(newHealth, (int)maxHealth); Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth"); pawn.MaxHealth = (int)maxHealth; Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iMaxHealth"); return true; } public static bool AddHealth(CCSPlayerPawn? pawn, int extraHealth, int? maxHealth = null) { if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE || pawn.Health <= 0) return false; maxHealth ??= pawn.MaxHealth; if (pawn.Health == maxHealth) return false; 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"); return true; } 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; var p = PlayerManager.GetPlayerFromEvent(player); if (p == null || !p.IsValid) return; pawn.Health = (int)p.PawnHealth; Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth"); } public static void SetPlayerInvisibility(CCSPlayerController player, float percentInvisibility) { if (player == null || !player.IsValid || player.PlayerPawn == null) return; var playerPawn = player.PlayerPawn.Value; if (playerPawn != null) { var color = Color.FromArgb(Math.Max(255 - (int)(255 * percentInvisibility), 0), 255, 255, 255); playerPawn.Render = color; Utilities.SetStateChanged(playerPawn, "CBaseModelEntity", "m_clrRender"); } } 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); } private static string GetInvisibleSignature(string id) { if (string.IsNullOrEmpty(id)) return ""; var sb = new System.Text.StringBuilder(); foreach (char c in id) for (int i = 0; i < 8; i++) sb.Append(((c >> i) & 1) == 1 ? "\u200B" : "\u200C"); return sb.ToString(); } public static void UpdateMenu(CCSPlayerController? player, ConcurrentBag<(string, string)> items) { if (player == null) return; var manager = GetMenuManager(); if (manager == null) return; var playerInfo = PlayerManager.GetPlayerByIndex(player!.Index); if (playerInfo == null) return; bool isIlliterate = Illiterate.CheckIlliterateSkill(player); Dictionary> list = []; foreach (var item in items) { string encodedText = isIlliterate ? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(item.Item1)!) : System.Net.WebUtility.HtmlEncode(item.Item1); string uniqueKey = GetInvisibleSignature(item.Item2) + $"\u202A{encodedText}\u202C"; list.TryAdd(uniqueKey, (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 = PlayerManager.GetPlayerByIndex(player!.Index); if (playerInfo == null || !playerInfo.DisplayHUD) return; if (player.IsBot) { var pool = new List(); foreach (var enemy in enemies) if (!string.IsNullOrEmpty(enemy.Item2)) pool.Add(enemy.Item2); if (lastElement != null && !string.IsNullOrEmpty(lastElement.Value.Item2)) pool.Add(lastElement.Value.Item2); if (pool.Count > 0) { string randomTarget = pool[Random.Shared.Next(pool.Count)]; jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [player, new[] { randomTarget }]); } 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) ? "" : $"\u202A{your_skill}:\u202C
"; string skillLine = Illiterate.CheckIlliterateSkill(player) ? $"\u202A{Illiterate.GetRandomText(player.GetSkillName(skillData.Skill))}\u202C
" : $"\u202A{player.GetSkillName(skillData.Skill)}\u202C
"; var skill_select_info = player.GetTranslation($"{playerInfo.Skill.ToString().ToLowerInvariant()}_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) { string encodedEnemyName = isIlliterate ? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(enemy.Item1)!) : System.Net.WebUtility.HtmlEncode(enemy.Item1); string uniqueKey = GetInvisibleSignature(enemy.Item2) + $"\u202A{encodedEnemyName}\u202C"; menu.Add(uniqueKey, (p, option) => { jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { enemy.Item2 }]); manager.CloseMenu(p); }); } if (lastElement != null) { string encodedLastElement = isIlliterate ? System.Net.WebUtility.HtmlEncode(Illiterate.GetRandomText(lastElement.Value.Item1)!) : System.Net.WebUtility.HtmlEncode(lastElement.Value.Item1); menu.Add($"\u202A{encodedLastElement}\u202C", (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.Value?.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.FirstOrDefault(t => t.IsValid && (CsTeam)t.TeamNum == CsTeam.CounterTerrorist); var tTeams = teams.FirstOrDefault(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.FirstOrDefault(t => t.IsValid && (CsTeam)t.TeamNum == CsTeam.CounterTerrorist); var tTeams = teams.FirstOrDefault(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; } }