From: Eisbison Date: Sun, 14 Mar 2021 17:49:44 +0000 (+0100) Subject: Add version v1.5 changes X-Git-Url: https://git.deb.at/?a=commitdiff_plain;h=46328fc162cfe3eaed74d980e7e1d40c794b2cc5;p=rhonda%2Ftheotherroles.git Add version v1.5 changes --- diff --git a/README.md b/README.md index ce49c1c..baa1cbc 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,20 @@ Even more roles are coming soon :) # Releases : | Among Us - Version| Mod Version | Link | |----------|-------------|-----------------| -| **2021.3.5s** | v1.4 | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v1.4/TheOtherRoles.zip) -| **2021.3.5s** | v1.3 | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v1.3/TheOtherRoles.zip) +| 2021.3.5s | v1.5 | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v1.5/TheOtherRoles.zip) +| 2021.3.5s | v1.4 | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v1.4/TheOtherRoles.zip) +| 2021.3.5s | v1.3 | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v1.3/TheOtherRoles.zip) | 2020.12.19s | v1.1 | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v1.1/TheOtherRoles.zip) | 2020.12.19s | v1.0 | [Download](https://github.com/Eisbison/TheOtherRoles/files/6097191/TheOtherRoles.zip) | \ +**Changes in v1.5:** +- Time Master - Buff: He is not affected by his rewind anymore, which gives him more utility. Players will now be rewinded out of vents. +- Child - Nerf: The child now grows up (see [child](#child)) and becomes a normal crewmate at some point. A growing child is not killable anymore. Some tasks are still not doable for the small child, we are working on that. But eventually when growing up it can do all the tasks as it's size increases. +- Seer - Nerf: Added an option that sets how often the Seer mistakes the player for another. +- Spy - Nerf: The Spy now only sees the additional information when he activates his "Spy mode". That should stops the spy from camping the admin table/vitals. +- Other: Camouflager/Morphling cooldowns were fixed. Custom regions code was removed to enable 3rd party tools. Some minor bugfixes. + **Changes in v1.4:** - Fixing a Camoflager/Morphling animation bug - Fixing a bug where the Swapper could swap votes even if they was dead @@ -59,7 +67,7 @@ Even more roles are coming soon :) - A bug was removed where the game of the Lovers crashed if they were the last players killed by the host of the lobby. # Installation -1. Download and unzip the newest [release](https://github.com/Eisbison/TheOtherRoles/releases/download/v1.4/TheOtherRoles.zip) +1. Download and unzip the newest [release](https://github.com/Eisbison/TheOtherRoles/releases/download/v1.5/TheOtherRoles.zip) 2. Find the folder of your game, for steams players you can right click in steam, on the game, a menu will appear proposing you to go to the folders.3. Make a copy of your game, it's not obligatory but advise, put it where you want. 4. Drag or extract the files from the zip into your game, at the .exe level. 6. Run the game (the first launch might take a while) @@ -68,6 +76,10 @@ Not working? You might want to install the dependency [vc_redist](https://aka.ms ![Install](https://i.imgur.com/pvBAyZN.png) +# Custom Servers +We highly recommend you to play on custom servers rather than on the official ones. A guide on how to set up a server will be added soon. In order to make your client able to connect to a custom server we recommend using [Unify](https://github.com/moltenmods/unify). Just add +the *.dll* file into your Among Us/BepInEx/plugins folder and you're good to go. + # Credits & Resources [Reactor](https://github.com/NuclearPowered/Reactor) - The framework used\ [BepInEx](https://github.com/BepInEx) - Used to hook game functions\ @@ -190,6 +202,8 @@ The Seer has the ability to find out about people's intentions. The information Depending on the selected option they either directly see the role of the player (appended to their name) or if the player is a good (name text changes to yellow) or a bad (name text changes to a darker color) player.\ Depending on the selected option the player will get a blue flash on their screen if they are being revealed by the Seer. +The Seer can make mistakes (how many depends on the selected option). If a mistake happens, the information of another player (not the target, not the Seer) gets revealed. + \ **NOTE** - **Good players:** Crewmates with all their special roles\ @@ -204,6 +218,7 @@ Depending on the selected option the player will get a blue flash on their scree |----------|:-------------:|:-------------:| | Seer Spawn Chance | - | - | Seer Cooldown | Cooldown for his special ability. This cooldown will **not** be reset after a meeting | - +| Seer Chance Of Seeing Right | Sets the chance of how likely the Seer makes a mistake | - | Info That Seer Reveals | Description above | "Role", "Good/Bad" | Players That See When They Are Being Revealed | Selects the group of players that get notified when they are being revealed | "Everyone", "The Good", "The Bad", "Nobody" ----------------------- @@ -212,6 +227,8 @@ Depending on the selected option the player will get a blue flash on their scree ### **Team: Crewmates** The Engineer (if alive) can fix one sabotage per game from anywhere on the map.\ The Engineer can use vents. +Because of the vents the Engineer might not be able to start some tasks using the "Use" button, +you can double click on the tasks instead. \ **NOTE** - The kill button of Impostors activates if they stand next to a vent where the Engineer is. They can also kill them there. No other action (e.g. Morphling sample, Shifter shift, ...) can affect players inside vents. @@ -253,16 +270,20 @@ Depending on the options the Lighter can have a bigger or smaller vision than ot ## Child ### **Team: Crewmates** -The Child won't be harmed by anyone, since if it dies everyone besides the child will lose the game.\ +The Child won't be harmed by anyone until it turns 18 years old, then it becomes a regular crewmate.\ The Child's character is smaller and hence visible to everyone in the game.\ -The child works with the crewmate and aims to play out the strength of its "invincibility".\ -It does **not** want to win the game as a dead child and the impostors **don't want** to deny -a crewmate win by killing a child (changes will come to prevent this behaviour). +The child works with the crewmates and aims to play out the strength of its "invincibility" in the early game.\ +If the child is voted out before it turns 18, everyone in the lobby loses. +\ +**NOTE** +- Impostors can't kill the child (the button does not work) until it turns 18 +- The Sheriff always suicides if he tries to kill the child ### Game Options | Name | Description | |----------|:-------------:| | Child Spawn Chance | - +| Child | Child Growing Up Duration ----------------------- ## Medic @@ -303,7 +324,8 @@ The Mayor leades the crewmates by having a vote that counts twice. ## Spy ### **Team: Crewmates** -The Spy gets more information than others from the admin table and vitals. \ +If the Spy activates the "Spy mode", the Spy gets more information than others from the admin table and vitals for a set duration.\ +Otherwise he sees the same information as everyone else. **Admin table:** The spy can see the colors of the players on the table.\ **Vitals**: The spy can see how long dead players have been dead for.\ \ @@ -313,6 +335,8 @@ The Spy gets more information than others from the admin table and vitals. \ | Name | Description | |----------|:-------------:| | Spy Spawn Chance | - +| Spy Cooldown| - +| Spy Duration | Sets how long the "Spy mode" remains active ----------------------- @@ -338,7 +362,7 @@ can only use them, if the player they exchanged roles with have not used them be ## Time Master ### **Team: Crewmates** -The Time Master can rewind the time.\ +The Time Master can rewind the time. The Time Master won't be affected by the rewind.\ If set in the options, players will be revived during the time rewind.\ Only movement and kills are affected (not cooldowns, meetings, tasks, ...) diff --git a/Source Code/.gitignore b/Source Code/.gitignore new file mode 100644 index 0000000..8d4a6c0 --- /dev/null +++ b/Source Code/.gitignore @@ -0,0 +1,2 @@ +bin +obj \ No newline at end of file diff --git a/Source Code/BonusRoles.cs b/Source Code/BonusRoles.cs index fe6f1d7..8684017 100644 --- a/Source Code/BonusRoles.cs +++ b/Source Code/BonusRoles.cs @@ -19,7 +19,7 @@ namespace BonusRoles [HarmonyPatch] public static class BonusRoles { - public static System.Random rnd = new System.Random(); + public static System.Random rnd = new System.Random((int)DateTime.Now.Ticks); public static void clearAndReloadRoles() { Jester.clearAndReload(); @@ -297,11 +297,12 @@ namespace BonusRoles public static class Seer { public static PlayerControl seer; public static Color color = new Color(60f / 255f, 181f / 255f, 100f / 255f, 1); - public static List revealedPlayers = new List(); + public static Dictionary revealedPlayers = new Dictionary(); public static float cooldown = float.MaxValue; public static int kindOfInfo = 0; public static int playersWithNotification = 0; + public static float chanceOfSeeingRight = 100; public static PlayerControl currentTarget; @@ -314,10 +315,11 @@ namespace BonusRoles public static void clearAndReload() { seer = null; - revealedPlayers = new List(); + revealedPlayers = new Dictionary(); cooldown = BonusRolesPlugin.seerCooldown.GetValue(); kindOfInfo = BonusRolesPlugin.seerKindOfInfo.GetValue(); playersWithNotification = BonusRolesPlugin.seerPlayersWithNotification.GetValue(); + chanceOfSeeingRight = BonusRolesPlugin.seerChanceOfSeeingRight.GetValue(); } } @@ -383,23 +385,54 @@ namespace BonusRoles private static Sprite adminTableIcon; public static Color color = new Color(252f / 255f, 90f / 255f, 30f / 255f, 1); + public static float cooldown = float.MaxValue; + public static float duration = 10f; + + public static float spyTimer = 0f; + public static Sprite getAdminTableIconSprite() { if (adminTableIcon) return adminTableIcon; adminTableIcon = Helpers.loadSpriteFromResources("BonusRoles.Resources.AdminTableIcon.png", 350f); return adminTableIcon; } + private static Sprite buttonSprite; + public static Sprite getButtonSprite() { + if (buttonSprite) return buttonSprite; + buttonSprite = Helpers.loadSpriteFromResources("BonusRoles.Resources.SpyButton.png", 100f); + return buttonSprite; + } + public static void clearAndReload() { spy = null; + spyTimer = 0f; + cooldown = BonusRolesPlugin.spyCooldown.GetValue(); + duration = BonusRolesPlugin.spySpyingDuration.GetValue(); } } public static class Child { public static PlayerControl child; public static Color color = Color.white; + + public static float growingUpDuration = float.MaxValue; + public static DateTime timeOfGrowthStart = DateTime.UtcNow; public static void clearAndReload() { child = null; + growingUpDuration = BonusRolesPlugin.childGrowingUpDuration.GetValue(); + timeOfGrowthStart = DateTime.UtcNow; + } + + public static float growingProgress() { + if (timeOfGrowthStart == null) return 0f; + + float timeSinceStart = (float)(DateTime.UtcNow - timeOfGrowthStart).TotalMilliseconds; + return Mathf.Clamp(timeSinceStart/(growingUpDuration*1000), 0f, 1f); + } + + public static bool isGrownUp() { + return growingProgress() == 1f; } } } \ No newline at end of file diff --git a/Source Code/BonusRoles.csproj b/Source Code/BonusRoles.csproj index 3df26ef..803c675 100644 --- a/Source Code/BonusRoles.csproj +++ b/Source Code/BonusRoles.csproj @@ -4,7 +4,7 @@ 1.0.0 NuclearPowered/Mappings:0.2.0 - BonusRoles + TheOtherRoles Eisbison @@ -40,6 +40,7 @@ + diff --git a/Source Code/Buttons.cs b/Source Code/Buttons.cs index 2fe81fd..55d2620 100644 --- a/Source Code/Buttons.cs +++ b/Source Code/Buttons.cs @@ -7,6 +7,8 @@ using UnityEngine; using static BonusRoles.BonusRoles; using Reactor.Extensions; using System.Collections.Generic; +using System.Linq; +using UnhollowerBaseLib; namespace BonusRoles { @@ -22,6 +24,7 @@ namespace BonusRoles private static CustomButton seerRevealButton; private static CustomButton morphlingButton; private static CustomButton camouflagerButton; + private static CustomButton spyButton; public static void setCustomButtonCooldowns() { engineerRepairButton.MaxTimer = 0f; @@ -33,6 +36,9 @@ namespace BonusRoles seerRevealButton.MaxTimer = Seer.cooldown; morphlingButton.MaxTimer = Morphling.cooldown; camouflagerButton.MaxTimer = Camouflager.cooldown; + spyButton.MaxTimer = Spy.cooldown; + + spyButton.EffectDuration = Spy.duration; } public static void Postfix(HudManager __instance) @@ -64,7 +70,7 @@ namespace BonusRoles } } }, - () => { return Engineer.engineer != null && Engineer.engineer == PlayerControl.LocalPlayer && __instance.UseButton.isActiveAndEnabled && !PlayerControl.LocalPlayer.Data.IsDead; }, + () => { return Engineer.engineer != null && Engineer.engineer == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { bool sabotageActive = false; foreach (PlayerTask task in PlayerControl.LocalPlayer.myTasks) @@ -72,7 +78,7 @@ namespace BonusRoles sabotageActive = true; return sabotageActive && !Engineer.usedRepair && PlayerControl.LocalPlayer.CanMove; }, - false, + () => {}, Engineer.getButtonSprite(), Vector3.zero, __instance @@ -105,9 +111,9 @@ namespace BonusRoles } } }, - () => { return Janitor.janitor != null && Janitor.janitor == PlayerControl.LocalPlayer && __instance.UseButton.isActiveAndEnabled && !PlayerControl.LocalPlayer.Data.IsDead; }, + () => { return Janitor.janitor != null && Janitor.janitor == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { return __instance.ReportButton.renderer.color == Palette.EnabledColor && PlayerControl.LocalPlayer.CanMove; }, - true, + () => { janitorCleanButton.Timer = janitorCleanButton.MaxTimer;}, Janitor.getButtonSprite(), Vector3.zero, __instance @@ -138,9 +144,9 @@ namespace BonusRoles sheriffKillButton.Timer = sheriffKillButton.MaxTimer; Sheriff.currentTarget = null; }, - () => { return Sheriff.sheriff != null && Sheriff.sheriff == PlayerControl.LocalPlayer && __instance.UseButton.isActiveAndEnabled && !PlayerControl.LocalPlayer.Data.IsDead; }, + () => { return Sheriff.sheriff != null && Sheriff.sheriff == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { return Sheriff.currentTarget && PlayerControl.LocalPlayer.CanMove; }, - true, + () => { sheriffKillButton.Timer = sheriffKillButton.MaxTimer;}, __instance.KillButton.renderer.sprite, Vector3.zero, __instance @@ -154,9 +160,9 @@ namespace BonusRoles RPCProcedure.timeMasterRewindTime(); timeMasterRewindTimeButton.Timer = timeMasterRewindTimeButton.MaxTimer; }, - () => { return TimeMaster.timeMaster != null && TimeMaster.timeMaster == PlayerControl.LocalPlayer && __instance.UseButton.isActiveAndEnabled && !PlayerControl.LocalPlayer.Data.IsDead; }, + () => { return TimeMaster.timeMaster != null && TimeMaster.timeMaster == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { return PlayerControl.LocalPlayer.CanMove; }, - true, + () => { timeMasterRewindTimeButton.Timer = timeMasterRewindTimeButton.MaxTimer;}, TimeMaster.getButtonSprite(), Vector3.zero, __instance @@ -172,9 +178,9 @@ namespace BonusRoles AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.medicSetShielded(Medic.currentTarget.PlayerId); }, - () => { return Medic.medic != null && Medic.medic == PlayerControl.LocalPlayer && __instance.UseButton.isActiveAndEnabled && !PlayerControl.LocalPlayer.Data.IsDead; }, + () => { return Medic.medic != null && Medic.medic == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { return !Medic.usedShield && Medic.currentTarget && PlayerControl.LocalPlayer.CanMove; }, - false, + () => {}, Medic.getButtonSprite(), Vector3.zero, __instance @@ -192,9 +198,9 @@ namespace BonusRoles RPCProcedure.shifterShift(Shifter.currentTarget.PlayerId); }, - () => { return Shifter.shifter != null && Shifter.shifter == PlayerControl.LocalPlayer && __instance.UseButton.isActiveAndEnabled && !PlayerControl.LocalPlayer.Data.IsDead; }, + () => { return Shifter.shifter != null && Shifter.shifter == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { return Shifter.currentTarget && PlayerControl.LocalPlayer.CanMove; }, - true, + () => { shifterShiftButton.Timer = shifterShiftButton.MaxTimer; }, Shifter.getButtonSprite(), Vector3.zero, __instance @@ -205,15 +211,26 @@ namespace BonusRoles () => { seerRevealButton.Timer = seerRevealButton.MaxTimer; + PlayerControl targetOrMistake = Seer.currentTarget; + if (rnd.Next(1, 101) > Seer.chanceOfSeeingRight) { + var players = PlayerControl.AllPlayerControls.ToArray().ToList(); + players.RemoveAll(p => p != null && (p.PlayerId == Seer.seer.PlayerId || p.PlayerId == Seer.currentTarget.PlayerId)); + int index = rnd.Next(0, players.Count); + + if (players.Count != 0 && players[index] != null) + targetOrMistake = players[index]; + } + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SeerReveal, Hazel.SendOption.None, -1); writer.Write(Seer.currentTarget.PlayerId); + writer.Write(targetOrMistake.PlayerId); AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.seerReveal(Seer.currentTarget.PlayerId); + RPCProcedure.seerReveal(Seer.currentTarget.PlayerId, targetOrMistake.PlayerId); }, - () => { return Seer.seer != null && Seer.seer == PlayerControl.LocalPlayer && __instance.UseButton.isActiveAndEnabled && !PlayerControl.LocalPlayer.Data.IsDead; }, + () => { return Seer.seer != null && Seer.seer == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { return Seer.currentTarget && PlayerControl.LocalPlayer.CanMove; }, - false, + () => {}, Seer.getButtonSprite(), Vector3.zero, __instance @@ -235,9 +252,15 @@ namespace BonusRoles morphlingButton.HasEffect = false; // Block effect on this click } }, - () => { return Morphling.morphling != null && Morphling.morphling == PlayerControl.LocalPlayer && __instance.UseButton.isActiveAndEnabled && !PlayerControl.LocalPlayer.Data.IsDead; }, + () => { return Morphling.morphling != null && Morphling.morphling == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { return (Morphling.currentTarget || Morphling.sampledTarget) && PlayerControl.LocalPlayer.CanMove; }, - true, + () => { + morphlingButton.Timer = morphlingButton.MaxTimer; + morphlingButton.Sprite = Morphling.getSampleSprite(); + morphlingButton.isEffectActive = false; + morphlingButton.killButtonManager.TimerText.Color = Palette.EnabledColor; + Morphling.sampledTarget = null; + }, Morphling.getSampleSprite(), new Vector3(0f, 1.3f, 0f), __instance, @@ -256,9 +279,13 @@ namespace BonusRoles AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.camouflagerCamouflage(); }, - () => { return Camouflager.camouflager != null && Camouflager.camouflager == PlayerControl.LocalPlayer && __instance.UseButton.isActiveAndEnabled && !PlayerControl.LocalPlayer.Data.IsDead; }, + () => { return Camouflager.camouflager != null && Camouflager.camouflager == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { return PlayerControl.LocalPlayer.CanMove; }, - true, + () => { + camouflagerButton.Timer = camouflagerButton.MaxTimer; + camouflagerButton.isEffectActive = false; + camouflagerButton.killButtonManager.TimerText.Color = Palette.EnabledColor; + }, Camouflager.getButtonSprite(), new Vector3(0f, 1.3f, 0f), __instance, @@ -266,6 +293,28 @@ namespace BonusRoles 10f, () => { camouflagerButton.Timer = camouflagerButton.MaxTimer; } ); + + // Spy button + spyButton = new CustomButton( + () => { + Spy.spyTimer = Spy.duration; + }, + () => { return Spy.spy != null && Spy.spy == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, + () => { return PlayerControl.LocalPlayer.CanMove; }, + () => { + spyButton.Timer = spyButton.MaxTimer; + spyButton.isEffectActive = false; + spyButton.killButtonManager.TimerText.Color = Palette.EnabledColor; + }, + Spy.getButtonSprite(), + Vector3.zero, + __instance, + true, + 0f, + () => { + spyButton.Timer = spyButton.MaxTimer; + } + ); } } } \ No newline at end of file diff --git a/Source Code/CredentialsPatch.cs b/Source Code/CredentialsPatch.cs index c2a8c0a..e104d9e 100644 --- a/Source Code/CredentialsPatch.cs +++ b/Source Code/CredentialsPatch.cs @@ -13,9 +13,9 @@ namespace BonusRoles static void Postfix(VersionShower __instance) { string spacer = new String('\n', 15); if (__instance.text.Text.Contains(spacer)) - __instance.text.Text += "\n[FFFFFFFF]- Loaded [FCCE03FF]TheOtherRoles[FFFFFFFF] v1.4\n by [FCCE03FF]Eisbison"; + __instance.text.Text += "\n[FFFFFFFF]- Loaded [FCCE03FF]TheOtherRoles[FFFFFFFF] v1.5\n by [FCCE03FF]Eisbison"; else - __instance.text.Text += spacer + "[FFFFFFFF]- Loaded [FCCE03FF]TheOtherRoles[FFFFFFFF] v1.4\n by [FCCE03FF]Eisbison"; + __instance.text.Text += spacer + "[FFFFFFFF]- Loaded [FCCE03FF]TheOtherRoles[FFFFFFFF] v1.5\n by [FCCE03FF]Eisbison"; } } diff --git a/Source Code/CustomButton.cs b/Source Code/CustomButton.cs index e89bf31..0f3aae1 100644 --- a/Source Code/CustomButton.cs +++ b/Source Code/CustomButton.cs @@ -13,25 +13,25 @@ public class CustomButton public Vector3 PositionOffset; public float MaxTimer = float.MaxValue; public float Timer = 0f; - public bool ResetTimeAfterMeeting; private Action OnClick; + private Action OnMeetingEnds; private Func HasButton; private Func CouldUse; private Action OnEffectEnds; public bool HasEffect; - private bool isEffectActive = false; - private float EffectDuration; + public bool isEffectActive = false; + public float EffectDuration; public Sprite Sprite; private HudManager hudManager; - public CustomButton(Action OnClick, Func HasButton, Func CouldUse, bool ResetTimeAfterMeeting, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager, bool HasEffect, float EffectDuration, Action OnEffectEnds) + public CustomButton(Action OnClick, Func HasButton, Func CouldUse, Action OnMeetingEnds, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager, bool HasEffect, float EffectDuration, Action OnEffectEnds) { this.hudManager = hudManager; this.OnClick = OnClick; this.HasButton = HasButton; this.CouldUse = CouldUse; this.PositionOffset = PositionOffset; - this.ResetTimeAfterMeeting = ResetTimeAfterMeeting; + this.OnMeetingEnds = OnMeetingEnds; this.HasEffect = HasEffect; this.EffectDuration = EffectDuration; this.OnEffectEnds = OnEffectEnds; @@ -60,13 +60,13 @@ public class CustomButton setActive(false); } - public CustomButton(Action OnClick, Func HasButton, Func CouldUse, bool ResetTimeAfterMeeting, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager) - : this(OnClick, HasButton, CouldUse, ResetTimeAfterMeeting, Sprite, PositionOffset, hudManager, false, 0f, () => {}) { } - + public CustomButton(Action OnClick, Func HasButton, Func CouldUse, Action OnMeetingEnds, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager) + : this(OnClick, HasButton, CouldUse, OnMeetingEnds, Sprite, PositionOffset, hudManager, false, 0f, () => {}) { } public static void HudUpdate() { buttons.RemoveAll(item => item.killButtonManager == null); + if (MeetingHud.Instance || ExileController.Instance) return; for (int i = 0; i < buttons.Count; i++) @@ -88,10 +88,8 @@ public class CustomButton { try { - if (buttons[i].ResetTimeAfterMeeting) { - buttons[i].Timer = buttons[i].MaxTimer; - buttons[i].Update(); - } + buttons[i].OnMeetingEnds(); + buttons[i].Update(); } catch (NullReferenceException) { @@ -131,7 +129,7 @@ public class CustomButton setActive(false); return; } - setActive(true); + setActive(hudManager.UseButton.isActiveAndEnabled); killButtonManager.renderer.sprite = Sprite; diff --git a/Source Code/EndGamePatch.cs b/Source Code/EndGamePatch.cs index 499d028..0ad3017 100644 --- a/Source Code/EndGamePatch.cs +++ b/Source Code/EndGamePatch.cs @@ -17,7 +17,7 @@ namespace BonusRoles { LoversTeamWin, LoversSoloWin, JesterWin, - ChildWin + ChildDied } static class AdditionalTempData { @@ -58,10 +58,11 @@ namespace BonusRoles { // Child win condition (should be implemented using a proper GameOverReason in the future) if (Child.child != null && Child.child.Data.IsImpostor) { - AdditionalTempData.winCondition = WinCondition.ChildWin; + AdditionalTempData.winCondition = WinCondition.ChildDied; TempData.winners = new Il2CppSystem.Collections.Generic.List(); WinningPlayerData wpd = new WinningPlayerData(Child.child.Data); - wpd.IsImpostor = false; + wpd.IsImpostor = false; + wpd.IsYou = false; TempData.winners.Add(wpd); } @@ -105,8 +106,8 @@ namespace BonusRoles { TextRenderer textRenderer = bonusText.GetComponent(); textRenderer.Text = ""; - if (AdditionalTempData.winCondition == WinCondition.ChildWin) { - textRenderer.Text = "Child Wins"; + if (AdditionalTempData.winCondition == WinCondition.ChildDied) { + textRenderer.Text = "Child Died"; textRenderer.Color = Child.color; } else if (AdditionalTempData.winCondition == WinCondition.JesterWin) { diff --git a/Source Code/GameHistory.cs b/Source Code/GameHistory.cs index 20c7552..933e195 100644 --- a/Source Code/GameHistory.cs +++ b/Source Code/GameHistory.cs @@ -23,10 +23,12 @@ namespace BonusRoles{ static class GameHistory { public static List> localPlayerPositions = new List>(); public static List deadPlayers = new List(); + public static DateTime localVentEnterTimePoint = DateTime.MinValue; public static void clearGameHistory() { localPlayerPositions = new List>(); deadPlayers = new List(); + localVentEnterTimePoint = DateTime.MinValue; } } } \ No newline at end of file diff --git a/Source Code/IntroPatch.cs b/Source Code/IntroPatch.cs index 653cae0..451b71a 100644 --- a/Source Code/IntroPatch.cs +++ b/Source Code/IntroPatch.cs @@ -164,7 +164,7 @@ namespace BonusRoles { __instance.__this.Title.Text = "Child"; __instance.__this.Title.Color = Child.color; - __instance.__this.ImpostorText.Text = "No one will harm you"; + __instance.__this.ImpostorText.Text = "No one will harm you until you grow up"; __instance.__this.BackgroundBar.material.color = Child.color; } } diff --git a/Source Code/Main.cs b/Source Code/Main.cs index 01e1710..158d00a 100644 --- a/Source Code/Main.cs +++ b/Source Code/Main.cs @@ -26,9 +26,9 @@ namespace BonusRoles // Role spawn chances public static CustomNumberOption mafiaSpawnChance = CustomOption.AddNumber("Mafia Spawn Chance", 100, 0, 100, 10); + public static CustomNumberOption loversSpawnChance = CustomOption.AddNumber("Lovers Spawn Chance", 100, 0, 100, 10); public static CustomNumberOption morphlingSpawnChance = CustomOption.AddNumber("Morphling Spawn Chance", 100, 0, 100, 10); public static CustomNumberOption camouflagerSpawnChance = CustomOption.AddNumber("Camouflager Spawn Chance", 100, 0, 100, 10); - public static CustomNumberOption loversSpawnChance = CustomOption.AddNumber("Lovers Spawn Chance", 100, 0, 100, 10); public static CustomNumberOption jesterSpawnChance = CustomOption.AddNumber("Jester Spawn Chance", 100, 0, 100, 10); public static CustomNumberOption mayorSpawnChance = CustomOption.AddNumber("Mayor Spawn Chance", 100, 0, 100, 10); public static CustomNumberOption engineerSpawnChance = CustomOption.AddNumber("Engineer Spawn Chance", 100, 0, 100, 10); @@ -63,64 +63,26 @@ namespace BonusRoles public static CustomToggleOption medicShowAttemptToShielded = CustomOption.AddToggle("Shielded Player Sees Murder Attempt", false); public static CustomNumberOption shifterCooldown = CustomOption.AddNumber("Shifter Cooldown", 30f, 10f, 60f, 2.5f); public static CustomNumberOption seerCooldown = CustomOption.AddNumber("Seer Cooldown (No Reset After Meeting)", 15f, 30f, 180f, 15f); + public static CustomNumberOption seerChanceOfSeeingRight = CustomOption.AddNumber("Seer Chance Of Seeing Right", 100, 0, 100, 5); public static CustomStringOption seerKindOfInfo = CustomOption.AddString("Info That Seer Reveals", new string[] {"Role", "Good/Bad"}); - public static CustomStringOption seerPlayersWithNotification = CustomOption.AddString("Players That See When They Are Being Revealed", new string[] {"Everyone", "The Good", "The Bad", "Nobody"}); + public static CustomStringOption seerPlayersWithNotification = CustomOption.AddString("Players That See When They Are Being Revealed", new string[] {"Everyone", "The Good", "The Bad", "Nobody"}); + public static CustomNumberOption spyCooldown = CustomOption.AddNumber("Spy Cooldown", 30f, 10f, 120f, 5f); + public static CustomNumberOption spySpyingDuration = CustomOption.AddNumber("Spy Duration", 10f, 2.5f, 60f, 2.5f); + public static CustomNumberOption childGrowingUpDuration = CustomOption.AddNumber("Child Growing Up Duration", 400f, 100f, 1500f, 100f); + + public static ConfigEntry DebugMode { get; private set; } + - public static ConfigEntry Ip { get; set; } - public static ConfigEntry Port { get; set; } - public static ConfigEntry Name { get; private set; } public override void Load() { - Ip = Config.Bind("Custom", "Ipv4 or Hostname", "127.0.0.1"); - Port = Config.Bind("Custom", "Port", (ushort)22023); - Name = Config.Bind("Custom", "Custom", "Custom"); - - var defaultRegions = ServerManager.DefaultRegions.ToList(); - var ip = Ip.Value; - if (Uri.CheckHostName(Ip.Value).ToString() == "Dns") - { - try - { - foreach (IPAddress address in Dns.GetHostAddresses(Ip.Value)) - { - if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) - { - ip = address.ToString(); break; - } - } - } - catch { } - } - - var port = Port.Value; - defaultRegions.Insert(0, new StaticRegionInfo( - Name.Value, (StringNames)3000, ip, new[] - { - new ServerInfo($"CustomServer", ip, Port.Value) - }).Duplicate() - ) ; - ServerManager.DefaultRegions = defaultRegions.ToArray(); + DebugMode = Config.Bind("Custom", "Enable Debug Mode", false); CustomOption.ShamelessPlug = false; Harmony.PatchAll(); } } - // Patch server name - [HarmonyPatch(typeof(LanguageUnit),"HOGGPANBLGP")] - public class ServerNamePatch { - public static bool Prefix(StringNames LENBLADGELB, string NNLLKLDPBAI, Il2CppReferenceArray DKBJCINDDCD, ref string __result) - { - if (LENBLADGELB == (StringNames)3000) - { - __result = BonusRolesPlugin.Name.Value; - return false; - } - return true; - } - } - // Deactivate bans, since I always leave my local testing game and ban myself [HarmonyPatch(typeof(StatsManager), nameof(StatsManager.AmBanned), MethodType.Getter)] public static class AmBannedPatch @@ -130,4 +92,52 @@ namespace BonusRoles __result = false; } } + + // Debugging tools + [HarmonyPatch(typeof(KeyboardJoystick), nameof(KeyboardJoystick.Update))] + public static class DebugManager + { + private static readonly System.Random random = new System.Random((int)DateTime.Now.Ticks); + private static List bots = new List(); + + public static void Postfix(KeyboardJoystick __instance) + { + if (!BonusRolesPlugin.DebugMode.Value) return; + + // Spawn dummys + if (Input.GetKeyDown(KeyCode.F)) { + var playerControl = UnityEngine.Object.Instantiate(AmongUsClient.Instance.PlayerPrefab); + var i = playerControl.PlayerId = (byte) GameData.Instance.GetAvailableId(); + + bots.Add(playerControl); + GameData.Instance.AddPlayer(playerControl); + AmongUsClient.Instance.Spawn(playerControl, -2, SpawnFlags.None); + + playerControl.transform.position = PlayerControl.LocalPlayer.transform.position; + playerControl.GetComponent().enabled = true; + playerControl.NetTransform.enabled = false; + playerControl.SetName(RandomString(10)); + playerControl.SetColor((byte) random.Next(Palette.PlayerColors.Length)); + playerControl.SetHat((uint) random.Next(HatManager.Instance.AllHats.Count),playerControl.Data.ColorId); + playerControl.SetPet((uint) random.Next(HatManager.Instance.AllPets.Count)); + playerControl.SetSkin((uint) random.Next(HatManager.Instance.AllSkins.Count)); + GameData.Instance.RpcSetTasks(playerControl.PlayerId, new byte[0]); + } + + // Terminate round + if(Input.GetKeyDown(KeyCode.L)) { + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ForceEnd, Hazel.SendOption.None, -1); + AmongUsClient.Instance.FinishRpcImmediately(writer); + RPCProcedure.forceEnd(); + } + } + + public static string RandomString(int length) + { + const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + return new string(Enumerable.Repeat(chars, length) + .Select(s => s[random.Next(s.Length)]).ToArray()); + } + } + } diff --git a/Source Code/MeetingPatch.cs b/Source Code/MeetingPatch.cs index f03e676..aa6f565 100644 --- a/Source Code/MeetingPatch.cs +++ b/Source Code/MeetingPatch.cs @@ -278,7 +278,7 @@ namespace BonusRoles } // Child win condition - if (Child.child != null && ExileController.Instance.exiled != null && ExileController.Instance.exiled.PlayerId == Child.child.PlayerId) { + if (Child.child != null && ExileController.Instance.exiled != null && ExileController.Instance.exiled.PlayerId == Child.child.PlayerId && !Child.isGrownUp()) { MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ChildDied, Hazel.SendOption.None, -1); AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.childDied(); diff --git a/Source Code/PlayerControlPatch.cs b/Source Code/PlayerControlPatch.cs index 5382fcf..010d591 100644 --- a/Source Code/PlayerControlPatch.cs +++ b/Source Code/PlayerControlPatch.cs @@ -18,7 +18,8 @@ namespace BonusRoles { if (localPlayerPositions.Count > 0) { // Set position var next = localPlayerPositions[0]; - PlayerControl.LocalPlayer.transform.position = next.Item1; + if (!PlayerControl.LocalPlayer.inVent) + PlayerControl.LocalPlayer.transform.position = next.Item1; localPlayerPositions.RemoveAt(0); if (localPlayerPositions.Count > 0) localPlayerPositions.RemoveAt(0); // Skip every second position to rewinde in half the time @@ -95,7 +96,7 @@ namespace BonusRoles { static void seerSetTarget() { if (Seer.seer == null || Seer.seer != PlayerControl.LocalPlayer) return; Seer.currentTarget = setAnyRoleTarget(); - if (Seer.currentTarget != null && Seer.revealedPlayers.Any(p => p.Data.PlayerId == Seer.currentTarget.Data.PlayerId)) Seer.currentTarget = null; // Remove target if already revealed + if (Seer.currentTarget != null && Seer.revealedPlayers.Keys.Any(p => p.Data.PlayerId == Seer.currentTarget.Data.PlayerId)) Seer.currentTarget = null; // Remove target if already revealed } @@ -140,14 +141,16 @@ namespace BonusRoles { class PerformKillPatch { public static bool Prefix(KillButtonManager __instance) { - // Block imposter shielded kill - if (__instance.isActiveAndEnabled && __instance.CurrentTarget && !__instance.isCoolingDown && !PlayerControl.LocalPlayer.Data.IsDead && PlayerControl.LocalPlayer.CanMove) { - if (Medic.shielded != null && Medic.shielded == __instance.CurrentTarget) { - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ShieldedMurderAttempt, Hazel.SendOption.None, -1); - AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.shieldedMurderAttempt(); - return false; - } + // Block impostor shielded kill + if (Medic.shielded != null && Medic.shielded == __instance.CurrentTarget) { + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ShieldedMurderAttempt, Hazel.SendOption.None, -1); + AmongUsClient.Instance.FinishRpcImmediately(writer); + RPCProcedure.shieldedMurderAttempt(); + return false; + } + // Block impostor not fully grown child kill + else if (Child.child != null && __instance.CurrentTarget == Child.child && !Child.isGrownUp()) { + return false; } return true; } @@ -252,13 +255,6 @@ namespace BonusRoles { RPCProcedure.loverSuicide(otherLover.PlayerId); } } - - // Child died - if (Child.child != null && PAIBDFDMIGK == Child.child) { - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ChildDied, Hazel.SendOption.None, -1); - AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.childDied(); - } } } diff --git a/Source Code/RPC.cs b/Source Code/RPC.cs index ae51dad..0672cea 100644 --- a/Source Code/RPC.cs +++ b/Source Code/RPC.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Collections; using System.Linq; using UnityEngine; +using System; namespace BonusRoles { @@ -179,10 +180,10 @@ namespace BonusRoles } public static void janitorClean(byte playerId) { - DeadBody[] array = Object.FindObjectsOfType(); + DeadBody[] array = UnityEngine.Object.FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { if (GameData.Instance.GetPlayerById(array[i].ParentId).PlayerId == playerId) - Object.Destroy(array[i].gameObject); + UnityEngine.Object.Destroy(array[i].gameObject); } } @@ -198,7 +199,27 @@ namespace BonusRoles } public static void timeMasterRewindTime() { + if (TimeMaster.timeMaster == null) return; + + PlayerControl lp = PlayerControl.LocalPlayer; + if (lp?.Data != null && !lp.Data.IsDead && lp.inVent) { + if ((float)(DateTime.UtcNow -localVentEnterTimePoint).TotalMilliseconds < 1000 * TimeMaster.rewindTime) { + foreach (Vent vent in ShipStatus.Instance.AllVents) { + bool canUse; + bool couldUse; + vent.CanUse(PlayerControl.LocalPlayer.Data, out canUse, out couldUse); + if (canUse) { + PlayerControl.LocalPlayer.MyPhysics.RpcExitVent(vent.Id); + vent.SetButtons(false); + } + } + } + } + + if (PlayerControl.LocalPlayer == TimeMaster.timeMaster) return; // Time Master himself does not rewind + TimeMaster.isRewinding = true; + if (MapBehaviour.Instance) MapBehaviour.Instance.Close(); if (Minigame.Instance) @@ -313,15 +334,17 @@ namespace BonusRoles } } - public static void seerReveal(byte playerId) { + public static void seerReveal(byte targetId, byte targetOrMistakeId) { if (Seer.seer == null) return; + + PlayerControl target = Helpers.playerById(targetId); + PlayerControl targetOrMistake = Helpers.playerById(targetOrMistakeId); - PlayerControl player = Helpers.playerById(playerId); - if (player != null && !Seer.revealedPlayers.Any(p => p.Data.PlayerId == playerId)) { - Seer.revealedPlayers.Add(player); + if (target != null && targetOrMistake != null && !Seer.revealedPlayers.Keys.Any(p => p.Data.PlayerId == targetId)) { + Seer.revealedPlayers.Add(target, targetOrMistake); - if (PlayerControl.LocalPlayer == player && HudManager.Instance?.FullScreen != null) { - SeerInfo si = SeerInfo.getSeerInfoForPlayer(player); + if (PlayerControl.LocalPlayer == target && HudManager.Instance?.FullScreen != null) { + SeerInfo si = SeerInfo.getSeerInfoForPlayer(target); // Use SeerInfo for target here, because we need the isGood of the targets role bool showNotification = false; if (Seer.playersWithNotification == 0 ) showNotification = true; else if (Seer.playersWithNotification == 1 && si.isGood) showNotification = true; @@ -442,7 +465,9 @@ namespace BonusRoles RPCProcedure.swapperSwap(playerId1, playerId2); break; case (byte)CustomRPC.SeerReveal: - RPCProcedure.seerReveal(HFPCBBHJIPJ.ReadByte()); + byte targetId = HFPCBBHJIPJ.ReadByte(); + byte targetOrMistakeId = HFPCBBHJIPJ.ReadByte(); + RPCProcedure.seerReveal(targetId, targetOrMistakeId); break; case (byte)CustomRPC.MorphlingMorph: RPCProcedure.morphlingMorph(HFPCBBHJIPJ.ReadByte()); diff --git a/Source Code/Resources/SpyButton.png b/Source Code/Resources/SpyButton.png new file mode 100644 index 0000000..1d5dd66 Binary files /dev/null and b/Source Code/Resources/SpyButton.png differ diff --git a/Source Code/RoleAssignmentPatch.cs b/Source Code/RoleAssignmentPatch.cs index 77df16d..2a82376 100644 --- a/Source Code/RoleAssignmentPatch.cs +++ b/Source Code/RoleAssignmentPatch.cs @@ -29,9 +29,9 @@ namespace BonusRoles AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.resetVariables(); - List crewmates = PlayerControl.AllPlayerControls.ToArray().ToList(); + List crewmates = PlayerControl.AllPlayerControls.ToArray().ToList().OrderBy(x => Guid.NewGuid()).ToList(); crewmates.RemoveAll(x => x.Data.IsImpostor); - List impostors = PlayerControl.AllPlayerControls.ToArray().ToList(); + List impostors = PlayerControl.AllPlayerControls.ToArray().ToList().OrderBy(x => Guid.NewGuid()).ToList(); impostors.RemoveAll(x => !x.Data.IsImpostor); // Special roles impostors can be converted to @@ -41,12 +41,6 @@ namespace BonusRoles setRoleToRandomPlayer((byte)RoleId.Mafioso, impostors); } - if (impostors.Count >= 1 && (rnd.Next(1, 101) <= BonusRolesPlugin.morphlingSpawnChance.GetValue())) - setRoleToRandomPlayer((byte)RoleId.Morphling, impostors); - - if (impostors.Count >= 1 && (rnd.Next(1, 101) <= BonusRolesPlugin.camouflagerSpawnChance.GetValue())) - setRoleToRandomPlayer((byte)RoleId.Camouflager, impostors); - // Special roles that involve crewmates and impostors if (rnd.Next(1, 101) <= BonusRolesPlugin.loversSpawnChance.GetValue()) { @@ -59,6 +53,13 @@ namespace BonusRoles } } + // Special roles impostors can be converted to + if (impostors.Count >= 1 && (rnd.Next(1, 101) <= BonusRolesPlugin.morphlingSpawnChance.GetValue())) + setRoleToRandomPlayer((byte)RoleId.Morphling, impostors); + + if (impostors.Count >= 1 && (rnd.Next(1, 101) <= BonusRolesPlugin.camouflagerSpawnChance.GetValue())) + setRoleToRandomPlayer((byte)RoleId.Camouflager, impostors); + // Special roles crewmates can be converted to if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.jesterSpawnChance.GetValue())) setRoleToRandomPlayer((byte)RoleId.Jester, crewmates); diff --git a/Source Code/UpdatePatch.cs b/Source Code/UpdatePatch.cs index 23f8274..d5dc2e6 100644 --- a/Source Code/UpdatePatch.cs +++ b/Source Code/UpdatePatch.cs @@ -191,22 +191,27 @@ namespace BonusRoles if (Seer.seer == null || Seer.seer != PlayerControl.LocalPlayer) return; // Update revealed players - foreach (PlayerControl p in Seer.revealedPlayers) { + foreach (KeyValuePair entry in Seer.revealedPlayers) { + PlayerControl target = entry.Key; + PlayerControl targetOrMistake = entry.Value; + + if (target == null || targetOrMistake == null) continue; + // Update color and name regarding settings and given info - string result = p.Data.PlayerName; - SeerInfo si = SeerInfo.getSeerInfoForPlayer(p); + string result = target.Data.PlayerName; + SeerInfo si = SeerInfo.getSeerInfoForPlayer(targetOrMistake); if (Seer.kindOfInfo == 0) - result = p.Data.PlayerName + " (" + si.roleName + ")"; + result = target.Data.PlayerName + " (" + si.roleName + ")"; else if (Seer.kindOfInfo == 1) { si.color = si.isGood ? new Color(250f / 255f, 217f / 255f, 52f / 255f, 1) : new Color (51f / 255f, 61f / 255f, 54f / 255f, 1); } // Set color and name - p.nameText.Color = si.color; - p.nameText.Text = result; + target.nameText.Color = si.color; + target.nameText.Text = result; if (MeetingHud.Instance != null) { foreach (PlayerVoteArea player in MeetingHud.Instance.playerStates) { - if (p.PlayerId == player.TargetPlayerId) { + if (target.PlayerId == player.TargetPlayerId) { player.NameText.Text = result; player.NameText.Color = si.color; break; @@ -216,6 +221,10 @@ namespace BonusRoles } } + static void spyUpdate() { + Spy.spyTimer -= Time.deltaTime; + } + static void camouflageAndMorphActions() { float oldCamouflageTimer = Camouflager.camouflageTimer; float oldMorphTimer = Morphling.morphTimer; @@ -292,14 +301,33 @@ namespace BonusRoles } } - public static void updatePlayerSizes() { + public static void childUpdate() { foreach (PlayerControl p in PlayerControl.AllPlayerControls) { if (p == null) continue; - if (Child.child != null && Child.child == p) - p.transform.localScale = new Vector3(0.35f, 0.35f, 1f); - else if (Morphling.morphling != null && Morphling.morphling == p && Morphling.morphTarget != null && Morphling.morphTarget == Child.child && Morphling.morphTimer > 0f) - p.transform.localScale = new Vector3(0.35f, 0.35f, 1f); + if (Child.child == null) { + p.transform.localScale = new Vector3(0.7f, 0.7f, 1f); + return; + } + + float growingProgress = Child.growingProgress(); + float scale = growingProgress * 0.35f + 0.35f; + string suffix = ""; + if (growingProgress != 1f) + suffix = " [FAD934FF](" + Mathf.FloorToInt(growingProgress * 18) + ")"; + + if (Child.child == p) { + p.transform.localScale = new Vector3(scale, scale, 1f); + p.nameText.Text += suffix; + if (MeetingHud.Instance != null) + foreach (PlayerVoteArea player in MeetingHud.Instance.playerStates) + if (player.NameText != null && p.PlayerId == player.TargetPlayerId) + player.NameText.Text += suffix; + } + else if (Morphling.morphling != null && Morphling.morphling == p && Morphling.morphTarget != null && Morphling.morphTarget == Child.child && Morphling.morphTimer > 0f) { + p.transform.localScale = new Vector3(scale, scale, 1f); + p.nameText.Text += suffix; + } else p.transform.localScale = new Vector3(0.7f, 0.7f, 1f); } @@ -326,9 +354,11 @@ namespace BonusRoles shifterClearTasks(); // Seer update seerUpdate(); + // Spy update(); + spyUpdate(); camouflageAndMorphActions(); - updatePlayerSizes(); + childUpdate(); } } } \ No newline at end of file diff --git a/Source Code/UsablesPatch.cs b/Source Code/UsablesPatch.cs index 8dea16b..5146a49 100644 --- a/Source Code/UsablesPatch.cs +++ b/Source Code/UsablesPatch.cs @@ -13,7 +13,7 @@ namespace BonusRoles { [HarmonyPatch(typeof(Vent), "CanUse")] - public static class VentPatch + public static class VentCanUsePatch { public static bool Prefix(Vent __instance, ref float __result, [HarmonyArgument(0)] GameData.PlayerInfo pc, [HarmonyArgument(1)] out bool canUse, [HarmonyArgument(2)] out bool couldUse) { @@ -47,6 +47,17 @@ namespace BonusRoles } } + [HarmonyPatch(typeof(Vent), "Use")] + public static class VentUsePatch { + public static void Prefix(Vent __instance) { + bool flag; + bool flag2; + __instance.CanUse(PlayerControl.LocalPlayer.Data, out flag, out flag2); + if (flag && !PlayerControl.LocalPlayer.inVent) + localVentEnterTimePoint = DateTime.UtcNow; + } + } + [HarmonyPatch(typeof(UseButtonManager), nameof(UseButtonManager.SetTarget))] class UseButtonSetTargetPatch { static void Postfix(UseButtonManager __instance) { @@ -115,7 +126,7 @@ namespace BonusRoles class VitalsMinigamePatch { static void Postfix(VitalsMinigame __instance) { // Spy show time since death - if (Spy.spy == null || Spy.spy != PlayerControl.LocalPlayer) return; + bool showSpyInfo = Spy.spy != null && Spy.spy == PlayerControl.LocalPlayer && Spy.spyTimer > 0; for (int k = 0; k < __instance.vitals.Length; k++) { VitalsPanel vitalsPanel = __instance.vitals[k]; @@ -125,7 +136,11 @@ namespace BonusRoles DeadPlayer deadPlayer = deadPlayers?.Where(x => x.player?.PlayerId == playerInfo?.PlayerId)?.FirstOrDefault(); if (deadPlayer != null && deadPlayer.timeOfDeath != null) { float timeSinceDeath = ((float)(DateTime.UtcNow - deadPlayer.timeOfDeath).TotalMilliseconds); - vitalsPanel.Text.Text = Math.Round(timeSinceDeath / 1000) + "s"; + + if (showSpyInfo) + vitalsPanel.Text.Text = Math.Round(timeSinceDeath / 1000) + "s"; + else + vitalsPanel.Text.Text = DestroyableSingleton.Instance.GetString(Palette.ShortColorNames[(int)playerInfo.ColorId], new UnhollowerBaseLib.Il2CppReferenceArray(0)); } } } @@ -139,9 +154,7 @@ namespace BonusRoles [HarmonyPatch(typeof(MapCountOverlay), nameof(MapCountOverlay.Update))] class MapCountOverlayUpdatePatch { static bool Prefix(MapCountOverlay __instance) { - // Spy save colors for each room on the admin panel - if (Spy.spy == null || Spy.spy != PlayerControl.LocalPlayer) return true; - + // Save colors for the Spy __instance.timer += Time.deltaTime; if (__instance.timer < 0.1f) { @@ -221,10 +234,11 @@ namespace BonusRoles [HarmonyPatch(typeof(CounterArea), nameof(CounterArea.UpdateCount))] class CounterAreaUpdateCountPatch { + private static Sprite defaultIcon; + static void Postfix(CounterArea __instance) { // Spy display saved colors on the admin panel - if (Spy.spy == null || Spy.spy != PlayerControl.LocalPlayer) return; - + bool showSpyInfo = Spy.spy != null && Spy.spy == PlayerControl.LocalPlayer && Spy.spyTimer > 0; if (players.ContainsKey(__instance.RoomType)) { List colors = players[__instance.RoomType]; @@ -232,9 +246,15 @@ namespace BonusRoles PoolableBehavior icon = __instance.myIcons[i]; SpriteRenderer renderer = icon.GetComponent(); - if (renderer != null && colors.Count > i && Spy.getAdminTableIconSprite() != null) { - renderer.sprite = Spy.getAdminTableIconSprite(); - renderer.color = colors[i]; + if (renderer != null) { + if (defaultIcon == null) defaultIcon = renderer.sprite; + if (showSpyInfo && colors.Count > i && Spy.getAdminTableIconSprite() != null) { + renderer.sprite = Spy.getAdminTableIconSprite(); + renderer.color = colors[i]; + } else { + renderer.sprite = defaultIcon; + renderer.color = Color.white; + } } } }