From 2edad25e6aa23f71b6d96393cae3fe7da86fe15f Mon Sep 17 00:00:00 2001 From: Gurge44 Date: Sat, 1 Aug 2026 12:20:08 +0200 Subject: [PATCH] some more features --- CTA/CustomTeamAssigner/CustomRoles.cs | 2 +- Gamemodes/CustomHnS.cs | 28 +++++++++++------ Modules/Utils.cs | 3 +- Patches/ChatCommandPatch.cs | 2 -- Resources/Lang/en_US.json | 3 ++ Roles/HideAndSeek/Disguiser.cs | 43 ++++++++++++++++++++------- 6 files changed, 58 insertions(+), 23 deletions(-) diff --git a/CTA/CustomTeamAssigner/CustomRoles.cs b/CTA/CustomTeamAssigner/CustomRoles.cs index 85b2638e5..1688796da 100644 --- a/CTA/CustomTeamAssigner/CustomRoles.cs +++ b/CTA/CustomTeamAssigner/CustomRoles.cs @@ -473,7 +473,7 @@ public enum CustomRoles Venter, Agent, Taskinator, - SNSSeeker, + Disguiser, // Speedrun Runner, diff --git a/Gamemodes/CustomHnS.cs b/Gamemodes/CustomHnS.cs index 522d218c6..758ea3414 100644 --- a/Gamemodes/CustomHnS.cs +++ b/Gamemodes/CustomHnS.cs @@ -64,10 +64,12 @@ internal static class CustomHnS ChatDuringGame = new BooleanOptionItem(id + 5, "FFA_ChatDuringGame", false, TabGroup.GameSettings) .SetGameMode(CustomGameMode.HideAndSeek) .SetColor(color); - + ShiftAndSeek = new BooleanOptionItem(id + 6, "HNS.ShiftAndSeek", false, TabGroup.GameSettings) .SetGameMode(CustomGameMode.HideAndSeek) - .SetColor(color); + .SetColor(color) + .RegisterUpdateValueEvent((_, _, newValue) => Options.CustomRoleSpawnChances[CustomRoles.Disguiser].SetValue(newValue * (100 / 5))) + .SetRunEventOnLoad(true); } public static void Init() @@ -82,7 +84,7 @@ internal static class CustomHnS HideAndSeekRoles = types .Select(x => (IHideAndSeekRole)Activator.CreateInstance(x)) .Where(x => x != null) - .Join(AllHnSRoles, x => x.GetType().Name.ToLower(), x => x.ToString().ToLower(), (Interface, Enum) => (Enum, Interface)) + .Join(AllHnSRoles.FindAll(role => !ShiftAndSeek.GetBool() ? (role is CustomRoles.Seeker or CustomRoles.Hider || role.GetMode() != 0) : (role == CustomRoles.Disguiser || (role is not (CustomRoles.Seeker or CustomRoles.Locator or CustomRoles.Dasher or CustomRoles.Venter or CustomRoles.Agent) && role.GetMode() != 0))), x => x.GetType().Name.ToLower(), x => x.ToString().ToLower(), (Interface, Enum) => (Enum, Interface)) .Where(x => (!x.Enum.OnlySpawnsWithPets() || Options.UsePets.GetBool()) && (x.Enum != CustomRoles.Agent || SeekerNum >= 2) && x.Interface.Count > 0 && (x.Interface.Team == Team.Neutral || x.Interface.Chance > IRandom.Instance.Next(100))) .OrderBy(x => x.Enum is CustomRoles.Seeker or CustomRoles.Hider ? 100 : IRandom.Instance.Next(100)) .GroupBy(x => x.Interface.Team) @@ -96,6 +98,9 @@ internal static class CustomHnS Main.AllPlayerKillCooldown.SetAllValues(Seeker.KillCooldown.GetFloat()); IsBlindTime = true; Utils.MarkEveryoneDirtySettingsV4(); + + float time = Seeker.BlindTime.GetFloat() + 14f; + if (PlayersSeeRoles.GetBool()) Main.EnumerateAlivePlayerControls().NotifyPlayers(string.Format(Translator.GetString("HNSSeekers"), string.Join(", ", PlayerRoles.Where(x => x.Value.Interface.Team == Team.Impostor).Select(x => x.Key.ColoredPlayerName()))), time + 3f, setName: false); LateTask.New(() => { @@ -108,15 +113,20 @@ internal static class CustomHnS .Do(x => x.pc.SetKillCooldown()); LateTask.New(() => Main.Instance.StartCoroutine(Utils.NotifyEveryoneAsync(noCache: false)), 3f, log: false); - }, Seeker.BlindTime.GetFloat() + 14f, "Blind Time Expire"); + }, time, "Blind Time Expire"); } - public static List GetAllHnsRoles(IEnumerable types) + public static List GetAllHnsRoles(Type[] types) { - return types - .Select(x => Enum.Parse(ignoreCase: true, value: x.Name)) - .Where(role => !ShiftAndSeek.GetBool() ? (role is CustomRoles.Seeker or CustomRoles.Hider || role.GetMode() != 0) : (role == CustomRoles.Disguiser || (role is not (CustomRoles.Seeker or CustomRoles.Locator or CustomRoles.Dasher or CustomRoles.Venter or CustomRoles.Agent) && role.GetMode() != 0))) - .ToList(); + List roles = []; + + foreach (Type type in types) + { + if (Enum.TryParse(type.Name, ignoreCase: true, out CustomRoles role)) + roles.Add(role); + } + + return roles; } private static Type[] CachedHnsTypes; diff --git a/Modules/Utils.cs b/Modules/Utils.cs index f12ebacdc..61c7be349 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -4283,7 +4283,8 @@ public static class Utils if (!onMeeting) Main.DiedThisRound.Add(target.PlayerId); // Record the first death - if (Main.FirstDied == string.Empty) Main.FirstDied = target.FriendCode; + if (Main.FirstDied == string.Empty && !disconnect) + Main.FirstDied = target.FriendCode; switch (target.GetCustomRole()) { diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index 0910d3b99..f88c91300 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -1961,12 +1961,10 @@ internal static class ChatCommands { winnerGM = CustomGameMode.HideAndSeek; CustomHnS.SNS = true; - Options.CustomRoleSpawnChances[CustomRoles.Disguiser].SetValue(100 / 5); } else if (winnerGM == CustomGameMode.HideAndSeek) { CustomHnS.SNS = false; - Options.CustomRoleSpawnChances[CustomRoles.Disguiser].SetValue(0); } Options.GameMode.SetValue((int)winnerGM - 1, doSave: true, doSync: true); diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 1c40aa815..dd21611ff 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -4462,6 +4462,7 @@ "TimeDecreaseOnOtherTaskComplete": "When Completing a Task That Doesn't Fit Any Category, Reduce the Game Time by", "TimeDecreaseOnShortTaskComplete": "When Completing a Short Task, Reduce the Game Time by", "TimeDecreaseOnSituationalTaskComplete": "When Completing a Situational Task, Reduce the Game Time by", + "HNSSeekers": "The <#ff1919>Seekers are: {0}", "SeekerCanVent": "Seeker Can Vent", "SeekerSpeed": "Seeker Speed", "SeekerVision": "Seeker Vision", @@ -4546,7 +4547,9 @@ "DisguiserPenaltyForWrongKillAttempt": "Penalty For Wrong Kill Attempts", "DisguiserPFWKA.BlockKill": "Block Kill", "DisguiserPFWKA.Suicide": "Suicide", + "DisguiserPFWKA.ResetKillCooldown": "Reset Kill Cooldown", "DisguiserToleranceBeforeSuicide": "Tolerance (= number of wrong kill attempts allowed before suiciding)", + "DisguiserResetKillCooldownTo": "Reset Kill Cooldown To", "DisguiserShapeshiftCooldown": "Disguiser Shapeshift Cooldown", "DisguiserShapeshiftDuration": "Disguiser Shapeshift Duration", "DisguiserShapeshiftAnimation": "Disguiser Shapeshift Animation", diff --git a/Roles/HideAndSeek/Disguiser.cs b/Roles/HideAndSeek/Disguiser.cs index ef8c45508..f03b25db1 100644 --- a/Roles/HideAndSeek/Disguiser.cs +++ b/Roles/HideAndSeek/Disguiser.cs @@ -9,6 +9,7 @@ public class Disguiser : RoleBase, IHideAndSeekRole public static OptionItem CanOnlyKillShapeshiftTarget; public static OptionItem PenaltyForWrongKillAttempt; public static OptionItem ToleranceBeforeSuicide; + public static OptionItem ResetKillCooldownTo; public static OptionItem ShapeshiftCooldown; public static OptionItem ShapeshiftDuration; public static OptionItem ShapeshiftAnimation; @@ -43,10 +44,16 @@ public class Disguiser : RoleBase, IHideAndSeekRole .SetColor(new(179, 70, 70, byte.MaxValue)) .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Disguiser]); - PenaltyForWrongKillAttempt = new StringOptionItem(69_213_1106, "DisguiserPenaltyForWrongKillAttempt", ["DisguiserPFWKA.BlockKill", "DisguiserPFWKA.Suicide"], 1, TabGroup.ImpostorRoles) + PenaltyForWrongKillAttempt = new StringOptionItem(69_213_1106, "DisguiserPenaltyForWrongKillAttempt", ["DisguiserPFWKA.BlockKill", "DisguiserPFWKA.Suicide", "DisguiserPFWKA.ResetKillCooldown"], 1, TabGroup.ImpostorRoles) .SetGameMode(CustomGameMode.HideAndSeek) .SetColor(new(179, 70, 70, byte.MaxValue)) - .SetParent(CanOnlyKillShapeshiftTarget); + .SetParent(CanOnlyKillShapeshiftTarget) + .RegisterUpdateValueEvent((_, _, newValue) => + { + ToleranceBeforeSuicide.SetHidden(newValue != 1); + ResetKillCooldownTo.SetHidden(newValue != 2); + }) + .SetRunEventOnLoad(true); ToleranceBeforeSuicide = new IntegerOptionItem(69_213_1107, "DisguiserToleranceBeforeSuicide", new(0, 14, 1), 2, TabGroup.ImpostorRoles) .SetGameMode(CustomGameMode.HideAndSeek) @@ -54,19 +61,25 @@ public class Disguiser : RoleBase, IHideAndSeekRole .SetColor(new(179, 70, 70, byte.MaxValue)) .SetParent(PenaltyForWrongKillAttempt); - ShapeshiftCooldown = new IntegerOptionItem(69_213_1108, "DisguiserShapeshiftCooldown", new(0, 180, 1), 0, TabGroup.ImpostorRoles) + ResetKillCooldownTo = new FloatOptionItem(69_213_1108, "DisguiserResetKillCooldownTo", new(0.5f, 120f, 0.5f), 10f, TabGroup.ImpostorRoles) + .SetGameMode(CustomGameMode.HideAndSeek) + .SetValueFormat(OptionFormat.Seconds) + .SetColor(new(179, 70, 70, byte.MaxValue)) + .SetParent(PenaltyForWrongKillAttempt); + + ShapeshiftCooldown = new IntegerOptionItem(69_213_1109, "DisguiserShapeshiftCooldown", new(0, 180, 1), 0, TabGroup.ImpostorRoles) .SetGameMode(CustomGameMode.HideAndSeek) .SetValueFormat(OptionFormat.Seconds) .SetColor(new(179, 70, 70, byte.MaxValue)) .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Disguiser]); - ShapeshiftDuration = new IntegerOptionItem(69_213_1109, "DisguiserShapeshiftDuration", new(0, 180, 1), 30, TabGroup.ImpostorRoles) + ShapeshiftDuration = new IntegerOptionItem(69_213_1110, "DisguiserShapeshiftDuration", new(0, 180, 1), 30, TabGroup.ImpostorRoles) .SetGameMode(CustomGameMode.HideAndSeek) .SetValueFormat(OptionFormat.Seconds) .SetColor(new(179, 70, 70, byte.MaxValue)) .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Disguiser]); - ShapeshiftAnimation = new BooleanOptionItem(69_213_1110, "DisguiserShapeshiftAnimation", true, TabGroup.ImpostorRoles) + ShapeshiftAnimation = new BooleanOptionItem(69_213_1111, "DisguiserShapeshiftAnimation", true, TabGroup.ImpostorRoles) .SetGameMode(CustomGameMode.HideAndSeek) .SetColor(new(179, 70, 70, byte.MaxValue)) .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Disguiser]); @@ -100,12 +113,22 @@ public class Disguiser : RoleBase, IHideAndSeekRole { if (CanOnlyKillShapeshiftTarget.GetBool() && killer.shapeshiftTargetPlayerId != target.PlayerId) { - if (PenaltyForWrongKillAttempt.GetValue() == 1) + switch (PenaltyForWrongKillAttempt.GetValue()) { - if (killer.GetAbilityUseLimit() < 1) - killer.Suicide(); - else - killer.RpcRemoveAbilityUse(); + case 1: + { + if (killer.GetAbilityUseLimit() < 1) + killer.Suicide(); + else + killer.RpcRemoveAbilityUse(); + + break; + } + case 2: + { + killer.SetKillCooldown(ResetKillCooldownTo.GetFloat()); + break; + } } return false;