]> git.deb.at Git - rhonda/theotherroles.git/commitdiff
bump version
authordwd0tcom <info@dominikwierl.com>
Wed, 15 Dec 2021 18:07:17 +0000 (19:07 +0100)
committerdwd0tcom <info@dominikwierl.com>
Wed, 15 Dec 2021 18:07:17 +0000 (19:07 +0100)
20 files changed:
.gitignore
Images/.DS_Store [new file with mode: 0644]
README.md
TheOtherRoles/Buttons.cs
TheOtherRoles/CustomOptionHolder.cs
TheOtherRoles/Main.cs
TheOtherRoles/Modules/CustomHats.cs
TheOtherRoles/Modules/CustomOptions.cs
TheOtherRoles/Objects/CustomButton.cs
TheOtherRoles/Patches/CredentialsPatch.cs
TheOtherRoles/Patches/EndGamePatch.cs
TheOtherRoles/Patches/MeetingPatch.cs
TheOtherRoles/Patches/PlayerControlPatch.cs
TheOtherRoles/Patches/RoleAssignmentPatch.cs
TheOtherRoles/Patches/UpdatePatch.cs
TheOtherRoles/Patches/UsablesPatch.cs
TheOtherRoles/RPC.cs
TheOtherRoles/RoleInfo.cs
TheOtherRoles/TheOtherRoles.cs
TheOtherRoles/TheOtherRoles.csproj

index 375e88d6b482721a0936db498cec016b2cb434eb..44db07a460df7172e13fde82558a00c1b107692c 100644 (file)
@@ -1,4 +1,3 @@
 .vs
 .vscode/tasks.json
-TODO
-.DS_Store
+TODO
\ No newline at end of file
diff --git a/Images/.DS_Store b/Images/.DS_Store
new file mode 100644 (file)
index 0000000..5008ddf
Binary files /dev/null and b/Images/.DS_Store differ
index 63f644c1dd99325d1ba09e5cc98f8419011c38dc..5f4cce47cd07c9d2603587fa11d3e39faaafacce 100644 (file)
--- a/README.md
+++ b/README.md
@@ -38,6 +38,7 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein
 # Releases
 | Among Us - Version| Mod Version | Link |
 |----------|-------------|-----------------|
+| 2021.12.14s| v3.3.0| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v3.3.0/TheOtherRoles.zip)
 | 2021.11.9.5s| v3.2.4| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v3.2.4/TheOtherRoles.zip)
 | 2021.11.9.5s| v3.2.3| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v3.2.3/TheOtherRoles.zip)
 | 2021.11.9.5s| v3.2.2| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v3.2.2/TheOtherRoles.zip)
@@ -88,6 +89,17 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein
 <details>
   <summary>Click to show the Changelog</summary>
 
+**Version 3.3.0**
+- Update to Among Us version 2021.12.14s
+- Fixed a bug where the Pursuer won if the Pursuer was the last killed or voted player
+- Fixed a bug where the option "Enable Mod Roles And Block Vanilla Roles" was not set correctly
+- New option for the Guesser "Evil Guesser can guess spy"
+- New option for the Guesser "Other Guesser Spawn Rate"
+- New ability for the Hacker "Mobile Gadgets" (including vitals & admin table)
+- New option for the Hacker "Max Mobile Gadget Charges"
+- New option for the Hacker "Number Of Tasks Needed For Recharging"
+- Fixed some UI bugs during the meeting  
+
 **Version 3.2.4**
 - Fixed a bug where the Vampire teleported when the bitten player died
 - The settings UI has been improved by [Amsyar Rasyiq](https://github.com/amsyarasyiq)
@@ -825,6 +837,8 @@ Depending on the options, the Guesser can't guess the shielded player and depend
 | Guesser Can Shoot Multiple Times Per Meeting| -
 | Guesses Visible In Ghost Chat| -
 | Guesses Ignore The Medic Shield| -
+| Evil Guesser Can Guess The Spy| -
+| Other Guesser Spawn Rate| -
 
 -----------------------
 
@@ -1035,6 +1049,7 @@ If the Hacker activates the "Hacker mode", the Hacker gets more information than
 Otherwise they see the same information as everyone else.
 **Admin table:** The Hacker can see the colors (or color types) of the players on the table.\
 **Vitals**: The Hacker can see how long dead players have been dead for.\
+The Hacker can access his mobile gadgets (vitals & admin table), with a maximum of charges (uses) and a configurable amount of tasks needed to recharge.\
 \
 **NOTE:**
 - If the Morphling morphs or the Camouflager camouflages, the colors on the admin table change accordingly
@@ -1048,6 +1063,8 @@ Otherwise they see the same information as everyone else.
 | Hacker Cooldown| -
 | Hacker Duration | Sets how long the "Hacker mode" remains active
 | Hacker Only Sees Color Type | Sets if the Hacker sees the player colors on the admin table or only white/gray (for Lighter and darker colors)
+| Max Mobile Gadget Charges| -
+| Number Of Tasks Needed For Recharging | Number of tasks to get a charge
 -----------------------
 
 
index 9c9f2526223b7fe7aa4a3c82da80d9b3ac1dc4e8..7f4677589909fe21b310e4c80cfa484f6acda1ea 100644 (file)
@@ -4,6 +4,7 @@ using System;
 using UnityEngine;
 using static TheOtherRoles.TheOtherRoles;
 using TheOtherRoles.Objects;
+using System.Linq;
 
 namespace TheOtherRoles
 {
@@ -19,6 +20,8 @@ namespace TheOtherRoles
         private static CustomButton morphlingButton;
         private static CustomButton camouflagerButton;
         private static CustomButton hackerButton;
+        private static CustomButton hackerVitalsButton;
+        private static CustomButton hackerAdminTableButton;
         private static CustomButton trackerTrackPlayerButton;
         private static CustomButton trackerTrackCorpsesButton;
         private static CustomButton vampireKillButton;
@@ -41,6 +44,8 @@ namespace TheOtherRoles
 
         public static TMPro.TMP_Text securityGuardButtonScrewsText;
         public static TMPro.TMP_Text pursuerButtonBlanksText;
+        public static TMPro.TMP_Text hackerAdminTableChargesText;
+        public static TMPro.TMP_Text hackerVitalsChargesText;
 
         public static void setCustomButtonCooldowns() {
             engineerRepairButton.MaxTimer = 0f;
@@ -52,6 +57,8 @@ namespace TheOtherRoles
             morphlingButton.MaxTimer = Morphling.cooldown;
             camouflagerButton.MaxTimer = Camouflager.cooldown;
             hackerButton.MaxTimer = Hacker.cooldown;
+            hackerVitalsButton.MaxTimer = Hacker.cooldown;
+            hackerAdminTableButton.MaxTimer = Hacker.cooldown;
             vampireKillButton.MaxTimer = Vampire.cooldown;
             trackerTrackPlayerButton.MaxTimer = 0f;
             garlicButton.MaxTimer = 0f;
@@ -74,6 +81,8 @@ namespace TheOtherRoles
 
             timeMasterShieldButton.EffectDuration = TimeMaster.shieldDuration;
             hackerButton.EffectDuration = Hacker.duration;
+            hackerVitalsButton.EffectDuration = Hacker.duration;
+            hackerAdminTableButton.EffectDuration = Hacker.duration;
             vampireKillButton.EffectDuration = Vampire.delay;
             lighterButton.EffectDuration = Lighter.duration; 
             camouflagerButton.EffectDuration = Camouflager.duration;
@@ -352,16 +361,97 @@ namespace TheOtherRoles
                     hackerButton.actionButton.cooldownTimerText.color = Palette.EnabledColor;
                 },
                 Hacker.getButtonSprite(),
-                new Vector3(-1.8f, -0.06f, 0),
+                new Vector3(0f, 1f, 0),
                 __instance,
                 KeyCode.F,
                 true,
                 0f,
-                () => {
-                    hackerButton.Timer = hackerButton.MaxTimer;
-                }
+                () => { hackerButton.Timer = hackerButton.MaxTimer;}
             );
 
+            hackerAdminTableButton = new CustomButton(
+               () => {
+                   if (!MapBehaviour.Instance || !MapBehaviour.Instance.isActiveAndEnabled)
+                       DestroyableSingleton<HudManager>.Instance.ShowMap((System.Action<MapBehaviour>)(m => m.ShowCountOverlay()));
+
+                   Hacker.chargesAdminTable--;
+               },
+               () => { return Hacker.hacker != null && Hacker.hacker == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead;},
+               () => {
+                   if (hackerAdminTableChargesText != null) hackerAdminTableChargesText.text = $"{Hacker.chargesAdminTable} / {Hacker.toolsNumber}";
+                   return PlayerControl.LocalPlayer.CanMove && Hacker.chargesAdminTable > 0; 
+               },
+               () => {
+                   hackerAdminTableButton.Timer = hackerAdminTableButton.MaxTimer;
+                   hackerAdminTableButton.isEffectActive = false;
+                   hackerAdminTableButton.actionButton.cooldownTimerText.color = Palette.EnabledColor;
+               },
+               Hacker.getAdminSprite(),
+               new Vector3(-1.8f, -0.06f, 0),
+               __instance,
+               KeyCode.Q,
+               true,
+               0f,
+               () => { 
+                   hackerAdminTableButton.Timer = hackerAdminTableButton.MaxTimer;
+                   if (MapBehaviour.Instance && MapBehaviour.Instance.isActiveAndEnabled) MapBehaviour.Instance.Close();
+               },
+               PlayerControl.GameOptions.MapId == 3,
+               "ADMIN"
+           );
+
+            // Hacker Admin Table Charges
+            hackerAdminTableChargesText = GameObject.Instantiate(hackerAdminTableButton.actionButton.cooldownTimerText, hackerAdminTableButton.actionButton.cooldownTimerText.transform.parent);
+            hackerAdminTableChargesText.text = "";
+            hackerAdminTableChargesText.enableWordWrapping = false;
+            hackerAdminTableChargesText.transform.localScale = Vector3.one * 0.5f;
+            hackerAdminTableChargesText.transform.localPosition += new Vector3(-0.05f, 0.7f, 0);
+
+            hackerVitalsButton = new CustomButton(
+               () => {
+                   if (!Minigame.Instance) {
+                       if (Hacker.vitals == null) {
+                           var e = UnityEngine.Object.FindObjectsOfType<SystemConsole>().FirstOrDefault(x => x.gameObject.name.Contains("panel_vitals"));
+                           if (e == null || Camera.main == null) return;
+                           Hacker.vitals = UnityEngine.Object.Instantiate(e.MinigamePrefab, Camera.main.transform, false);
+                       }
+                       Hacker.vitals.transform.SetParent(Camera.main.transform, false);
+                       Hacker.vitals.transform.localPosition = new Vector3(0.0f, 0.0f, -50f);
+                       Hacker.vitals.Begin(null);
+                   }
+                   Hacker.chargesVitals--;
+               },
+               () => { return Hacker.hacker != null && Hacker.hacker == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead;},
+               () => {
+                   if (hackerVitalsChargesText != null) hackerVitalsChargesText.text = $"{Hacker.chargesVitals} / {Hacker.toolsNumber}";
+                   return PlayerControl.LocalPlayer.CanMove && Hacker.chargesVitals > 0;
+               },
+               () => {
+                   hackerVitalsButton.Timer = hackerVitalsButton.MaxTimer;
+                   hackerVitalsButton.isEffectActive = false;
+                   hackerVitalsButton.actionButton.cooldownTimerText.color = Palette.EnabledColor;
+               },
+               Hacker.getVitalsSprite(),
+               new Vector3(-2.7f, -0.06f, 0),
+               __instance,
+               KeyCode.Q,
+               true,
+               0f,
+               () => { 
+                   hackerVitalsButton.Timer = hackerVitalsButton.MaxTimer;
+                   if (Minigame.Instance) Hacker.vitals.ForceClose();
+               },
+               false,
+               "VITALS"
+           );
+
+            // Hacker Vitals Charges
+            hackerVitalsChargesText = GameObject.Instantiate(hackerVitalsButton.actionButton.cooldownTimerText, hackerVitalsButton.actionButton.cooldownTimerText.transform.parent);
+            hackerVitalsChargesText.text = "";
+            hackerVitalsChargesText.enableWordWrapping = false;
+            hackerVitalsChargesText.transform.localScale = Vector3.one * 0.5f;
+            hackerVitalsChargesText.transform.localPosition += new Vector3(-0.05f, 0.7f, 0);
+
             // Tracker button
             trackerTrackPlayerButton = new CustomButton(
                 () => {
index 0545db72af80f078a76e66014b1ccc8bf5d2b840..e574e551b8634621e719049a69604da7d6e41067 100644 (file)
@@ -57,6 +57,8 @@ namespace TheOtherRoles {
         public static CustomOption guesserHasMultipleShotsPerMeeting;
         public static CustomOption guesserShowInfoInGhostChat;
         public static CustomOption guesserKillsThroughShield;
+        public static CustomOption guesserEvilCanKillSpy;
+        public static CustomOption guesserSpawnBothRate;
 
         public static CustomOption jesterSpawnRate;
         public static CustomOption jesterCanCallEmergency;
@@ -143,6 +145,8 @@ namespace TheOtherRoles {
         public static CustomOption hackerCooldown;
         public static CustomOption hackerHackeringDuration;
         public static CustomOption hackerOnlyColorType;
+        public static CustomOption hackerToolsNumber;
+        public static CustomOption hackerRechargeTasksNumber;
 
         public static CustomOption trackerSpawnRate;
         public static CustomOption trackerUpdateIntervall;
@@ -228,7 +232,7 @@ namespace TheOtherRoles {
             
             // Role Options
             presetSelection = CustomOption.Create(0, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Preset"), presets, null, true);
-            activateRoles = CustomOption.Create(7, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Enable Mod Roles And Block Vanilla Roles"), true, null, true);
+            activateRoles = CustomOption.Create(1, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Enable Mod Roles And Block Vanilla Roles"), true, null, true);
 
             // Using new id's for the options to not break compatibilty with older versions
             crewmateRolesCountMin = CustomOption.Create(300, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Minimum Crewmate Roles"), 0f, 0f, 15f, 1f, null, true);
@@ -299,6 +303,8 @@ namespace TheOtherRoles {
             guesserHasMultipleShotsPerMeeting = CustomOption.Create(313, "Guesser Can Shoot Multiple Times Per Meeting", false, guesserSpawnRate);
             guesserShowInfoInGhostChat = CustomOption.Create(314, "Guesses Visible In Ghost Chat", true, guesserSpawnRate);
             guesserKillsThroughShield  = CustomOption.Create(315, "Guesses Ignore The Medic Shield", true, guesserSpawnRate);
+            guesserEvilCanKillSpy  = CustomOption.Create(316, "Evil Guesser Can Guess The Spy", true, guesserSpawnRate);
+            guesserSpawnBothRate = CustomOption.Create(317, "Other Guesser Spawn Rate", rates, guesserSpawnRate);
 
             jesterSpawnRate = CustomOption.Create(60, cs(Jester.color, "Jester"), rates, null, true);
             jesterCanCallEmergency = CustomOption.Create(61, "Jester can call emergency meeting", true, jesterSpawnRate);
@@ -385,6 +391,8 @@ namespace TheOtherRoles {
             hackerCooldown = CustomOption.Create(171, "Hacker Cooldown", 30f, 0f, 60f, 5f, hackerSpawnRate);
             hackerHackeringDuration = CustomOption.Create(172, "Hacker Duration", 10f, 2.5f, 60f, 2.5f, hackerSpawnRate);
             hackerOnlyColorType = CustomOption.Create(173, "Hacker Only Sees Color Type", false, hackerSpawnRate);
+            hackerToolsNumber = CustomOption.Create(174, "Max Mobile Gadget Charges", 5f, 1f, 30f, 1f, hackerSpawnRate);
+            hackerRechargeTasksNumber = CustomOption.Create(175, "Number Of Tasks Needed For Recharging", 2f, 1f, 5f, 1f, hackerSpawnRate);
 
             trackerSpawnRate = CustomOption.Create(200, cs(Tracker.color, "Tracker"), rates, null, true);
             trackerUpdateIntervall = CustomOption.Create(201, "Tracker Update Intervall", 5f, 2.5f, 30f, 2.5f, trackerSpawnRate);
index 43033adb688723d22cd643e26616abc15a8ea0ab..b0ca0da1da86facacebd46161640b4407a510eb0 100644 (file)
@@ -21,7 +21,7 @@ namespace TheOtherRoles
     public class TheOtherRolesPlugin : BasePlugin
     {
         public const string Id = "me.eisbison.theotherroles";
-        public const string VersionString = "3.2.4";
+        public const string VersionString = "3.3.0";
         public static System.Version Version = System.Version.Parse(VersionString);
         internal static BepInEx.Logging.ManualLogSource Logger;
 
index d46d26b0d8e1eba44ed9962c90655406c89e8955..b94279b83f82b515e4baf3a22ca0c0e482dd8e3c 100644 (file)
@@ -357,7 +357,7 @@ namespace TheOtherRoles.Modules {
                     YOffset = createHatPackage(value, key, YOffset, __instance);
                 }
 
-                __instance.scroller.YBounds.max = -(YOffset + 4.1f);
+                __instance.scroller.ContentYBounds.max = -(YOffset + 4.1f);
             }
         }
 
index c18c9c4ae5c28d1e4d60756cf6cca1b2a80815bd..7590699260f53c39d43159eec4e5094db54934b9 100644 (file)
@@ -263,7 +263,7 @@ namespace TheOtherRoles {
         public static void Postfix(GameOptionsMenu __instance) {
             if (__instance.Children.Length < 100) return; // TODO: Introduce a cleaner way to seperate the TOR settings from the game settings
 
-            __instance.GetComponentInParent<Scroller>().YBounds.max = -0.5F + __instance.Children.Length * 0.55F; 
+            __instance.GetComponentInParent<Scroller>().ContentYBounds.max = -0.5F + __instance.Children.Length * 0.55F; 
             timer += Time.deltaTime;
             if (timer < 0.1f) return;
             timer = 0f;
index 1d6f19fe4f3a4910c0f7166442a65d45e53af4a5..4cf49c62915d96f56b17d8107e996089d623edab 100644 (file)
@@ -26,8 +26,9 @@ namespace TheOtherRoles.Objects {
         private HudManager hudManager;
         private bool mirror;
         private KeyCode? hotkey;
+        private string buttonText;
 
-        public CustomButton(Action OnClick, Func<bool> HasButton, Func<bool> CouldUse, Action OnMeetingEnds, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager, KeyCode? hotkey, bool HasEffect, float EffectDuration, Action OnEffectEnds, bool mirror = false)
+        public CustomButton(Action OnClick, Func<bool> HasButton, Func<bool> CouldUse, Action OnMeetingEnds, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager, KeyCode? hotkey, bool HasEffect, float EffectDuration, Action OnEffectEnds, bool mirror = false, string buttonText = "")
         {
             this.hudManager = hudManager;
             this.OnClick = OnClick;
@@ -41,21 +42,22 @@ namespace TheOtherRoles.Objects {
             this.Sprite = Sprite;
             this.mirror = mirror;
             this.hotkey = hotkey;
+            this.buttonText = buttonText;
             Timer = 16.2f;
             buttons.Add(this);
             actionButton = UnityEngine.Object.Instantiate(hudManager.KillButton, hudManager.KillButton.transform.parent);
             PassiveButton button = actionButton.GetComponent<PassiveButton>();
-            this.showButtonText = actionButton.graphic.sprite == Sprite;
+            this.showButtonText = (actionButton.graphic.sprite == Sprite || buttonText != "");
             button.OnClick = new Button.ButtonClickedEvent();
             button.OnClick.AddListener((UnityEngine.Events.UnityAction)onClickEvent);
 
             setActive(false);
         }
 
-        public CustomButton(Action OnClick, Func<bool> HasButton, Func<bool> CouldUse, Action OnMeetingEnds, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager, KeyCode? hotkey, bool mirror = false)
-        : this(OnClick, HasButton, CouldUse, OnMeetingEnds, Sprite, PositionOffset, hudManager, hotkey, false, 0f, () => {}, mirror) { }
+        public CustomButton(Action OnClick, Func<bool> HasButton, Func<bool> CouldUse, Action OnMeetingEnds, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager, KeyCode? hotkey, bool mirror = false, string buttonText = "")
+        : this(OnClick, HasButton, CouldUse, OnMeetingEnds, Sprite, PositionOffset, hudManager, hotkey, false, 0f, () => {}, mirror, buttonText) { }
 
-        void onClickEvent()
+        public void onClickEvent()
         {
             if (this.Timer < 0f && HasButton() && CouldUse())
             {
@@ -137,6 +139,9 @@ namespace TheOtherRoles.Objects {
             setActive(hudManager.UseButton.isActiveAndEnabled);
 
             actionButton.graphic.sprite = Sprite;
+            if (showButtonText && buttonText != ""){
+                actionButton.OverrideText(buttonText);
+            }
             actionButton.buttonLabelText.enabled = showButtonText; // Only show the text if it's a kill button
             if (hudManager.UseButton != null) {
                 Vector3 pos = hudManager.UseButton.transform.localPosition;
index 034bc1550ab3b3b3c84dbb8bab76dcb2694e341e..ad477095f0f0746c3f83fb18f8290a070c4d9afc 100644 (file)
@@ -19,6 +19,7 @@ Button design by <color=#FCCE03FF>Bavari</color></size>";
 $@"Modded by <color=#FCCE03FF>Eisbison</color>, <color=#FCCE03FF>Thunderstorm584</color>, <color=#FCCE03FF>EndOfFile</color> & <color=#FCCE03FF>Mallöris</color>
 Design by <color=#FCCE03FF>Bavari</color>";
 
+
         public static string contributorsCredentials = "<size=80%>GitHub Contributors: Alex2911, amsyarasyiq, gendelo3</size>";
 
         [HarmonyPatch(typeof(VersionShower), nameof(VersionShower.Start))]
index 21732d4a8b448cf332ee9cdb54524f97990c2708..371b2c4a65c50da4f4d096019c2f42dab23b729c 100644 (file)
@@ -208,7 +208,7 @@ namespace TheOtherRoles.Patches {
             }
 
             // Possible Additional winner: Pursuer
-            if (Pursuer.pursuer != null && !Pursuer.pursuer.Data.IsDead) {
+            if (Pursuer.pursuer != null && !Pursuer.pursuer.Data.IsDead && !Pursuer.notAckedExiled) {
                 if (!TempData.winners.ToArray().Any(x => x.PlayerName == Pursuer.pursuer.Data.PlayerName))
                     TempData.winners.Add(new WinningPlayerData(Pursuer.pursuer.Data));
                 AdditionalTempData.additionalWinConditions.Add(WinCondition.AdditionalAlivePursuerWin);
index e9c09cde356d0546994240b3436d281a309a11f9..f5a835274bdda11be5c5bdcea0de44336b1af82b 100644 (file)
@@ -170,10 +170,15 @@ namespace TheOtherRoles.Patches {
                 Swapper.playerId1 = Byte.MaxValue;
                 Swapper.playerId2 = Byte.MaxValue;
 
-                // Lovers save next to be exiled, because RPC of ending game comes before RPC of exiled
+                // Lovers & Pursuer save next to be exiled, because RPC of ending game comes before RPC of exiled
                 Lovers.notAckedExiledIsLover = false;
-                if (exiled != null)
+                Pursuer.notAckedExiled = false;
+                if (exiled != null) {
                     Lovers.notAckedExiledIsLover = ((Lovers.lover1 != null && Lovers.lover1.PlayerId == exiled.PlayerId) || (Lovers.lover2 != null && Lovers.lover2.PlayerId == exiled.PlayerId));
+                    Pursuer.notAckedExiled = (Pursuer.pursuer != null && Pursuer.pursuer.PlayerId == exiled.PlayerId);
+                }
+                    
+                               
             }
         }
 
@@ -253,7 +258,8 @@ namespace TheOtherRoles.Patches {
             Transform selectedButton = null;
 
             foreach (RoleInfo roleInfo in RoleInfo.allRoleInfos) {
-                if (roleInfo.roleId == RoleId.Lover || roleInfo.roleId == RoleId.Guesser || roleInfo == RoleInfo.niceMini) continue; // Not guessable roles
+                RoleId guesserRole = (Guesser.niceGuesser != null && PlayerControl.LocalPlayer.PlayerId == Guesser.niceGuesser.PlayerId) ? RoleId.NiceGuesser :  RoleId.EvilGuesser;
+                if (roleInfo.roleId == RoleId.Lover || roleInfo.roleId == guesserRole || roleInfo == RoleInfo.niceMini || (!Guesser.evilGuesserCanGuessSpy && guesserRole == RoleId.EvilGuesser && roleInfo.roleId == RoleId.Spy)) continue; // Not guessable roles
                 Transform buttonParent = (new GameObject()).transform;
                 buttonParent.SetParent(container);
                 Transform button = UnityEngine.Object.Instantiate(buttonTemplate, buttonParent);
@@ -277,10 +283,10 @@ namespace TheOtherRoles.Patches {
                         buttons.ForEach(x => x.GetComponent<SpriteRenderer>().color = x == selectedButton ? Color.red : Color.white);
                     } else {
                         PlayerControl focusedTarget = Helpers.playerById((byte)__instance.playerStates[buttonTarget].TargetPlayerId);
-                        if (!(__instance.state == MeetingHud.VoteStates.Voted || __instance.state == MeetingHud.VoteStates.NotVoted) || focusedTarget == null || Guesser.remainingShots <= 0 ) return;
+                        if (!(__instance.state == MeetingHud.VoteStates.Voted || __instance.state == MeetingHud.VoteStates.NotVoted) || focusedTarget == null || Guesser.remainingShots(PlayerControl.LocalPlayer.PlayerId) <= 0 ) return;
 
                         if (!Guesser.killsThroughShield && focusedTarget == Medic.shielded) { // Depending on the options, shooting the shielded player will not allow the guess, notifiy everyone about the kill attempt and close the window
-                            __instance.playerStates.ToList().ForEach(x => x.gameObject.SetActive(true));
+                            __instance.playerStates.ToList().ForEach(x => x.gameObject.SetActive(true)); 
                             UnityEngine.Object.Destroy(container.gameObject);
 
                             MessageWriter murderAttemptWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ShieldedMurderAttempt, Hazel.SendOption.Reliable, -1);
@@ -297,18 +303,19 @@ namespace TheOtherRoles.Patches {
                         // Reset the GUI
                         __instance.playerStates.ToList().ForEach(x => x.gameObject.SetActive(true));
                         UnityEngine.Object.Destroy(container.gameObject);
-                        if (Guesser.hasMultipleShotsPerMeeting && Guesser.remainingShots > 1 && dyingTarget != PlayerControl.LocalPlayer)
+                        if (Guesser.hasMultipleShotsPerMeeting && Guesser.remainingShots(PlayerControl.LocalPlayer.PlayerId) > 1 && dyingTarget != PlayerControl.LocalPlayer)
                             __instance.playerStates.ToList().ForEach(x => { if (x.TargetPlayerId == dyingTarget.PlayerId && x.transform.FindChild("ShootButton") != null) UnityEngine.Object.Destroy(x.transform.FindChild("ShootButton").gameObject); });
                         else
                             __instance.playerStates.ToList().ForEach(x => { if (x.transform.FindChild("ShootButton") != null) UnityEngine.Object.Destroy(x.transform.FindChild("ShootButton").gameObject); });
 
                         // Shoot player and send chat info if activated
                         MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.GuesserShoot, Hazel.SendOption.Reliable, -1);
+                        writer.Write(PlayerControl.LocalPlayer.PlayerId);
                         writer.Write(dyingTarget.PlayerId);
                         writer.Write(focusedTarget.PlayerId);
                         writer.Write((byte)roleInfo.roleId);
                         AmongUsClient.Instance.FinishRpcImmediately(writer);
-                        RPCProcedure.guesserShoot(dyingTarget.PlayerId, focusedTarget.PlayerId, (byte)roleInfo.roleId);
+                        RPCProcedure.guesserShoot(PlayerControl.LocalPlayer.PlayerId, dyingTarget.PlayerId, focusedTarget.PlayerId, (byte)roleInfo.roleId);
                     }
                 }));
 
@@ -320,7 +327,7 @@ namespace TheOtherRoles.Patches {
         [HarmonyPatch(typeof(PlayerVoteArea), nameof(PlayerVoteArea.Select))]
         class PlayerVoteAreaSelectPatch {
             static bool Prefix(MeetingHud __instance) {
-                return !(PlayerControl.LocalPlayer != null && PlayerControl.LocalPlayer == Guesser.guesser && guesserUI != null);
+                return !(PlayerControl.LocalPlayer != null && Guesser.isGuesser(PlayerControl.LocalPlayer.PlayerId) && guesserUI != null);
             }
         }
 
@@ -354,6 +361,13 @@ namespace TheOtherRoles.Patches {
                 }
             }
 
+            //Fix visor in Meetings 
+            foreach (PlayerVoteArea pva in __instance.playerStates) {
+                if(pva.PlayerIcon != null && pva.PlayerIcon.VisorSlot != null){
+                    pva.PlayerIcon.VisorSlot.transform.position += new Vector3(0, 0, -1f);
+                }
+            }
+
             // Add overlay for spelled players
             if (Witch.witch != null && Witch.futureSpelled != null) {
                 foreach (PlayerVoteArea pva in __instance.playerStates) {
@@ -368,10 +382,10 @@ namespace TheOtherRoles.Patches {
             }
 
             // Add Guesser Buttons
-            if (Guesser.guesser != null && PlayerControl.LocalPlayer == Guesser.guesser && !Guesser.guesser.Data.IsDead && Guesser.remainingShots > 0) {
+            if (Guesser.isGuesser(PlayerControl.LocalPlayer.PlayerId) && !PlayerControl.LocalPlayer.Data.IsDead && Guesser.remainingShots(PlayerControl.LocalPlayer.PlayerId) > 0) {
                 for (int i = 0; i < __instance.playerStates.Length; i++) {
                     PlayerVoteArea playerVoteArea = __instance.playerStates[i];
-                    if (playerVoteArea.AmDead || playerVoteArea.TargetPlayerId == Guesser.guesser.PlayerId) continue;
+                    if (playerVoteArea.AmDead || playerVoteArea.TargetPlayerId == PlayerControl.LocalPlayer.PlayerId) continue;
 
                     GameObject template = playerVoteArea.Buttons.transform.Find("CancelButton").gameObject;
                     GameObject targetBox = UnityEngine.Object.Instantiate(template, playerVoteArea.transform);
index 5f7985104e7ef717a4b8cbd1b185d9341e4b8aa9..7398da854967e50f9a605831417e837268fcf92f 100644 (file)
@@ -387,11 +387,17 @@ namespace TheOtherRoles.Patches {
                     TMPro.TextMeshPro meetingInfo = meetingInfoTransform != null ? meetingInfoTransform.GetComponent<TMPro.TextMeshPro>() : null;
                     if (meetingInfo == null && playerVoteArea != null) {
                         meetingInfo = UnityEngine.Object.Instantiate(playerVoteArea.NameText, playerVoteArea.NameText.transform.parent);
-                        meetingInfo.transform.localPosition += Vector3.down * 0.20f;
-                        meetingInfo.fontSize *= 0.75f;
+                        meetingInfo.transform.localPosition += Vector3.down * 0.10f;
+                        meetingInfo.fontSize *= 0.60f;
                         meetingInfo.gameObject.name = "Info";
                     }
 
+                    // Set player name higher to align in middle
+                    if (meetingInfo != null && playerVoteArea != null) {
+                        var playerName = playerVoteArea.NameText;
+                        playerName.transform.localPosition = new Vector3(0.3384f, (0.0311f + 0.0683f), -0.1f);    
+                    }
+
                     var (tasksCompleted, tasksTotal) = TasksHandler.taskInfo(p.Data);
                     string roleNames = RoleInfo.GetRolesString(p, true);
                     string taskInfo = tasksTotal > 0 ? $"<color=#FAD934FF>({tasksCompleted}/{tasksTotal})</color>" : "";
@@ -676,6 +682,16 @@ namespace TheOtherRoles.Patches {
             }
         }
 
+        public static void hackerUpdate() {
+            if (Hacker.hacker == null || PlayerControl.LocalPlayer != Hacker.hacker || Hacker.hacker.Data.IsDead) return;
+            var (playerCompleted, _) = TasksHandler.taskInfo(Hacker.hacker.Data);
+            if (playerCompleted == Hacker.rechargedTasks) {
+                Hacker.rechargedTasks += Hacker.rechargeTasksNumber;
+                if (Hacker.toolsNumber > Hacker.chargesVitals) Hacker.chargesVitals++;
+                if (Hacker.toolsNumber > Hacker.chargesAdminTable) Hacker.chargesAdminTable++;
+            }
+        }
+
         static void pursuerSetTarget() {
             if (Pursuer.pursuer == null || Pursuer.pursuer != PlayerControl.LocalPlayer) return;
             Pursuer.target = setTarget();
@@ -767,6 +783,7 @@ namespace TheOtherRoles.Patches {
                 pursuerSetTarget();
                 // Witch
                 witchSetTarget();
+                hackerUpdate();
             } 
         }
     }
index f2bd6e4b8e5ca3a7d5cc6896f82068dbad883552..2afc83e6c21057c50fb713411a95e9bb53878f7e 100644 (file)
@@ -161,9 +161,11 @@ namespace TheOtherRoles.Patches {
 
             // Assign Guesser (chance to be impostor based on setting)
             if (data.impostors.Count > 0 && data.maxImpostorRoles > 0 &&  rnd.Next(1, 101) <= CustomOptionHolder.guesserIsImpGuesserRate.getSelection() * 10) {
-                data.impSettings.Add((byte)RoleId.Guesser, CustomOptionHolder.guesserSpawnRate.getSelection());
+                data.impSettings.Add((byte)RoleId.EvilGuesser, CustomOptionHolder.guesserSpawnRate.getSelection());
+                if (data.crewmates.Count > 0 && data.maxCrewmateRoles > 0) data.crewSettings.Add((byte)RoleId.NiceGuesser, CustomOptionHolder.guesserSpawnBothRate.getSelection()); // Second guesser
             } else if (data.crewmates.Count > 0 && data.maxCrewmateRoles > 0) {
-                data.crewSettings.Add((byte)RoleId.Guesser, CustomOptionHolder.guesserSpawnRate.getSelection());
+                data.crewSettings.Add((byte)RoleId.NiceGuesser, CustomOptionHolder.guesserSpawnRate.getSelection());
+                if (data.impostors.Count > 0 && data.maxImpostorRoles > 0) data.impSettings.Add((byte)RoleId.EvilGuesser, CustomOptionHolder.guesserSpawnBothRate.getSelection()); // Second guesser
             }
         }
 
index d081c1bd2fa776ff917d83c292d88f59ef3e2717..d31cb9c1a8ef26b671955c67b4c112a81ca02b7c 100644 (file)
@@ -107,8 +107,10 @@ namespace TheOtherRoles.Patches {
                 setPlayerNameColor(SecurityGuard.securityGuard, SecurityGuard.color);
             } else if (Arsonist.arsonist != null && Arsonist.arsonist == PlayerControl.LocalPlayer) {
                 setPlayerNameColor(Arsonist.arsonist, Arsonist.color);
-            } else if (Guesser.guesser != null && Guesser.guesser == PlayerControl.LocalPlayer) {
-                setPlayerNameColor(Guesser.guesser, Guesser.guesser.Data.Role.IsImpostor ? Palette.ImpostorRed : Guesser.color);
+            } else if (Guesser.niceGuesser != null && Guesser.niceGuesser == PlayerControl.LocalPlayer) {
+                setPlayerNameColor(Guesser.niceGuesser, Guesser.color);
+            } else if (Guesser.evilGuesser != null && Guesser.evilGuesser == PlayerControl.LocalPlayer) {
+                setPlayerNameColor(Guesser.evilGuesser, Palette.ImpostorRed);
             } else if (Bait.bait != null && Bait.bait == PlayerControl.LocalPlayer) {
                 setPlayerNameColor(Bait.bait, Bait.color);
             } else if (Vulture.vulture != null && Vulture.vulture == PlayerControl.LocalPlayer) {
index 5ccd09eabdd114bde796c5257788bb8a93fdefa4..6cfa5ab5005ecbcf4ec6177b12e2376d73334caf 100644 (file)
@@ -253,6 +253,13 @@ namespace TheOtherRoles.Patches {
                     
                     }
                 }
+
+                //Fix Visor in Vitals
+                foreach (VitalsPanel panel in __instance.vitals) {
+                    if (panel.PlayerIcon != null && panel.PlayerIcon.Skin != null) {
+                         panel.PlayerIcon.Skin.transform.position = new Vector3(0, 0, 0f);
+                    }
+                }
             }
         }
 
index d550f9b6905a7de1f79e637c024d52f5fdb52d95..fef63561e57b1233bb726f312fccf0e8b3afa958 100644 (file)
@@ -45,7 +45,8 @@ namespace TheOtherRoles
         Warlock,
         SecurityGuard,
         Arsonist,
-        Guesser,
+        EvilGuesser,
+        NiceGuesser,
         BountyHunter,
         Bait,
         Vulture,
@@ -243,8 +244,11 @@ namespace TheOtherRoles
                     case RoleId.Arsonist:
                         Arsonist.arsonist = player;
                         break;
-                    case RoleId.Guesser:
-                        Guesser.guesser = player;
+                    case RoleId.EvilGuesser:
+                        Guesser.evilGuesser = player;
+                        break;
+                    case RoleId.NiceGuesser:
+                        Guesser.niceGuesser = player;
                         break;
                     case RoleId.BountyHunter:
                         BountyHunter.bountyHunter = player;
@@ -453,8 +457,8 @@ namespace TheOtherRoles
                 Spy.spy = oldShifter;
             if (SecurityGuard.securityGuard != null && SecurityGuard.securityGuard == player)
                 SecurityGuard.securityGuard = oldShifter;
-            if (Guesser.guesser != null && Guesser.guesser == player)
-                Guesser.guesser = oldShifter;
+            if (Guesser.niceGuesser != null && Guesser.niceGuesser == player)
+                Guesser.niceGuesser = oldShifter;
             if (Bait.bait != null && Bait.bait == player) {
                 Bait.bait = oldShifter;
                 if (Bait.bait.Data.IsDead) Bait.reported = true;
@@ -583,7 +587,7 @@ namespace TheOtherRoles
             // Other roles
             if (player == Jester.jester) Jester.clearAndReload();
             if (player == Arsonist.arsonist) Arsonist.clearAndReload();
-            if (player == Guesser.guesser) Guesser.clearAndReload();
+            if (Guesser.isGuesser(player.PlayerId)) Guesser.clear(player.PlayerId);
             if (!ignoreLovers && (player == Lovers.lover1 || player == Lovers.lover2)) { // The whole Lover couple is being erased
                 Lovers.clearAndReload(); 
             }
@@ -719,14 +723,14 @@ namespace TheOtherRoles
             }
         }
 
-        public static void guesserShoot(byte dyingTargetId, byte guessedTargetId, byte guessedRoleId) {
+        public static void guesserShoot(byte killerId, byte dyingTargetId, byte guessedTargetId, byte guessedRoleId) {
             PlayerControl dyingTarget = Helpers.playerById(dyingTargetId);
             if (dyingTarget == null ) return;
             dyingTarget.Exiled();
             PlayerControl dyingLoverPartner = Lovers.bothDie ? dyingTarget.getPartner() : null; // Lover check
             byte partnerId = dyingLoverPartner != null ? dyingLoverPartner.PlayerId : dyingTargetId;
 
-            Guesser.remainingShots = Mathf.Max(0, Guesser.remainingShots - 1);
+            Guesser.remainingShots(killerId, true);
             if (Constants.ShouldPlaySfx()) SoundManager.Instance.PlaySound(dyingTarget.KillSfx, false, 0.8f);
             if (MeetingHud.Instance) {
                 foreach (PlayerVoteArea pva in MeetingHud.Instance.playerStates) {
@@ -738,9 +742,10 @@ namespace TheOtherRoles
                 if (AmongUsClient.Instance.AmHost) 
                     MeetingHud.Instance.CheckForEndVoting();
             }
-            if (HudManager.Instance != null && Guesser.guesser != null)
+            PlayerControl guesser = Helpers.playerById(killerId);
+            if (HudManager.Instance != null && guesser != null)
                 if (PlayerControl.LocalPlayer == dyingTarget) 
-                    HudManager.Instance.KillOverlay.ShowKillAnimation(Guesser.guesser.Data, dyingTarget.Data);
+                    HudManager.Instance.KillOverlay.ShowKillAnimation(guesser.Data, dyingTarget.Data);
                 else if (dyingLoverPartner != null && PlayerControl.LocalPlayer == dyingLoverPartner) 
                     HudManager.Instance.KillOverlay.ShowKillAnimation(dyingLoverPartner.Data, dyingLoverPartner.Data);
             
@@ -749,7 +754,7 @@ namespace TheOtherRoles
                 RoleInfo roleInfo = RoleInfo.allRoleInfos.FirstOrDefault(x => (byte)x.roleId == guessedRoleId);
                 string msg = $"Guesser guessed the role {roleInfo?.name ?? ""} for {guessedTarget.Data.PlayerName}!";
                 if (AmongUsClient.Instance.AmClient && DestroyableSingleton<HudManager>.Instance)
-                    DestroyableSingleton<HudManager>.Instance.Chat.AddChat(Guesser.guesser, msg);
+                    DestroyableSingleton<HudManager>.Instance.Chat.AddChat(guesser, msg);
                 if (msg.IndexOf("who", StringComparison.OrdinalIgnoreCase) >= 0)
                     DestroyableSingleton<Assets.CoreScripts.Telemetry>.Instance.SendWho();
             }
@@ -909,10 +914,11 @@ namespace TheOtherRoles
                     RPCProcedure.arsonistWin();
                     break;
                 case (byte)CustomRPC.GuesserShoot:
+                    byte killerId = reader.ReadByte();
                     byte dyingTarget = reader.ReadByte();
                     byte guessedTarget = reader.ReadByte();
                     byte guessedRoleId = reader.ReadByte();
-                    RPCProcedure.guesserShoot(dyingTarget, guessedTarget, guessedRoleId);
+                    RPCProcedure.guesserShoot(killerId, dyingTarget, guessedTarget, guessedRoleId);
                     break;
                 case (byte)CustomRPC.VultureWin:
                     RPCProcedure.vultureWin();
index 3e4196b1d2b1c183a4927f73f97a098362ddf015..04bbdf6ca81dbb0072daba19f08963d765482634 100644 (file)
@@ -56,8 +56,8 @@ namespace TheOtherRoles
         public static RoleInfo spy = new RoleInfo("Spy", Spy.color, "Confuse the <color=#FF1919FF>Impostors</color>", "Confuse the Impostors", RoleId.Spy);
         public static RoleInfo securityGuard = new RoleInfo("Security Guard", SecurityGuard.color, "Seal vents and place cameras", "Seal vents and place cameras", RoleId.SecurityGuard);
         public static RoleInfo arsonist = new RoleInfo("Arsonist", Arsonist.color, "Let them burn", "Let them burn", RoleId.Arsonist, true);
-        public static RoleInfo goodGuesser = new RoleInfo("Nice Guesser", Guesser.color, "Guess and shoot", "Guess and shoot", RoleId.Guesser);
-        public static RoleInfo badGuesser = new RoleInfo("Evil Guesser", Palette.ImpostorRed, "Guess and shoot", "Guess and shoot", RoleId.Guesser);
+        public static RoleInfo goodGuesser = new RoleInfo("Nice Guesser", Guesser.color, "Guess and shoot", "Guess and shoot", RoleId.NiceGuesser);
+        public static RoleInfo badGuesser = new RoleInfo("Evil Guesser", Palette.ImpostorRed, "Guess and shoot", "Guess and shoot", RoleId.EvilGuesser);
         public static RoleInfo bait = new RoleInfo("Bait", Bait.color, "Bait your enemies", "Bait your enemies", RoleId.Bait);
         public static RoleInfo vulture = new RoleInfo("Vulture", Vulture.color, "Eat Corpses to win", "Eat dead bodies", RoleId.Vulture, true);
         public static RoleInfo medium = new RoleInfo("Medium", Medium.color, "Question the souls of the dead to gain informations", "Question the souls", RoleId.Medium);
@@ -150,7 +150,8 @@ namespace TheOtherRoles
             if (p == Spy.spy) infos.Add(spy);
             if (p == SecurityGuard.securityGuard) infos.Add(securityGuard);
             if (p == Arsonist.arsonist) infos.Add(arsonist);
-            if (p == Guesser.guesser) infos.Add(p.Data.Role.IsImpostor ? badGuesser : goodGuesser);
+            if (p == Guesser.niceGuesser) infos.Add(goodGuesser);
+            if (p == Guesser.evilGuesser) infos.Add(badGuesser);
             if (p == BountyHunter.bountyHunter) infos.Add(bountyHunter);
             if (p == Bait.bait) infos.Add(bait);
             if (p == Vulture.vulture) infos.Add(vulture);
index 93a08c4801939605a226542975f553b8d401e299..320fde337f287c78dd8dc0aa091a67af35f69607 100644 (file)
@@ -478,26 +478,58 @@ namespace TheOtherRoles
 
     public static class Hacker {
         public static PlayerControl hacker;
+        public static Minigame vitals = null;
         public static Color color = new Color32(117, 250, 76, byte.MaxValue);
 
         public static float cooldown = 30f;
         public static float duration = 10f;
+        public static float toolsNumber = 5f;
         public static bool onlyColorType = false;
         public static float hackerTimer = 0f;
+        public static int rechargeTasksNumber = 2;
+        public static int rechargedTasks = 2;
+        public static int chargesVitals = 1;
+        public static int chargesAdminTable = 1;
 
         private static Sprite buttonSprite;
+        private static Sprite vitalsSprite;
+        private static Sprite adminSprite;
+
         public static Sprite getButtonSprite() {
             if (buttonSprite) return buttonSprite;
             buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.HackerButton.png", 115f);
             return buttonSprite;
         }
 
+        public static Sprite getVitalsSprite() {
+            if (vitalsSprite) return vitalsSprite;
+            vitalsSprite = HudManager.Instance.UseButton.fastUseSettings[ImageNames.VitalsButton].Image;
+            return vitalsSprite;
+        }
+
+        public static Sprite getAdminSprite() {
+            byte mapId = PlayerControl.GameOptions.MapId;
+            UseButtonSettings button = HudManager.Instance.UseButton.fastUseSettings[ImageNames.PolusAdminButton]; // Polus
+            if (mapId == 0 || mapId == 3) button = HudManager.Instance.UseButton.fastUseSettings[ImageNames.AdminMapButton]; // Skeld || Dleks
+            else if (mapId == 1) button = HudManager.Instance.UseButton.fastUseSettings[ImageNames.MIRAAdminButton]; // Mira HQ
+            else if (mapId == 4) button = HudManager.Instance.UseButton.fastUseSettings[ImageNames.AirshipAdminButton]; // Airship
+            adminSprite = button.Image;
+            return adminSprite;
+        }
+
         public static void clearAndReload() {
             hacker = null;
+            vitals = null;
             hackerTimer = 0f;
+            adminSprite = null;
             cooldown = CustomOptionHolder.hackerCooldown.getFloat();
             duration = CustomOptionHolder.hackerHackeringDuration.getFloat();
             onlyColorType = CustomOptionHolder.hackerOnlyColorType.getBool();
+            toolsNumber = CustomOptionHolder.hackerToolsNumber.getFloat();
+            rechargeTasksNumber = Mathf.RoundToInt(CustomOptionHolder.hackerRechargeTasksNumber.getFloat());
+            rechargedTasks = Mathf.RoundToInt(CustomOptionHolder.hackerRechargeTasksNumber.getFloat());
+            chargesVitals = Mathf.RoundToInt(CustomOptionHolder.hackerToolsNumber.getFloat()) / 2;
+            chargesAdminTable = Mathf.RoundToInt(CustomOptionHolder.hackerToolsNumber.getFloat()) / 2;
         }
     }
 
@@ -969,14 +1001,17 @@ namespace TheOtherRoles
     }
 
     public static class Guesser {
-        public static PlayerControl guesser;
+        public static PlayerControl niceGuesser;
+        public static PlayerControl evilGuesser;
         public static Color color = new Color32(255, 255, 0, byte.MaxValue);
         private static Sprite targetSprite;
 
-        public static int remainingShots = 2;
+        public static int remainingShotsEvilGuesser = 2;
+        public static int remainingShotsNiceGuesser = 2;
         public static bool hasMultipleShotsPerMeeting = false;
         public static bool showInfoInGhostChat = true;
         public static bool killsThroughShield = true;
+        public static bool evilGuesserCanGuessSpy = true;
 
         public static Sprite getTargetSprite() {
             if (targetSprite) return targetSprite;
@@ -984,13 +1019,37 @@ namespace TheOtherRoles
             return targetSprite;
         }
 
+        public static bool isGuesser (byte playerId) {
+            if ((niceGuesser != null && niceGuesser.PlayerId == playerId) || (evilGuesser != null && evilGuesser.PlayerId == playerId)) return true;
+            return false;
+        }
+
+        public static void clear (byte playerId) {
+            if (niceGuesser != null && niceGuesser.PlayerId == playerId) niceGuesser = null;
+            else if (evilGuesser != null && evilGuesser.PlayerId == playerId) evilGuesser = null;
+        }
+
+        public static int remainingShots(byte playerId, bool shoot = false) {
+            int remainingShots = remainingShotsEvilGuesser;
+            if (niceGuesser.PlayerId == playerId) {
+                remainingShots = remainingShotsNiceGuesser;
+                if (shoot) remainingShotsNiceGuesser = Mathf.Max(0, remainingShotsNiceGuesser - 1);
+            } else if (shoot) {
+                remainingShotsEvilGuesser = Mathf.Max(0, remainingShotsEvilGuesser - 1);
+            }
+            return remainingShots;
+        }
+
         public static void clearAndReload() {
-            guesser = null;
-            
-            remainingShots = Mathf.RoundToInt(CustomOptionHolder.guesserNumberOfShots.getFloat());
+            niceGuesser = null;
+            evilGuesser = null;
+
+            remainingShotsEvilGuesser = Mathf.RoundToInt(CustomOptionHolder.guesserNumberOfShots.getFloat());
+            remainingShotsNiceGuesser = Mathf.RoundToInt(CustomOptionHolder.guesserNumberOfShots.getFloat());
             hasMultipleShotsPerMeeting = CustomOptionHolder.guesserHasMultipleShotsPerMeeting.getBool();
             showInfoInGhostChat = CustomOptionHolder.guesserShowInfoInGhostChat.getBool();
             killsThroughShield = CustomOptionHolder.guesserKillsThroughShield.getBool();
+            evilGuesserCanGuessSpy = CustomOptionHolder.guesserEvilCanKillSpy.getBool();
         }
     }
 
@@ -1170,6 +1229,7 @@ namespace TheOtherRoles
         public static List<PlayerControl> blankedList = new List<PlayerControl>();
         public static int blanks = 0;
         public static Sprite blank;
+        public static bool notAckedExiled = false;
 
         public static float cooldown = 30f;
         public static int blanksNumber = 5;
@@ -1185,6 +1245,7 @@ namespace TheOtherRoles
             target = null;
             blankedList = new List<PlayerControl>();
             blanks = 0;
+            notAckedExiled = false;
 
             cooldown = CustomOptionHolder.pursuerCooldown.getFloat();
             blanksNumber = Mathf.RoundToInt(CustomOptionHolder.pursuerBlanksNumber.getFloat());
index 6e0becc7d740026da0b53fa1afedbb6087ff27ec..bf6f4d3c1900b1380bcc844e4f8e1efde805b144 100644 (file)
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
         <TargetFramework>netstandard2.1</TargetFramework>
-        <Version>3.2.4</Version>
+        <Version>3.3.0</Version>
         <Description>TheOtherRoles</Description>
         <Authors>Eisbison</Authors>
     </PropertyGroup>