jRandomSkills/jRandomSkills - SRC Files/src/utils/SkillUtils.cs
2025-10-16 19:12:57 +02:00

408 lines
No EOL
19 KiB
C#

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.Utils;
using System.Runtime.InteropServices;
using WASDMenuAPI.Classes;
using WASDSharedAPI;
using System.Collections.Concurrent;
using src.player;
using src.player.skills;
namespace src.utils
{
public static class SkillUtils
{
private static readonly MemoryFunctionWithReturn<IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, int> HEGrenadeProjectile_CreateFunc = new(GameData.GetSignature("HEGrenadeProjectile_CreateFunc"));
private static readonly MemoryFunctionVoid<nint, float, RoundEndReason, nint, nint> TerminateRoundFunc = new(GameData.GetSignature("CCSGameRules_TerminateRound"));
public static void PrintToChat(CCSPlayerController player, string msg, bool isError)
{
string checkIcon = isError ? $"{ChatColors.DarkRed}✖{ChatColors.LightRed}" : $"{ChatColors.Green}✔{ChatColors.Lime}";
player.PrintToChat($" {ChatColors.DarkRed}► {ChatColors.Green}[{ChatColors.DarkRed} {jRandomSkills.Tag} {ChatColors.Green}] {checkIcon} {msg}");
}
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 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 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;
if (scale <= 0) return;
playerPawn.CBodyComponent.SceneNode.GetSkeletonInstance().Scale = scale;
Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_CBodyComponent");
Server.NextFrame(() => playerPawn.AcceptInput("SetScale", null, null, scale.ToString()));
}
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 TakeHealth(CCSPlayerPawn? pawn, int damage)
{
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
return;
if (pawn.Controller.Value != null && pawn.Controller.Value.IsValid)
{
var playerInfo = jRandomSkills.Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == pawn.Controller.Value.SteamID);
if (playerInfo == null) return;
if (playerInfo.Skill == Skills.Jester && Jester.GetJesterMode())
return;
}
int newHealth = (int)(pawn.Health - damage);
pawn.Health = newHealth;
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth");
if (pawn.Health <= 0)
Server.NextFrame(() =>
{
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 void AddHealth(CCSPlayerPawn? pawn, int extraHealth, int maxHealth = 100)
{
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
return;
int newHealth = (int)(pawn.Health + extraHealth);
pawn.Health = Math.Min(newHealth, maxHealth);
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth");
pawn.MaxHealth = maxHealth;
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iMaxHealth");
}
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;
Dictionary<string, Action<CCSPlayerController, IWasdMenuOption>> list = [];
foreach (var item in items)
list.TryAdd(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)? 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 = $"<font class='fontSize-{(string.IsNullOrEmpty(your_skill) ? "l" : "ml")}'> </font>";
string infoLine = string.IsNullOrEmpty(your_skill)
? ""
: $"<font class='fontWeight-Bold fontSize-{config.HeaderLineSize}' color='{config.HeaderLineColor}'>{your_skill}:</font><br>";
string skillLine = $"<font class='fontWeight-Bold fontSize-{config.SkillLineSize}' color='{skillData.Color}'>{player.GetSkillName(skillData.Skill)}</font><br>";
var skill_select_info = player.GetTranslation($"{playerInfo.Skill.ToString().ToLower()}_select_info");
string remainingLine = string.IsNullOrWhiteSpace(skill_select_info)
? ""
: $"<font class='fontSize-{config.WSADMenuSelectInfoLineSize}' color='{config.WSADMenuSelectInfoLineColor}'>{skill_select_info}</font><br>";
var hudContent = infoLine + skillLine + remainingLine;
string controllsLine =
$"{emptySymbol}<font class='fontSize-{config.WSADMenuControllsLineSize}' color='{config.WSADMenuControllsLineColor1}'>{player.GetTranslation($"menu_controlls_scroll")}</font>"
+ $"<font class='fontSize-{config.WSADMenuControllsLineSize}' color='{config.WSADMenuControllsLineColor2}'>{player.GetTranslation($"menu_controlls_padding")}</font>"
+ $"<font class='fontSize-{config.WSADMenuControllsLineSize}' color='{config.WSADMenuControllsLineColor3}'>{player.GetTranslation($"menu_controlls_select")}</font>{emptySymbol}";
string itemText = $"<font class='fontSize-{config.WSADMenuItemLineSize}' color='{config.WSADMenuItemLineColor}'>{{0}}</font><br>";
string itemHoverText = $"<font class='fontSize-{config.WSADMenuItemLineSize}'><font color='purple'>[ </font><font color='{config.WSADMenuItemHoverLineColor}'>{{0}}</font><font color='purple'> ]</font></font><br>";
IWasdMenu menu = manager.CreateMenu(hudContent, itemText, itemHoverText, controllsLine);
foreach (var enemy in enemies)
menu.Add(enemy.Item1, (p, option) =>
{
jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { enemy.Item2 }]);
manager.CloseMenu(p);
});
if (lastElement != null)
menu.Add(lastElement.Value.Item1, (p, option) =>
{
jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { lastElement.Value.Item2 }]);
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<CCSTeam>("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);
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<int>() ?? 24;
int halfRounds = maxRounds / 2;
int overtimeMaxRounds = ConVar.Find("mp_overtime_maxrounds")?.GetPrimitiveValue<int>() ?? 6;
int overtimeLimit = ConVar.Find("mp_overtime_limit")?.GetPrimitiveValue<int>() ?? 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<MCCSMatch>(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<CCSTeam>("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;
}
}