From 2769500674fb2b540ca842f8621f2ec8773eef3a Mon Sep 17 00:00:00 2001 From: Gurge44 Date: Fri, 7 Jun 2024 15:32:06 +0200 Subject: [PATCH] New role Echo, Fix settings menu not opening on main menu --- CTA/CustomTeamAssigner/CustomRoles.cs | 1 + CustomRoles.cs | 1 + Modules/CustomRolesHelper.cs | 6 ++ Modules/OptionHolder.cs | 79 ++++++++++++++------------- Patches/ChatCommandPatch.cs | 2 +- Patches/CredentialsPatch.cs | 40 ++++++++++++++ Patches/HudSpritePatch.cs | 3 + Patches/PlayerControlPatch.cs | 7 ++- Resources/Lang/en_US.json | 4 ++ Roles/Impostor/Echo.cs | 70 ++++++++++++++++++++++++ 10 files changed, 171 insertions(+), 42 deletions(-) create mode 100644 Roles/Impostor/Echo.cs diff --git a/CTA/CustomTeamAssigner/CustomRoles.cs b/CTA/CustomTeamAssigner/CustomRoles.cs index 0934ead73..d28dedea5 100644 --- a/CTA/CustomTeamAssigner/CustomRoles.cs +++ b/CTA/CustomTeamAssigner/CustomRoles.cs @@ -41,6 +41,7 @@ public enum CustomRoles Disperser, Duellist, Dazzler, + Echo, Escapee, Eraser, EvilGuesser, diff --git a/CustomRoles.cs b/CustomRoles.cs index 0934ead73..d28dedea5 100644 --- a/CustomRoles.cs +++ b/CustomRoles.cs @@ -41,6 +41,7 @@ public enum CustomRoles Disperser, Duellist, Dazzler, + Echo, Escapee, Eraser, EvilGuesser, diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index c14854c1d..d4b1b54e0 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -113,6 +113,7 @@ internal static class CustomRolesHelper CustomRoles.Dictator => CustomRoles.Crewmate, CustomRoles.Inhibitor => CustomRoles.Impostor, CustomRoles.Kidnapper => CustomRoles.Shapeshifter, + CustomRoles.Echo => CustomRoles.Shapeshifter, CustomRoles.Abyssbringer => CustomRoles.Shapeshifter, CustomRoles.Overheat => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter, CustomRoles.Generator => CustomRoles.Shapeshifter, @@ -585,6 +586,7 @@ internal static class CustomRolesHelper CustomRoles.Overheat or CustomRoles.Underdog or CustomRoles.Abyssbringer or + CustomRoles.Echo or CustomRoles.Inhibitor or CustomRoles.Kidnapper or CustomRoles.Generator or @@ -783,9 +785,13 @@ internal static class CustomRolesHelper CustomRoles.TicketsStealer; public static bool ForceCancelShapeshift(this CustomRoles role) => role is + CustomRoles.Echo or CustomRoles.Hangman or CustomRoles.Generator; + public static bool IsNoAnimationShifter(this CustomRoles role) => role is + CustomRoles.Echo; + public static bool CheckAddonConflict(CustomRoles role, PlayerControl pc) => role.IsAdditionRole() && (!Main.NeverSpawnTogetherCombos.TryGetValue(pc.GetCustomRole(), out var bannedAddonList) || !bannedAddonList.Contains(role)) && pc.GetCustomRole() is not CustomRoles.GuardianAngelEHR and not CustomRoles.God && !pc.Is(CustomRoles.Madmate) && !pc.Is(CustomRoles.GM) && role is not CustomRoles.Lovers && !pc.Is(CustomRoles.Needy) && (!pc.HasSubRole() || pc.GetCustomSubRoles().Count < Options.NoLimitAddonsNumMax.GetInt()) && (!Options.AddonCanBeSettings.TryGetValue(role, out var o) || ((o.Imp.GetBool() || !pc.GetCustomRole().IsImpostor()) && (o.Neutral.GetBool() || !pc.GetCustomRole().IsNeutral()) && (o.Crew.GetBool() || !pc.IsCrewmate()))) && role switch { CustomRoles.Energetic when !Options.UsePets.GetBool() => false, diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index b49eaaa17..9787d8343 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -776,46 +777,46 @@ public static class Options GroupOptions(); GroupAddons(); +#if DEBUG // Used for generating the table of roles for the README - // This is commented out because it's not needed for the mod to function - // - // try - // { - // var sb = new StringBuilder(); - // var grouped = Enum.GetValues().GroupBy(x => - // { - // if (x is CustomRoles.GM or CustomRoles.Philantropist or CustomRoles.Konan or CustomRoles.KB_Normal or CustomRoles.Killer or CustomRoles.Potato or CustomRoles.Tasker or CustomRoles.NotAssigned or CustomRoles.LovingCrewmate or CustomRoles.LovingImpostor or CustomRoles.Convict || HnSManager.AllHnSRoles.Contains(x) || x.IsVanilla() || x.ToString().Contains("EHR")) return 4; - // if (x.IsAdditionRole()) return 3; - // if (x.IsImpostor() || x.IsMadmate()) return 0; - // if (x.IsNeutral()) return 1; - // if (x.IsCrewmate()) return 2; - // return 4; - // }).ToDictionary(x => x.Key, x => x.ToArray()); - // var max = grouped.Max(x => x.Value.Length); - // for (int i = 0; i < max; i++) - // { - // var cr = grouped[2].ElementAtOrDefault(i); - // var crew = Translator.GetString(cr.ToString()); - // var ir = grouped[0].ElementAtOrDefault(i); - // var imp = Translator.GetString(ir.ToString()); - // if (ir == default) imp = string.Empty; - // var nr = grouped[1].ElementAtOrDefault(i); - // var neu = Translator.GetString(nr.ToString()); - // if (nr == default) neu = string.Empty; - // var a = grouped[3].ElementAtOrDefault(i); - // var add = Translator.GetString(a.ToString()); - // if (a == default) add = string.Empty; - // sb.AppendLine($"| {crew, 17} | {imp, 17} | {neu, 17} | {add, 17} |"); - // } - // - // const string path = "./roles.txt"; - // if (!File.Exists(path)) File.Create(path).Close(); - // File.WriteAllText(path, sb.ToString()); - // } - // catch (Exception e) - // { - // Utils.ThrowException(e); - // } + try + { + var sb = new System.Text.StringBuilder(); + var grouped = Enum.GetValues().GroupBy(x => + { + if (x is CustomRoles.GM or CustomRoles.Philantropist or CustomRoles.Konan or CustomRoles.KB_Normal or CustomRoles.Killer or CustomRoles.Potato or CustomRoles.Tasker or CustomRoles.NotAssigned or CustomRoles.LovingCrewmate or CustomRoles.LovingImpostor or CustomRoles.Convict || HnSManager.AllHnSRoles.Contains(x) || x.IsVanilla() || x.ToString().Contains("EHR")) return 4; + if (x.IsAdditionRole()) return 3; + if (x.IsImpostor() || x.IsMadmate()) return 0; + if (x.IsNeutral()) return 1; + if (x.IsCrewmate()) return 2; + return 4; + }).ToDictionary(x => x.Key, x => x.ToArray()); + var max = grouped.Max(x => x.Value.Length); + for (int i = 0; i < max; i++) + { + var cr = grouped[2].ElementAtOrDefault(i); + var crew = Translator.GetString(cr.ToString()); + var ir = grouped[0].ElementAtOrDefault(i); + var imp = Translator.GetString(ir.ToString()); + if (ir == default) imp = string.Empty; + var nr = grouped[1].ElementAtOrDefault(i); + var neu = Translator.GetString(nr.ToString()); + if (nr == default) neu = string.Empty; + var a = grouped[3].ElementAtOrDefault(i); + var add = Translator.GetString(a.ToString()); + if (a == default) add = string.Empty; + sb.AppendLine($"| {crew,17} | {imp,17} | {neu,17} | {add,17} |"); + } + + const string path = "./roles.txt"; + if (!File.Exists(path)) File.Create(path).Close(); + File.WriteAllText(path, sb.ToString()); + } + catch (Exception e) + { + Utils.ThrowException(e); + } +#endif }); } diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index cea4b3050..934a3f2db 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -1382,7 +1382,7 @@ internal class ChatCommands return; } - if ((player.IsAlive() || ExileController.Instance) && Lovers.PrivateChat.GetBool() && (ExileController.Instance || !GameStates.IsMeeting)) + if (GameStates.IsInGame && ((player.IsAlive() || ExileController.Instance) && Lovers.PrivateChat.GetBool() && (ExileController.Instance || !GameStates.IsMeeting))) { ChatManager.SendPreviousMessagesToAll(); canceled = true; diff --git a/Patches/CredentialsPatch.cs b/Patches/CredentialsPatch.cs index 6b2ed35ba..929069b59 100644 --- a/Patches/CredentialsPatch.cs +++ b/Patches/CredentialsPatch.cs @@ -283,4 +283,44 @@ internal class ModManagerLateUpdatePatch new(0.4f, offsetY, __instance.localCamera.nearClipPlane + 0.1f)); } } +} + +[HarmonyPatch(typeof(OptionsMenuBehaviour), nameof(OptionsMenuBehaviour.Open))] +class OptionsMenuBehaviourOpenPatch +{ + public static bool Prefix(OptionsMenuBehaviour __instance) + { + try + { + if (DestroyableSingleton.InstanceExists && !DestroyableSingleton.Instance.SettingsButton.activeSelf) + return false; + } + catch + { + } + + __instance.ResetText(); + if (__instance.gameObject.activeSelf) + { + if (!__instance.Toggle) return false; + __instance.GetComponent().Close(); + } + else + { + if (Minigame.Instance != null) Minigame.Instance.Close(); + __instance.OpenTabGroup(0); + __instance.UpdateButtons(); + __instance.gameObject.SetActive(true); + if (DestroyableSingleton.InstanceExists) ConsoleJoystick.SetMode_MenuAdditive(); + if (!__instance.grabbedControllerButtons) + { + __instance.grabbedControllerButtons = true; + __instance.GrabControllerButtons(); + } + + ControllerManager.Instance.OpenOverlayMenu("OptionsMenu", __instance.BackButton, __instance.DefaultButtonSelected, __instance.ControllerSelectable); + } + + return false; + } } \ No newline at end of file diff --git a/Patches/HudSpritePatch.cs b/Patches/HudSpritePatch.cs index fd558dfc4..c06a2db11 100644 --- a/Patches/HudSpritePatch.cs +++ b/Patches/HudSpritePatch.cs @@ -64,6 +64,9 @@ public static class HudSpritePatch switch (player.GetCustomRole()) { + case CustomRoles.Echo: + newAbilityButton = player.IsShifted() ? Kill : CustomButton.Get("Puttpuer"); + break; case CustomRoles.Shifter: newKillButton = CustomButton.Get("Swap"); break; diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 983c71703..6d4ff82e1 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -652,7 +652,8 @@ class ShapeshiftPatch isSSneeded = false; } - bool forceCancel = shapeshifter.GetCustomRole().ForceCancelShapeshift(); + var role = shapeshifter.GetCustomRole(); + bool forceCancel = role.ForceCancelShapeshift(); if (shapeshifter.Is(CustomRoles.Camouflager) && !shapeshifting) Camouflager.Reset(); if (Changeling.ChangedRole.TryGetValue(shapeshifter.PlayerId, out var changed) && changed && shapeshifter.GetRoleTypes() != RoleTypes.Shapeshifter) @@ -665,8 +666,10 @@ class ShapeshiftPatch bool shouldAlwaysCancel = shouldCancel && Options.DisableAllShapeshiftAnimations.GetBool(); bool doSSwithoutAnim = isSSneeded && shouldAlwaysCancel; + doSSwithoutAnim |= isSSneeded && role.IsNoAnimationShifter(); isSSneeded &= !shouldAlwaysCancel; forceCancel |= shouldAlwaysCancel; + isSSneeded &= !doSSwithoutAnim; // Forced rewriting in case the name cannot be corrected due to the timing of canceling the transformation being off. if (!shapeshifting && !shapeshifter.Is(CustomRoles.Glitch) && isSSneeded) @@ -674,7 +677,7 @@ class ShapeshiftPatch LateTask.New(() => { NotifyRoles(NoCache: true); }, 1.2f, "ShapeShiftNotify"); } - if ((!shapeshifting || !isSSneeded) && !Swapster.FirstSwapTarget.ContainsKey(shapeshifter.PlayerId)) + if (!(shapeshifting && doSSwithoutAnim) && !isSSneeded && !Swapster.FirstSwapTarget.ContainsKey(shapeshifter.PlayerId)) { LateTask.New(shapeshifter.RpcResetAbilityCooldown, 0.01f, log: false); } diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index fdfc29181..6312acdd9 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -3047,6 +3047,10 @@ "CameraManInfo": "View Cameras anytime", "CameraManInfoLong": "(Crewmates):\\nAs the Camera Man, you can vent/pet to instantly teleport to the security room.", "TPBackWhenMoveAway": "Teleport Back When Moving Away From Cameras", + "Echo": "Echo", + "EchoInfo": "Make Players Die Where You Want", + "EchoInfoLong": "(Impostors):\\nAs the Echo, you can shapeshift into a player to swap locations and skins with them. During the shapeshift, your target cannot move. When you unshift, the swap reverts and the target dies exactly where you were.\\n\\nYou cannot kill normally.", + "EchoShapeshiftButtonText": "Control", "NiceHacker": "Hacker", "NiceHackerInfo": "Watch Admin Table from anywhere", "NiceHackerInfoLong": "(Hacker):\\nThis role is different for modded clients and vanilla clients.\\n\\nAs a modded client, whenever you open the map (and have ability uses left), the admin map will show up. You may be able to move while having the map open depending on settings.\\n\\nAs a vanilla client, you may enter a vent/pet your pet to get a notification saying how many people are in different rooms on the ship (essentially admin map).", diff --git a/Roles/Impostor/Echo.cs b/Roles/Impostor/Echo.cs new file mode 100644 index 000000000..e5804bca5 --- /dev/null +++ b/Roles/Impostor/Echo.cs @@ -0,0 +1,70 @@ +namespace EHR.Roles.Impostor +{ + public class Echo : RoleBase + { + public static bool On; + + private static OptionItem ShapeshiftCooldown; + private static OptionItem ShapeshiftDuration; + public override bool IsEnable => On; + + public static void SetupCustomOption() + { + int id = 647600; + const TabGroup tab = TabGroup.ImpostorRoles; + const CustomRoles role = CustomRoles.Echo; + Options.SetupRoleOptions(id++, tab, role); + var parent = Options.CustomRoleSpawnChances[role]; + ShapeshiftCooldown = new FloatOptionItem(++id, "ShapeshiftCooldown", new(0f, 180f, 0.5f), 30f, tab) + .SetParent(parent) + .SetValueFormat(OptionFormat.Seconds); + ShapeshiftDuration = new FloatOptionItem(++id, "ShapeshiftDuration", new(0f, 180f, 0.5f), 10f, tab) + .SetParent(parent) + .SetValueFormat(OptionFormat.Seconds); + } + + public override void Add(byte playerId) + { + On = true; + } + + public override void Init() + { + On = false; + } + + public override void SetButtonTexts(HudManager hud, byte id) + { + bool shifted = id.IsPlayerShifted(); + string text = !shifted ? "EchoShapeshiftButtonText" : "KillButtonText"; + hud.AbilityButton.OverrideText(Translator.GetString(text)); + } + + public override bool CanUseKillButton(PlayerControl pc) => false; + + public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting) + { + if (shapeshifting) + { + var pos = target.Pos(); + if (!shapeshifter.RpcCheckAndMurder(target, check: true) || !target.TP(shapeshifter)) return false; + target.RpcShapeshift(shapeshifter, false); + Main.AllPlayerSpeed[target.PlayerId] = Main.MinSpeed; + target.MarkDirtySettings(); + shapeshifter.TP(pos); + } + else + { + target = Utils.GetPlayerById(shapeshifter.shapeshiftTargetPlayerId); + if (target == null) return true; + target.RpcShapeshift(target, false); + var pos = shapeshifter.Pos(); + shapeshifter.TP(target); + target.TP(pos); + LateTask.New(() => target.Suicide(PlayerState.DeathReason.Kill, shapeshifter), 0.2f, "Echo Unshift Kill"); + } + + return true; + } + } +} \ No newline at end of file