]> git.deb.at Git - rhonda/theotherroles.git/commitdiff
v2.8.0
authorEisbison <eisbison.creator@gmail.com>
Sat, 7 Aug 2021 12:15:54 +0000 (14:15 +0200)
committerEisbison <eisbison.creator@gmail.com>
Sat, 7 Aug 2021 12:15:54 +0000 (14:15 +0200)
15 files changed:
README.md
TheOtherRoles/Buttons.cs
TheOtherRoles/CustomOptions.cs
TheOtherRoles/Helpers.cs
TheOtherRoles/Main.cs
TheOtherRoles/Modules/ChatCommands.cs
TheOtherRoles/Patches/ExileControllerPatch.cs
TheOtherRoles/Patches/MeetingPatch.cs
TheOtherRoles/Patches/PlayerControlPatch.cs
TheOtherRoles/Patches/RoleAssignmentPatch.cs
TheOtherRoles/Patches/UpdatePatch.cs
TheOtherRoles/RPC.cs
TheOtherRoles/RoleInfo.cs
TheOtherRoles/TheOtherRoles.cs
TheOtherRoles/TheOtherRoles.csproj

index 24eff2662e7742f5b38bf0867810be52b878434d..ab1ef2b98a5a5277e3583d62d2cb7aaa0123a763 100644 (file)
--- a/README.md
+++ b/README.md
@@ -30,13 +30,15 @@ Even more roles are coming soon :)
 |  | [Swapper](#swapper) |  |  |
 |  | [Time Master](#time-master) |  |  |
 |  |  [Tracker](#tracker) |  |  |
+|  |  [Bait](#bait) |  |
 
 The [Role Assignment](#role-assignment) sections explains how the roles are being distributed among the players.
 
 # Releases
 | Among Us - Version| Mod Version | Link |
 |----------|-------------|-----------------|
-| **2021.6.30s**| v2.7.3| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.7.3/TheOtherRoles.zip)
+| 2021.6.30s| v2.8.0| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.8.0/TheOtherRoles.zip)
+| 2021.6.30s| v2.7.3| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.7.3/TheOtherRoles.zip)
 | 2021.6.15s| v2.7.1| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.7.1/TheOtherRoles.zip)
 | 2021.6.15s| v2.7.0| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.7.0/TheOtherRoles.zip)
 | 2021.5.25.2s| v2.6.7| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.6.7/TheOtherRoles.zip)
@@ -72,6 +74,12 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein
 <details>
   <summary>Click to show the Changelog</summary>
 
+**Version 2.8.0**
+- **New Role:** [Bait](#bait) created by [Mallöris](https://github.com/Mallaris) 
+- Added Tracker Option: "Tracker Reset Target After Meeting" (feature created by [MaximeGillot](https://github.com/MaximeGillot))
+- Added Snitch Options: "Include Team Jackal" and "Use Different Arrow Color For Team Jackal" (feature created by [Mallöris](https://github.com/Mallaris))
+- Added Medic Option: "Shield Will Be Set After Next Meeting" (feature created by [Mallöris](https://github.com/Mallaris))
+
 **Version 2.7.3**
 - Updated to Among Us v2021.6.30
 - Updated BepInEx version
@@ -866,6 +874,7 @@ The Medic's other feature shows when they report a corpse: they will see how lon
 **NOTE:**
 - If the shielded player is a Lover and the other Lover dies, they nevertheless kill themselves.
 - If the Shifter has a shield or their target has a Shield, the shielded player switches.
+- Shields set after the next meeting, will be set before a possible shift is being performed.
 
 
 ### Game Options
@@ -874,6 +883,7 @@ The Medic's other feature shows when they report a corpse: they will see how lon
 | Medic Spawn Chance | - | -
 | Show Shielded Player | Sets who sees if a player has a shield | "Everyone", "Shielded + Medic", "Medic"
 | Shielded Player Sees Murder Attempt| Whether a shielded player sees if someone tries to kill him | True/false |
+| Shield Will Be Set After Next Meeting | - | True/false
 -----------------------
 
 ## Mayor
@@ -973,7 +983,8 @@ and can't fix lights and comms.
 
 ## Tracker
 ### **Team: Crewmates**
-The Tracker can select one player in a game and tracks this player for the rest of the game.
+The Tracker can select one player to track.
+Depending on the options the Tracker can track a different person after each meeting or the Tracker tracks the same person for the whole game.
 An arrow points to the last tracked position of the player.
 The arrow updates it's position every few seconds (configurable).
 
@@ -982,18 +993,21 @@ The arrow updates it's position every few seconds (configurable).
 |----------|:-------------:|
 | Tracker Spawn Chance | -
 | Tracker Update Intervall | Sets how often the position is being updated
+| Tracker Reset Target After Meeting | -
 -----------------------
 
 ## Snitch
 ### **Team: Crewmates**
-When the Snitch finishes all the tasks, arrows will appear (only visible to the Snitch) that point to the Impostors.
-When the Snitch has one task left (configurable) the Snitch will be revealed to the Impostors, also with an arrow.
+When the Snitch finishes all the tasks, arrows will appear (only visible to the Snitch) that point to the Impostors (depending on the options also to members of team Jackal).
+When the Snitch has one task left (configurable) the Snitch will be revealed to the Impostors (depending on the options also to members of team Jackal) with an arrow pointing to the Snitch.
 
 ### Game Options
 | Name | Description
 |----------|:-------------:|
 | Snitch Spawn Chance | -
-| Task Count Where Impostors See Snitch | -
+| Task Count Where The Snitch Will Be Revealed | -
+| Include Team Jackal | -
+| Use Different Arrow Color For Team Jackal | -
 -----------------------
 
 ## Jackal
@@ -1103,6 +1117,21 @@ The vents will be sealed after the next meeting, players can't enter or exit sea
 | Number Of Screws Per Vent | The number of screws it takes to seal a vent
 -----------------------
 
+## Bait
+### **Team: Crewmates**
+\
+Created by  [Mallöris](https://github.com/Mallaris)\
+\
+The Bait is a Crewmate that if killed, forces the killer to self report the body (you can configure a delay in the options).
+Additionally, the Bait can see if someone is inside a vent (depending on the options the exact vent gets
+an outline or all vents do).
+
+### Game Options
+| Name | Description
+|----------|:-------------:|
+| Bait Spawn Chance | -
+| Bait Highlight All Vents | If set to true, all vents will be highlighted if a player is inside of one of them. If set to false, only the vents where players are siting in will be highlighted.
+| Bait Report Delay | -
 
 # Source code
 It's bad I know, this is a side project and my second week of modding. So there are no best practices around here.
index 6520d3409e6ccc972a895bd5509b2f701ca1a377..a1a5887c96ef39423bc353bc11ba21ebdb00e552 100644 (file)
@@ -227,10 +227,13 @@ namespace TheOtherRoles
                 () => {
                     medicShieldButton.Timer = 0f;
  
-                    MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.MedicSetShielded, Hazel.SendOption.Reliable, -1);
+                    MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, Medic.setShieldAfterMeeting ? (byte)CustomRPC.SetFutureShielded : (byte)CustomRPC.MedicSetShielded, Hazel.SendOption.Reliable, -1);
                     writer.Write(Medic.currentTarget.PlayerId);
                     AmongUsClient.Instance.FinishRpcImmediately(writer);
-                    RPCProcedure.medicSetShielded(Medic.currentTarget.PlayerId);
+                    if (Medic.setShieldAfterMeeting)
+                        RPCProcedure.setFutureShielded(Medic.currentTarget.PlayerId);
+                    else
+                        RPCProcedure.medicSetShielded(Medic.currentTarget.PlayerId);
                 },
                 () => { return Medic.medic != null && Medic.medic == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; },
                 () => { return !Medic.usedShield && Medic.currentTarget && PlayerControl.LocalPlayer.CanMove; },
@@ -354,7 +357,7 @@ namespace TheOtherRoles
                 },
                 () => { return Tracker.tracker != null && Tracker.tracker == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; },
                 () => { return PlayerControl.LocalPlayer.CanMove && Tracker.currentTarget != null && !Tracker.usedTracker; },
-                () => { },
+                () => { if(Tracker.resetTargetAfterMeeting) Tracker.resetTracked(); },
                 Tracker.getButtonSprite(),
                 new Vector3(-1.3f, 0, 0),
                 __instance,
index 58116e9e0b1971c1bd5ba08f684361f43031ceb3..e63afc0b307bb4f664f718c4450a42a06d4cc58a 100644 (file)
@@ -113,6 +113,7 @@ namespace TheOtherRoles {
         public static CustomOption medicSpawnRate;
         public static CustomOption medicShowShielded;
         public static CustomOption medicShowAttemptToShielded;
+        public static CustomOption medicSetShieldAfterMeeting;
 
         public static CustomOption swapperSpawnRate;
         public static CustomOption swapperCanCallEmergency;
@@ -130,9 +131,12 @@ namespace TheOtherRoles {
 
         public static CustomOption trackerSpawnRate;
         public static CustomOption trackerUpdateIntervall;
+        public static CustomOption trackerResetTargetAfterMeeting;
 
         public static CustomOption snitchSpawnRate;
-        public static CustomOption snitchLeftTasksForImpostors;
+        public static CustomOption snitchLeftTasksForReveal;
+        public static CustomOption snitchIncludeTeamJackal;
+        public static CustomOption snitchTeamJackalUseDifferentArrowColor;
 
         public static CustomOption spySpawnRate;
         public static CustomOption spyCanDieToSheriff;
@@ -158,6 +162,10 @@ namespace TheOtherRoles {
         public static CustomOption securityGuardCamPrice;
         public static CustomOption securityGuardVentPrice;
 
+        public static CustomOption baitSpawnRate;
+        public static CustomOption baitHighlightAllVents;
+        public static CustomOption baitReportDelay;
+
         public static CustomOption maxNumberOfMeetings;
         public static CustomOption blockSkippingInEmergencyMeetings;
         public static CustomOption noVoteIsSelfVote;
@@ -292,6 +300,7 @@ namespace TheOtherRoles {
             medicSpawnRate = CustomOption.Create(140, cs(Medic.color, "Medic"), rates, null, true);
             medicShowShielded = CustomOption.Create(143, "Show Shielded Player", new string[] {"Everyone", "Shielded + Medic", "Medic"}, medicSpawnRate);
             medicShowAttemptToShielded = CustomOption.Create(144, "Shielded Player Sees Murder Attempt", false, medicSpawnRate);
+            medicSetShieldAfterMeeting = CustomOption.Create(145, "Shield Will Be Set After The Next Meeting", false, medicSpawnRate);
 
             swapperSpawnRate = CustomOption.Create(150, cs(Swapper.color, "Swapper"), rates, null, true);
             swapperCanCallEmergency = CustomOption.Create(151, "Swapper can call emergency meeting", false, swapperSpawnRate);
@@ -309,9 +318,11 @@ namespace TheOtherRoles {
 
             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);
-
+            trackerResetTargetAfterMeeting = CustomOption.Create(202, "Tracker Reset Target After Meeting", false, trackerSpawnRate);
             snitchSpawnRate = CustomOption.Create(210, cs(Snitch.color, "Snitch"), rates, null, true);
-            snitchLeftTasksForImpostors = CustomOption.Create(211, "Task Count Where Impostors See Snitch", 1f, 0f, 5f, 1f, snitchSpawnRate);
+            snitchLeftTasksForReveal = CustomOption.Create(211, "Task Count Where The Snitch Will Be Revealed", 1f, 0f, 5f, 1f, snitchSpawnRate);
+            snitchIncludeTeamJackal = CustomOption.Create(212, "Include Team Jackal", false, snitchSpawnRate);
+            snitchTeamJackalUseDifferentArrowColor = CustomOption.Create(213, "Use Different Arrow Color For Team Jackal", true, snitchIncludeTeamJackal);
 
             spySpawnRate = CustomOption.Create(240, cs(Spy.color, "Spy"), rates, null, true);
             spyCanDieToSheriff = CustomOption.Create(241, "Spy Can Die To Sheriff", false, spySpawnRate);
@@ -325,6 +336,10 @@ namespace TheOtherRoles {
             securityGuardCamPrice = CustomOption.Create(283, "Number Of Screws Per Cam", 2f, 1f, 15f, 1f, securityGuardSpawnRate);
             securityGuardVentPrice = CustomOption.Create(284, "Number Of Screws Per Vent", 1f, 1f, 15f, 1f, securityGuardSpawnRate);
 
+            baitSpawnRate = CustomOption.Create(330, cs(Bait.color, "Bait"), rates, null, true);
+            baitHighlightAllVents = CustomOption.Create(331, "Highlight All Vents If A Vent Is Occupied", false, baitSpawnRate);
+            baitReportDelay = CustomOption.Create(332, "Bait Report Delay", 0f, 0f, 10f, 1f, baitSpawnRate);
+
             // Other options
             maxNumberOfMeetings = CustomOption.Create(3, "Number Of Meetings (excluding Mayor meeting)", 10, 0, 15, 1, null, true);
             blockSkippingInEmergencyMeetings = CustomOption.Create(4, "Block Skipping In Emergency Meetings", false);
@@ -643,7 +658,7 @@ namespace TheOtherRoles {
             var hudString = sb.ToString();
 
             int defaultSettingsLines = 19;
-            int roleSettingsLines = defaultSettingsLines + 34;
+            int roleSettingsLines = defaultSettingsLines + 35;
             int detailedSettingsP1 = roleSettingsLines + 37;
             int detailedSettingsP2 = detailedSettingsP1 + 38;
             int end1 = hudString.TakeWhile(c => (defaultSettingsLines -= (c == '\n' ? 1 : 0)) > 0).Count();
index 1d5915fc32cf2e567720af9250e0fb7287c4a453..5446ec83d7619eff03de99fc55a1f8d0ef0fcef7 100644 (file)
@@ -126,7 +126,6 @@ namespace TheOtherRoles {
             return true;
         }
 
-
         public static void refreshRoleDescription(PlayerControl player) {
             if (player == null) return;
 
index a61cf0302a1ab8b230397f459d721fa994a8e3e6..65b06faed4d8f84c95c1fd0716abe333f9870de3 100644 (file)
@@ -21,7 +21,7 @@ namespace TheOtherRoles
     public class TheOtherRolesPlugin : BasePlugin
     {
         public const string Id = "me.eisbison.theotherroles";
-        public const string VersionString = "2.7.3";
+        public const string VersionString = "2.8.0";
         public static System.Version Version = System.Version.Parse(VersionString);
 
         public Harmony Harmony { get; } = new Harmony(Id);
index 4ca9050d939588edfcc2496487e00d5285d16e7f..480c10fd4c5ad02bc4ec15a3b381f3a8c6b745df 100644 (file)
@@ -57,6 +57,16 @@ namespace TheOtherRoles.Modules {
                         __instance.AddChat(PlayerControl.LocalPlayer, "Changed color succesfully");;
                     } 
                 }
+
+                if (text.ToLower().StartsWith("/tp ") && PlayerControl.LocalPlayer.Data.IsDead) {
+                    string playerName = text.Substring(4).ToLower();
+                    PlayerControl target = PlayerControl.AllPlayerControls.ToArray().ToList().FirstOrDefault(x => x.Data.PlayerName.ToLower().Equals(playerName));
+                    if (target != null) {
+                        PlayerControl.LocalPlayer.transform.position = target.transform.position;
+                        handled = true;
+                    }
+                }
+
                 if (handled) {
                     __instance.TextArea.Clear();
                     __instance.quickChatMenu.ResetGlyphs();
index 5b0eaa694c26f08bc83854a75aa52d026f2de558..b8b0b87c8a81220701cc3996560da060c6146671 100644 (file)
@@ -16,6 +16,14 @@ namespace TheOtherRoles.Patches {
     [HarmonyPatch(typeof(ExileController), "Begin")]
     class ExileControllerBeginPatch {
         public static void Prefix(ExileController __instance, [HarmonyArgument(0)]ref GameData.PlayerInfo exiled, [HarmonyArgument(1)]bool tie) {
+            // Medic shield
+            if (Medic.medic != null && AmongUsClient.Instance.AmHost && Medic.futureShielded != null && !Medic.medic.Data.IsDead) { // We need to send the RPC from the host here, to make sure that the order of shifting and setting the shield is correct(for that reason the futureShifted and futureShielded are being synced)
+                MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.MedicSetShielded, Hazel.SendOption.Reliable, -1);
+                writer.Write(Medic.futureShielded.PlayerId);
+                AmongUsClient.Instance.FinishRpcImmediately(writer);
+                RPCProcedure.medicSetShielded(Medic.futureShielded.PlayerId);
+            }
+
             // Shifter shift
             if (Shifter.shifter != null && AmongUsClient.Instance.AmHost && Shifter.futureShift != null) { // We need to send the RPC from the host here, to make sure that the order of shifting and erasing is correct (for that reason the futureShifted and futureErased are being synced)
                 MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ShifterShift, Hazel.SendOption.Reliable, -1);
index 7e92806458d2a9e7d7fa482c0f328e1643911b3c..052a87e6f77af7095763351ca9f974666b698582 100644 (file)
@@ -38,7 +38,7 @@ namespace TheOtherRoles.Patches {
                     }
                 }
                 // Swapper swap votes
-                if (Swapper.swapper != null && !Swapper.swapper.isDead) { // Don't swap if swapper is dead
+                if (Swapper.swapper != null && !Swapper.swapper.Data.IsDead) {
                     PlayerVoteArea swapped1 = null;
                     PlayerVoteArea swapped2 = null;
                     foreach (PlayerVoteArea playerVoteArea in __instance.playerStates) {
@@ -111,18 +111,17 @@ namespace TheOtherRoles.Patches {
             
             static bool Prefix(MeetingHud __instance, Il2CppStructArray<MeetingHud.VoterState> states) {
                 // Swapper swap
-                if (Swapper.swapper != null && !Swapper.swapper.isDead) { // Don'T swap if dead
-                    PlayerVoteArea swapped1 = null;
-                    PlayerVoteArea swapped2 = null;
-                    foreach (PlayerVoteArea playerVoteArea in __instance.playerStates) {
-                        if (playerVoteArea.TargetPlayerId == Swapper.playerId1) swapped1 = playerVoteArea;
-                        if (playerVoteArea.TargetPlayerId == Swapper.playerId2) swapped2 = playerVoteArea;
-                    }
-                    bool doSwap = swapped1 != null && swapped2 != null;
-                    if (doSwap) {
-                        __instance.StartCoroutine(Effects.Slide3D(swapped1.transform, swapped1.transform.localPosition, swapped2.transform.localPosition, 1.5f));
-                        __instance.StartCoroutine(Effects.Slide3D(swapped2.transform, swapped2.transform.localPosition, swapped1.transform.localPosition, 1.5f));
-                    }
+
+                PlayerVoteArea swapped1 = null;
+                PlayerVoteArea swapped2 = null;
+                foreach (PlayerVoteArea playerVoteArea in __instance.playerStates) {
+                    if (playerVoteArea.TargetPlayerId == Swapper.playerId1) swapped1 = playerVoteArea;
+                    if (playerVoteArea.TargetPlayerId == Swapper.playerId2) swapped2 = playerVoteArea;
+                }
+                bool doSwap = swapped1 != null && swapped2 != null && Swapper.swapper != null && !Swapper.swapper.Data.IsDead;
+                if (doSwap) {
+                    __instance.StartCoroutine(Effects.Slide3D(swapped1.transform, swapped1.transform.localPosition, swapped2.transform.localPosition, 1.5f));
+                    __instance.StartCoroutine(Effects.Slide3D(swapped2.transform, swapped2.transform.localPosition, swapped1.transform.localPosition, 1.5f));
                 }
 
 
index 029143ad51a1a9ac6ef3dff433b3fbc9771ee87b..a15210f32d6de78c7a66274459ed03273a3ac4ec 100644 (file)
@@ -435,8 +435,7 @@ namespace TheOtherRoles.Patches {
             var (playerCompleted, playerTotal) = TasksHandler.taskInfo(Snitch.snitch.Data);
             int numberOfTasks = playerTotal - playerCompleted;
 
-            if (PlayerControl.LocalPlayer.Data.IsImpostor && numberOfTasks <= Snitch.taskCountForImpostors)
-            {
+            if (numberOfTasks <= Snitch.taskCountForReveal && (PlayerControl.LocalPlayer.Data.IsImpostor || (Snitch.includeTeamJackal && (PlayerControl.LocalPlayer == Jackal.jackal || PlayerControl.LocalPlayer == Sidekick.sidekick)))) {
                 if (Snitch.localArrows.Count == 0) Snitch.localArrows.Add(new Arrow(Color.blue));
                 if (Snitch.localArrows.Count != 0 && Snitch.localArrows[0] != null)
                 {
@@ -449,11 +448,12 @@ namespace TheOtherRoles.Patches {
                 int arrowIndex = 0;
                 foreach (PlayerControl p in PlayerControl.AllPlayerControls)
                 {
-                    if (p.Data.IsImpostor && !p.Data.IsDead)
-                    {
-                        if (arrowIndex >= Snitch.localArrows.Count) Snitch.localArrows.Add(new Arrow(Color.blue));
-                        if (arrowIndex < Snitch.localArrows.Count && Snitch.localArrows[arrowIndex] != null)
-                        {
+                    if (!p.Data.IsDead && (p.Data.IsImpostor || (Snitch.includeTeamJackal && (p == Jackal.jackal || p == Sidekick.sidekick)))) {
+                        if (arrowIndex >= Snitch.localArrows.Count) {
+                            if (Snitch.teamJackalUseDifferentArrowColor && (p == Jackal.jackal || p == Sidekick.sidekick)) Snitch.localArrows.Add(new Arrow(Jackal.color));
+                            else Snitch.localArrows.Add(new Arrow(Palette.ImpostorRed));
+                        }
+                        if (arrowIndex < Snitch.localArrows.Count && Snitch.localArrows[arrowIndex] != null) {
                             Snitch.localArrows[arrowIndex].arrow.SetActive(true);
                             Snitch.localArrows[arrowIndex].Update(p.transform.position);
                         }
@@ -517,6 +517,41 @@ namespace TheOtherRoles.Patches {
             }
         }
 
+        static void baitUpdate() {
+            if (Bait.bait == null || Bait.bait != PlayerControl.LocalPlayer) return;
+
+            // Bait report
+            if (Bait.bait.Data.IsDead && !Bait.reported) {
+                Bait.reportDelay -= Time.fixedDeltaTime;
+                DeadPlayer deadPlayer = deadPlayers?.Where(x => x.player?.PlayerId == Bait.bait.PlayerId)?.FirstOrDefault();
+                if (deadPlayer.killerIfExisting != null && Bait.reportDelay <= 0f) {
+                    deadPlayer.killerIfExisting.CmdReportDeadBody(Bait.bait.Data);
+                    Bait.reported = true;
+                }
+            }
+
+            // Bait Vents
+            if (ShipStatus.Instance?.AllVents != null) {
+                var ventsWithPlayers = new List<int>();
+                foreach (PlayerControl player in PlayerControl.AllPlayerControls) {
+                    if (player.inVent) {
+                        Vent target = ShipStatus.Instance.AllVents.OrderBy(x => Vector2.Distance(x.transform.position, player.GetTruePosition())).FirstOrDefault();
+                        if (target != null) ventsWithPlayers.Add(target.Id);
+                    }
+                }
+
+                foreach (Vent vent in ShipStatus.Instance.AllVents) {
+                    if (vent.myRend == null || vent.myRend.material == null) continue;
+                    if (ventsWithPlayers.Contains(vent.Id) || (ventsWithPlayers.Count > 0 && Bait.highlightAllVents)) {
+                        vent.myRend.material.SetFloat("_Outline", 1f);
+                        vent.myRend.material.SetColor("_OutlineColor", Color.yellow);
+                    } else {
+                        vent.myRend.material.SetFloat("_Outline", 0);
+                    }
+                }
+            }
+        }
+
         public static void Postfix(PlayerControl __instance) {
             if (AmongUsClient.Instance.GameState != InnerNet.InnerNetClient.GameStates.Started) return;
 
@@ -574,6 +609,8 @@ namespace TheOtherRoles.Patches {
                 snitchUpdate();
                 // BountyHunter
                 bountyHunterUpdate();
+                // Bait
+                baitUpdate();
             } 
         }
     }
index 77b63bfc4bd178bc118d965eadd0fdbe358a86ee..94ede4934f7e57b6d36eac75f68837a766b35329 100644 (file)
@@ -90,6 +90,7 @@ namespace TheOtherRoles.Patches {
             crewSettings.Add((byte)RoleId.Hacker, CustomOptionHolder.hackerSpawnRate.getSelection());
             crewSettings.Add((byte)RoleId.Tracker, CustomOptionHolder.trackerSpawnRate.getSelection());
             crewSettings.Add((byte)RoleId.Snitch, CustomOptionHolder.snitchSpawnRate.getSelection());
+            crewSettings.Add((byte)RoleId.Bait, CustomOptionHolder.baitSpawnRate.getSelection());
             if (impostors.Count > 1) {
                 // Only add Spy if more than 1 impostor as the spy role is otherwise useless
                 crewSettings.Add((byte)RoleId.Spy, CustomOptionHolder.spySpawnRate.getSelection());
index 0b8477743e5b9792a590cf905cb39e1aedaa9134..ebb0da6a80b6faa55c3d3014543330175920b521 100644 (file)
@@ -116,6 +116,8 @@ namespace TheOtherRoles.Patches {
                 setPlayerNameColor(Arsonist.arsonist, Arsonist.color);
             } else if (Guesser.guesser != null && Guesser.guesser == PlayerControl.LocalPlayer) {
                 setPlayerNameColor(Guesser.guesser, Guesser.guesser.Data.IsImpostor ? Palette.ImpostorRed : Guesser.color);
+            } else if (Bait.bait != null && Bait.bait == PlayerControl.LocalPlayer) {
+                setPlayerNameColor(Bait.bait, Bait.color);
             }
 
             // No else if here, as a Lover of team Jackal needs the colors
index 795557a93fc30e85f1d3be0ec4446590770fa136..740376f84bbb455ba3d85fd090b5ef588b6e2bd3 100644 (file)
@@ -47,6 +47,7 @@ namespace TheOtherRoles
         Arsonist,
         Guesser,
         BountyHunter,
+        Bait,
         Crewmate,
         Impostor
     }
@@ -62,6 +63,7 @@ namespace TheOtherRoles
         VersionHandshake,
         UseUncheckedVent,
         UncheckedMurderPlayer,
+
         // Role functionality
 
         EngineerFixLights = 81,
@@ -87,6 +89,7 @@ namespace TheOtherRoles
         ErasePlayerRoles,
         SetFutureErased,
         SetFutureShifted,
+        SetFutureShielded,
         PlaceJackInTheBox,
         LightsOut,
         WarlockCurseKill,
@@ -230,6 +233,9 @@ namespace TheOtherRoles
                     case RoleId.BountyHunter:
                         BountyHunter.bountyHunter = player;
                         break;
+                    case RoleId.Bait:
+                        Bait.bait = player;
+                        break;
                     }
                 }
         }
@@ -326,9 +332,8 @@ namespace TheOtherRoles
 
         public static void medicSetShielded(byte shieldedId) {
             Medic.usedShield = true;
-            foreach (PlayerControl player in PlayerControl.AllPlayerControls)
-                if (player.PlayerId == shieldedId)
-                    Medic.shielded = player;
+            Medic.shielded = Helpers.playerById(shieldedId);
+            Medic.futureShielded = null;
         }
 
         public static void shieldedMurderAttempt() {
@@ -411,6 +416,8 @@ namespace TheOtherRoles
                 SecurityGuard.securityGuard = oldShifter;
             if (Guesser.guesser != null && Guesser.guesser == player)
                 Guesser.guesser = oldShifter;
+            if (Bait.bait != null && Bait.bait == player)
+                Bait.bait = oldShifter;
             
             // Set cooldowns to max for both players
             if (PlayerControl.LocalPlayer == oldShifter || PlayerControl.LocalPlayer == player)
@@ -542,6 +549,7 @@ namespace TheOtherRoles
             if (player == Swapper.swapper) Swapper.clearAndReload();
             if (player == Spy.spy) Spy.clearAndReload();
             if (player == SecurityGuard.securityGuard) SecurityGuard.clearAndReload();
+            if (player == Bait.bait) Bait.clearAndReload();
 
             // Impostor roles
             if (player == Morphling.morphling) Morphling.clearAndReload();
@@ -585,6 +593,11 @@ namespace TheOtherRoles
         public static void setFutureShifted(byte playerId) {
             Shifter.futureShift = Helpers.playerById(playerId);
         }
+
+        public static void setFutureShielded(byte playerId) {
+            Medic.futureShielded = Helpers.playerById(playerId);
+            Medic.usedShield = true;
+        }
         
         public static void placeJackInTheBox(byte[] buff) {
             Vector3 position = Vector3.zero;
@@ -815,6 +828,9 @@ namespace TheOtherRoles
                 case (byte)CustomRPC.SetFutureShifted:
                     RPCProcedure.setFutureShifted(reader.ReadByte());
                     break;
+                case (byte)CustomRPC.SetFutureShielded:
+                    RPCProcedure.setFutureShielded(reader.ReadByte());
+                    break;
                 case (byte)CustomRPC.PlaceJackInTheBox:
                     RPCProcedure.placeJackInTheBox(reader.ReadBytesAndSize());
                     break;
index 01a15c184959fb350001e1ceeda3202a8a590514..afe05d470b4ebe88db1bda26b79474a0dc558a0c 100644 (file)
@@ -56,6 +56,7 @@ namespace TheOtherRoles
         public static RoleInfo arsonist = new RoleInfo("Arsonist", Arsonist.color, "Let them burn", "Let them burn", RoleId.Arsonist);
         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 bait = new RoleInfo("Bait", Bait.color, "Bait your enemies", "Bait your enemies", RoleId.Bait);
         public static RoleInfo impostor = new RoleInfo("Impostor", Palette.ImpostorRed, Helpers.cs(Palette.ImpostorRed, "Sabotage and kill everyone"), "Sabotage and kill everyone", RoleId.Impostor);
         public static RoleInfo crewmate = new RoleInfo("Crewmate", Color.white, "Find the Impostors", "Find the Impostors", RoleId.Crewmate);
         public static RoleInfo lover = new RoleInfo("Lover", Lovers.color, $"You are in love", $"You are in love", RoleId.Lover);
@@ -98,7 +99,8 @@ namespace TheOtherRoles
             snitch,
             spy,
             securityGuard,
-            bountyHunter
+            bountyHunter,
+            bait
         };
 
         public static List<RoleInfo> getRoleInfoForPlayer(PlayerControl p) {
@@ -138,6 +140,7 @@ namespace TheOtherRoles
             if (p == Arsonist.arsonist) infos.Add(arsonist);
             if (p == Guesser.guesser) infos.Add(p.Data.IsImpostor ? badGuesser : goodGuesser);
             if (p == BountyHunter.bountyHunter) infos.Add(bountyHunter);
+            if (p == Bait.bait) infos.Add(bait);
 
             // Default roles
             if (infos.Count == 0 && p.Data.IsImpostor) infos.Add(impostor); // Just Impostor
index 9790580533da99cc795827846d327491d05c8cad..53c697207996ad5eeab9f9eee47b87dfc9e6c48a 100644 (file)
@@ -53,6 +53,7 @@ namespace TheOtherRoles
             Arsonist.clearAndReload();
             Guesser.clearAndReload();
             BountyHunter.clearAndReload();
+            Bait.clearAndReload();
         }
 
         public static class Jester {
@@ -239,11 +240,14 @@ namespace TheOtherRoles
     public static class Medic {
         public static PlayerControl medic;
         public static PlayerControl shielded;
+        public static PlayerControl futureShielded;
+        
         public static Color color = new Color32(126, 251, 194, byte.MaxValue);
         public static bool usedShield;
 
         public static int showShielded = 0;
         public static bool showAttemptToShielded = false;
+        public static bool setShieldAfterMeeting = false;
 
         public static Color shieldedColor = new Color32(0, 221, 255, byte.MaxValue);
         public static PlayerControl currentTarget;
@@ -258,10 +262,12 @@ namespace TheOtherRoles
         public static void clearAndReload() {
             medic = null;
             shielded = null;
+            futureShielded = null;
             currentTarget = null;
             usedShield = false;
             showShielded = CustomOptionHolder.medicShowShielded.getSelection();
             showAttemptToShielded = CustomOptionHolder.medicShowAttemptToShielded.getBool();
+            setShieldAfterMeeting = CustomOptionHolder.medicSetShieldAfterMeeting.getBool();
         }
     }
 
@@ -533,6 +539,7 @@ namespace TheOtherRoles
         public static Color color = new Color32(100, 58, 220, byte.MaxValue);
 
         public static float updateIntervall = 5f;
+        public static bool resetTargetAfterMeeting = false;
 
         public static PlayerControl currentTarget;
         public static PlayerControl tracked;
@@ -547,17 +554,21 @@ namespace TheOtherRoles
             return buttonSprite;
         }
 
-        public static void clearAndReload() {
-            tracker = null;
-            currentTarget = null;
-            tracked = null;
+        public static void resetTracked() {
+            currentTarget = tracked = null;
             usedTracker = false;
-            timeUntilUpdate = 0f;
-            updateIntervall = CustomOptionHolder.trackerUpdateIntervall.getFloat();
             if (arrow?.arrow != null) UnityEngine.Object.Destroy(arrow.arrow);
             arrow = new Arrow(Color.blue);
             if (arrow.arrow != null) arrow.arrow.SetActive(false);
         }
+
+        public static void clearAndReload() {
+            tracker = null;
+            resetTracked();
+            timeUntilUpdate = 0f;
+            updateIntervall = CustomOptionHolder.trackerUpdateIntervall.getFloat();
+            resetTargetAfterMeeting = CustomOptionHolder.trackerResetTargetAfterMeeting.getBool();
+        }
     }
 
     public static class Vampire {
@@ -606,7 +617,10 @@ namespace TheOtherRoles
         public static Color color = new Color32(184, 251, 79, byte.MaxValue);
 
         public static List<Arrow> localArrows = new List<Arrow>();
-        public static int taskCountForImpostors = 1;
+        public static int taskCountForReveal = 1;
+        public static bool includeTeamJackal = false;
+        public static bool teamJackalUseDifferentArrowColor = true;
+
 
         public static void clearAndReload() {
             if (localArrows != null) {
@@ -615,7 +629,9 @@ namespace TheOtherRoles
                     UnityEngine.Object.Destroy(arrow.arrow);
             }
             localArrows = new List<Arrow>();
-            taskCountForImpostors = Mathf.RoundToInt(CustomOptionHolder.snitchLeftTasksForImpostors.getFloat());
+            taskCountForReveal = Mathf.RoundToInt(CustomOptionHolder.snitchLeftTasksForReveal.getFloat());
+            includeTeamJackal = CustomOptionHolder.snitchIncludeTeamJackal.getBool();
+            teamJackalUseDifferentArrowColor = CustomOptionHolder.snitchTeamJackalUseDifferentArrowColor.getBool();
             snitch = null;
         }
     }
@@ -993,4 +1009,21 @@ namespace TheOtherRoles
             arrowUpdateIntervall = CustomOptionHolder.bountyHunterArrowUpdateIntervall.getFloat();
         }
     }
+
+    public static class Bait {
+        public static PlayerControl bait;
+        public static Color color = new Color32(0, 247, 255, byte.MaxValue);
+
+        public static bool highlightAllVents = false;
+        public static float reportDelay = 0f;
+
+        public static bool reported = false;
+
+        public static void clearAndReload() {
+            bait = null;
+            reported = false;
+            highlightAllVents = CustomOptionHolder.baitHighlightAllVents.getBool();
+            reportDelay = CustomOptionHolder.baitReportDelay.getFloat();
+        }
+    }
 }
\ No newline at end of file
index aa25c6e4733df8d8df443d22dce9fb0a30365ee3..cc663b5d60dee99a619158cf6d8daa1c9afc0053 100644 (file)
@@ -1,13 +1,12 @@
 <Project Sdk="Microsoft.NET.Sdk">
     <PropertyGroup>
         <TargetFramework>netstandard2.1</TargetFramework>
-        <Version>2.7.3</Version>
+        <Version>2.8.0</Version>
         <Description>TheOtherRoles</Description>
         <Authors>Eisbison</Authors>
     </PropertyGroup>
 
     <PropertyGroup>
-        <GameVersion>2021.6.15</GameVersion>
         <DefineConstants>$(DefineConstants);STEAM</DefineConstants>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     </PropertyGroup>