1.2.3.b2
This commit is contained in:
parent
0e86f54e0a
commit
98f6e65b01
98 changed files with 3391 additions and 397 deletions
|
|
@ -47,8 +47,10 @@ namespace src.player
|
|||
Instance.RegisterEventHandler<EventRoundStart>(RoundStart);
|
||||
Instance.RegisterEventHandler<EventRoundEnd>(RoundEnd);
|
||||
|
||||
Instance.RegisterEventHandler<EventPlayerDeath>(PlayerDeathPre, HookMode.Pre);
|
||||
Instance.RegisterEventHandler<EventPlayerDeath>(PlayerDeath);
|
||||
Instance.RegisterEventHandler<EventPlayerBlind>(PlayerBlind);
|
||||
Instance.RegisterEventHandler<EventPlayerHurt>(PlayerHurtPre, HookMode.Pre);
|
||||
Instance.RegisterEventHandler<EventPlayerHurt>(PlayerHurt);
|
||||
Instance.RegisterEventHandler<EventPlayerJump>(PlayerJump);
|
||||
Instance.RegisterEventHandler<EventBotTakeover>(BotTakeover);
|
||||
|
|
@ -89,6 +91,22 @@ namespace src.player
|
|||
// Keeping the plugin alive is safer than installing a stale global hook at load time.
|
||||
}
|
||||
|
||||
public static void Unload()
|
||||
{
|
||||
TryUnhook(() => VirtualFunctions.CBaseEntity_TakeDamageOldFunc.Unhook(OnTakeDamage, HookMode.Pre));
|
||||
TryUnhook(() => VirtualFunctions.CBaseTrigger_StartTouchFunc.Unhook(OnTriggerEnter, HookMode.Post));
|
||||
TryUnhook(() => VirtualFunctions.CBaseTrigger_EndTouchFunc.Unhook(OnTriggerExit, HookMode.Pre));
|
||||
TryUnhook(() => VirtualFunctions.CCSPlayer_ItemServices_CanAcquireFunc.Unhook(OnWeaponCanAcquire, HookMode.Pre));
|
||||
TryUnhook(() => Instance.UnhookUserMessage(208, PlayerMakeSound));
|
||||
TryUnhook(() => Instance.RemoveListener<CheckTransmit>(CheckTransmit));
|
||||
}
|
||||
|
||||
private static void TryUnhook(Action unhook)
|
||||
{
|
||||
try { unhook(); }
|
||||
catch (Exception ex) { Server.PrintToConsole($"[jRandomSkills] unhook failed: {ex.Message}"); }
|
||||
}
|
||||
|
||||
private static jSkill_SkillInfo ChooseSkillByRarityAndMax(List<jSkill_SkillInfo> candidates, Dictionary<Skills, int> assignmentCounts, Config.GameModes gameMode)
|
||||
{
|
||||
if (candidates == null || candidates.Count == 0) return noneSkill;
|
||||
|
|
@ -180,12 +198,53 @@ namespace src.player
|
|||
continue;
|
||||
}
|
||||
|
||||
InvokeSkill(p.Skill, "OnTakeDamage", args);
|
||||
InvokeOnTakeDamage(p.Skill, h, args);
|
||||
}
|
||||
|
||||
if (deferred == null) return;
|
||||
foreach (var skill in deferred)
|
||||
InvokeOnTakeDamage(skill, h, args);
|
||||
}
|
||||
|
||||
private static void InvokeOnTakeDamage(Skills skill, DynamicHook h, object[] args)
|
||||
{
|
||||
if (Config.LoadedConfig.DebugMode != true)
|
||||
{
|
||||
InvokeSkill(skill, "OnTakeDamage", args);
|
||||
return;
|
||||
}
|
||||
|
||||
var info = h.GetParam<CTakeDamageInfo>(1);
|
||||
float before = info == null ? 0f : info.Damage;
|
||||
|
||||
InvokeSkill(skill, "OnTakeDamage", args);
|
||||
|
||||
float after = info == null ? 0f : info.Damage;
|
||||
if (Math.Abs(before - after) > 0.01f)
|
||||
Debug.WriteToDebug($"[DMG] {skill} changed damage {before:0.#} -> {after:0.#}{DescribeDamageTarget(h)}");
|
||||
}
|
||||
|
||||
private static string DescribeDamageTarget(DynamicHook h)
|
||||
{
|
||||
try
|
||||
{
|
||||
var victimEntity = h.GetParam<CEntityInstance>(0);
|
||||
if (victimEntity == null || !victimEntity.IsValid) return string.Empty;
|
||||
|
||||
var pawn = victimEntity.As<CCSPlayerPawn>();
|
||||
if (pawn == null || !pawn.IsValid || pawn.DesignerName != "player") return string.Empty;
|
||||
|
||||
var controller = pawn.Controller.Value?.As<CCSPlayerController>();
|
||||
if (controller == null || !controller.IsValid) return string.Empty;
|
||||
|
||||
uint routedIndex = PlayerManager.GetPlayerEvent(controller)?.Index ?? controller.Index;
|
||||
return $" on {controller.PlayerName} [idx={controller.Index} skill={PlayerManager.GetPlayerByIndex(controller.Index)?.Skill}" +
|
||||
$" routedIdx={routedIndex} routedSkill={PlayerManager.GetPlayerByIndex(routedIndex)?.Skill}]";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static HookResult PlayerMakeSound(UserMessage um)
|
||||
|
|
@ -314,6 +373,63 @@ namespace src.player
|
|||
}
|
||||
}
|
||||
|
||||
private static HookResult PlayerHurtPre(EventPlayerHurt @event, GameEventInfo info)
|
||||
{
|
||||
lock (setLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (@event.DmgHealth <= 0 && @event.DmgArmor <= 0) return HookResult.Continue;
|
||||
|
||||
var victim = PlayerManager.GetPlayerEvent(@event.Userid);
|
||||
if (victim == null || !victim.IsValid) return HookResult.Continue;
|
||||
|
||||
var victimInfo = PlayerManager.GetPlayerByIndex(victim.Index);
|
||||
if (victimInfo == null || victimInfo.IsDrawing) return HookResult.Continue;
|
||||
|
||||
bool suppressed = AskSkillSuppressesHit(victimInfo.Skill, @event);
|
||||
|
||||
if (!suppressed)
|
||||
{
|
||||
var attacker = PlayerManager.GetPlayerEvent(@event.Attacker);
|
||||
if (attacker != null && attacker.IsValid && attacker.Index != victim.Index)
|
||||
{
|
||||
var attackerInfo = PlayerManager.GetPlayerByIndex(attacker.Index);
|
||||
if (attackerInfo != null && !attackerInfo.IsDrawing && attackerInfo.Skill != victimInfo.Skill)
|
||||
suppressed = AskSkillSuppressesHit(attackerInfo.Skill, @event);
|
||||
}
|
||||
}
|
||||
|
||||
if (!suppressed) return HookResult.Continue;
|
||||
|
||||
if (@event.DmgArmor > 0)
|
||||
{
|
||||
var pawn = victim.PlayerPawn?.Value;
|
||||
if (pawn != null && pawn.IsValid)
|
||||
{
|
||||
pawn.ArmorValue += @event.DmgArmor;
|
||||
Utilities.SetStateChanged(pawn, "CCSPlayerPawn", "m_ArmorValue");
|
||||
}
|
||||
}
|
||||
|
||||
@event.DmgHealth = 0;
|
||||
@event.DmgArmor = 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteToDebug($"PlayerHurtPre failed: {ex.Message}");
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AskSkillSuppressesHit(Skills skill, EventPlayerHurt @event)
|
||||
{
|
||||
if (skill == Skills.None) return false;
|
||||
return (bool?)Instance.SkillAction(skill.ToString(), "PlayerHurtPre", [@event]) == true;
|
||||
}
|
||||
|
||||
private static HookResult PlayerHurt(EventPlayerHurt @event, GameEventInfo info)
|
||||
{
|
||||
lock (setLock)
|
||||
|
|
@ -546,6 +662,9 @@ namespace src.player
|
|||
DisplayHUD = true,
|
||||
SkillUsed = false,
|
||||
};
|
||||
|
||||
UpdateSkillHudExpired(playerInfo, Skills.None);
|
||||
|
||||
PlayerManager.Register(playerInfo);
|
||||
}
|
||||
}
|
||||
|
|
@ -609,14 +728,7 @@ namespace src.player
|
|||
var skillPlayer = PlayerManager.GetPlayerByIndex(player!.Index);
|
||||
if (skillPlayer == null) return HookResult.Continue;
|
||||
|
||||
var temp = skillPlayer.SkillHudExpired;
|
||||
skillPlayer.SkillHudExpired = DateTime.MinValue;
|
||||
|
||||
Instance.AddTimer(5f, () =>
|
||||
{
|
||||
if (skillPlayer.SkillHudExpired == DateTime.MinValue)
|
||||
skillPlayer.SkillHudExpired = temp;
|
||||
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||
skillPlayer.HudSuppressedUntil = DateTime.Now.AddSeconds(5);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
|
@ -653,6 +765,7 @@ namespace src.player
|
|||
{
|
||||
bool isWarmup = Instance.GameRules == null || Instance.GameRules.WarmupPeriod == true;
|
||||
isTransmitRegistered = false;
|
||||
SkillUtils.ClearKillCredits();
|
||||
Instance.AddTimer(.1f, () => DisableAll(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
||||
|
||||
foreach (var player in Utilities.GetPlayers().Where(p => p != null && p.IsValid && !p.IsHLTV && p.Team is CsTeam.CounterTerrorist or CsTeam.Terrorist))
|
||||
|
|
@ -785,6 +898,7 @@ namespace src.player
|
|||
private static HookResult RoundEnd(EventRoundEnd @event, GameEventInfo info)
|
||||
{
|
||||
Illiterate.Disable();
|
||||
DispatchToActiveSkills("RoundEnd");
|
||||
|
||||
lock (setLock)
|
||||
{
|
||||
|
|
@ -833,6 +947,40 @@ namespace src.player
|
|||
}
|
||||
}
|
||||
|
||||
private static HookResult PlayerDeathPre(EventPlayerDeath @event, GameEventInfo info)
|
||||
{
|
||||
try
|
||||
{
|
||||
var victim = @event.Userid;
|
||||
if (victim == null || !victim.IsValid) return HookResult.Continue;
|
||||
|
||||
if (!SkillUtils.TryConsumeKillCredit(victim.Index, out uint attackerIndex, out string? weapon))
|
||||
return HookResult.Continue;
|
||||
|
||||
var attacker = Utilities.GetPlayerFromIndex((int)attackerIndex);
|
||||
if (attacker == null || !attacker.IsValid || attacker.Index == victim.Index)
|
||||
return HookResult.Continue;
|
||||
|
||||
@event.Attacker = attacker;
|
||||
|
||||
if (!string.IsNullOrEmpty(weapon))
|
||||
@event.Weapon = weapon;
|
||||
|
||||
var matchStats = attacker.ActionTrackingServices?.MatchStats;
|
||||
if (matchStats != null)
|
||||
{
|
||||
matchStats.Kills++;
|
||||
Utilities.SetStateChanged(attacker, "CCSPlayerController", "m_pActionTrackingServices");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteToDebug($"PlayerDeathPre kill credit failed: {ex.Message}");
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private static HookResult PlayerDeath(EventPlayerDeath @event, GameEventInfo info)
|
||||
{
|
||||
long perfStart = PerfLog.Start();
|
||||
|
|
@ -955,26 +1103,65 @@ namespace src.player
|
|||
|
||||
private static readonly Dictionary<uint, jSkill_SkillInfo> nextRoundPicks = [];
|
||||
|
||||
private static jSkill_SkillInfo PickSkillForPlayer(CCSPlayerController player, jSkill_PlayerInfo skillPlayer, List<CCSPlayerController> validPlayers, Dictionary<Skills, int> assignmentCounts, Config.GameModes gameMode)
|
||||
private sealed class PickContext
|
||||
{
|
||||
List<jSkill_SkillInfo> skillList = [.. SkillData.Skills];
|
||||
skillList.RemoveAll(s => s?.Skill == Skills.None);
|
||||
if (!player.IsBot)
|
||||
skillList.RemoveAll(s => !string.IsNullOrEmpty(SkillsInfo.GetValue<string>(s.Skill, "requiredPermission")) && !AdminManager.PlayerHasPermissions(player, SkillsInfo.GetValue<string>(s.Skill, "requiredPermission")));
|
||||
public required List<jSkill_SkillInfo> BaseList { get; init; }
|
||||
public required Dictionary<Skills, string> RequiredPermissions { get; init; }
|
||||
public required HashSet<Skills> NeedsTeammates { get; init; }
|
||||
public required HashSet<Skills> CtOnly { get; init; }
|
||||
public required HashSet<Skills> TOnly { get; init; }
|
||||
public required int TerroristCount { get; init; }
|
||||
public required int CounterTerroristCount { get; init; }
|
||||
}
|
||||
|
||||
private static PickContext BuildPickContext(List<CCSPlayerController> validPlayers)
|
||||
{
|
||||
Dictionary<Skills, string> perms = [];
|
||||
foreach (var s in SkillData.Skills)
|
||||
{
|
||||
if (s == null || s.Skill == Skills.None) continue;
|
||||
string perm = SkillsInfo.GetValue<string>(s.Skill, "requiredPermission");
|
||||
if (!string.IsNullOrEmpty(perm)) perms[s.Skill] = perm;
|
||||
}
|
||||
|
||||
return new PickContext
|
||||
{
|
||||
BaseList = [.. SkillData.Skills.Where(s => s != null && s.Skill != Skills.None)],
|
||||
RequiredPermissions = perms,
|
||||
NeedsTeammates = ToSkillSet(SkillsInfo.LoadedConfig.Where(s => s.NeedsTeammates).Select(s => s.Name)),
|
||||
CtOnly = ToSkillSet(counterterroristSkills.Select(s => s.Name)),
|
||||
TOnly = ToSkillSet(terroristSkills.Select(s => s.Name)),
|
||||
TerroristCount = validPlayers.Count(p => p.Team == CsTeam.Terrorist),
|
||||
CounterTerroristCount = validPlayers.Count(p => p.Team == CsTeam.CounterTerrorist),
|
||||
};
|
||||
}
|
||||
|
||||
private static HashSet<Skills> ToSkillSet(IEnumerable<string> names)
|
||||
{
|
||||
HashSet<Skills> set = [];
|
||||
foreach (var name in names)
|
||||
if (Enum.TryParse<Skills>(name, out var skill)) set.Add(skill);
|
||||
return set;
|
||||
}
|
||||
|
||||
private static jSkill_SkillInfo PickSkillForPlayer(CCSPlayerController player, jSkill_PlayerInfo skillPlayer, PickContext ctx, Dictionary<Skills, int> assignmentCounts, Config.GameModes gameMode)
|
||||
{
|
||||
List<jSkill_SkillInfo> skillList = [.. ctx.BaseList];
|
||||
|
||||
if (!player.IsBot && ctx.RequiredPermissions.Count != 0)
|
||||
skillList.RemoveAll(s => ctx.RequiredPermissions.TryGetValue(s.Skill, out var perm) && !AdminManager.PlayerHasPermissions(player, perm));
|
||||
|
||||
if (gameMode != Config.GameModes.FullRandom)
|
||||
skillList.RemoveAll(s => s?.Skill == skillPlayer?.Skill || s?.Skill == skillPlayer?.SpecialSkill);
|
||||
|
||||
if (validPlayers.Count(p => p.Team == player.Team) == 1)
|
||||
{
|
||||
SkillsInfo.DefaultSkillInfo[] skillsNeedsTeammates = [.. SkillsInfo.LoadedConfig.Where(s => s.NeedsTeammates)];
|
||||
skillList.RemoveAll(s => skillsNeedsTeammates.Any(s2 => s2.Name == s.Skill.ToString()));
|
||||
}
|
||||
int teamCount = player.Team == CsTeam.Terrorist ? ctx.TerroristCount : ctx.CounterTerroristCount;
|
||||
if (teamCount == 1)
|
||||
skillList.RemoveAll(s => ctx.NeedsTeammates.Contains(s.Skill));
|
||||
|
||||
if (player.Team == CsTeam.Terrorist)
|
||||
skillList.RemoveAll(s => counterterroristSkills.Any(s2 => s2.Name == s.Skill.ToString()));
|
||||
skillList.RemoveAll(s => ctx.CtOnly.Contains(s.Skill));
|
||||
else
|
||||
skillList.RemoveAll(s => terroristSkills.Any(s2 => s2.Name == s.Skill.ToString()));
|
||||
skillList.RemoveAll(s => ctx.TOnly.Contains(s.Skill));
|
||||
|
||||
if (gameMode == Config.GameModes.NoRepeat && playersSkills.TryGetValue(player.Index, out ConcurrentBag<jSkill_SkillInfo>? skills))
|
||||
{
|
||||
|
|
@ -1029,13 +1216,15 @@ namespace src.player
|
|||
.Where(p => p != null && p.IsValid && !p.IsHLTV)
|
||||
.Where(p => { try { return p.Team is CsTeam.CounterTerrorist or CsTeam.Terrorist; } catch { return false; } }).ToList();
|
||||
|
||||
var ctx = BuildPickContext(validPlayers);
|
||||
|
||||
Dictionary<Skills, int> assignmentCounts = [];
|
||||
foreach (var player in validPlayers)
|
||||
{
|
||||
var skillPlayer = PlayerManager.GetPlayerByIndex(player.Index);
|
||||
if (skillPlayer == null) continue;
|
||||
|
||||
var pick = PickSkillForPlayer(player, skillPlayer, validPlayers, assignmentCounts, gameMode);
|
||||
var pick = PickSkillForPlayer(player, skillPlayer, ctx, assignmentCounts, gameMode);
|
||||
nextRoundPicks[player.Index] = pick;
|
||||
|
||||
if (pick.Skill != Skills.None)
|
||||
|
|
@ -1116,6 +1305,8 @@ namespace src.player
|
|||
else assignmentCounts[sp.Skill] = 1;
|
||||
}
|
||||
|
||||
PickContext? pickContext = null;
|
||||
|
||||
foreach (var player in validPlayers)
|
||||
{
|
||||
if (player == null) continue;
|
||||
|
|
@ -1126,6 +1317,7 @@ namespace src.player
|
|||
if (skillPlayer == null) continue;
|
||||
|
||||
skillPlayer.IsDrawing = false;
|
||||
skillPlayer.HudOnDeathBlocked = null;
|
||||
if (player.PlayerPawn.Value == null || !player.PlayerPawn.IsValid)
|
||||
{
|
||||
skillPlayer.Skill = Skills.None;
|
||||
|
|
@ -1142,7 +1334,10 @@ namespace src.player
|
|||
if (nextRoundPicks.TryGetValue(player.Index, out var pre) && IsPickStillValid(pre, player, validPlayers, assignmentCounts))
|
||||
randomSkill = pre;
|
||||
else
|
||||
randomSkill = PickSkillForPlayer(player, skillPlayer, validPlayers, assignmentCounts, gameMode);
|
||||
{
|
||||
pickContext ??= BuildPickContext(validPlayers);
|
||||
randomSkill = PickSkillForPlayer(player, skillPlayer, pickContext, assignmentCounts, gameMode);
|
||||
}
|
||||
}
|
||||
else if (gameMode == Config.GameModes.TeamSkills)
|
||||
randomSkill = player.Team == CsTeam.Terrorist ? tSkill : ctSkill;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue