diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index 5b8fc782a4..9482310c2d 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -787,7 +787,6 @@ public static void RpcMurderPlayer(this PlayerControl killer, PlayerControl targ DollMaster.CheckMurderAsPossessed(killer, target); return; } - killer.RpcMurderPlayer(target, true); } diff --git a/Modules/GameState.cs b/Modules/GameState.cs index f59534b33a..58c1f05e14 100644 --- a/Modules/GameState.cs +++ b/Modules/GameState.cs @@ -409,6 +409,13 @@ public bool IsEqual(PlayerVersion pv) return pv.version == version && pv.tag == tag; } } +public enum KilledType +{ + Directly, + Indirectly, + Remotely, + Suicide_ +} public static class GameStates { public static bool InGame = false; diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 35d61e93de..e96ee96548 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -23,6 +23,7 @@ using TOHE.Roles.Core; using static TOHE.Translator; using TOHE.Patches; +using MS.Internal.Xml.XPath; namespace TOHE; @@ -1693,8 +1694,108 @@ public static List GetPlayerListByIds(this IEnumerable Play } public static List GetPlayerListByRole(this CustomRoles role) => GetPlayerListByIds(Main.PlayerStates.Values.Where(x => x.MainRole == role).Select(r => r.PlayerId)); - - public static IEnumerable GetRoleBasesByType () where t : RoleBase + + public static bool IsSameTeammate(this PlayerControl player, PlayerControl target, out Custom_Team team) + { + team = default; + if (player.IsAnySubRole(x => x.IsConverted())) + { + var Compare = player.GetCustomSubRoles().First(x => x.IsConverted()); + + team = player.Is(CustomRoles.Madmate) ? Custom_Team.Impostor : Custom_Team.Neutral; + return target.Is(Compare); + } + else if (!target.IsAnySubRole(x => x.IsConverted())) + { + team = player.GetCustomRole().GetCustomRoleTeam(); + return target.Is(team); + } + + + return false; + } + public static string DetermineSetMessage(PlayerControl Player, PlayerControl killer, out Dictionary DeterminedMessage) + { + string[] Translatables = { "Messenger.KillerLastKillIn", + "Messenger.KillerExistIn", "Messenger.KillersRoleIs", + "Messenger.KilledType", "Messenger.MyTypeIs", "Messenger.TheLastKillers", + "Messenger.PlayerOnSameTeam", "Messenger.LazyFuck", "Messenger.KillersFaction", "Messenger.ThisRoleExists"}; + + static t CreateAndInvoke(Func func) + { + return func.Invoke(); + } + + List TakeMsg = [..Translatables]; + var Msg = new StringBuilder(); + DeterminedMessage = []; + for (int i = 1; i <= 3; i++) + { + var ran = TakeMsg.RandomElement(); + TakeMsg.Remove(ran); + var CurrentMessage = ""; + try + { + CurrentMessage = ran switch + { + "Messenger.KillerLastKillIn" when killer != null && killer.IsAlive() && Main.LastKillerRoom.TryGetValue(Player.PlayerId, out var pokoj) => string.Format(GetString("Messenger.KillerLastKillIn"), GetString($"{pokoj.RoomId}")), + "Messenger.KillerExistIn" when Main.AllAlivePlayerControls.Where(x => x.GetCustomRole().IsImpostor() || x.IsNeutralKiller() || x.IsNeutralApocalypse() || x.IsTransformedNeutralApocalypse()).Shuffle(IRandom.Instance).FirstOrDefault() is not null and PlayerControl killar => CreateAndInvoke(() => + { // yes using Apoc/TApoc may not be 100% accurate but they may or may not keep the game keep going and I'm too lazy to make a specific check + SystemTypes room = killar.GetPlainShipRoom().RoomId; + return string.Format(GetString("Messenger.KillerExistIn"), GetString($"{room}")); + }), + "Messenger.KillersRoleIs" when Main.RememberRoleOfDeadBodyKiller != "" => string.Format(GetString("Messenger.KillersRoleIs"), Main.RememberRoleOfDeadBodyKiller), + "Messenger.KilledType" when Main.PlayerKilledBy.TryGetValue(Player.PlayerId, out var KilledType) => string.Format(GetString("Messenger.KilledType"), GetString($"{KilledType}")), + "Messenger.MyTypeIs" => string.Format(GetString("Messenger.MyTypeIs"), (!Player.IsAnySubRole(x => x.IsConverted() && !Player.Is(CustomRoles.Madmate)) ? Player.GetCustomRole().GetCustomRoleTeam() : Custom_Team.Neutral)), + "Messenger.TheLastKillers" when Main.AllAlivePlayerControls.Where(x => x.GetCustomRole().IsImpostor() || x.IsNeutralKiller() || x.IsNeutralApocalypse()).Any() => CreateAndInvoke(() => + { + var Killers = Main.AllAlivePlayerControls.Where(x => x.GetCustomRole().IsImpostor() || x.IsNeutralKiller() || x.IsNeutralApocalypse()); + var msg = new StringBuilder(); + Killers.Do(x => msg.Append($"{GetString($"{x.GetCustomRole()}")}, ")); + return string.Format(GetString("Messenger.TheLastKillers"), msg.ToString()); + }), + "Messenger.PlayerOnSameTeam" when Main.AllAlivePlayerControls.Shuffle(IRandom.Instance).FirstOrDefault(x => Player.IsSameTeammate(x, out _)) is not null and PlayerControl friend => string.Format(GetString("Messenger.PlayerOnSameTeam"), friend.GetRealName(clientData: true)), + "Messenger.LazyFuck" => CreateAndInvoke(() => + { + var mintask = Main.AllAlivePlayerControls.Where(x => !x.HasImpKillButton()).Min(x => x.GetPlayerTaskState().CompletedTasksCount); + var lazyfuck = Main.AllAlivePlayerControls.First(x => x.GetPlayerTaskState().CompletedTasksCount == mintask); + + var suspects = Main.AllAlivePlayerControls.Where(x => x.HasImpKillButton()).AddItem(lazyfuck); + + var ScapeGoat = suspects.Shuffle(IRandom.Instance).First(); + + return string.Format(GetString("Messenger.LazyFuck"), ScapeGoat.GetRealName(clientData: true)); + }), + + "Messenger.KillersFaction" when Main.RememberTeamOfDeadBodyKiller != null => string.Format(GetString("Messenger.KillersFaction"), GetString($"Team{Main.RememberTeamOfDeadBodyKiller.Value}")), + + _ => CreateAndInvoke(() => + { + var rndPC = Main.AllAlivePlayerControls.ToArray().RandomElement(); + return string.Format(GetString("Messenger.ThisRoleExists"), GetString($"{rndPC.GetCustomRole()}")); + }), + }; + } + catch(Exception exx) + { + CurrentMessage = CreateAndInvoke(() => + { + var rndPC = Main.AllAlivePlayerControls.ToArray().RandomElement(); + return string.Format(GetString("Messenger.ThisRoleExists"), GetString($"{rndPC.GetCustomRole()}")); + }); + Logger.Warn($" The case ( {ran} ) Returned an error", "Utils.DetermineSetMessag"); + Utils.ThrowException(exx); + } + DeterminedMessage[i] = CurrentMessage; + Msg.Append($"{i}) " + CurrentMessage + "\n"); + } + + + return Msg.ToString(); + + } + +public static IEnumerable GetRoleBasesByType () where t : RoleBase { try { @@ -2112,6 +2213,7 @@ static int GetInfoSize(string RoleInfo) TargetSuffix.Append(seerRoleClass?.GetSuffix(seer, target, isForMeeting: isForMeeting)); TargetSuffix.Append(CustomRoleManager.GetSuffixOthers(seer, target, isForMeeting: isForMeeting)); + TargetSuffix.Append(Messenger.GetSuffix(target, isForMeeting)); if (TargetSuffix.Length > 0) { diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index d8d06c4ebb..c7e9e7658e 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -6,6 +6,8 @@ using System.Text.RegularExpressions; using TOHE.Modules; using TOHE.Modules.ChatManager; +using TOHE.Roles._Ghosts_.Crewmate; +using TOHE.Roles.AddOns.Common; using TOHE.Roles.Core; using TOHE.Roles.Core.AssignManager; using TOHE.Roles.Crewmate; @@ -67,7 +69,10 @@ public static bool Prefix(ChatController __instance) if (Nemesis.NemesisMsgCheck(PlayerControl.LocalPlayer, text)) goto Canceled; if (Retributionist.RetributionistMsgCheck(PlayerControl.LocalPlayer, text)) goto Canceled; if (Medium.MsMsg(PlayerControl.LocalPlayer, text)) goto Canceled; - if (PlayerControl.LocalPlayer.GetRoleClass() is Swapper sw && sw.SwapMsg(PlayerControl.LocalPlayer, text)) goto Canceled; + if (PlayerControl.LocalPlayer.GetRoleClass() is Swapper sw && sw.SwapMsg(PlayerControl.LocalPlayer, text)) goto Canceled; + if (Telepathy.TelepathyMessage(PlayerControl.LocalPlayer, args)) goto Canceled; + if (Messenger.CheckMessage(PlayerControl.LocalPlayer, args)) goto Canceled; + Directory.CreateDirectory(modTagsFiles); Directory.CreateDirectory(vipTagsFiles); Directory.CreateDirectory(sponsorTagsFiles); @@ -1888,6 +1893,8 @@ public static void OnReceiveChat(PlayerControl player, string text, out bool can if (Medium.MsMsg(player, text)) { Logger.Info($"Is Medium command", "OnReceiveChat"); return; } if (Nemesis.NemesisMsgCheck(player, text)) { Logger.Info($"Is Nemesis Revenge command", "OnReceiveChat"); return; } if (Retributionist.RetributionistMsgCheck(player, text)) { Logger.Info($"Is Retributionist Revenge command", "OnReceiveChat"); return; } + if (Telepathy.TelepathyMessage(player, args)) { Logger.Info("Is Telepathy MSG command", "OnRecieveChat"); return; } + if (Messenger.CheckMessage(player, args)) { Logger.Info("Is Messenger MSG command", "OnRecieveChat"); return; } Directory.CreateDirectory(modTagsFiles); Directory.CreateDirectory(vipTagsFiles); diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index 99b71f8c2f..53e98b7cb8 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -928,6 +928,7 @@ public static void NotifyRoleSkillOnMeetingStart() { pc?.GetRoleClass()?.OnMeetingHudStart(pc); Main.PlayerStates.Do(plr => plr.Value.RoleClass.OnOthersMeetingHudStart(pc)); + Messenger.NotifyAddonOnMeeting(pc); foreach (var csId in Cyber.CyberDead) { diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 663737540c..47c004c33d 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -119,6 +119,8 @@ public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] PlayerC Logger.Info($"End: CustomRoleManager.OnCheckMurder", "CheckMurder"); + Main.PlayerKilledBy[target.PlayerId] = KilledType.Directly; + //== Kill target == __instance.RpcMurderPlayer(target); //============ @@ -487,6 +489,21 @@ public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)] Player Utils.NotifyRoles(SpecifySeer: killer); Utils.NotifyRoles(SpecifySeer: target); + + _ = new LateTask(() => { + + if (!Main.PlayerKilledBy.ContainsKey(target.PlayerId)) + { + + if (Vector2.Distance(target.GetRealKiller().GetCustomPosition(), target.transform.position) > 2f) + Main.PlayerKilledBy[target.PlayerId] = KilledType.Remotely; + else if (target.GetRealKiller() == target) + Main.PlayerKilledBy[target.PlayerId] = KilledType.Suicide_; + else + Main.PlayerKilledBy[target.PlayerId] = KilledType.Indirectly; + } + + }, 1f, "Set Main.Playerkilled by in MurderPlayer Patch"); } public static void AfterPlayerDeathTasks(PlayerControl killer, PlayerControl target, bool inMeeting) { @@ -930,6 +947,7 @@ public static void AfterReportTasks(PlayerControl player, NetworkedPlayerInfo ta Main.AllKillers.Clear(); GuessManager.GuesserGuessed.Clear(); + Logger.Info($"target is null? - {target == null}", "AfterReportTasks"); Logger.Info($"target.Object is null? - {target?.Object == null}", "AfterReportTasks"); Logger.Info($"target.PlayerId is - {target?.PlayerId}", "AfterReportTasks"); @@ -939,6 +957,11 @@ public static void AfterReportTasks(PlayerControl player, NetworkedPlayerInfo ta try { playerStates.RoleClass?.OnReportDeadBody(player, target); + + if (!Utils.GetPlayerById(playerStates.PlayerId).IsAlive() && target?.Object?.GetRealKiller() != null) + { + Main.LastKillerRoom[target.PlayerId] = target.Object.GetRealKiller().GetPlainShipRoom(); + } } catch (Exception error) { @@ -947,6 +970,17 @@ public static void AfterReportTasks(PlayerControl player, NetworkedPlayerInfo ta Logger.SendInGame($"Error: {error}"); } } + + Main.RememberTeamOfDeadBodyKiller = null; + Main.RememberRoleOfDeadBodyKiller = ""; + if (target?.Object?.GetRealKiller() != null) + { + Main.RememberRoleOfDeadBodyKiller = GetString($"{target.Object.GetRealKiller().GetCustomRole()}"); + if (!target.Object.GetRealKiller().IsAnySubRole(x => x.IsConverted() && !target.Object.Is(CustomRoles.Madmate))) + Main.RememberTeamOfDeadBodyKiller = target.Object.GetRealKiller().GetCustomRole().GetCustomRoleTeam(); + else + Main.RememberTeamOfDeadBodyKiller = Custom_Team.Neutral; + } Rebirth.OnReportDeadBody(); // Alchemist & Bloodlust @@ -1857,6 +1891,8 @@ public static void Postfix(PlayerControl __instance) Utils.LateExileTask.Add(SelfExile); } } + if (!Main.PlayerKilledBy.ContainsKey(__instance.PlayerId)) + Main.PlayerKilledBy[__instance.PlayerId] = KilledType.Indirectly; } catch (Exception exx) { diff --git a/Patches/onGameStartedPatch.cs b/Patches/onGameStartedPatch.cs index 46eca25926..9a3b2c5228 100644 --- a/Patches/onGameStartedPatch.cs +++ b/Patches/onGameStartedPatch.cs @@ -67,6 +67,9 @@ public static void Postfix(AmongUsClient __instance) Main.clientIdList.Clear(); PlayerControlSetRolePatch.DidSetGhost.Clear(); + Main.LastKillerRoom.Clear(); + Main.RememberTeamOfDeadBodyKiller = null; + Main.PlayerKilledBy.Clear(); Main.CheckShapeshift.Clear(); Main.ShapeshiftTarget.Clear(); @@ -198,33 +201,12 @@ public static void Postfix(AmongUsClient __instance) RoleClass?.OnInit(); } + CustomRoleManager.AddonClasses.Values.Where(x => x != null).Do(x => x.Init()); + LastImpostor.Init(); TargetArrow.Init(); LocateArrow.Init(); DoubleTrigger.Init(); - Workhorse.Init(); - Diseased.Init(); - Clumsy.Init(); - Aware.Init(); - Radar.Init(); - Glow.Init(); - Sleuth.Init(); - Bait.Init(); - Antidote.Init(); - Fool.Init(); - Burst.Init(); - DoubleShot.Init(); - Lucky.Init(); - Bewilder.Init(); - //ChiefOfPolice.Init(); - Cyber.Init(); - Oiiai.Init(); - Tired.Init(); - Statue.Init(); - Ghoul.Init(); - Rainbow.Init(); - Rebirth.Init(); - Evader.Init(); //FFA FFAManager.Init(); diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 762fc06151..7f4865fd94 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -300,6 +300,7 @@ "Doppelganger": "Doppelganger", "PunchingBag": "Punching Bag", "Doomsayer": "Doomsayer", + "Telepathy": "Telepathy", "Shroud": "Shroud", "Werewolf": "Werewolf", "Shaman": "Shaman", @@ -318,6 +319,7 @@ "Necromancer": "Necromancer", "Warden": "Warden", "Minion": "Minion", + "Messenger": "Messenger", "Ghastly": "Ghastly", "LastImpostor": "Last Impostor", "Overclocked": "Overclocked", @@ -471,6 +473,7 @@ "UnderdogInfo": "Start killing on a low player count", "LudopathInfo": "Your kill cooldown is random", "GodfatherInfo": "Convert players to Refugees by voting", + "MessengerInfo": "Speak from above", "ChronomancerInfo": "Kill in bursts", "PitfallInfo": "Setup traps around the map", "EvilMiniInfo": "No one can hurt you until you grow up", @@ -559,6 +562,7 @@ "LawyerInfo": "Help your target win!", "VectorInfo": "Jump in! Jump out!", "JackalInfo": "Murder everyone", + "TelepathyInfo": "Connect with the living", "GodInfo": "Everything is under your control", "InnocentInfo": "Get someone ejected by making them kill you", "PelicanInfo": "Eat all players", @@ -857,6 +861,7 @@ "HuntsmanInfoLong": "(Neutrals):\nAs the Huntsman, you are given a certain number of targets that reset every meeting. If you successfully eliminate one of your targets, your kill cooldown goes down permanently by the set amount. However, if you kill someone who is not one of your targets, your kill cooldown permanently increases by the set amount. A colored name indicates your targets.", "MiniInfoLong": "(Crewmate or Impostor):\nThe Mini has two roles. A Nice or Evil Mini is chosen.\n\nUse'/r nice mini' and '/r evil mini' respectively for more details.", "JesterInfoLong": "(Neutrals):\nIf the Jester gets voted out, the Jester wins the game alone. If the Jester is still alive at the end of the game, the Jester loses the game. Note: Jester, Executioner, and Innocent can win together.", + "TelepathyInfoLong": "(Crewmates [Ghost]):\nAs the Telepathy, You can use your protect button to connect with someone, connecting with someone again simply switches your target. \n\nIn the upcoming meeting you will be able to answer a /tms {yes/no} question they have, or give them a set of possible messages depending on the settings (/tms {0/1/2}).", "TerroristInfoLong": "(Neutrals):\nIf the Terrorist dies after completing all tasks, the Terrorist wins the game alone. (They can win by either being voted out or killed).", "ExecutionerInfoLong": "(Neutrals):\nThe Executioner is a role with an execution target, indicated by a diamond symbol「♦」next to their name. If the execution target is killed, the Executioner's role will change to either Crewmate, Jester, or Opportunist, depending on the game settings. However, if the execution target is voted out in the meeting, the Executioner wins. Note: Jester, Executioner, and Innocent can win together.", "LawyerInfoLong": "(Neutrals):\nLawyer has a target to defend, which will be indicated by a diamond 「♦」 next to their name.\nIf your target wins, you win.\nIf they lose, you lose.", @@ -937,6 +942,7 @@ "TiebreakerInfoLong": "(Add-ons):\nWhen tie vote, priority will be given to the target voted by the Tiebreaker. Note: If multiple Tiebreakers choose different tie targets simultaneously, the skills of the Tiebreaker will not take effect.", "ObliviousInfoLong": "(Add-ons):\nDetective and Cleaners won't be Oblivious. The Oblivious cannot report dead bodies. Note: Bait killed by Oblivious will still report automatically, and Oblivious can still be used as a scapegoat for Anonymous.", "BewilderInfoLong": "(Add-ons):\nBewilder may have a smaller/bigger vision. When the Bewilder has died, the murderer's vision may become the same as the Bewilder's, depending on the settings.", + "MessengerInfoLong": "(Add-ons):\nAs The Messenger, when you die, the next/upcoming meeting you will have 3 messages to choose from and send for everyone to see.\n\nBy using the /mms [0/1/2] command.", "WorkhorseInfoLong": "(Add-ons):\nThe first player to complete all the tasks will become Workhorse, and Workhorse will give the player extra tasks. The Host sets the number of additional tasks.", "FoolInfoLong": "(Add-ons):\nSleuth and Mechanic won't be Fool. Fools can't repair any sabotage.", "AvangerInfoLong": "(Add-ons):\nHost can set whether the Impostor can become an Avenger. When the Avenger is killed (voted out, and irregular kills will not count), the Avenger will revenge a random player.", @@ -2085,6 +2091,48 @@ "MediumNotifySelf": "You established contact with {0}. Please ask them questions and wait for them to respond.\n\nRemaining ability uses: {1}", "MediumKnowPlayerDead": "Someone died somewhere", + "Messenger.KillerLastKillIn": "My killer was last in {0}", + "Messenger.KillerExistIn": "There is a killer in {0}", + "Messenger.KillersRoleIs": "The killer's role is {0}", + "Messenger.KilledType": "I was killed {0}", + "Messenger.MyTypeIs": "I am on the {0} team", + "Messenger.TheLastKillers": "The remaining killers are:\n{0}", + "Messenger.PlayerOnSameTeam": "{0} is on my team", + "Messenger.LazyFuck": "{0} has done none or the least amount of tasks", + "Messenger.KillersFaction": "The killer's faction is {0}", + "Messenger.ThisRoleExists": "There is a {0} alive", + "MessengersImpsHear": "Impostors See Messenger Msg", + "MessengersNeutsHear": "Neutrals See Messenger Msg", + "EveryoneKnowsMessenger": "Everyone Knows Messenger", + + "MessengerTitle": "MESSENGER", + "MessengerTitleTarget": "A Voice From Above Whispers...", + "MessengerUsage": "Type /mms {1/2/3} to send a message.", + "Messenger.Msg": "What would you like the public to know?\n\n{0}\n\nUse /mms [1/2/3] to answer.", + + "Suicide_": "By Suicide", + "Remotely": "Remotely", + "Indirectly": "Indirectly", + "Directly": "Directly", + + "TelepathyUses": "Max Connections", + "Telepathy_MessageMode": "Message Mode", + "Telepathy_MessageMode_YesNo": "Yes/No Confirmation", + "Telepathy_MessageMode_Message": "Helpful Messages", + "TelepathyCantConnect": "Could not connect with target", + "TelepathyConnectWith": "You have made a connection with {0}", + "TelepathyYesNoUsage": "Use /tms {yes/no} to answer the player", + "TelepathyTitle": "TELEPATHY", + "TelepathyTitleTarget": "The Telepathy whispers to you...", + "TelepathyYes": "The telepathy confirms your inquiry", + "TelepathyNo": "The telepathy denies your inquiry", + "TelepathyConfirmSelf": "You have succesfully sent your message", + "TelepathyMODE2Usage": "Use /tms {1/2/3} to submit your answers", + "TelepathyNotifyTarget": "The Telepathy {0} has made a connection with you, meaning you may ask it a question and it might answer back confirming or denying it.", + "TelepathyNotifyTargetMODE2": "The Telepathy {0} has made a connection with you, meaning you might hear useful info from them.", + "TelepathyNotifySelf": "You have made a connection with {0}, answer with /tms [yes/no] to one of their questions.\n Note: They may try to disguise their question, so be on watch", + "TelepathyNotifySelfMODE2": "What would you like to whisper to {0}?\n\n{1}\n\nUse /tms [1/2/3] to answer.", + "TelepathyTargetDisconnect": "Your target has Died/Disconnected", "SpurtMinSpeed": "Min Speed", "SpurtMaxSpeed": "Max Speed", "SpurtModule": "Speed Modulator", diff --git a/Resources/roleColor.json b/Resources/roleColor.json index bbeb4b1575..fb08dc3d14 100644 --- a/Resources/roleColor.json +++ b/Resources/roleColor.json @@ -242,5 +242,7 @@ "Ghastly": "#9ad6d4", "Glow": "#E2F147", "Radar": "#1eff1e", - "Rebirth": "#f08c22" + "Rebirth": "#f08c22", + "Telepathy": "#9470b8", + "Messenger": "#bca387" } diff --git a/Roles/(Ghosts)/Crewmate/Telepathy.cs b/Roles/(Ghosts)/Crewmate/Telepathy.cs new file mode 100644 index 0000000000..53f4220271 --- /dev/null +++ b/Roles/(Ghosts)/Crewmate/Telepathy.cs @@ -0,0 +1,209 @@ +using AmongUs.GameOptions; +using TOHE.Roles.Core; +using System; +using UnityEngine; +using static TOHE.Options; +using static TOHE.Translator; +using static TOHE.MeetingHudStartPatch; +using static TOHE.Utils; + +namespace TOHE.Roles._Ghosts_.Crewmate; + +internal class Telepathy : RoleBase +{ + //===========================SETUP================================\\ + private const int Id = 29300; + public static bool HasEnabled => CustomRoleManager.HasEnabled(CustomRoles.Telepathy); + public override CustomRoles ThisRoleBase => CustomRoles.GuardianAngel; + public override Custom_RoleType ThisRoleType => Custom_RoleType.CrewmateGhosts; + //==================================================================\\ + + private static OptionItem AbilityCooldown; + private static OptionItem AbilityUses; + private static OptionItem MessageMode; + public bool HasMessaged; + public static Dictionary TargetPlayer = []; + private Dictionary Determinemessage = []; + private enum MessageModeCount + { + Telepathy_MessageMode_YesNo, + Telepathy_MessageMode_Message + } + + public override void SetupCustomOption() + { + SetupRoleOptions(Id, TabGroup.CrewmateRoles, CustomRoles.Telepathy); + AbilityCooldown = FloatOptionItem.Create(Id + 10, GeneralOption.GuardianAngelBase_ProtectCooldown, new(2.5f, 120f, 2.5f), 35f, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Telepathy]) + .SetValueFormat(OptionFormat.Seconds); + AbilityUses = IntegerOptionItem.Create(Id + 11, "TelepathyUses", new(1, 99, 1), 14, TabGroup.CrewmateRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Telepathy]) + .SetValueFormat(OptionFormat.Times); + MessageMode = StringOptionItem.Create(Id + 12, "Telepathy_MessageMode", EnumHelper.GetAllNames(), 0, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Telepathy]); + } + public override void Init() + { + TargetPlayer.Clear(); + } + public override void Add(byte playerId) + { + AbilityLimit = AbilityUses.GetFloat(); + } + public override void ApplyGameOptions(IGameOptions opt, byte playerId) + { + AURoleOptions.GuardianAngelCooldown = AbilityCooldown.GetFloat(); + AURoleOptions.ProtectionDurationSeconds = 0f; + } + public override bool OnCheckProtect(PlayerControl angel, PlayerControl target) + { + if (AbilityLimit < 1) return false; + if (TargetPlayer.ContainsValue(target.PlayerId)) + { + angel.Notify(GetString("TelepathyCantConnect")); + return false; + } + + + TargetPlayer[angel.PlayerId] = target.PlayerId; + angel.Notify(string.Format(GetString("TelepathyConnectWith"), target.GetRealName(clientData: true))); + AbilityLimit--; + SendSkillRPC(); + angel.RpcResetAbilityCooldown(); + + return false; + } + public override void OnOtherTargetsReducedToAtoms(PlayerControl DeadPlayer) + { + if (TargetPlayer.ContainsValue(DeadPlayer.PlayerId)) + { + TargetPlayer.Remove(TargetPlayer.First(x => x.Value == DeadPlayer.PlayerId).Key); + if (_Player != null) + { + if (GameStates.IsMeeting || Main.MeetingIsStarted) + { + Utils.SendMessage(GetString("TelepathyTargetDisconnect"), _state.PlayerId); + } + else + { + _Player.Notify(GetString("TelepathyTargetDisconnect")); + } + } + } + } + public static bool TelepathyMessage(PlayerControl pc, string[] args) + { + if (!AmongUsClient.Instance.AmHost || !GameStates.IsMeeting || pc == null) return false; + if (!TargetPlayer.TryGetValue(pc.PlayerId, out var tar) || GetPlayerById(tar) == null) return false; + if (args.Length < 2) return false; + if (((Telepathy)pc.GetRoleClass())?.HasMessaged is null or true) return false; + + Dictionary DetermineMessage = ((Telepathy)pc.GetRoleClass()).Determinemessage; + + + string[] cmds = {"/tms"}; // Here you can add custom cmds + if (!cmds.Any(x => x.Equals(args[0], StringComparison.OrdinalIgnoreCase))) return false; + + switch (MessageMode.GetInt()) + { + case 0: + var Validation = (args[1].Equals(GetString("Yes"), StringComparison.OrdinalIgnoreCase) || args[1].Equals(GetString("No"), StringComparison.OrdinalIgnoreCase)); + if (!Validation) + { + SendMessage(GetString("TelepathyYesNoUsage"), pc.PlayerId, title: GetString("TelepathyTitle")); + break; + } + var confirm = args[1].Equals(GetString("Yes"), StringComparison.OrdinalIgnoreCase); + ((Telepathy)pc.GetRoleClass()).HasMessaged = true; + + SendMessage(GetString("Telepathy" + (confirm ? "Yes" : "No")), TargetPlayer[pc.PlayerId], ColorString(GetRoleColor(CustomRoles.Telepathy), GetString("TelepathyTitleTarget"))); + SendMessage(GetString("TelepathyConfirmSelf"), pc.PlayerId, ColorString(GetRoleColor(CustomRoles.Telepathy), GetString("TelepathyTitle"))); + + break; + + + case 1: + if (!int.TryParse(args[1], out int id) || !DetermineMessage.ContainsKey(id)) + { + SendMessage(GetString("TelepathyMODE2Usage"), pc.PlayerId, title: ColorString(GetRoleColor(CustomRoles.Telepathy), GetString("TelepathyTitle"))); + break; + } + ((Telepathy)pc.GetRoleClass()).HasMessaged = true; + + Utils.SendMessage(DetermineMessage[id], TargetPlayer[pc.PlayerId], title: ColorString(GetRoleColor(CustomRoles.Telepathy), GetString("TelepathyTitleTarget"))); + SendMessage(GetString("TelepathyConfirmSelf"), pc.PlayerId, ColorString(GetRoleColor(CustomRoles.Telepathy), GetString("TelepathyTitle"))); + + break; + } + + + return true; + } + public override void OnReportDeadBody(PlayerControl reporter, NetworkedPlayerInfo target) + { + HasMessaged = false; + } + public override void AfterMeetingTasks() + { + TargetPlayer.Clear(); + } + + public override void OnOthersMeetingHudStart(PlayerControl pc) + { + if (_Player == null) return; + + switch (MessageMode.GetInt()) + { + case 0: + if (TargetPlayer.TryGetValue(_Player.PlayerId, out var t) && t == pc.PlayerId) + AddMsg(string.Format(GetString("TelepathyNotifyTarget"), _Player.GetRealName(clientData: true)), pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Telepathy), GetString("TelepathyTitle"))); + + break; + + + case 1: + if (TargetPlayer.TryGetValue(_Player.PlayerId, out var w) && w == pc.PlayerId) + AddMsg(string.Format(GetString("TelepathyNotifyTargetMODE2"), _Player.GetRealName(clientData: true)), pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Telepathy), GetString("TelepathyTitle"))); + + break; + } + } + public override void OnMeetingHudStart(PlayerControl pc) + { + if (_Player == null) return; + + switch (MessageMode.GetInt()) + { + case 0: + if (TargetPlayer.TryGetValue(pc.PlayerId, out var tar) && Utils.GetPlayerById(tar) != null) + AddMsg(string.Format(GetString("TelepathyNotifySelf"), Utils.GetPlayerById(tar).GetRealName(clientData: true)), pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Telepathy), GetString("TelepathyTitle"))); + break; + + case 1: + if (TargetPlayer.TryGetValue(pc.PlayerId, out var rat) && Utils.GetPlayerById(rat) != null) + { + var msg = DetermineSetMessage(pc, pc.GetRealKiller(), out Determinemessage); + AddMsg(string.Format(GetString("TelepathyNotifySelfMODE2"), Utils.GetPlayerById(rat).GetRealName(clientData: true), msg), pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Telepathy), GetString("TelepathyTitle"))); + } + break; + } + } + + public override string GetSuffixOthers(PlayerControl seer, PlayerControl seen, bool isForMeeting = false) + { + if (!isForMeeting) return string.Empty; + + var checkTar = TargetPlayer.Where(x => x.Value == seer.PlayerId); + if (TargetPlayer.TryGetValue(seer.PlayerId, out var tar) && tar == seen.PlayerId) + { + return ColorString(GetRoleColor(CustomRoles.Telepathy), "¿"); + } + else if (checkTar.Any()) + { + var Telepathy = Utils.GetPlayerById(checkTar.First().Key); + + return seen == Telepathy ? ColorString(GetRoleColor(CustomRoles.Telepathy), "¿") : ""; + } + + return string.Empty; + } + public override string GetProgressText(byte playerId, bool coms) + => ColorString(AbilityLimit >= 1 ? GetRoleColor(CustomRoles.Telepathy).ShadeColor(0.25f) : Color.gray, $"({AbilityLimit})"); +} diff --git a/Roles/(Ghosts)/Impostor/Minion.cs b/Roles/(Ghosts)/Impostor/Minion.cs index 028cea3458..574de2a9da 100644 --- a/Roles/(Ghosts)/Impostor/Minion.cs +++ b/Roles/(Ghosts)/Impostor/Minion.cs @@ -44,7 +44,7 @@ public override void ApplyGameOptions(IGameOptions opt, byte playerId) } public override string GetLowerTextOthers(PlayerControl seer, PlayerControl target = null, bool isForMeeting = false, bool isForHud = false) { - if ((seer.GetCustomRole().IsImpostorTeam()) && Main.PlayerStates[target.PlayerId].IsBlackOut && !isForMeeting) + if ((_Player?.IsSameTeammate(target, out _) == true) && Main.PlayerStates[target.PlayerId].IsBlackOut && !isForMeeting) { var blinded = Translator.GetString("Minion_Blind"); return ColorString(GetRoleColor(CustomRoles.Minion), $"『{blinded}』"); @@ -53,8 +53,8 @@ public override string GetLowerTextOthers(PlayerControl seer, PlayerControl targ } public override bool OnCheckProtect(PlayerControl killer, PlayerControl target) { - var ImpPVC = target.GetCustomRole().IsImpostor(); - if (!ImpPVC || killer.IsAnySubRole(x => x.IsConverted() && !killer.Is(CustomRoles.Madmate))) + var ImpPVC = killer.IsSameTeammate(target, out _); + if (!ImpPVC) { Main.PlayerStates[target.PlayerId].IsBlackOut = true; target.MarkDirtySettings(); diff --git a/Roles/AddOns/Common/Antidote.cs b/Roles/AddOns/Common/Antidote.cs index 731dd38844..1668a43ac6 100644 --- a/Roles/AddOns/Common/Antidote.cs +++ b/Roles/AddOns/Common/Antidote.cs @@ -24,7 +24,7 @@ public void SetupCustomOption() AntidoteCDReset = BooleanOptionItem.Create(Id + 14, "AntidoteCDReset", true, TabGroup.Addons, false).SetParent(CustomRoleSpawnChances[CustomRoles.Antidote]); } - public static void Init() + public void Init() { KilledAntidote = []; IsEnable = false; diff --git a/Roles/AddOns/Common/Aware.cs b/Roles/AddOns/Common/Aware.cs index bd22bfca4b..e52c98801d 100644 --- a/Roles/AddOns/Common/Aware.cs +++ b/Roles/AddOns/Common/Aware.cs @@ -22,7 +22,7 @@ public void SetupCustomOption() AwareknowRole = BooleanOptionItem.Create(Id + 13, "AwareKnowRole", true, TabGroup.Addons, false).SetParent(CustomRoleSpawnChances[CustomRoles.Aware]); } - public static void Init() + public void Init() { AwareInteracted = []; IsEnable = false; diff --git a/Roles/AddOns/Common/Bait.cs b/Roles/AddOns/Common/Bait.cs index a63b425bde..ffecdb4731 100644 --- a/Roles/AddOns/Common/Bait.cs +++ b/Roles/AddOns/Common/Bait.cs @@ -30,7 +30,7 @@ public void SetupCustomOption() BaitCanBeReportedUnderAllConditions = BooleanOptionItem.Create(Id + 17, "BaitCanBeReportedUnderAllConditions", false, TabGroup.Addons, false).SetParent(CustomRoleSpawnChances[CustomRoles.Bait]); } - public static void Init() + public void Init() { BaitAlive = []; } diff --git a/Roles/AddOns/Common/Bewilder.cs b/Roles/AddOns/Common/Bewilder.cs index 255fe00802..f93420ac6d 100644 --- a/Roles/AddOns/Common/Bewilder.cs +++ b/Roles/AddOns/Common/Bewilder.cs @@ -21,7 +21,7 @@ public void SetupCustomOption() KillerGetBewilderVision = BooleanOptionItem.Create(Id + 14, "KillerGetBewilderVision", true, TabGroup.Addons, false).SetParent(CustomRoleSpawnChances[CustomRoles.Bewilder]); } - public static void Init() + public void Init() { IsEnable = false; } diff --git a/Roles/AddOns/Common/Burst.cs b/Roles/AddOns/Common/Burst.cs index 45668a2e61..6c911f3cee 100644 --- a/Roles/AddOns/Common/Burst.cs +++ b/Roles/AddOns/Common/Burst.cs @@ -19,7 +19,7 @@ public void SetupCustomOption() .SetValueFormat(OptionFormat.Seconds); } - public static void Init() + public void Init() { BurstBodies.Clear(); IsEnable = false; diff --git a/Roles/AddOns/Common/Cyber.cs b/Roles/AddOns/Common/Cyber.cs index 7ecb3378c0..28a07f6bf1 100644 --- a/Roles/AddOns/Common/Cyber.cs +++ b/Roles/AddOns/Common/Cyber.cs @@ -23,7 +23,7 @@ public void SetupCustomOption() CyberKnown = BooleanOptionItem.Create(Id + 16, "CyberKnown", true, TabGroup.Addons, false).SetParent(CustomRoleSpawnChances[CustomRoles.Cyber]); } - public static void Init() + public void Init() { CyberDead = []; } diff --git a/Roles/AddOns/Common/Diseased.cs b/Roles/AddOns/Common/Diseased.cs index ac8b77c984..193cd66130 100644 --- a/Roles/AddOns/Common/Diseased.cs +++ b/Roles/AddOns/Common/Diseased.cs @@ -21,7 +21,7 @@ public void SetupCustomOption() DiseasedCDReset = BooleanOptionItem.Create(Id + 14, "DiseasedCDReset", true, TabGroup.Addons, false).SetParent(CustomRoleSpawnChances[CustomRoles.Diseased]); } - public static void Init() + public void Init() { KilledDiseased = []; IsEnable = false; diff --git a/Roles/AddOns/Common/DoubleShot.cs b/Roles/AddOns/Common/DoubleShot.cs index 573c718023..7291bff57b 100644 --- a/Roles/AddOns/Common/DoubleShot.cs +++ b/Roles/AddOns/Common/DoubleShot.cs @@ -22,7 +22,7 @@ public void SetupCustomOption() NeutralCanBeDoubleShot = BooleanOptionItem.Create(19212, "NeutralCanBeDoubleShot", true, TabGroup.Addons, false) .SetParent(CustomRoleSpawnChances[CustomRoles.DoubleShot]); } - public static void Init() + public void Init() { IsActive = []; } diff --git a/Roles/AddOns/Common/Evader.cs b/Roles/AddOns/Common/Evader.cs index bb87f27128..4345e6d304 100644 --- a/Roles/AddOns/Common/Evader.cs +++ b/Roles/AddOns/Common/Evader.cs @@ -21,7 +21,7 @@ public void SetupCustomOption() .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Evader]) .SetValueFormat(OptionFormat.Percent); } - public static void Init() + public void Init() { SkillLimit.Clear(); } diff --git a/Roles/AddOns/Common/Fool.cs b/Roles/AddOns/Common/Fool.cs index 33864f8513..a35097f6b2 100644 --- a/Roles/AddOns/Common/Fool.cs +++ b/Roles/AddOns/Common/Fool.cs @@ -12,7 +12,7 @@ public void SetupCustomOption() SetupAdtRoleOptions(Id, CustomRoles.Fool, canSetNum: true, tab: TabGroup.Addons, teamSpawnOptions: true); } - public static void Init() + public void Init() { IsEnable = false; } diff --git a/Roles/AddOns/Common/Glow.cs b/Roles/AddOns/Common/Glow.cs index 9a94932da1..971272232a 100644 --- a/Roles/AddOns/Common/Glow.cs +++ b/Roles/AddOns/Common/Glow.cs @@ -31,7 +31,7 @@ public void SetupCustomOption() .SetValueFormat(OptionFormat.Multiplier); } - public static void Init() + public void Init() { InRadius.Clear(); IsEnable = false; diff --git a/Roles/AddOns/Common/Lucky.cs b/Roles/AddOns/Common/Lucky.cs index 90d2102d3d..c1f3f16a20 100644 --- a/Roles/AddOns/Common/Lucky.cs +++ b/Roles/AddOns/Common/Lucky.cs @@ -18,7 +18,7 @@ public void SetupCustomOption() .SetValueFormat(OptionFormat.Percent); } - public static void Init() + public void Init() { LuckyAvoid = []; } diff --git a/Roles/AddOns/Common/Messenger.cs b/Roles/AddOns/Common/Messenger.cs new file mode 100644 index 0000000000..22140e3562 --- /dev/null +++ b/Roles/AddOns/Common/Messenger.cs @@ -0,0 +1,82 @@ +using static TOHE.Options; +using static TOHE.Utils; +using static TOHE.Translator; +using System; +using static TOHE.MeetingHudStartPatch; +using TOHE.Roles._Ghosts_.Crewmate; +using TOHE.Roles.Core; +using System.Diagnostics.Contracts; + +namespace TOHE.Roles.AddOns.Common; + +public class Messenger : IAddon +{ + private const int Id = 29200; + private static OptionItem ImpostorsHearMessage; + private static OptionItem NeutralsHearMessage; + private static OptionItem KnowMessenger; + public AddonTypes Type => AddonTypes.Helpful; + public static Dictionary> Determinemessage = []; + public static Dictionary DidSay = []; + public void SetupCustomOption() + { + SetupAdtRoleOptions(Id, CustomRoles.Messenger, canSetNum: true, teamSpawnOptions: true); + ImpostorsHearMessage = BooleanOptionItem.Create(Id + 10, "MessengersImpsHear", true, TabGroup.Addons, false).SetParent(CustomRoleSpawnChances[CustomRoles.Messenger]); + NeutralsHearMessage = BooleanOptionItem.Create(Id + 11, "MessengersNeutsHear", true, TabGroup.Addons, false).SetParent(CustomRoleSpawnChances[CustomRoles.Messenger]); + KnowMessenger = BooleanOptionItem.Create(Id + 12, "EveryoneKnowsMessenger", false, TabGroup.Addons, false).SetParent(CustomRoleSpawnChances[CustomRoles.Messenger]); + } + public void Init() + { + DidSay.Clear(); + Determinemessage.Clear(); + } + public static string GetSuffix(PlayerControl seen, bool ismeeting) + { + if (!seen.Is(CustomRoles.Messenger) || !ismeeting || !KnowMessenger.GetBool() || seen.IsAlive() || DidSay.TryGetValue(seen.PlayerId, out var say) && say) return string.Empty; + + return ColorString(GetRoleColor(CustomRoles.Messenger), "⌘"); + } + public static void NotifyAddonOnMeeting(PlayerControl pc) + { + if (!pc.Is(CustomRoles.Messenger) || DidSay.TryGetValue(pc.PlayerId, out _) || pc.IsAlive()) return; + + var msg = DetermineSetMessage(pc, pc.GetRealKiller(), out var det); + Determinemessage[pc.PlayerId] = det; + AddMsg(string.Format(GetString("Messenger.Msg"), msg), pc.PlayerId, ColorString(GetRoleColor(CustomRoles.Messenger), GetString("MessengerTitle"))); + + } + public static bool CheckMessage(PlayerControl pc, string[] args) + { + if (!AmongUsClient.Instance.AmHost || !GameStates.IsMeeting || pc == null || pc.IsAlive()) return false; + if (!Determinemessage.TryGetValue(pc.PlayerId, out var determineMessage)) return false; + if (!pc.Is(CustomRoles.Messenger) || DidSay.TryGetValue(pc.PlayerId, out bool said) && said) return false; + if (args.Length < 2) return false; + + string[] cmds = { "/mms" }; // Here you can add custom cmds + if (!cmds.Any(x => x.Equals(args[0], StringComparison.OrdinalIgnoreCase))) return false; + + + if (!int.TryParse(args[1], out int id) || !determineMessage.ContainsKey(id)) + { + SendMessage(GetString("MessengerUsage"), pc.PlayerId, title: ColorString(GetRoleColor(CustomRoles.Messenger), GetString("MessengerTitle"))); + return false; + } + DidSay[pc.PlayerId] = true; + + List ExceptList = []; + if (!ImpostorsHearMessage.GetBool()) + ExceptList.AddRange(Main.AllPlayerControls.Where(x => x.GetCustomRole().IsImpostorTeamV2()).Select(x => x.PlayerId)); + if (!NeutralsHearMessage.GetBool()) + ExceptList.AddRange(Main.AllPlayerControls.Where(x => x.GetCustomRole().IsNeutralTeamV2()).Select(x => x.PlayerId)); + + foreach (var par in Main.AllAlivePlayerControls.ExceptBy(ExceptList, x => x.PlayerId)) + { + SendMessage(determineMessage[id], par.PlayerId, title: ColorString(GetRoleColor(CustomRoles.Messenger), GetString("MessengerTitleTarget"))); + } + SendMessage(GetString("TelepathyConfirmSelf"), pc.PlayerId, ColorString(GetRoleColor(CustomRoles.Messenger), GetString("MessengerTitle"))); + + + return true; + + } +} \ No newline at end of file diff --git a/Roles/AddOns/Common/Oiiai.cs b/Roles/AddOns/Common/Oiiai.cs index 24e421051a..9a049032e7 100644 --- a/Roles/AddOns/Common/Oiiai.cs +++ b/Roles/AddOns/Common/Oiiai.cs @@ -36,7 +36,7 @@ public void SetupCustomOption() CanPassOn = BooleanOptionItem.Create(Id + 14, "OiiaiCanPassOn", true, TabGroup.Addons, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Oiiai]); ChangeNeutralRole = StringOptionItem.Create(Id + 15, "NeutralChangeRolesForOiiai", EnumHelper.GetAllNames(), 1, TabGroup.Addons, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Oiiai]); } - public static void Init() + public void Init() { playerIdList.Clear(); Eraser.ErasedRoleStorage.Clear(); diff --git a/Roles/AddOns/Common/Radar.cs b/Roles/AddOns/Common/Radar.cs index 1f9b36335b..3cd078726e 100644 --- a/Roles/AddOns/Common/Radar.cs +++ b/Roles/AddOns/Common/Radar.cs @@ -18,7 +18,7 @@ public void SetupCustomOption() SetupAdtRoleOptions(Id, CustomRoles.Radar, canSetNum: true, tab: TabGroup.Addons, teamSpawnOptions: true); } - public static void Init() + public void Init() { ClosestPlayer.Clear(); IsEnable = false; diff --git a/Roles/AddOns/Common/Rainbow.cs b/Roles/AddOns/Common/Rainbow.cs index 1c000efb86..cfc6b440c2 100644 --- a/Roles/AddOns/Common/Rainbow.cs +++ b/Roles/AddOns/Common/Rainbow.cs @@ -21,7 +21,7 @@ public void SetupCustomOption() ChangeInCamouflage = BooleanOptionItem.Create(Id + 14, "RainbowInCamouflage", true, TabGroup.Addons, false) .SetParent(CustomRoleSpawnChances[CustomRoles.Rainbow]); } - public static void Init() + public void Init() { LastColorChange = Utils.GetTimeStamp(); isEnabled = false; diff --git a/Roles/AddOns/Common/Rebirth.cs b/Roles/AddOns/Common/Rebirth.cs index d76422b80c..dc945d0ff3 100644 --- a/Roles/AddOns/Common/Rebirth.cs +++ b/Roles/AddOns/Common/Rebirth.cs @@ -21,7 +21,7 @@ public void SetupCustomOption() .SetValueFormat(OptionFormat.Times); OnlyVoted = BooleanOptionItem.Create(Id + 12, "RebirthCountVotes", false, TabGroup.Addons, false).SetParent(CustomRoleSpawnChances[CustomRoles.Rebirth]); } - public static void Init() + public void Init() { Rebirths.Clear(); VotedCount.Clear(); diff --git a/Roles/AddOns/Common/Sleuth.cs b/Roles/AddOns/Common/Sleuth.cs index 3a1caebd4b..afa4b39625 100644 --- a/Roles/AddOns/Common/Sleuth.cs +++ b/Roles/AddOns/Common/Sleuth.cs @@ -15,7 +15,7 @@ public void SetupCustomOption() SleuthCanKnowKillerRole = BooleanOptionItem.Create(Id + 13, "SleuthCanKnowKillerRole", true, TabGroup.Addons, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Sleuth]); } - public static void Init() + public void Init() { SleuthNotify = []; } diff --git a/Roles/AddOns/Common/Statue.cs b/Roles/AddOns/Common/Statue.cs index f3a75e9349..66d54309e2 100644 --- a/Roles/AddOns/Common/Statue.cs +++ b/Roles/AddOns/Common/Statue.cs @@ -24,7 +24,7 @@ public void SetupCustomOption() .SetValueFormat(OptionFormat.Times); } - public static void Init() + public void Init() { CountNearplr = []; tempSpeed = []; diff --git a/Roles/AddOns/Common/Tired.cs b/Roles/AddOns/Common/Tired.cs index c2064e6391..2838146050 100644 --- a/Roles/AddOns/Common/Tired.cs +++ b/Roles/AddOns/Common/Tired.cs @@ -26,7 +26,7 @@ public void SetupCustomOption() .SetValueFormat(OptionFormat.Seconds); } - public static void Init() + public void Init() { playerIdList = []; } diff --git a/Roles/AddOns/Crewmate/Ghoul.cs b/Roles/AddOns/Crewmate/Ghoul.cs index d86be8dadf..05de16aa0d 100644 --- a/Roles/AddOns/Crewmate/Ghoul.cs +++ b/Roles/AddOns/Crewmate/Ghoul.cs @@ -14,7 +14,7 @@ public void SetupCustomOption() SetupAdtRoleOptions(Id, CustomRoles.Ghoul, canSetNum: true, tab: TabGroup.Addons); } - public static void Init() + public void Init() { KillGhoul = []; IsEnable = false; diff --git a/Roles/AddOns/Crewmate/Workhorse.cs b/Roles/AddOns/Crewmate/Workhorse.cs index 635444d06b..3306760036 100644 --- a/Roles/AddOns/Crewmate/Workhorse.cs +++ b/Roles/AddOns/Crewmate/Workhorse.cs @@ -32,7 +32,7 @@ public void SetupCustomOption() .SetValueFormat(OptionFormat.Pieces); OptionSnitchCanBeWorkhorse = BooleanOptionItem.Create(Id + 14, "SnitchCanBeWorkhorse", false, TabGroup.Addons, false).SetParent(CustomRoleSpawnChances[CustomRoles.Workhorse]); } - public static void Init() + public void Init() { playerIdList.Clear(); IsEnable = false; diff --git a/Roles/AddOns/IAddon.cs b/Roles/AddOns/IAddon.cs index 0305acaac0..7647269ab8 100644 --- a/Roles/AddOns/IAddon.cs +++ b/Roles/AddOns/IAddon.cs @@ -17,6 +17,8 @@ public interface IAddon public AddonTypes Type { get; } public void SetupCustomOption(); + public void Init() + { } public void OnFixedUpdate(PlayerControl pc) { } public void OnFixedUpdateLowLoad(PlayerControl pc) diff --git a/Roles/AddOns/Impostor/Clumsy.cs b/Roles/AddOns/Impostor/Clumsy.cs index 1cb50bb9cd..fa2b479dea 100644 --- a/Roles/AddOns/Impostor/Clumsy.cs +++ b/Roles/AddOns/Impostor/Clumsy.cs @@ -19,7 +19,7 @@ public void SetupCustomOption() .SetValueFormat(OptionFormat.Percent); } - public static void Init() + public void Init() { HasMissed = []; } diff --git a/Roles/Core/AssignManager/RoleAssign.cs b/Roles/Core/AssignManager/RoleAssign.cs index 395e3d0b39..72c603eabf 100644 --- a/Roles/Core/AssignManager/RoleAssign.cs +++ b/Roles/Core/AssignManager/RoleAssign.cs @@ -156,7 +156,7 @@ public static void StartSelect() // DistinctBy - Removes duplicate roles if there are any // Shuffle - Shuffles all roles in the list into a randomized order // Take - Takes the first x roles of the list ... x is the maximum number of roles we could need of that team - + Roles[RoleAssignType.Impostor] = Roles[RoleAssignType.Impostor].Shuffle(rd).Take(optImpNum).ToList(); Roles[RoleAssignType.NeutralKilling] = Roles[RoleAssignType.NeutralKilling].Shuffle(rd).Take(optNeutralKillingNum).ToList(); Roles[RoleAssignType.NeutralApocalypse] = Roles[RoleAssignType.NeutralApocalypse].Shuffle(rd).Take(optNeutralApocalypseNum).ToList(); diff --git a/Roles/Crewmate/Medium.cs b/Roles/Crewmate/Medium.cs index 5b608d99f9..21a6a340e1 100644 --- a/Roles/Crewmate/Medium.cs +++ b/Roles/Crewmate/Medium.cs @@ -7,6 +7,7 @@ using static TOHE.Translator; using static TOHE.MeetingHudStartPatch; using InnerNet; +using TOHE.Roles._Ghosts_.Crewmate; namespace TOHE.Roles.Crewmate; diff --git a/main.cs b/main.cs index 2510b14d0b..61485973ca 100644 --- a/main.cs +++ b/main.cs @@ -155,6 +155,10 @@ public class Main : BasePlugin public static readonly HashSet PlayersDiedInMeeting = []; public static readonly Dictionary AllKillers = []; public static readonly Dictionary OvverideOutfit = []; + public static readonly Dictionary LastKillerRoom = []; + public static readonly Dictionary PlayerKilledBy = []; + public static Custom_Team? RememberTeamOfDeadBodyKiller; + public static string RememberRoleOfDeadBodyKiller = ""; public static readonly Dictionary CheckShapeshift = []; public static readonly Dictionary ShapeshiftTarget = []; @@ -703,6 +707,7 @@ public enum CustomRoles Ghastly, Hawk, Warden, + Telepathy, //Crewmate Addict, @@ -920,6 +925,7 @@ public enum CustomRoles Reach, Rebound, Spurt, + Messenger, Recruit, Seer, Silent,