]> git.deb.at Git - rhonda/theotherroles.git/commitdiff
Update source code
authorEisbison <eisbison.creator@gmail.com>
Fri, 26 Mar 2021 11:11:31 +0000 (12:11 +0100)
committerEisbison <eisbison.creator@gmail.com>
Fri, 26 Mar 2021 11:11:31 +0000 (12:11 +0100)
23 files changed:
Source Code/Arrow.cs [new file with mode: 0644]
Source Code/BonusRoles.cs [deleted file]
Source Code/BonusRoles.csproj [deleted file]
Source Code/Buttons.cs
Source Code/CredentialsPatch.cs
Source Code/CustomButton.cs
Source Code/EndGamePatch.cs
Source Code/Footprint.cs
Source Code/GameHistory.cs
Source Code/GameOptionsPatch.cs
Source Code/Garlic.cs [new file with mode: 0644]
Source Code/Helpers.cs
Source Code/IntroPatch.cs
Source Code/Main.cs
Source Code/MeetingPatch.cs
Source Code/PlayerControlPatch.cs
Source Code/RPC.cs
Source Code/RoleAssignmentPatch.cs
Source Code/ShipStatusPatch.cs
Source Code/TheOtherRoles.cs [new file with mode: 0644]
Source Code/TheOtherRoles.csproj [new file with mode: 0644]
Source Code/UpdatePatch.cs
Source Code/UsablesPatch.cs

diff --git a/Source Code/Arrow.cs b/Source Code/Arrow.cs
new file mode 100644 (file)
index 0000000..a4d0360
--- /dev/null
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using UnityEngine;
+using static TheOtherRoles.TheOtherRoles;
+
+namespace TheOtherRoles{
+    public class Arrow {
+        public float perc = 0.925f;
+        public SpriteRenderer image;
+        public GameObject arrow;
+        private Vector3 oldTarget;
+
+        private static Sprite sprite;
+        public static Sprite getSprite() {
+            if (sprite) return sprite;
+            sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Arrow.png", 200f);
+            return sprite;
+        }
+
+
+        public Arrow(Color color) {
+            arrow = new GameObject("Arrow");
+            arrow.layer = 5;
+            image = arrow.AddComponent<SpriteRenderer>();
+            image.sprite = getSprite();
+            image.color = color;
+        }
+
+        public void Update() {
+            Vector3 target = oldTarget;
+            if (target == null) target = Vector3.zero;
+            Update(target);
+        }
+
+        public void Update(Vector3 target)
+        {
+            if (arrow == null) return;
+            oldTarget = target;
+
+            Camera main = Camera.main;
+            Vector2 vector = target - main.transform.position;
+            float num = vector.magnitude / (main.orthographicSize * perc);
+            image.enabled = ((double)num > 0.3);
+            Vector2 vector2 = main.WorldToViewportPoint(target);
+            if (Between(vector2.x, 0f, 1f) && Between(vector2.y, 0f, 1f))
+            {
+                arrow.transform.position = target - (Vector3)vector.normalized * 0.6f;
+                float num2 = Mathf.Clamp(num, 0f, 1f);
+                arrow.transform.localScale = new Vector3(num2, num2, num2);
+            }
+            else
+            {
+                Vector2 vector3 = new Vector2(Mathf.Clamp(vector2.x * 2f - 1f, -1f, 1f), Mathf.Clamp(vector2.y * 2f - 1f, -1f, 1f));
+                float orthographicSize = main.orthographicSize;
+                float num3 = main.orthographicSize * main.aspect;
+                Vector3 vector4 = new Vector3(Mathf.LerpUnclamped(0f, num3 * 0.88f, vector3.x), Mathf.LerpUnclamped(0f, orthographicSize * 0.79f, vector3.y), 0f);
+                arrow.transform.position = main.transform.position + vector4;
+                arrow.transform.localScale = Vector3.one;
+            }
+
+            LookAt2d(arrow.transform, target);
+        }
+
+        private void LookAt2d(Transform transform, Vector3 target) {
+            Vector3 vector = target - transform.position;
+            vector.Normalize();
+            float num = Mathf.Atan2(vector.y, vector.x);
+            if (transform.lossyScale.x < 0f)
+                num += 3.1415927f;
+            transform.rotation = Quaternion.Euler(0f, 0f, num * 57.29578f);
+        }
+
+        private bool Between(float value, float min, float max) {
+            return value > min && value < max;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Source Code/BonusRoles.cs b/Source Code/BonusRoles.cs
deleted file mode 100644 (file)
index 8684017..0000000
+++ /dev/null
@@ -1,438 +0,0 @@
-using System.Net;
-using System.Linq;
-using BepInEx;
-using BepInEx.Configuration;
-using BepInEx.IL2CPP;
-using HarmonyLib;
-using Hazel;
-using Reactor;
-using System;
-using System.Collections.Generic;
-using System.Collections;
-using System.IO;
-using UnityEngine;
-using Reactor.Unstrip;
-using Reactor.Extensions;
-
-namespace BonusRoles
-{
-    [HarmonyPatch]
-    public static class BonusRoles
-    {
-        public static System.Random rnd = new System.Random((int)DateTime.Now.Ticks);
-
-        public static void clearAndReloadRoles() {
-            Jester.clearAndReload();
-            Mayor.clearAndReload();
-            Engineer.clearAndReload();
-            Sheriff.clearAndReload();
-            Lighter.clearAndReload();
-            Godfather.clearAndReload();
-            Mafioso.clearAndReload();
-            Janitor.clearAndReload();
-            Detective.clearAndReload();
-            TimeMaster.clearAndReload();
-            Medic.clearAndReload();
-            Shifter.clearAndReload();
-            Swapper.clearAndReload();
-            Lovers.clearAndReload();
-            Seer.clearAndReload();
-            Morphling.clearAndReload();
-            Camouflager.clearAndReload();
-            Spy.clearAndReload();
-            Child.clearAndReload();
-        }
-
-        public static class Jester {
-            public static PlayerControl jester;
-            public static Color color = new Color(255f / 255f, 84f / 255f, 167f / 255f, 1);
-
-            public static void clearAndReload() {
-                jester = null;
-            }
-        }
-
-        public static class Mayor {
-            public static PlayerControl mayor;
-            public static Color color = new Color(105f / 255f, 58f / 255f, 58f / 255f, 1);
-
-            public static void clearAndReload() {
-                mayor = null;
-            }
-        }
-
-        public static class Engineer {
-            public static PlayerControl engineer;
-            public static Color color = new Color(98f / 255f, 216f / 255f, 240f / 255f, 1);
-            public static bool usedRepair;
-            private static Sprite buttonSprite;
-
-            public static Sprite getButtonSprite() {
-                if (buttonSprite) return buttonSprite;
-                buttonSprite = Helpers.loadSpriteFromResources("BonusRoles.Resources.RepairButton.png", 100f);
-                return buttonSprite;
-            }
-
-            public static void clearAndReload() {
-                engineer = null;
-                usedRepair = false;
-            }
-        }
-
-        public static class Godfather {
-            public static PlayerControl godfather;
-            public static Color color = Palette.ImpostorRed;
-
-            public static void clearAndReload() {
-                godfather = null;
-            }
-        }
-
-        public static class Mafioso {
-            public static PlayerControl mafioso;
-            public static Color color = Palette.ImpostorRed;
-
-            public static void clearAndReload() {
-                mafioso = null;
-            }
-        }
-
-
-        public static class Janitor {
-            public static PlayerControl janitor;
-            public static Color color = Palette.ImpostorRed;
-
-            public static float cooldown = float.MaxValue;
-
-            private static Sprite buttonSprite;
-            public static Sprite getButtonSprite() {
-                if (buttonSprite) return buttonSprite;
-                buttonSprite = Helpers.loadSpriteFromResources("BonusRoles.Resources.CleanButton.png", 100f);
-                return buttonSprite;
-            }
-
-            public static void clearAndReload() {
-                janitor = null;
-                cooldown = BonusRolesPlugin.janitorCooldown.GetValue();
-            }
-        }
-
-        public static class Sheriff {
-            public static PlayerControl sheriff;
-            public static Color color = new Color(255f / 255f, 204f / 255f, 0f / 255f, 1);
-
-            public static float cooldown = float.MaxValue;
-            public static bool jesterCanDieToSheriff = false;
-
-            public static PlayerControl currentTarget;
-
-            public static void clearAndReload() {
-                sheriff = null;
-                currentTarget = null;
-                cooldown = BonusRolesPlugin.sheriffCooldown.GetValue();
-                jesterCanDieToSheriff = BonusRolesPlugin.jesterCanDieToSheriff.GetValue();
-            }
-        }
-
-        public static class Lighter {
-            public static PlayerControl lighter;
-            public static Color color = new Color(250f / 255f, 204f / 255f, 37f / 255f, 1);
-            
-            public static float lighterVision = 2f;
-
-            public static void clearAndReload() {
-                lighter = null;
-                lighterVision = BonusRolesPlugin.lighterVision.GetValue();
-            }
-        }
-
-        public static class Detective {
-            public static PlayerControl detective;
-            public static Color color = new Color(2f / 255f, 61f / 255f, 156f / 255f, 1);
-
-            public static float footprintIntervall = 1f;
-            public static float footprintDuration = 1f;
-            public static bool anonymousFootprints = false;
-
-            public static float timer = 1f;
-
-            public static void clearAndReload() {
-                detective = null;
-                anonymousFootprints = BonusRolesPlugin.detectiveAnonymousFootprints.GetValue();
-                footprintIntervall = BonusRolesPlugin.detectiveFootprintIntervall.GetValue();
-                footprintDuration = BonusRolesPlugin.detectiveFootprintDuration.GetValue();
-                timer = footprintIntervall;
-            }
-        }
-    }
-
-    public static class TimeMaster {
-        public static PlayerControl timeMaster;
-        public static Color color = new Color(114f / 255f, 234f / 255f, 247f / 255f, 1);
-
-        public static bool reviveDuringRewind = false;
-        public static float rewindTime = 3f;
-        public static float cooldown = float.MaxValue;
-
-        public static bool isRewinding = false;
-
-        private static Sprite buttonSprite;
-        public static Sprite getButtonSprite() {
-            if (buttonSprite) return buttonSprite;
-            buttonSprite = Helpers.loadSpriteFromResources("BonusRoles.Resources.RewindButton.png", 100f);
-            return buttonSprite;
-        }
-
-        public static void clearAndReload() {
-            timeMaster = null;
-            isRewinding = false;
-            reviveDuringRewind = BonusRolesPlugin.timeMasterReviveDuringRewind.GetValue();
-            rewindTime = BonusRolesPlugin.timeMasterRewindTime.GetValue();
-            cooldown = BonusRolesPlugin.timeMasterCooldown.GetValue();
-        }
-    }
-
-    public static class Medic {
-        public static PlayerControl medic;
-        public static PlayerControl shielded;
-        public static Color color = new Color(0f / 255f, 80f / 255f, 105f / 255f, 1);
-        public static bool usedShield;
-
-        public static float reportNameDuration = 10f;
-        public static float reportColorDuration = 20f;
-        public static int showShielded = 0;
-        public static bool showAttemptToShielded = false;
-
-        public static Color shieldedColor = new Color(0f / 255f, 221f / 255f, 255f / 255f, 1);
-        public static PlayerControl currentTarget;
-
-        private static Sprite buttonSprite;
-        public static Sprite getButtonSprite() {
-            if (buttonSprite) return buttonSprite;
-            buttonSprite = Helpers.loadSpriteFromResources("BonusRoles.Resources.ShieldButton.png", 100f);
-            return buttonSprite;
-        }
-
-        public static void clearAndReload() {
-            medic = null;
-            shielded = null;
-            currentTarget = null;
-            usedShield = false;
-            shieldedColor = new Color(0f / 255f, 221f / 255f, 255f / 255f, 1);
-            reportNameDuration = BonusRolesPlugin.medicReportNameDuration.GetValue();
-            reportColorDuration = BonusRolesPlugin.medicReportColorDuration.GetValue();
-            showShielded = BonusRolesPlugin.medicShowShielded.GetValue();
-            showAttemptToShielded = BonusRolesPlugin.medicShowAttemptToShielded.GetValue();
-        }
-    }
-
-    public static class Shifter {
-        public static PlayerControl shifter;
-        public static Color color = new Color(90f / 255f, 90f / 255f, 90f / 255f, 1);
-    
-        public static float cooldown = float.MaxValue;
-
-        public static PlayerControl currentTarget;
-
-        private static Sprite buttonSprite;
-        public static Sprite getButtonSprite() {
-            if (buttonSprite) return buttonSprite;
-            buttonSprite = Helpers.loadSpriteFromResources("BonusRoles.Resources.ShiftButton.png", 100f);
-            return buttonSprite;
-        }
-
-        public static void clearAndReload() {
-            shifter = null;
-            currentTarget = null;
-            cooldown = BonusRolesPlugin.shifterCooldown.GetValue();
-        }
-    }
-
-    public static class Swapper {
-        public static PlayerControl swapper;
-        public static Color color = new Color(240f / 255f, 128f / 255f, 72f / 255f, 1);
-        private static Sprite spriteCheck;
-
-        public static byte playerId1 = Byte.MaxValue;
-        public static byte playerId2 = Byte.MaxValue;
-
-        public static Sprite getCheckSprite() {
-            if (spriteCheck) return spriteCheck;
-            spriteCheck = Helpers.loadSpriteFromResources("BonusRoles.Resources.SwapperCheck.png", 150f);
-            return spriteCheck;
-        }
-
-        public static void clearAndReload() {
-            swapper = null;
-            playerId1 = Byte.MaxValue;
-            playerId2 = Byte.MaxValue;
-        }
-    }
-
-    public static class Lovers {
-        public static PlayerControl lover1;
-        public static PlayerControl lover2;
-        public static Color color = new Color(252f / 255f, 3f / 255f, 190f / 255f, 1);
-
-        public static bool bothDie = true;
-        // Lovers save if next to be exiled is a lover, because RPC of ending game comes before RPC of exiled
-        public static bool notAckedExiledIsLover = false;
-
-        public static bool existingAndAlive() {
-            return lover1 != null && lover2 != null && !lover1.Data.IsDead && !lover2.Data.IsDead && !notAckedExiledIsLover; // ADD NOT ACKED IS LOVER
-        }
-
-        public static bool existingWithImpLover() {
-            return lover1 != null && lover2 != null && (lover1.Data.IsImpostor || lover2.Data.IsImpostor);
-        }
-
-        public static void clearAndReload() {
-            lover1 = null;
-            lover2 = null;
-            notAckedExiledIsLover = false;
-            bothDie = BonusRolesPlugin.loversBothDie.GetValue();
-        }
-    }
-
-    public static class Seer {
-        public static PlayerControl seer;
-        public static Color color = new Color(60f / 255f, 181f / 255f, 100f / 255f, 1);
-        public static Dictionary<PlayerControl, PlayerControl> revealedPlayers = new Dictionary<PlayerControl, PlayerControl>();
-
-        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;
-        
-        private static Sprite buttonSprite;
-        public static Sprite getButtonSprite() {
-            if (buttonSprite) return buttonSprite;
-            buttonSprite = Helpers.loadSpriteFromResources("BonusRoles.Resources.SeerButton.png", 100f);
-            return buttonSprite;
-        }
-
-        public static void clearAndReload() {
-            seer = null;
-            revealedPlayers = new Dictionary<PlayerControl, PlayerControl>();
-            cooldown = BonusRolesPlugin.seerCooldown.GetValue();
-            kindOfInfo = BonusRolesPlugin.seerKindOfInfo.GetValue();
-            playersWithNotification = BonusRolesPlugin.seerPlayersWithNotification.GetValue();
-            chanceOfSeeingRight = BonusRolesPlugin.seerChanceOfSeeingRight.GetValue();
-        }
-    }
-
-    public static class Morphling {
-        public static PlayerControl morphling;
-        public static Color color = Palette.ImpostorRed;
-        private static Sprite sampleSprite;
-        private static Sprite morphSprite;
-    
-        public static float cooldown = float.MaxValue;
-
-        public static PlayerControl currentTarget;
-        public static PlayerControl sampledTarget;
-        public static PlayerControl morphTarget;
-        public static float morphTimer = 0f;
-
-
-        public static void clearAndReload() {
-            morphling = null;
-            currentTarget = null;
-            sampledTarget = null;
-            morphTarget = null;
-            morphTimer = 0f;
-            cooldown = BonusRolesPlugin.morphlingCooldown.GetValue();
-        }
-
-        public static Sprite getSampleSprite() {
-            if (sampleSprite) return sampleSprite;
-            sampleSprite = Helpers.loadSpriteFromResources("BonusRoles.Resources.SampleButton.png", 100f);
-            return sampleSprite;
-        }
-
-        public static Sprite getMorphSprite() {
-            if (morphSprite) return morphSprite;
-            morphSprite = Helpers.loadSpriteFromResources("BonusRoles.Resources.MorphButton.png", 100f);
-            return morphSprite;
-        }
-    }
-
-    public static class Camouflager {
-        public static PlayerControl camouflager;
-        public static Color color = Palette.ImpostorRed;
-    
-        public static float cooldown = float.MaxValue;
-        public static float camouflageTimer = 0f;
-
-        private static Sprite buttonSprite;
-        public static Sprite getButtonSprite() {
-            if (buttonSprite) return buttonSprite;
-            buttonSprite = Helpers.loadSpriteFromResources("BonusRoles.Resources.CamoButton.png", 100f);
-            return buttonSprite;
-        }
-
-        public static void clearAndReload() {
-            camouflager = null;
-            camouflageTimer = 0f;
-            cooldown = BonusRolesPlugin.camouflagerCooldown.GetValue();
-        }
-    }
-
-    public static class Spy {
-        public static PlayerControl spy;
-        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
deleted file mode 100644 (file)
index 803c675..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-    <PropertyGroup>
-        <TargetFramework>netstandard2.1</TargetFramework>
-        <Version>1.0.0</Version>
-        <Mappings>NuclearPowered/Mappings:0.2.0</Mappings>
-
-        <Description>TheOtherRoles</Description>
-        <Authors>Eisbison</Authors>
-    </PropertyGroup>
-
-    <PropertyGroup Condition="'$(GamePlatform)' == 'Steam'">
-        <GameVersion>2021.3.5s</GameVersion>
-        <DefineConstants>$(DefineConstants);STEAM</DefineConstants>
-    </PropertyGroup>
-
-    <PropertyGroup Condition="'$(GamePlatform)' == 'Itch'">
-        <GameVersion>2021.3.5i</GameVersion>
-        <DefineConstants>$(DefineConstants);ITCH</DefineConstants>
-    </PropertyGroup>
-
-    <ItemGroup>
-        <Deobfuscate Include="$(AmongUs)\BepInEx\plugins\Reactor-$(GameVersion).dll" />
-
-        <PackageReference Include="Reactor.OxygenFilter.MSBuild" Version="0.2.9" />
-    </ItemGroup>
-
-    <ItemGroup>
-      <Reference Include="Essentials">
-        <HintPath>$(AmongUs)\BepInEx\plugins\Essentials-$(GameVersion).dll</HintPath>
-      </Reference>
-    </ItemGroup>
-
-    <ItemGroup>
-        <EmbeddedResource Include="Resources\RepairButton.png"/>
-        <EmbeddedResource Include="Resources\CleanButton.png"/>
-        <EmbeddedResource Include="Resources\RewindButton.png"/>
-        <EmbeddedResource Include="Resources\ShieldButton.png"/>
-        <EmbeddedResource Include="Resources\ShiftButton.png"/>
-        <EmbeddedResource Include="Resources\SeerButton.png"/>
-        <EmbeddedResource Include="Resources\MorphButton.png"/>
-        <EmbeddedResource Include="Resources\SampleButton.png"/>
-        <EmbeddedResource Include="Resources\CamoButton.png"/>
-        <EmbeddedResource Include="Resources\SpyButton.png"/>
-        <EmbeddedResource Include="Resources\Footprint.png"/>
-        <EmbeddedResource Include="Resources\AdminTableIcon.png"/>
-        <EmbeddedResource Include="Resources\SwapperCheck.png"/>
-    </ItemGroup>
-
-    <Target Name="Copy" AfterTargets="Reobfuscate">
-        <Copy SourceFiles="$(OutputPath)reobfuscated/$(AssemblyName)-$(GameVersion).dll" DestinationFolder="$(AmongUs)/BepInEx/plugins/" Condition="'$(Configuration)' == 'Debug'" />
-    </Target>
-</Project>
\ No newline at end of file
index 55d2620bc8cfca153704544128dcceaea43f43d7..fc16fecf1e5ca7f143ac80e1321c540142e9c595 100644 (file)
@@ -4,13 +4,13 @@ using System;
 using System.IO;
 using System.Net.Http;
 using UnityEngine;
-using static BonusRoles.BonusRoles;
+using static TheOtherRoles.TheOtherRoles;
 using Reactor.Extensions;
 using System.Collections.Generic;
 using System.Linq;
 using UnhollowerBaseLib;
 
-namespace BonusRoles
+namespace TheOtherRoles
 {
     [HarmonyPatch(typeof(HudManager), nameof(HudManager.Start))]
     static class HudManagerStartPatch
@@ -25,6 +25,9 @@ namespace BonusRoles
         private static CustomButton morphlingButton;
         private static CustomButton camouflagerButton;
         private static CustomButton spyButton;
+        private static CustomButton trackerButton;
+        private static CustomButton vampireKillButton;
+        private static CustomButton garlicButton;
 
         public static void setCustomButtonCooldowns() {
             engineerRepairButton.MaxTimer = 0f;
@@ -37,8 +40,12 @@ namespace BonusRoles
             morphlingButton.MaxTimer = Morphling.cooldown;
             camouflagerButton.MaxTimer = Camouflager.cooldown;
             spyButton.MaxTimer = Spy.cooldown;
+            vampireKillButton.MaxTimer = PlayerControl.GameOptions.KillCooldown;
+            trackerButton.MaxTimer = 0f;
+            garlicButton.MaxTimer = 0f;
 
             spyButton.EffectDuration = Spy.duration;
+            vampireKillButton.EffectDuration= Vampire.delay;
         }
 
         public static void Postfix(HudManager __instance)
@@ -80,7 +87,7 @@ namespace BonusRoles
                 },
                 () => {},
                 Engineer.getButtonSprite(),
-                Vector3.zero,
+                new Vector3(-1.3f, 0, 0),
                 __instance
             );
 
@@ -115,7 +122,7 @@ namespace BonusRoles
                 () => { return __instance.ReportButton.renderer.color == Palette.EnabledColor  && PlayerControl.LocalPlayer.CanMove; },
                 () => { janitorCleanButton.Timer = janitorCleanButton.MaxTimer;},
                 Janitor.getButtonSprite(),
-                Vector3.zero,
+                new Vector3(-1.3f, 0, 0),
                 __instance
             );
 
@@ -148,7 +155,7 @@ namespace BonusRoles
                 () => { return Sheriff.currentTarget && PlayerControl.LocalPlayer.CanMove; },
                 () => { sheriffKillButton.Timer = sheriffKillButton.MaxTimer;},
                 __instance.KillButton.renderer.sprite,
-                Vector3.zero,
+                new Vector3(-1.3f, 0, 0),
                 __instance
             );
 
@@ -164,7 +171,7 @@ namespace BonusRoles
                 () => { return PlayerControl.LocalPlayer.CanMove; },
                 () => { timeMasterRewindTimeButton.Timer = timeMasterRewindTimeButton.MaxTimer;},
                 TimeMaster.getButtonSprite(),
-                Vector3.zero,
+                new Vector3(-1.3f, 0, 0),
                 __instance
             );
 
@@ -182,7 +189,7 @@ namespace BonusRoles
                 () => { return !Medic.usedShield && Medic.currentTarget && PlayerControl.LocalPlayer.CanMove; },
                 () => {},
                 Medic.getButtonSprite(),
-                Vector3.zero,
+                new Vector3(-1.3f, 0, 0),
                 __instance
             );
 
@@ -202,7 +209,7 @@ namespace BonusRoles
                 () => { return Shifter.currentTarget && PlayerControl.LocalPlayer.CanMove; },
                 () => { shifterShiftButton.Timer = shifterShiftButton.MaxTimer; },
                 Shifter.getButtonSprite(),
-                Vector3.zero,
+                new Vector3(-1.3f, 0, 0),
                 __instance
             );
 
@@ -232,7 +239,7 @@ namespace BonusRoles
                 () => { return Seer.currentTarget && PlayerControl.LocalPlayer.CanMove; },
                 () => {},
                 Seer.getButtonSprite(),
-                Vector3.zero,
+                new Vector3(-1.3f, 0, 0),
                 __instance
             );
 
@@ -262,7 +269,7 @@ namespace BonusRoles
                     Morphling.sampledTarget = null;
                 },
                 Morphling.getSampleSprite(),
-                new Vector3(0f, 1.3f, 0f),
+                new Vector3(-1.3f, 1.3f, 0f),
                 __instance,
                 true,
                 10f,
@@ -287,7 +294,7 @@ namespace BonusRoles
                     camouflagerButton.killButtonManager.TimerText.Color = Palette.EnabledColor;
                 },
                 Camouflager.getButtonSprite(),
-                new Vector3(0f, 1.3f, 0f),
+                new Vector3(-1.3f, 1.3f, 0f),
                 __instance,
                 true,
                 10f,
@@ -307,7 +314,7 @@ namespace BonusRoles
                     spyButton.killButtonManager.TimerText.Color = Palette.EnabledColor;
                 },
                 Spy.getButtonSprite(),
-                Vector3.zero,
+                new Vector3(-1.3f, 0, 0),
                 __instance,
                 true,
                 0f,
@@ -315,6 +322,87 @@ namespace BonusRoles
                     spyButton.Timer = spyButton.MaxTimer;
                 }
             );
+
+            // Tracker button
+            trackerButton = new CustomButton(
+                () => {
+                    MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.TrackerUsedTracker, Hazel.SendOption.None, -1);
+                    writer.Write(Tracker.currentTarget.PlayerId);
+                    AmongUsClient.Instance.FinishRpcImmediately(writer);
+                    RPCProcedure.trackerUsedTracker(Tracker.currentTarget.PlayerId);
+                },
+                () => { return Tracker.tracker != null && Tracker.tracker == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; },
+                () => { return PlayerControl.LocalPlayer.CanMove && Tracker.currentTarget != null && !Tracker.usedTracker; },
+                () => { },
+                Tracker.getButtonSprite(),
+                new Vector3(-1.3f, 0, 0),
+                __instance
+            );
+
+            vampireKillButton = new CustomButton(
+                () => {
+                    if (Helpers.handleMurderAttempt(Vampire.currentTarget)) {
+                        if (Vampire.targetNearGarlic) {
+                                       PlayerControl.LocalPlayer.RpcMurderPlayer(Vampire.currentTarget);
+                            vampireKillButton.HasEffect = false; // Block effect on this click
+                            vampireKillButton.Timer = vampireKillButton.MaxTimer;
+                        } else {
+                            Vampire.bitten = Vampire.currentTarget;
+                            Reactor.Coroutines.Start(Vampire.killWithDelay());
+                            MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.VampireBiteNotification, Hazel.SendOption.None, -1);
+                            writer.Write(Vampire.bitten.PlayerId);
+                            AmongUsClient.Instance.FinishRpcImmediately(writer);
+                            RPCProcedure.vampireBiteNotification(Vampire.bitten.PlayerId); 
+                            vampireKillButton.HasEffect = true; // Trigger effect on this click
+                        }
+                    } else {
+                        vampireKillButton.HasEffect = false; // Block effect if no action was fired
+                    }
+                },
+                () => { return Vampire.vampire != null && Vampire.vampire == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; },
+                () => {
+                    if (Vampire.targetNearGarlic)
+                        vampireKillButton.killButtonManager.renderer.sprite = __instance.KillButton.renderer.sprite;
+                    else
+                        vampireKillButton.killButtonManager.renderer.sprite = Vampire.getButtonSprite();
+                    return Vampire.currentTarget != null && PlayerControl.LocalPlayer.CanMove;
+                },
+                () => {
+                    vampireKillButton.Timer = vampireKillButton.MaxTimer;
+                    vampireKillButton.isEffectActive = false;
+                    vampireKillButton.killButtonManager.TimerText.Color = Palette.EnabledColor;
+                },
+                Vampire.getButtonSprite(),
+                new Vector3(-1.3f, 0, 0),
+                __instance,
+                false,
+                0f,
+                () => {
+                    vampireKillButton.Timer = vampireKillButton.MaxTimer;
+                }
+            );
+
+            garlicButton = new CustomButton(
+                () => {
+                    Vampire.localPlacedGarlic = true;
+                    var pos = PlayerControl.LocalPlayer.transform.position;
+                    byte[] buff = new byte[sizeof(float) * 2];
+                    Buffer.BlockCopy(BitConverter.GetBytes(pos.x), 0, buff, 0*sizeof(float), sizeof(float));
+                    Buffer.BlockCopy(BitConverter.GetBytes(pos.y), 0, buff, 1*sizeof(float), sizeof(float));
+
+                    MessageWriter writer = AmongUsClient.Instance.StartRpc(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.PlaceGarlic, Hazel.SendOption.None);
+                    writer.WriteBytesAndSize(buff);
+                    writer.EndMessage();
+                    RPCProcedure.placeGarlic(buff); 
+                },
+                () => { return !Vampire.localPlacedGarlic && !PlayerControl.LocalPlayer.Data.IsDead && Vampire.garlicsActive; },
+                () => { return PlayerControl.LocalPlayer.CanMove && !Vampire.localPlacedGarlic; },
+                () => { },
+                Vampire.getGarlicButtonSprite(),
+                Vector3.zero,
+                __instance,
+                true
+            );
         }
     }
 }
\ No newline at end of file
index e104d9efcb25ef21be36a52c3879b9862acb3478..9b4394facc8b3add278172e289314b68358929ab 100644 (file)
@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace BonusRoles
+namespace TheOtherRoles
 {
     [HarmonyPatch(typeof(VersionShower), nameof(VersionShower.Start))]
     public static class VersionShowerPatch
@@ -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.5\n  by [FCCE03FF]Eisbison";
+                __instance.text.Text += "\n[FFFFFFFF]- Loaded [FCCE03FF]TheOtherRoles[FFFFFFFF] v1.7\n  by [FCCE03FF]Eisbison";
             else
-                __instance.text.Text += spacer + "[FFFFFFFF]- Loaded [FCCE03FF]TheOtherRoles[FFFFFFFF] v1.5\n  by [FCCE03FF]Eisbison";
+                __instance.text.Text += spacer + "[FFFFFFFF]- Loaded [FCCE03FF]TheOtherRoles[FFFFFFFF] v1.7\n  by [FCCE03FF]Eisbison";
         }
     }
 
index 0f3aae100f156d1f34bf12c0bdcae51dc35a9161..7f1b7c04dfb69b96e88f502daa5abaa4dc7c2fbe 100644 (file)
@@ -23,8 +23,9 @@ public class CustomButton
     public float EffectDuration;
     public Sprite Sprite;
     private HudManager hudManager;
+    private bool mirror;
 
-    public CustomButton(Action OnClick, Func<bool> HasButton, Func<bool> CouldUse, Action OnMeetingEnds, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager, bool HasEffect, float EffectDuration, Action OnEffectEnds)
+    public CustomButton(Action OnClick, Func<bool> HasButton, Func<bool> CouldUse, Action OnMeetingEnds, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager, bool HasEffect, float EffectDuration, Action OnEffectEnds, bool mirror = false)
     {
         this.hudManager = hudManager;
         this.OnClick = OnClick;
@@ -36,6 +37,7 @@ public class CustomButton
         this.EffectDuration = EffectDuration;
         this.OnEffectEnds = OnEffectEnds;
         this.Sprite = Sprite;
+        this.mirror = mirror;
         Timer = 16.2f;
         buttons.Add(this);
         killButtonManager = UnityEngine.Object.Instantiate(hudManager.KillButton, hudManager.transform);
@@ -60,8 +62,8 @@ public class CustomButton
         setActive(false);
     }
 
-    public CustomButton(Action OnClick, Func<bool> HasButton, Func<bool> CouldUse, Action OnMeetingEnds, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager)
-    : this(OnClick, HasButton, CouldUse, OnMeetingEnds, Sprite, PositionOffset, hudManager, false, 0f, () => {}) { }
+    public CustomButton(Action OnClick, Func<bool> HasButton, Func<bool> CouldUse, Action OnMeetingEnds, Sprite Sprite, Vector3 PositionOffset, HudManager hudManager, bool mirror = false)
+    : this(OnClick, HasButton, CouldUse, OnMeetingEnds, Sprite, PositionOffset, hudManager, false, 0f, () => {}, mirror) { }
 
     public static void HudUpdate()
     {
@@ -132,15 +134,12 @@ public class CustomButton
         setActive(hudManager.UseButton.isActiveAndEnabled);
 
         killButtonManager.renderer.sprite = Sprite;
-
-        if (killButtonManager.transform.position == hudManager.KillButton.transform.position)
-        {
-            Vector3 vector = killButtonManager.transform.localPosition;
-            vector += new Vector3(PositionOffset.x, PositionOffset.y);
-            killButtonManager.transform.localPosition = vector;
+        if (hudManager.UseButton != null) {
+            Vector3 pos = hudManager.UseButton.transform.localPosition;
+            if (mirror) pos = new Vector3(-pos.x, pos.y, pos.z);
+            killButtonManager.transform.localPosition = pos + PositionOffset;
+            if (hudManager.KillButton != null) hudManager.KillButton.transform.localPosition = hudManager.UseButton.transform.localPosition - new Vector3(1.3f, 0, 0); // Align the kill button (because it's on another position depending on the screen resolution)
         }
-
-
         if (CouldUse()) {
             killButtonManager.renderer.color = Palette.EnabledColor;
             killButtonManager.renderer.material.SetFloat("_Desat", 0f);
index 0ad301783fa9cb2e485524e3d86bec91942f1015..11184766d9983b5c778526eeab432176a2197537 100644 (file)
@@ -1,7 +1,7 @@
   
 using HarmonyLib;
-using static BonusRoles.BonusRoles;
-using static BonusRoles.GameHistory;
+using static TheOtherRoles.TheOtherRoles;
+using static TheOtherRoles.GameHistory;
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
@@ -11,13 +11,14 @@ using UnhollowerBaseLib;
 using System;
 using System.Text;
 
-namespace BonusRoles {
+namespace TheOtherRoles {
     enum WinCondition {
         Default,
         LoversTeamWin,
         LoversSoloWin,
         JesterWin,
-        ChildDied
+        BountyHunterWin,
+        JesterAndBountyHunterWin
     }
 
     static class AdditionalTempData {
@@ -55,24 +56,26 @@ namespace BonusRoles {
                 if (jesterWinner != null) TempData.winners.Remove(jesterWinner);
             }
 
-
-            // Child win condition (should be implemented using a proper GameOverReason in the future)
-            if (Child.child != null && Child.child.Data.IsImpostor) {
-                AdditionalTempData.winCondition = WinCondition.ChildDied;
+            // Jester and Bounty Hunter win condition (should be implemented using a proper GameOverReason in the future)
+            bool jesterWin = Jester.jester != null && Jester.jester.Data.IsImpostor;
+            bool bountyHunterWin = BountyHunter.bountyHunter != null && BountyHunter.bountyHunter.Data.IsImpostor;
+            if (jesterWin || bountyHunterWin) {
                 TempData.winners = new Il2CppSystem.Collections.Generic.List<WinningPlayerData>();
-                WinningPlayerData wpd = new WinningPlayerData(Child.child.Data);
-                wpd.IsImpostor = false;
-                wpd.IsYou = false;
-                TempData.winners.Add(wpd);
-            }
-
-            // Jester win condition (should be implemented using a proper GameOverReason in the future)
-            else if (Jester.jester != null && Jester.jester.Data.IsImpostor) {
-                AdditionalTempData.winCondition = WinCondition.JesterWin;
-                TempData.winners = new Il2CppSystem.Collections.Generic.List<WinningPlayerData>();
-                WinningPlayerData wpd = new WinningPlayerData(Jester.jester.Data);
-                wpd.IsImpostor = false; 
-                TempData.winners.Add(wpd);
+                if (jesterWin) {
+                    WinningPlayerData wpd = new WinningPlayerData(Jester.jester.Data);
+                    wpd.IsImpostor = false; 
+                    TempData.winners.Add(wpd);
+                    AdditionalTempData.winCondition = WinCondition.JesterWin;
+                }
+                if (bountyHunterWin) {
+                    WinningPlayerData wpd = new WinningPlayerData(BountyHunter.bountyHunter.Data);
+                    wpd.IsImpostor = false; 
+                    TempData.winners.Add(wpd);
+                    if (AdditionalTempData.winCondition == WinCondition.JesterWin)
+                        AdditionalTempData.winCondition = WinCondition.JesterAndBountyHunterWin;
+                    else
+                        AdditionalTempData.winCondition = WinCondition.BountyHunterWin;  
+                }
             }
 
             // Lovers win conditions (should be implemented using a proper GameOverReason in the future)
@@ -106,14 +109,18 @@ namespace BonusRoles {
             TextRenderer textRenderer = bonusText.GetComponent<TextRenderer>();
             textRenderer.Text = "";
 
-            if (AdditionalTempData.winCondition == WinCondition.ChildDied) {
-                textRenderer.Text = "Child Died";
-                textRenderer.Color = Child.color;
-            }
-            else if (AdditionalTempData.winCondition == WinCondition.JesterWin) {
+            if (AdditionalTempData.winCondition == WinCondition.JesterWin) {
                 textRenderer.Text = "Jester Wins";
                 textRenderer.Color = Jester.color;
             }
+            else if (AdditionalTempData.winCondition == WinCondition.BountyHunterWin) {
+                textRenderer.Text = "Bounty Hunter Wins";
+                textRenderer.Color = BountyHunter.color;
+            }
+            else if (AdditionalTempData.winCondition == WinCondition.JesterAndBountyHunterWin) {
+                textRenderer.Text = "[AD653BFF]Bounty Hunter[FFFFFFFF] and [FF54A7FF]Jester[FFFFFFFF] Win";
+                textRenderer.Color = Color.white;
+            }
             else if (AdditionalTempData.winCondition == WinCondition.LoversTeamWin) {
                 if (AdditionalTempData.localIsLover) {
                     __instance.WinText.Text = "Double Victory";
index 7cc24c806cb989b28cd87c53ac68305e47a70463..ac41abc461994c9070aff16978d9203d6ad442fe 100644 (file)
@@ -2,9 +2,9 @@ using System;
 using System.Collections.Generic;
 using System.Collections;
 using UnityEngine;
-using static BonusRoles.BonusRoles;
+using static TheOtherRoles.TheOtherRoles;
 
-namespace BonusRoles{
+namespace TheOtherRoles{
     class Footprint {
         private static List<Footprint> footprints = new List<Footprint>();
         private static Sprite sprite;
@@ -14,7 +14,7 @@ namespace BonusRoles{
 
         public static Sprite getFootprintSprite() {
             if (sprite) return sprite;
-            sprite = Helpers.loadSpriteFromResources("BonusRoles.Resources.Footprint.png", 600f);
+            sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Footprint.png", 600f);
             return sprite;
         }
 
@@ -45,9 +45,7 @@ namespace BonusRoles{
 
         IEnumerator CoFadeOutAndDestroy(float duration)
         {
-            for (float t = 0f; t < duration; t += Time.deltaTime)
-            {
-                // Check not null since if game ends, this access throws an error
+            for (float t = 0f; t < duration; t += Time.deltaTime) {
                 if (spriteRenderer) spriteRenderer.color = new Color(color.r, color.g, color.b, Mathf.Clamp(1f - t/duration, 0f, 1f));
 
                 yield return null;
index 933e19548174e724d9fa054b16c224b592037988..f4b663321b45d38af2598658c53377c9df6e88f2 100644 (file)
@@ -2,9 +2,9 @@ using System.Collections.Generic;
 using System.Collections;
 using System;
 using UnityEngine;
-using static BonusRoles.BonusRoles;
+using static TheOtherRoles.TheOtherRoles;
 
-namespace BonusRoles{
+namespace TheOtherRoles{
     public class DeadPlayer
     {
         public PlayerControl player;
index 5e592b4764210a2ad48f712cd667023524963e01..046762bdff617c1a5a390f1f3f023c89f5933fd8 100644 (file)
@@ -3,13 +3,15 @@ using System;
 using System.IO;
 using System.Net.Http;
 using UnityEngine;
-using static BonusRoles.BonusRoles;
+using static TheOtherRoles.TheOtherRoles;
 using Reactor.Extensions;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
+using System.Text;
+using Essentials.Options;
 
-namespace BonusRoles
+namespace TheOtherRoles
 {
     [HarmonyPriority(Priority.Low)] 
     [HarmonyPatch(typeof(GameOptionsMenu), nameof(GameOptionsMenu.Update))]
@@ -20,4 +22,57 @@ namespace BonusRoles
             __instance.GetComponentInParent<Scroller>().YBounds.max = -0.5F + __instance.Children.Length * 0.5F;
         }
     }
+
+    [HarmonyPatch(typeof(KeyboardJoystick), nameof(KeyboardJoystick.Update))]
+    public static class GameOptionsNextPagePatch
+    {
+        private static readonly System.Random random = new System.Random((int)DateTime.Now.Ticks);
+        private static List<PlayerControl> bots = new List<PlayerControl>();
+
+        public static void Postfix(KeyboardJoystick __instance)
+        {
+            if(Input.GetKeyDown(KeyCode.Tab)) {
+                TheOtherRolesPlugin.optionsPage = (TheOtherRolesPlugin.optionsPage + 1) % 3;
+            }
+        }
+    }
+
+    [HarmonyPatch(typeof(GameOptionsData), "NHJLMAAHKJF")]
+    class GameOptionsDataPatch
+    {
+
+        private static void Postfix(ref string __result)
+        {
+            StringBuilder stringBuilder = new StringBuilder(__result);
+            foreach (CustomOption customOption in TheOtherRolesPlugin.options) {
+                stringBuilder.AppendLine(string.Format("{0}[]:   {1}", customOption.Name, customOption));   
+            }
+            var hudString = stringBuilder.ToString();
+
+            int defaultSettingsLines = 19;
+            int roleSettingsLines = 19 + 22;
+            int end1 = hudString.TakeWhile(c => (defaultSettingsLines -= (c == '\n' ? 1 : 0)) > 0).Count();
+            int end2 = hudString.TakeWhile(c => (roleSettingsLines -= (c == '\n' ? 1 : 0)) > 0).Count();
+            int counter = TheOtherRolesPlugin.optionsPage;
+            if (counter == 0) {
+                hudString = hudString.Substring(0, end1) + "\n";   
+            } else if (counter == 1) {
+                hudString = hudString.Substring(end1 + 1, end2 - end1);
+                // Temporary fix, should add a new CustomOption for spaces
+                int gap = 2;
+                int index = hudString.TakeWhile(c => (gap -= (c == '\n' ? 1 : 0)) > 0).Count();
+                hudString = hudString.Insert(index, "\n");
+                gap = 7;
+                index = hudString.TakeWhile(c => (gap -= (c == '\n' ? 1 : 0)) > 0).Count();
+                hudString = hudString.Insert(index + 1, "\n");
+                gap = 11;
+                index = hudString.TakeWhile(c => (gap -= (c == '\n' ? 1 : 0)) > 0).Count();
+                hudString = hudString.Insert(index + 1, "\n");
+            } else if (counter == 2) {
+                hudString = hudString.Substring(end2 + 1);
+            }
+            hudString += "\n Press tab for more...";
+            __result = hudString;
+        }
+    }
 }
\ No newline at end of file
diff --git a/Source Code/Garlic.cs b/Source Code/Garlic.cs
new file mode 100644 (file)
index 0000000..d2f6f6a
--- /dev/null
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using UnityEngine;
+using static TheOtherRoles.TheOtherRoles;
+
+namespace TheOtherRoles{
+    class Garlic {
+        public static List<Garlic> garlics = new List<Garlic>();
+
+        public GameObject garlic;
+        private GameObject background;
+
+        private static Sprite garlicSprite;
+        public static Sprite getGarlicSprite() {
+            if (garlicSprite) return garlicSprite;
+            garlicSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Garlic.png", 300f);
+            return garlicSprite;
+        }
+
+        private static Sprite backgroundSprite;
+        public static Sprite getBackgroundSprite() {
+            if (backgroundSprite) return backgroundSprite;
+            backgroundSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.GarlicBackground.png", 60f);
+            return backgroundSprite;
+        }
+
+        public Garlic(Vector2 p) {
+            garlic = new GameObject("Garlic");
+            background = new GameObject("Background");
+            background.transform.SetParent(garlic.transform);
+            Vector3 position = new Vector3(p.x, p.y, PlayerControl.LocalPlayer.transform.position.z + 1f);
+            garlic.transform.position = position;
+            garlic.transform.localPosition = position;
+            background.transform.localPosition = new Vector3(0 , 0, 0.01f);
+
+            var garlicRenderer = garlic.AddComponent<SpriteRenderer>();
+            garlicRenderer.sprite = getGarlicSprite();
+            var backgroundRenderer = background.AddComponent<SpriteRenderer>();
+            backgroundRenderer.sprite = getBackgroundSprite();
+
+
+            garlic.SetActive(true);
+            garlics.Add(this);
+        }
+
+        public static void clearGarlics() {
+            garlics = new List<Garlic>();
+        }
+
+        public static void UpdateAll() {
+            foreach (Garlic garlic in garlics) {
+                if (garlic != null)
+                    garlic.Update();
+            }
+        }
+
+        public void Update() {
+            if (background != null)
+                background.transform.Rotate(Vector3.forward * 6 * Time.deltaTime);
+        }
+    }
+}
\ No newline at end of file
index 3b354831998e7db8fe3bebba4b98d504d3870aaa..590473ddfe2ccd094cba755f1904ea46c47d769e 100644 (file)
@@ -7,9 +7,11 @@ using System.Collections;
 using Reactor.Unstrip;
 using UnhollowerBaseLib;
 using UnityEngine;
-using static BonusRoles.BonusRoles;
+using static TheOtherRoles.TheOtherRoles;
+using HarmonyLib;
+using Hazel;
 
-namespace BonusRoles {
+namespace TheOtherRoles {
     public static class Helpers {
 
         public static Sprite LoadSpriteFromEmbeddedResources(string resource, float PixelPerUnit) {
@@ -107,6 +109,25 @@ namespace BonusRoles {
             if (renderer != null) renderer.enabled = false;
         }
 
+
+        public static bool handleMurderAttempt(PlayerControl target, bool notifyOthers = true) {
+            // Block impostor shielded kill
+            if (Medic.shielded != null && Medic.shielded == target) {
+                if (notifyOthers) {
+                    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 && target == Child.child && !Child.isGrownUp()) {
+                return false;
+            }
+            return true;
+        }
+
         public static IEnumerator Slide2D(Transform target, Vector2 source, Vector2 dest, float duration = 0.75f)
         {
             Vector3 temp = default(Vector3);
@@ -186,6 +207,11 @@ namespace BonusRoles {
                 c = Camouflager.color;
                 g = false;
             }
+            else if (Vampire.vampire != null && p == Vampire.vampire) {
+                r = "Vampire";
+                c = Vampire.color;
+                g = false;
+            }
             else if (Detective.detective != null && p == Detective.detective) {
                 r = "Detective";
                 c = Detective.color;
@@ -229,6 +255,19 @@ namespace BonusRoles {
                 r = "Child";
                 c = Child.color;
             }
+            else if (BountyHunter.bountyHunter != null && p == BountyHunter.bountyHunter) {
+                r = "Bounty Hunter";
+                c = BountyHunter.color;
+                g = false;
+            }
+            else if (Tracker.tracker != null && p == Tracker.tracker) {
+                r = "Tracker";
+                c = Tracker.color;
+            }
+            else if (Snitch.snitch != null && p == Snitch.snitch) {
+                r = "Snitch";
+                c = Snitch.color;
+            }
             else if (p.Data.IsImpostor) { // Just Impostor
                 r = "Impostor";
                 c = Palette.ImpostorRed;
index 451b71aafe46695407b44124bf7f7918c3d6a328..42495f39205d67a7b71c410d1abcee97a8e33073 100644 (file)
@@ -1,10 +1,10 @@
 using HarmonyLib;
 using System;
-using static BonusRoles.BonusRoles;
+using static TheOtherRoles.TheOtherRoles;
 using UnityEngine;
 
 
-namespace BonusRoles
+namespace TheOtherRoles
 {
     [HarmonyPatch(typeof(IntroCutscene.CoBegin__d), nameof(IntroCutscene.CoBegin__d.MoveNext))]
     class IntroPatch
@@ -88,6 +88,14 @@ namespace BonusRoles
                 __instance.__this.ImpostorText.Text = "Camouflage and kill the crewmates";
                 __instance.__this.BackgroundBar.material.color = Camouflager.color;
             }
+            else if (PlayerControl.LocalPlayer == Vampire.vampire)
+            {
+                __instance.__this.ImpostorText.gameObject.SetActive(true);
+                __instance.__this.Title.Text = "Vampire";
+                __instance.__this.Title.Color = Vampire.color;
+                __instance.__this.ImpostorText.Text = "Kill the crewmates with your bites";
+                __instance.__this.BackgroundBar.material.color = Vampire.color;
+            }
             else if (PlayerControl.LocalPlayer == Sheriff.sheriff)
             {
                 __instance.__this.Title.Text = "Sheriff";
@@ -167,6 +175,27 @@ namespace BonusRoles
                 __instance.__this.ImpostorText.Text = "No one will harm you until you grow up";
                 __instance.__this.BackgroundBar.material.color = Child.color;
             }
+            else if (PlayerControl.LocalPlayer == BountyHunter.bountyHunter)
+            {
+                __instance.__this.Title.Text = "Bounty Hunter";
+                __instance.__this.Title.Color = BountyHunter.color;
+                __instance.__this.ImpostorText.Text = "Hunt [ED653BFF]" + BountyHunter.target?.Data?.PlayerName + "[FFFFFFFF] down";
+                __instance.__this.BackgroundBar.material.color = BountyHunter.color;
+            }
+            else if (PlayerControl.LocalPlayer == Tracker.tracker)
+            {
+                __instance.__this.Title.Text = "Tracker";
+                __instance.__this.Title.Color = Tracker.color;
+                __instance.__this.ImpostorText.Text = "Track the [FF1919FF]Impostors[FFFFFFFF] down";
+                __instance.__this.BackgroundBar.material.color = Tracker.color;
+            }
+            else if (PlayerControl.LocalPlayer == Snitch.snitch)
+            {
+                __instance.__this.Title.Text = "Snitch";
+                __instance.__this.Title.Color = Snitch.color;
+                __instance.__this.ImpostorText.Text = "Finish your tasks to find the [FF1919FF]Impostors[FFFFFFFF]";
+                __instance.__this.BackgroundBar.material.color = Snitch.color;
+            }
         }
     }
 }
index 158d00afa58a638eead4e422377788f37575640f..f1b189c3dd489105e72848282be8630e58f654ba 100644 (file)
@@ -6,81 +6,172 @@ using Hazel;
 using Reactor;
 using System.Collections.Generic;
 using Essentials.Options;
-using UnityEngine;
 using System.Linq;
 using System.Net;
 using System.IO;
 using System;
 using System.Reflection;
 using UnhollowerBaseLib;
+using UnityEngine;
+using static TheOtherRoles.TheOtherRoles;
 
-namespace BonusRoles
+namespace TheOtherRoles
 {
     [BepInPlugin(Id)]
     [BepInProcess("Among Us.exe")]
     [BepInDependency(ReactorPlugin.Id)]
-    public class BonusRolesPlugin : BasePlugin
+    public class TheOtherRolesPlugin : BasePlugin
     {
-        public const string Id = "me.eisbison.bonusroles";
+        public const string Id = "me.eisbison.theotherroles";
         public Harmony Harmony { get; } = new Harmony(Id);
 
-        // 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 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);
-        public static CustomNumberOption sheriffSpawnChance = CustomOption.AddNumber("Sheriff Spawn Chance", 100, 0, 100, 10);
-        public static CustomNumberOption lighterSpawnChance = CustomOption.AddNumber("Lighter Spawn Chance", 100, 0, 100, 10);
-        public static CustomNumberOption detectiveSpawnChance = CustomOption.AddNumber("Detective Spawn Chance", 100, 0, 100, 10);
-        public static CustomNumberOption timeMasterSpawnChance = CustomOption.AddNumber("Time Master Spawn Chance", 100, 0, 100, 10);
-        public static CustomNumberOption medicSpawnChance = CustomOption.AddNumber("Medic Spawn Chance", 100, 0, 100, 10);
-        public static CustomNumberOption shifterSpawnChance = CustomOption.AddNumber("Shifter Spawn Chance", 100, 0, 100, 10);
-        public static CustomNumberOption swapperSpawnChance = CustomOption.AddNumber("Swapper Spawn Chance", 100, 0, 100, 10);
-        public static CustomNumberOption seerSpawnChance = CustomOption.AddNumber("Seer Spawn Chance", 100, 0, 100, 10);
-        public static CustomNumberOption spySpawnChance = CustomOption.AddNumber("Spy Spawn Chance", 100, 0, 100, 10);
-        public static CustomNumberOption childSpawnChance = CustomOption.AddNumber("Child Spawn Chance", 100, 0, 100, 10);
+        // Roles
+        public static int num = 0;
+        public static string[] rates = new string[]{"0%", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%"};
+
+        public static CustomNumberOption crewmateRolesCount = CustomOption.AddNumber(num++.ToString(), "[CCCC00FF]Number Of Crewmate/Neutral Roles", 10, 0, 10, 0.5f);
+        public static CustomNumberOption impostorRolesCount = CustomOption.AddNumber(num++.ToString(), "[CCCC00FF]Number Of Impostor Roles", 3, 0, 3, 0.5f);
+
+        public static CustomStringOption mafiaSpawnRate = CustomOption.AddString(num++.ToString(), cs(Janitor.color) + "Mafia", rates);
+        public static CustomStringOption morphlingSpawnRate = CustomOption.AddString(num++.ToString(), cs(Morphling.color) + "Morphling", rates);
+        public static CustomStringOption camouflagerSpawnRate = CustomOption.AddString(num++.ToString(), cs(Camouflager.color) + "Camouflager", rates);
+        public static CustomStringOption vampireSpawnRate = CustomOption.AddString(num++.ToString(), cs(Vampire.color) + "Vampire", rates);
+
+        public static CustomStringOption loversSpawnRate = CustomOption.AddString(num++.ToString(), cs(Lovers.color) + "Lovers", rates);
+        public static CustomStringOption jesterSpawnRate = CustomOption.AddString(num++.ToString(), cs(Jester.color) + "Jester", rates);
+        public static CustomStringOption shifterSpawnRate = CustomOption.AddString(num++.ToString(), cs(Shifter.color) + "Shifter", rates);
+
+        public static CustomStringOption mayorSpawnRate = CustomOption.AddString(num++.ToString(), cs(Mayor.color) + "Mayor", rates);
+        public static CustomStringOption engineerSpawnRate = CustomOption.AddString(num++.ToString(), cs(Engineer.color) + "Engineer", rates);
+        public static CustomStringOption sheriffSpawnRate = CustomOption.AddString(num++.ToString(), cs(Sheriff.color) + "Sheriff", rates);
+        public static CustomStringOption lighterSpawnRate = CustomOption.AddString(num++.ToString(), cs(Lighter.color) + "Lighter", rates);
+        public static CustomStringOption detectiveSpawnRate = CustomOption.AddString(num++.ToString(), cs(Detective.color) + "Detective", rates);
+        public static CustomStringOption timeMasterSpawnRate = CustomOption.AddString(num++.ToString(), cs(TimeMaster.color) + "Time Master", rates);
+        public static CustomStringOption medicSpawnRate = CustomOption.AddString(num++.ToString(), cs(Medic.color) + "Medic", rates);
+        public static CustomStringOption swapperSpawnRate = CustomOption.AddString(num++.ToString(), cs(Swapper.color) + "Swapper", rates);
+        public static CustomStringOption seerSpawnRate = CustomOption.AddString(num++.ToString(), cs(Seer.color) + "Seer", rates);
+        public static CustomStringOption spySpawnRate = CustomOption.AddString(num++.ToString(), cs(Spy.color) + "Spy", rates);
+        public static CustomStringOption childSpawnRate = CustomOption.AddString(num++.ToString(), cs(Child.color) + "Child", rates);
+        // public static CustomStringOption bountyHunterSpawnRate = CustomOption.AddString(num++.ToString(), cs(BountyHunter.color) + "Bounty Hunter", rates);
+        public static CustomStringOption trackerSpawnRate = CustomOption.AddString(num++.ToString(), cs(Tracker.color) + "Tracker", rates);
+        public static CustomStringOption snitchSpawnRate = CustomOption.AddString(num++.ToString(), cs(Snitch.color) + "Snitch", rates);
 
         // Role settings
-        public static CustomNumberOption janitorCooldown = CustomOption.AddNumber("Janitor Cooldown", 30f, 10f, 60f, 2.5f);
-        public static CustomNumberOption morphlingCooldown = CustomOption.AddNumber("Morphling Cooldown", 30f, 10f, 60f, 2.5f);
-        public static CustomNumberOption camouflagerCooldown = CustomOption.AddNumber("Camouflager Cooldown", 30f, 10f, 60f, 2.5f);
-        public static CustomToggleOption loversBothDie = CustomOption.AddToggle("Both Lovers Die", true); 
-        public static CustomNumberOption sheriffCooldown = CustomOption.AddNumber("Sheriff Cooldown", 30f, 10f, 60f, 2.5f);
-        public static CustomToggleOption jesterCanDieToSheriff = CustomOption.AddToggle("Jester Can Die To Sheriff", false); 
-        public static CustomNumberOption lighterVision = CustomOption.AddNumber("Lighter Vision", 2f, 0.25f, 5f, 0.25f);
-        public static CustomToggleOption detectiveAnonymousFootprints = CustomOption.AddToggle("Anonymous Footprints", false); 
-        public static CustomNumberOption detectiveFootprintIntervall = CustomOption.AddNumber("Footprint Intervall", 0.5f, 0.25f, 10f, 0.25f);
-        public static CustomNumberOption detectiveFootprintDuration = CustomOption.AddNumber("Footprint Duration", 5f, 0.25f, 10f, 0.25f);
-        public static CustomNumberOption timeMasterCooldown = CustomOption.AddNumber("Time Master Cooldown", 30f, 10f, 60f, 2.5f);
-        public static CustomNumberOption timeMasterRewindTime = CustomOption.AddNumber("Rewind Time", 3f, 1f, 10f, 1f);
-        public static CustomToggleOption timeMasterReviveDuringRewind = CustomOption.AddToggle("Revive During Rewind", false);
-        public static CustomNumberOption medicReportNameDuration = CustomOption.AddNumber("Time Where Medic Reports Will Have Name", 10, 0, 60, 2.5f);
-        public static CustomNumberOption medicReportColorDuration = CustomOption.AddNumber("Time Where Medic Reports Will Have Color Type", 20, 0, 120, 2.5f);
-        public static CustomStringOption medicShowShielded = CustomOption.AddString("Show Shielded Player", new string[] {"Everyone", "Shielded + Medic", "Medic"});
-        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 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 CustomNumberOption janitorCooldown = CustomOption.AddNumber(num++.ToString(), "Janitor Cooldown", 30f, 10f, 60f, 2.5f);
+        public static CustomNumberOption morphlingCooldown = CustomOption.AddNumber(num++.ToString(), "Morphling Cooldown", 30f, 10f, 60f, 2.5f);
+        public static CustomNumberOption camouflagerCooldown = CustomOption.AddNumber(num++.ToString(), "Camouflager Cooldown", 30f, 10f, 60f, 2.5f);
+        public static CustomNumberOption vampireKillDelay = CustomOption.AddNumber(num++.ToString(), "Vampire Kill Delay", 10f, 2.5f, 30f, 2.5f);
+
+        public static CustomToggleOption loversBothDie = CustomOption.AddToggle(num++.ToString(), "Both Lovers Die", true);
+        public static CustomNumberOption shifterCooldown = CustomOption.AddNumber(num++.ToString(), "Shifter Cooldown", 30f, 10f, 60f, 2.5f);
+
+        public static CustomNumberOption sheriffCooldown = CustomOption.AddNumber(num++.ToString(), "Sheriff Cooldown", 30f, 10f, 60f, 2.5f);
+        public static CustomToggleOption jesterCanDieToSheriff = CustomOption.AddToggle(num++.ToString(), "Jester Can Die To Sheriff", false);
+        public static CustomNumberOption lighterVision = CustomOption.AddNumber(num++.ToString(), "Lighter Vision", 2f, 0.25f, 5f, 0.25f);
+        public static CustomToggleOption detectiveAnonymousFootprints = CustomOption.AddToggle(num++.ToString(), "Anonymous Footprints", false); 
+        public static CustomNumberOption detectiveFootprintIntervall = CustomOption.AddNumber(num++.ToString(), "Footprint Intervall", 0.5f, 0.25f, 10f, 0.25f);
+        public static CustomNumberOption detectiveFootprintDuration = CustomOption.AddNumber(num++.ToString(), "Footprint Duration", 5f, 0.25f, 10f, 0.25f);
+        public static CustomNumberOption timeMasterCooldown = CustomOption.AddNumber(num++.ToString(), "Time Master Cooldown", 30f, 10f, 60f, 2.5f);
+        public static CustomNumberOption timeMasterRewindTime = CustomOption.AddNumber(num++.ToString(), "Rewind Time", 3f, 1f, 10f, 1f);
+        public static CustomToggleOption timeMasterReviveDuringRewind = CustomOption.AddToggle(num++.ToString(), "Revive During Rewind", false);
+        public static CustomNumberOption medicReportNameDuration = CustomOption.AddNumber(num++.ToString(), "Time Where Medic Reports Will Have Name", 10, 0, 60, 2.5f);
+        public static CustomNumberOption medicReportColorDuration = CustomOption.AddNumber(num++.ToString(), "Time Where Medic Reports Will Have Color Type", 20, 0, 120, 2.5f);
+        public static CustomStringOption medicShowShielded = CustomOption.AddString(num++.ToString(), "Show Shielded Player", new string[] {"Everyone", "Shielded + Medic", "Medic"});
+        public static CustomToggleOption medicShowAttemptToShielded = CustomOption.AddToggle(num++.ToString(), "Shielded Player Sees Murder Attempt", false);
+        public static CustomNumberOption seerCooldown = CustomOption.AddNumber(num++.ToString(), "Seer Cooldown (No Reset After Meeting)", 15f, 30f, 180f, 15f);
+        public static CustomNumberOption seerChanceOfSeeingRight = CustomOption.AddNumber(num++.ToString(), "Seer Chance Of Seeing Right", 100, 0, 100, 5);
+        public static CustomStringOption seerKindOfInfo = CustomOption.AddString(num++.ToString(), "Info That Seer Reveals", new string[] {"Role", "Good/Bad"});
+        public static CustomStringOption seerPlayersWithNotification = CustomOption.AddString(num++.ToString(), "Players That See When They Are Being Revealed", new string[] {"Everyone", "The Good", "The Bad", "Nobody"});
+        public static CustomNumberOption spyCooldown = CustomOption.AddNumber(num++.ToString(), "Spy Cooldown", 30f, 10f, 120f, 5f);
+        public static CustomNumberOption spySpyingDuration = CustomOption.AddNumber(num++.ToString(), "Spy Duration", 10f, 2.5f, 60f, 2.5f);
+        public static CustomNumberOption childGrowingUpDuration = CustomOption.AddNumber(num++.ToString(), "Child Growing Up Duration", 400f, 100f, 1500f, 100f);
+        // public static CustomToggleOption bountyHunterNotifyBounty = CustomOption.AddToggle(num++.ToString(), "Bounty Gets Notified", true); 
+        public static CustomNumberOption trackerUpdateIntervall = CustomOption.AddNumber(num++.ToString(), "Tracker Update Intervall", 5f, 2.5f, 30f, 2.5f);
+        public static CustomNumberOption snitchLeftTasksForImpostors = CustomOption.AddNumber(num++.ToString(), "Task Count Where Impostors See Snitch", 1f, 0f, 5f, 1f);
+
+        public static List<CustomOption> options = new List<CustomOption>();
+        public static int optionsPage = 1;
 
         public static ConfigEntry<bool> DebugMode { get; private set; }
 
-
-
-        public override void Load()
-        {
+        public override void Load() {
             DebugMode  = Config.Bind("Custom", "Enable Debug Mode", false);
 
             CustomOption.ShamelessPlug = false;
             Harmony.PatchAll();
         }
+
+        public static string cs(Color c) {
+            return string.Format("[{0:X2}{1:X2}{2:X2}{3:X2}]", ToByte(c.r), ToByte(c.g), ToByte(c.b), ToByte(c.a));
+        }
+        private static byte ToByte(float f) {
+            f = Mathf.Clamp01(f);
+            return (byte)(f * 255);
+        }
+
+        public TheOtherRolesPlugin() {
+            options = new List<CustomOption>() {
+                crewmateRolesCount,
+                impostorRolesCount,
+
+                mafiaSpawnRate,
+                morphlingSpawnRate,
+                camouflagerSpawnRate,
+                vampireSpawnRate,
+
+                loversSpawnRate,
+                jesterSpawnRate,
+                shifterSpawnRate,
+
+                mayorSpawnRate,
+                engineerSpawnRate,
+                sheriffSpawnRate,
+                lighterSpawnRate,
+                detectiveSpawnRate,
+                timeMasterSpawnRate,
+                medicSpawnRate,
+                swapperSpawnRate,
+                seerSpawnRate,
+                spySpawnRate,
+                childSpawnRate,
+                // bountyHunterSpawnRate,
+                trackerSpawnRate,
+                snitchSpawnRate,
+
+                janitorCooldown,
+                morphlingCooldown,
+                camouflagerCooldown,
+                vampireKillDelay,
+
+                loversBothDie,
+                shifterCooldown,
+
+                sheriffCooldown,
+                jesterCanDieToSheriff,
+                lighterVision,
+                detectiveAnonymousFootprints,
+                detectiveFootprintIntervall,
+                detectiveFootprintDuration,
+                timeMasterCooldown,
+                timeMasterRewindTime,
+                timeMasterReviveDuringRewind,
+                medicReportNameDuration,
+                medicReportColorDuration,
+                medicShowShielded,
+                medicShowAttemptToShielded,
+                seerCooldown,
+                seerChanceOfSeeingRight,
+                seerKindOfInfo,
+                seerPlayersWithNotification,
+                spyCooldown,
+                spySpyingDuration,
+                childGrowingUpDuration,
+                // bountyHunterNotifyBounty,
+                trackerUpdateIntervall,
+                snitchLeftTasksForImpostors
+            };
+        }
     }
 
     // Deactivate bans, since I always leave my local testing game and ban myself
@@ -102,7 +193,7 @@ namespace BonusRoles
 
         public static void Postfix(KeyboardJoystick __instance)
         {
-            if (!BonusRolesPlugin.DebugMode.Value) return;
+            if (!TheOtherRolesPlugin.DebugMode.Value) return;
 
             // Spawn dummys
             if (Input.GetKeyDown(KeyCode.F)) {
index aa6f565ce2928aa6a3d59bfe22dbf6dafe905746..152c01c54c3026a29e683dc267b8d146bd7ce77f 100644 (file)
@@ -3,13 +3,13 @@ using Hazel;
 using System.Collections.Generic;
 using System.Linq;
 using UnhollowerBaseLib;
-using static BonusRoles.BonusRoles;
+using static TheOtherRoles.TheOtherRoles;
 using System.Collections;
 using System;
 using System.Text;
 using UnityEngine;
 
-namespace BonusRoles
+namespace TheOtherRoles
 {
     [HarmonyPatch(typeof(MeetingHud), "CalculateVotes")]
     class MeetingCalculateVotesPatch {
@@ -258,6 +258,17 @@ namespace BonusRoles
         }
     }
 
+    [HarmonyPatch(typeof(ExileController), "Begin")]
+    class ExileBeginPatch {
+        public static void Prefix(ref GameData.PlayerInfo IHDMFDEEDEL, bool DCHFIBODGIL) {
+            // Prevent growing Child exile
+            if (Child.child != null && IHDMFDEEDEL != null && IHDMFDEEDEL.PlayerId == Child.child.PlayerId && !Child.isGrownUp()) {
+                IHDMFDEEDEL = null;
+            }
+        }
+    }
+
+
     [HarmonyPatch(typeof(UnityEngine.Object), nameof(UnityEngine.Object.Destroy), new Type[] { typeof(UnityEngine.Object) })]
     class MeetingExiledEndPatch
     {
@@ -268,20 +279,15 @@ namespace BonusRoles
                 // Reset custom button timers where necessary
                 CustomButton.MeetingEndedUpdate();
 
-                // Jester win condition
-                if (Jester.jester != null && ExileController.Instance.exiled != null && ExileController.Instance.exiled.PlayerId == Jester.jester.PlayerId)
-                {
-                    MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.JesterWin, Hazel.SendOption.None, -1);
-                    AmongUsClient.Instance.FinishRpcImmediately(writer);
-
-                    RPCProcedure.jesterWin();
-                }
-
-                // Child win condition
-                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();
+                // Jester and Bounty Hunter win condition
+                if (ExileController.Instance.exiled != null) {
+                    byte exiledId = ExileController.Instance.exiled.PlayerId;
+                    if ((Jester.jester != null && Jester.jester.PlayerId == exiledId) || (BountyHunter.bountyHunter != null && !BountyHunter.bountyHunter.Data.IsDead && BountyHunter.target != null && BountyHunter.target.PlayerId == exiledId)) {
+                        MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.JesterBountyHunterWin, Hazel.SendOption.None, -1);
+                        writer.Write(exiledId);
+                        AmongUsClient.Instance.FinishRpcImmediately(writer);
+                        RPCProcedure.jesterBountyHunterWin(exiledId);
+                    }
                 }
             }
         }
@@ -327,6 +333,12 @@ namespace BonusRoles
                         __result = ExileController.Instance.exiled.PlayerName + " was The Spy.";
                     else if(Child.child != null && ExileController.Instance.exiled.Object.PlayerId == Child.child.PlayerId)
                         __result = ExileController.Instance.exiled.PlayerName + " was The Child.";
+                    else if(BountyHunter.bountyHunter != null && ExileController.Instance.exiled.Object.PlayerId == BountyHunter.bountyHunter.PlayerId)
+                        __result = ExileController.Instance.exiled.PlayerName + " was The Bounty Hunter.";
+                    else if(Tracker.tracker != null && ExileController.Instance.exiled.Object.PlayerId == Tracker.tracker.PlayerId)
+                        __result = ExileController.Instance.exiled.PlayerName + " was The Tracker.";
+                    else if(Snitch.snitch != null && ExileController.Instance.exiled.Object.PlayerId == Snitch.snitch.PlayerId)
+                        __result = ExileController.Instance.exiled.PlayerName + " was The Snitch.";
                     else
                         __result = ExileController.Instance.exiled.PlayerName + " was not The Impostor.";
                 }
@@ -346,6 +358,8 @@ namespace BonusRoles
                         __result = ExileController.Instance.exiled.PlayerName + " was The ImpLover.";
                     else if(Lovers.lover2 != null && ExileController.Instance.exiled.Object.PlayerId == Lovers.lover2.PlayerId)
                         __result = ExileController.Instance.exiled.PlayerName + " was The ImpLover.";
+                    else if(Vampire.vampire != null && ExileController.Instance.exiled.Object.PlayerId == Vampire.vampire.PlayerId)
+                        __result = ExileController.Instance.exiled.PlayerName + " was The Vamipre.";
                 }
 
                 // Hide number of remaining impostors on Jester win
index 010d59113d60beb01069b964a7a589b3869bf8a1..ba587533e26ebd258ee9c2e3de5e63a807870685 100644 (file)
@@ -5,11 +5,11 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
-using static BonusRoles.BonusRoles;
-using static BonusRoles.GameHistory;
+using static TheOtherRoles.TheOtherRoles;
+using static TheOtherRoles.GameHistory;
 using UnityEngine;
 
-namespace BonusRoles {
+namespace TheOtherRoles {
     [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))]
     public static class PlayerControlFixedUpdatePatch
     {
@@ -44,7 +44,7 @@ namespace BonusRoles {
             }
         }
 
-        static PlayerControl setAnyRoleTarget() {
+        static PlayerControl setTarget(bool onlyCrewmates = false, bool targetPlayersInVents = false) {
             PlayerControl result = null;
             float num = GameOptionsData.KillDistances[Mathf.Clamp(PlayerControl.GameOptions.KillDistance, 0, 2)];
             if (!ShipStatus.Instance) return result;
@@ -54,10 +54,10 @@ namespace BonusRoles {
             for (int i = 0; i < allPlayers.Count; i++)
             {
                 GameData.PlayerInfo playerInfo = allPlayers[i];
-                if (!playerInfo.Disconnected && playerInfo.PlayerId != PlayerControl.LocalPlayer.PlayerId && !playerInfo.IsDead)
+                if (!playerInfo.Disconnected && playerInfo.PlayerId != PlayerControl.LocalPlayer.PlayerId && !playerInfo.IsDead && (!onlyCrewmates || !playerInfo.IsImpostor))
                 {
                     PlayerControl @object = playerInfo.Object;
-                    if (@object && !@object.inVent)
+                    if (@object && (!@object.inVent || targetPlayersInVents))
                     {
                         Vector2 vector = @object.GetTruePosition() - truePosition;
                         float magnitude = vector.magnitude;
@@ -74,30 +74,34 @@ namespace BonusRoles {
 
         static void medicSetTarget() {
             if (Medic.medic == null || Medic.medic != PlayerControl.LocalPlayer) return;
-            Medic.currentTarget = setAnyRoleTarget();
+            Medic.currentTarget = setTarget();
         }
 
         static void shifterSetTarget() {
             if (Shifter.shifter == null || Shifter.shifter != PlayerControl.LocalPlayer) return;
-            Shifter.currentTarget = setAnyRoleTarget();
+            Shifter.currentTarget = setTarget();
         }
 
         
         static void morphlingSetTarget() {
             if (Morphling.morphling == null || Morphling.morphling != PlayerControl.LocalPlayer) return;
-            Morphling.currentTarget = setAnyRoleTarget();
+            Morphling.currentTarget = setTarget();
         }
         
         static void sheriffSetTarget() {
             if (Sheriff.sheriff == null || Sheriff.sheriff != PlayerControl.LocalPlayer) return;
-            Sheriff.currentTarget = setAnyRoleTarget();
+            Sheriff.currentTarget = setTarget();
         }
 
         static void seerSetTarget() {
             if (Seer.seer == null || Seer.seer != PlayerControl.LocalPlayer) return;
-            Seer.currentTarget = setAnyRoleTarget();
+            Seer.currentTarget = setTarget();
             if (Seer.currentTarget != null && Seer.revealedPlayers.Keys.Any(p => p.Data.PlayerId == Seer.currentTarget.Data.PlayerId)) Seer.currentTarget = null; // Remove target if already revealed
+        }
 
+        static void trackerSetTarget() {
+            if (Tracker.tracker == null || Tracker.tracker != PlayerControl.LocalPlayer) return;
+            Tracker.currentTarget = setTarget();
         }
 
         static void detectiveSetFootPrints() {
@@ -107,12 +111,74 @@ namespace BonusRoles {
             if (Detective.timer <= 0f) {
                 Detective.timer = Detective.footprintIntervall;
                 foreach (PlayerControl player in PlayerControl.AllPlayerControls) {
-                    if (player != null && player != PlayerControl.LocalPlayer && !player.Data.IsDead) {
+                    if (player != null && player != PlayerControl.LocalPlayer && !player.Data.IsDead && !player.inVent) {
                         new Footprint(Detective.footprintDuration, Detective.anonymousFootprints, player);
                     }
                 }
             }
+        }
+
+        static void vampireSetTarget() {
+            if (Vampire.vampire == null || Vampire.vampire != PlayerControl.LocalPlayer) return;
+                   PlayerControl target = setTarget(true, true);
+            bool targetNearGarlic = false;
+            if (target != null) {
+                foreach (Garlic garlic in Garlic.garlics) {
+                    if (Vector2.Distance(garlic.garlic.transform.position, target.transform.position) <= 1.91f) {
+                        targetNearGarlic = true;
+                    }
+                }
+            }
+            Vampire.targetNearGarlic = targetNearGarlic;
+            Vampire.currentTarget = target;
+        }
+
+        static void engineerUpdate() {
+            if (PlayerControl.LocalPlayer.Data.IsImpostor && Engineer.engineer != null) {
+                foreach (Vent vent in ShipStatus.Instance.AllVents) {
+                    if (vent.Field_7?.material != null) {
+                        if (Engineer.engineer.inVent) {
+                            vent.Field_7.material.SetFloat("_Outline", 1f);
+                            vent.Field_7.material.SetColor("_OutlineColor", Engineer.color);
+                        } else if (vent.Field_7.material.GetColor("_AddColor") != Color.red) {
+                            vent.Field_7.material.SetFloat("_Outline", 0);
+                        }
+                    }
+                }
+            }
+        }
+
+        static void trackerUpdate() {
+            if (Tracker.tracker == null || Tracker.tracked == null) return;
+
+            if (Tracker.arrow?.arrow != null && PlayerControl.LocalPlayer != Tracker.tracker) {
+                Tracker.arrow.arrow.SetActive(false);
+                return;
+            }
+
+            if (Tracker.arrow?.arrow != null && Tracker.tracked != null) {
+                Tracker.timeUntilUpdate -= Time.fixedDeltaTime;
+
+                if (Tracker.timeUntilUpdate <= 0f) {
+                    bool trackedOnMap = !Tracker.tracked.Data.IsDead;
+                    System.Console.WriteLine("Update");
+                    Vector3 position = Tracker.tracked.transform.position;
+                    if (!trackedOnMap) { // Check for dead body
+                        DeadBody body = UnityEngine.Object.FindObjectsOfType<DeadBody>().FirstOrDefault(b => b.ParentId == Tracker.tracked.PlayerId);
+                        if (body != null) {
+                            trackedOnMap = true;
+                            position = body.transform.position;
+                            System.Console.WriteLine("DeadBody");
+                        }
+                    }
 
+                    Tracker.arrow.Update(position);
+                    Tracker.arrow.arrow.SetActive(trackedOnMap);
+                    Tracker.timeUntilUpdate = Tracker.updateIntervall;
+                } else {
+                    Tracker.arrow.Update();
+                }
+            }
         }
 
         public static void Postfix(PlayerControl __instance) {
@@ -133,26 +199,42 @@ namespace BonusRoles {
                 seerSetTarget();
                 // Detective
                 detectiveSetFootPrints();
+                // Tracker
+                trackerSetTarget();
+                // Vampire
+                vampireSetTarget();
+                Garlic.UpdateAll();
+                // Engineer
+                engineerUpdate();
+                // Tracker
+                trackerUpdate();
             } 
         }
     }
 
+    [HarmonyPatch(typeof(HudManager), nameof(HudManager.OpenMeetingRoom))]
+    class StartMeetingHostPatch {
+        public static void Prefix(PlayerControl __instance) {
+            // Perform vampire bite kill before the meeting starts for HOST
+            if (!MeetingHud.Instance && AmongUsClient.Instance.AmHost)
+                RPCProcedure.vampireTryKill();
+        }
+    }
+
+    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.CoStartMeeting))]
+    class StartMeetingClientPatch {
+        public static void Prefix(PlayerControl __instance) {
+            // Perform vampire bite kill before the meeting starts for CLIENTS
+            if (AmongUsClient.Instance.AmClient)
+            RPCProcedure.vampireTryKill();
+        }
+    }
+
     [HarmonyPatch(typeof(KillButtonManager), nameof(KillButtonManager.PerformKill))]
     class PerformKillPatch
     {
         public static bool Prefix(KillButtonManager __instance) {
-            // 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;
+            return Helpers.handleMurderAttempt(__instance.CurrentTarget);
         }
     }
 
@@ -212,38 +294,23 @@ namespace BonusRoles {
     [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.MurderPlayer))]
     public static class MurderPlayerPatch
     {
-        public static void Prefix(PlayerControl __instance, PlayerControl PAIBDFDMIGK, out bool __state)
-        {
-            // __state denotes if the player performing the murder needs to be reset to crewmate
-            __state = false;
+        public static bool resetToCrewmate = false;
 
-            // Allow Sheriff to kill "as crewmate" by assigning the impostor role
-            if (Sheriff.sheriff != null && __instance == Sheriff.sheriff) {
-                __instance.Data.IsImpostor = true;
-                __state = true;
-            }
-            // Allow Shifter to suicide
-            else if (Shifter.shifter != null && __instance == Shifter.shifter) {
-                __instance.Data.IsImpostor = true;
-                __state = true;
-            }
-            // Allow Lover which is no impostor to suicide
-            else if ((Lovers.lover1 != null && __instance == Lovers.lover1) || (Lovers.lover2 != null && __instance == Lovers.lover2)) {
-                if (!__instance.Data.IsImpostor) {
-                    __instance.Data.IsImpostor = true;
-                    __state = true;
-                }
-            }
+        public static void Prefix(PlayerControl __instance, PlayerControl PAIBDFDMIGK)
+        {
+            // Allow everyone to murder players
+            resetToCrewmate = !__instance.Data.IsImpostor;
+            __instance.Data.IsImpostor = true;
         }
 
-        public static void Postfix(PlayerControl __instance, PlayerControl PAIBDFDMIGK, bool __state)
+        public static void Postfix(PlayerControl __instance, PlayerControl PAIBDFDMIGK)
         {
             // Collect dead player info
             DeadPlayer deadPlayer = new DeadPlayer(PAIBDFDMIGK, DateTime.UtcNow, DeathReason.Kill, __instance);
             GameHistory.deadPlayers.Add(deadPlayer);
 
-            // Reset killer to crewmate if __state
-            if (__state) __instance.Data.IsImpostor = false;
+            // Reset killer to crewmate if resetToCrewmate
+            if (resetToCrewmate) __instance.Data.IsImpostor = false;
 
             // Lover suicide trigger on murder
             if ((Lovers.lover1 != null && PAIBDFDMIGK == Lovers.lover1) || (Lovers.lover2 != null && PAIBDFDMIGK == Lovers.lover2)) {
@@ -279,7 +346,6 @@ namespace BonusRoles {
                     RPCProcedure.loverSuicide(otherLover.PlayerId);
                 }
             }
-
         }
     }
 }
index 0672ceabe35c09c911a5aec36f543c5ec3705b86..49c88988d290bc29e6751f4bc63f3913c79b507a 100644 (file)
@@ -1,37 +1,41 @@
 using HarmonyLib;
 using Hazel;
-using static BonusRoles.BonusRoles;
-using static BonusRoles.HudManagerStartPatch;
-using static BonusRoles.GameHistory;
+using static TheOtherRoles.TheOtherRoles;
+using static TheOtherRoles.HudManagerStartPatch;
+using static TheOtherRoles.GameHistory;
 using System.Collections.Generic;
 using System.Collections;
 using System.Linq;
 using UnityEngine;
 using System;
 
-namespace BonusRoles
+namespace TheOtherRoles
 {
     enum RoleId {
-        Jester = 0,
-        Mayor = 1,
-        Engineer = 2,
-        Sheriff = 3,
-        Lighter = 4,
-        Godfather = 5,
-        Mafioso = 6,
-        Janitor = 7,
-        Detective = 8,
-        TimeMaster = 9,
-        Medic = 10,
-        Shifter = 11,
-        Swapper = 12,
-        Lover1 = 13,
-        Lover2 = 14,
-        Seer = 15,
-        Morphling = 16,
-        Camouflager = 17,
-        Spy = 18,
-        Child = 19,
+        Jester,
+        Mayor,
+        Engineer,
+        Sheriff,
+        Lighter,
+        Godfather,
+        Mafioso,
+        Janitor,
+        Detective,
+        TimeMaster,
+        Medic,
+        Shifter,
+        Swapper,
+        Lover1,
+        Lover2,
+        Seer,
+        Morphling,
+        Camouflager,
+        Spy,
+        Child,
+        BountyHunter,
+        Tracker,
+        Vampire,
+        Snitch
     }
 
     enum CustomRPC
@@ -44,7 +48,7 @@ namespace BonusRoles
 
         // Role functionality
 
-        JesterWin = 80,
+        JesterBountyHunterWin = 80,
         EngineerFixLights = 81,
         EngineerUsedRepair = 82,
         JanitorClean = 83,
@@ -58,8 +62,12 @@ namespace BonusRoles
         SeerReveal = 91,
         MorphlingMorph = 92,
         CamouflagerCamouflage = 93,
-        ChildDied = 94,
+        TrackerUsedTracker = 94,
         LoverSuicide = 95,
+        SetBountyHunterTarget = 96,
+        VampireBiteNotification = 97,
+        VampireTryKill = 98,
+        PlaceGarlic = 99,
     }
 
     public static class RPCProcedure {
@@ -67,6 +75,7 @@ namespace BonusRoles
         // Main Controls
 
         public static void resetVariables() {
+            Garlic.clearGarlics();
             clearAndReloadRoles();
             clearGameHistory();
             setCustomButtonCooldowns();
@@ -148,19 +157,45 @@ namespace BonusRoles
                     case RoleId.Child:
                         Child.child = player;
                         break;
+                    case RoleId.BountyHunter:
+                        BountyHunter.bountyHunter = player;
+                        break;
+                    case RoleId.Tracker:
+                        Tracker.tracker = player;
+                        break;
+                    case RoleId.Vampire:
+                        Vampire.vampire = player;
+                        break;
+                    case RoleId.Snitch:
+                        Snitch.snitch = player;
+                        break;
                     }
                 }
         }
 
         // Role functionality
 
-        public static void jesterWin() {
-            Jester.jester.Revive();
-            Jester.jester.Data.IsDead = false;
-            Jester.jester.Data.IsImpostor = true;
+        public static void jesterBountyHunterWin(byte exiledId) {
+            PlayerControl exiled = Helpers.playerById(exiledId);
+            if (exiled == null) return;
+
+            bool jesterWin = false;
+            bool bountyHunterWin = false;
+
+            if (Jester.jester != null && exiled == Jester.jester) {
+                Jester.jester.Revive();
+                Jester.jester.Data.IsDead = false;
+                Jester.jester.Data.IsImpostor = true;
+                jesterWin = true;
+            }
+            if (BountyHunter.bountyHunter != null && !BountyHunter.bountyHunter.Data.IsDead && BountyHunter.target == exiled) {
+                BountyHunter.bountyHunter.Data.IsImpostor = true;
+                bountyHunterWin = true;
+            }
+
             foreach (PlayerControl player in PlayerControl.AllPlayerControls)
             {
-                if (player != Jester.jester)
+                if (player != null && player != Jester.jester && player != BountyHunter.bountyHunter)
                 {
                     player.RemoveInfected();
                     player.Die(DeathReason.Exile);
@@ -168,6 +203,17 @@ namespace BonusRoles
                     player.Data.IsImpostor = false;
                 }
             }
+            if (jesterWin && !bountyHunterWin && BountyHunter.bountyHunter != null) {
+                BountyHunter.bountyHunter.RemoveInfected();
+                BountyHunter.bountyHunter.Die(DeathReason.Exile);
+                BountyHunter.bountyHunter.Data.IsDead = true;
+                BountyHunter.bountyHunter.Data.IsImpostor = false;
+            } else if (bountyHunterWin && !jesterWin && Jester.jester != null) {
+                Jester.jester.RemoveInfected();
+                Jester.jester.Die(DeathReason.Exile);
+                Jester.jester.Data.IsDead = true;
+                Jester.jester.Data.IsImpostor = false;
+            }
         }
 
         public static void engineerFixLights() {
@@ -315,7 +361,13 @@ namespace BonusRoles
                         Spy.spy = oldShifter;
                     } else if (Child.child != null && Child.child == player) {
                         Child.child = oldShifter;
-                    } else { // Crewmate
+                    } else if (BountyHunter.bountyHunter != null && BountyHunter.bountyHunter == player) {
+                        BountyHunter.bountyHunter = oldShifter;
+                    } else if (Tracker.tracker != null && Tracker.tracker == player) {
+                        Tracker.tracker = oldShifter;
+                    } else if (Snitch.snitch != null && Snitch.snitch == player) {
+                        Snitch.snitch = oldShifter;
+                    }else { // Crewmate
                     }
                     
                     Shifter.shifter = player;
@@ -378,29 +430,51 @@ namespace BonusRoles
             Camouflager.camouflageTimer = 10f;
         }
 
-        public static void childDied() {
-            Child.child.Revive();
-            Child.child.Data.IsDead = false;
-            Child.child.Data.IsImpostor = true;
-            foreach (PlayerControl player in PlayerControl.AllPlayerControls)
-            {
-                if (player != null && player != Child.child)
-                {
-                    player.RemoveInfected();
-                    player.Die(DeathReason.Exile);
-                    player.Data.IsDead = true;
-                    player.Data.IsImpostor = false;
-                }
-            }
-        }
-
         public static void loverSuicide(byte remainingLoverId) {
             if (Lovers.lover1 != null && !Lovers.lover1.Data.IsDead && Lovers.lover1.PlayerId == remainingLoverId) {
                 Lovers.lover1.MurderPlayer(Lovers.lover1);
             } else if (Lovers.lover2 != null && !Lovers.lover2.Data.IsDead && Lovers.lover2.PlayerId == remainingLoverId) {
                 Lovers.lover2.MurderPlayer(Lovers.lover2);
             }
-        }     
+        }
+
+        public static void setBountyHunterTarget(byte targetId) {
+            foreach (PlayerControl player in PlayerControl.AllPlayerControls)
+                if (player.PlayerId == targetId)
+                    BountyHunter.target = player;
+        }
+
+        public static void vampireBiteNotification(byte targetId) {
+            if (Vampire.vampire == null) return;
+            foreach (PlayerControl player in PlayerControl.AllPlayerControls) {
+                if (player.PlayerId == targetId && !player.Data.IsDead) {
+                        Vampire.bitten = player;
+                }
+            }
+        }
+
+        public static void vampireTryKill() {
+            if (Vampire.vampire == null || Vampire.bitten == null) return;
+
+            if (!Vampire.bitten.Data.IsDead && Helpers.handleMurderAttempt(Vampire.bitten, false)) {
+                Vampire.bitten.MurderPlayer(Vampire.bitten); // Suicide because of the kill animation teleport
+            }
+            Vampire.bitten = null;
+        }
+
+        public static void placeGarlic(byte[] buff) {
+            Vector3 position = Vector3.zero;
+            position.x = BitConverter.ToSingle(buff, 0*sizeof(float));
+            position.y = BitConverter.ToSingle(buff, 1*sizeof(float));
+            new Garlic(position);
+        }
+
+        public static void trackerUsedTracker(byte targetId) {
+            Tracker.usedTracker = true;
+            foreach (PlayerControl player in PlayerControl.AllPlayerControls)
+                if (player.PlayerId == targetId)
+                    Tracker.tracked = player;
+        }
     }
 
     [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.HandleRpc))]
@@ -429,8 +503,8 @@ namespace BonusRoles
 
                 // Role functionality
 
-                case (byte)CustomRPC.JesterWin:
-                    RPCProcedure.jesterWin();
+                case (byte)CustomRPC.JesterBountyHunterWin:
+                    RPCProcedure.jesterBountyHunterWin(HFPCBBHJIPJ.ReadByte());
                     break;
                 case (byte)CustomRPC.EngineerFixLights:
                     RPCProcedure.engineerFixLights();
@@ -475,12 +549,24 @@ namespace BonusRoles
                 case (byte)CustomRPC.CamouflagerCamouflage:
                     RPCProcedure.camouflagerCamouflage();
                     break;
-                case (byte)CustomRPC.ChildDied:
-                    RPCProcedure.childDied();
-                    break;
                 case (byte)CustomRPC.LoverSuicide:
                     RPCProcedure.loverSuicide(HFPCBBHJIPJ.ReadByte());
                     break;
+                case (byte)CustomRPC.SetBountyHunterTarget:
+                    RPCProcedure.setBountyHunterTarget(HFPCBBHJIPJ.ReadByte());
+                    break;
+                case (byte)CustomRPC.VampireBiteNotification:
+                    RPCProcedure.vampireBiteNotification(HFPCBBHJIPJ.ReadByte());
+                    break;
+                case (byte)CustomRPC.VampireTryKill:
+                    RPCProcedure.vampireTryKill();
+                    break;
+                case (byte)CustomRPC.PlaceGarlic:
+                    RPCProcedure.placeGarlic(HFPCBBHJIPJ.ReadBytesAndSize());
+                    break;
+                case (byte)CustomRPC.TrackerUsedTracker:
+                    RPCProcedure.trackerUsedTracker(HFPCBBHJIPJ.ReadByte());
+                    break;
             }
         }
     }
index 2a82376b5efbe78032b5d389ef7a045f3849a2e5..ab6b3b50785c41c16aa482fe703e08dbbf92f47a 100644 (file)
@@ -3,19 +3,25 @@ using Hazel;
 using System.Collections.Generic;
 using System.Linq;
 using UnhollowerBaseLib;
+using UnityEngine;
 using System;
-using static BonusRoles.BonusRoles;
+using static TheOtherRoles.TheOtherRoles;
 
-namespace BonusRoles
+namespace TheOtherRoles
 {
     [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.RpcSetInfected))]
     class SetInfectedPatch
     {
+
         private static void setRoleToRandomPlayer(byte roleId, List<PlayerControl> playerList) {
             var index = rnd.Next(0, playerList.Count);
             byte playerId = playerList[index].PlayerId;
             playerList.RemoveAt(index);
 
+            setRoleToPlayer(roleId, playerId);
+        }
+
+        private static void setRoleToPlayer(byte roleId, byte playerId) {
             MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetRole, Hazel.SendOption.None, -1);
             writer.Write(roleId);
             writer.Write(playerId);
@@ -34,59 +40,126 @@ namespace BonusRoles
             List<PlayerControl> impostors = PlayerControl.AllPlayerControls.ToArray().ToList().OrderBy(x => Guid.NewGuid()).ToList();
             impostors.RemoveAll(x => !x.Data.IsImpostor);
 
-            // Special roles impostors can be converted to
-            if (impostors.Count >= 3 && (rnd.Next(1, 101) <= BonusRolesPlugin.mafiaSpawnChance.GetValue())) {
+            float crewCountSettings = TheOtherRolesPlugin.crewmateRolesCount.GetValue();
+            float impCountSettings = TheOtherRolesPlugin.impostorRolesCount.GetValue();
+
+            if (crewCountSettings % 1 == 0.5f) crewCountSettings += 0.5f * (rnd.Next(2) * 2 - 1);
+            if (impCountSettings % 1 == 0.5f) impCountSettings += 0.5f * (rnd.Next(2) * 2 - 1);
+
+            int maxCrewmateRoles = Mathf.Min(crewmates.Count, Mathf.RoundToInt(crewCountSettings));
+            int maxImpostorRoles = Mathf.Min(impostors.Count, Mathf.RoundToInt(impCountSettings));
+
+            Dictionary<byte, int> impSettings = new Dictionary<byte, int>();
+            Dictionary<byte, int> crewSettings = new Dictionary<byte, int>();
+            
+            impSettings.Add((byte)RoleId.Morphling, TheOtherRolesPlugin.morphlingSpawnRate.GetValue());
+            impSettings.Add((byte)RoleId.Camouflager, TheOtherRolesPlugin.camouflagerSpawnRate.GetValue());
+            impSettings.Add((byte)RoleId.Vampire, TheOtherRolesPlugin.vampireSpawnRate.GetValue());
+
+            crewSettings.Add((byte)RoleId.Jester, TheOtherRolesPlugin.jesterSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Mayor, TheOtherRolesPlugin.mayorSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Engineer, TheOtherRolesPlugin.engineerSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Sheriff, TheOtherRolesPlugin.sheriffSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Lighter, TheOtherRolesPlugin.lighterSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Detective, TheOtherRolesPlugin.detectiveSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.TimeMaster, TheOtherRolesPlugin.timeMasterSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Medic, TheOtherRolesPlugin.medicSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Shifter, TheOtherRolesPlugin.shifterSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Swapper,TheOtherRolesPlugin.swapperSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Seer, TheOtherRolesPlugin.seerSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Spy, TheOtherRolesPlugin.spySpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Child, TheOtherRolesPlugin.childSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Tracker, TheOtherRolesPlugin.trackerSpawnRate.GetValue());
+            crewSettings.Add((byte)RoleId.Snitch, TheOtherRolesPlugin.snitchSpawnRate.GetValue());
+            // crewSettings.Add((byte)RoleId.BountyHunter, TheOtherRolesPlugin.bountyHunterSpawnRate.GetValue()); BOUNTY HUNTER
+
+            // Set multiple player roles
+            if (impostors.Count >= 3 && maxImpostorRoles >= 3 && (rnd.Next(1, 101) <= TheOtherRolesPlugin.mafiaSpawnRate.GetValue() * 10)) {
                 setRoleToRandomPlayer((byte)RoleId.Godfather, impostors);
                 setRoleToRandomPlayer((byte)RoleId.Janitor, impostors);
                 setRoleToRandomPlayer((byte)RoleId.Mafioso, impostors);
+                maxImpostorRoles -= 3;
             }
 
-            // Special roles that involve crewmates and impostors
-            if (rnd.Next(1, 101) <= BonusRolesPlugin.loversSpawnChance.GetValue())
-            {
-                if (impostors.Count > 0 && crewmates.Count > 0 && rnd.Next(1, 101) <= 33) {
+            if (rnd.Next(1, 101) <= TheOtherRolesPlugin.loversSpawnRate.GetValue() * 10) {
+                if (impostors.Count > 0 && crewmates.Count > 0 && maxCrewmateRoles > 0 && maxImpostorRoles > 0 && rnd.Next(1, 101) <= 33) {
                     setRoleToRandomPlayer((byte)RoleId.Lover1, impostors); 
-                    setRoleToRandomPlayer((byte)RoleId.Lover2, crewmates); 
-                } else if (crewmates.Count >= 2) {
+                    setRoleToRandomPlayer((byte)RoleId.Lover2, crewmates);
+                    maxCrewmateRoles--;
+                    maxImpostorRoles--;
+                } else if (crewmates.Count >= 2 && maxCrewmateRoles >= 2) {
                     setRoleToRandomPlayer((byte)RoleId.Lover1, crewmates); 
                     setRoleToRandomPlayer((byte)RoleId.Lover2, crewmates); 
+                    maxCrewmateRoles -= 2; 
                 }
             }
 
-            // Special roles impostors can be converted to
-            if (impostors.Count >= 1 && (rnd.Next(1, 101) <= BonusRolesPlugin.morphlingSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Morphling, impostors);
+            // Set tickets and always active roles
 
-            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);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.mayorSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Mayor, crewmates);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.engineerSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Engineer, crewmates);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.sheriffSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Sheriff, crewmates);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.lighterSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Lighter, crewmates);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.detectiveSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Detective, crewmates);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.timeMasterSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.TimeMaster, crewmates);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.medicSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Medic, crewmates);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.shifterSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Shifter, crewmates);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.swapperSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Swapper, crewmates);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.seerSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Seer, crewmates);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.spySpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Spy, crewmates);
-            if (crewmates.Count > 0 && (rnd.Next(1, 101) <= BonusRolesPlugin.childSpawnChance.GetValue()))
-                setRoleToRandomPlayer((byte)RoleId.Child, crewmates);
+            List<byte> crewTickets = new List<byte>();
+            List<byte> impTickets = new List<byte>();
+
+            foreach (KeyValuePair<byte, int> entry in crewSettings) {
+                if (entry.Value == 0) { // Never
+                } else if (entry.Value == 10) { // Always
+                    if (crewmates.Count > 0 && maxCrewmateRoles > 0) {
+                        setRoleToRandomPlayer(entry.Key, crewmates);
+                        maxCrewmateRoles--;
+                    }
+                } else { // Other
+                    for (int i = 0; i < entry.Value; i++) crewTickets.Add(entry.Key);
+                }
+            }
+
+            foreach (KeyValuePair<byte, int> entry in impSettings) {
+                if (entry.Value == 0) { // Never
+                } else if (entry.Value == 10) { // Always
+                    if (impostors.Count > 0 && maxImpostorRoles > 0) {
+                        setRoleToRandomPlayer(entry.Key, impostors);
+                        maxImpostorRoles--;
+                    }
+                } else { // Other
+                    for (int i = 0; i < entry.Value; i++) impTickets.Add(entry.Key);
+                }
+            }
+
+            // Set solo player roles
+
+            for (int i = 0; i < maxCrewmateRoles; i++) {
+                if (crewTickets.Count > 0 && crewmates.Count > 0) {
+                    var index = rnd.Next(0, crewTickets.Count);
+                    byte roleId = crewTickets[index];
+                    crewTickets.RemoveAll(x => x == roleId);
+                    setRoleToRandomPlayer(roleId, crewmates);
+                }
+            }
+
+            for (int i = 0; i < maxImpostorRoles; i++) {
+                if (impTickets.Count > 0 && impostors.Count > 0) {
+                    var index = rnd.Next(0, impTickets.Count);
+                    byte roleId = impTickets[index];
+                    impTickets.RemoveAll(x => x == roleId);
+                    setRoleToRandomPlayer(roleId, impostors);
+                }
+            }
+
+            // if (crewmates.Count > 0 && (rnd.Next(1, 101) <= TheOtherRolesPlugin.bountyHunterSpawnRate.GetValue())) {
+            //     List<PlayerControl> availableTargets = PlayerControl.AllPlayerControls.ToArray().ToList();
+            //     var bountyHunterIndex = rnd.Next(0, crewmates.Count);
+            //     byte bountyHunterId = crewmates[bountyHunterIndex].PlayerId;
+
+            //     availableTargets.RemoveAll(x => x.Data.IsImpostor || x.PlayerId == bountyHunterId);
+
+            //     if (availableTargets.Count > 0) {
+            //         byte targetId = availableTargets[rnd.Next(0, availableTargets.Count)].PlayerId;
+            //         crewmates.RemoveAt(bountyHunterIndex);
+            //         setRoleToPlayer((byte)RoleId.BountyHunter, bountyHunterId);
+            //         writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetBountyHunterTarget, Hazel.SendOption.None, -1);
+            //         writer.Write(targetId);
+            //         AmongUsClient.Instance.FinishRpcImmediately(writer);
+            //         RPCProcedure.setBountyHunterTarget(targetId);
+            //     }
+            // }
         }
     }
 }
index 6636cc20a6edb42268665c77a272e5337ba965f2..c6ed7fbb27b1535c82255c8aab1b0e13e42b4878 100644 (file)
@@ -1,8 +1,8 @@
   
 using HarmonyLib;
-using static BonusRoles.BonusRoles;
+using static TheOtherRoles.TheOtherRoles;
 
-namespace BonusRoles {
+namespace TheOtherRoles {
 
     [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.CalculateLightRadius))]
     public class ShipStatusPatch {
diff --git a/Source Code/TheOtherRoles.cs b/Source Code/TheOtherRoles.cs
new file mode 100644 (file)
index 0000000..cf70fc2
--- /dev/null
@@ -0,0 +1,545 @@
+using System.Net;
+using System.Linq;
+using BepInEx;
+using BepInEx.Configuration;
+using BepInEx.IL2CPP;
+using HarmonyLib;
+using Hazel;
+using Reactor;
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.IO;
+using UnityEngine;
+using Reactor.Unstrip;
+using Reactor.Extensions;
+
+namespace TheOtherRoles
+{
+    [HarmonyPatch]
+    public static class TheOtherRoles
+    {
+        public static System.Random rnd = new System.Random((int)DateTime.Now.Ticks);
+
+        public static void clearAndReloadRoles() {
+            Jester.clearAndReload();
+            Mayor.clearAndReload();
+            Engineer.clearAndReload();
+            Sheriff.clearAndReload();
+            Lighter.clearAndReload();
+            Godfather.clearAndReload();
+            Mafioso.clearAndReload();
+            Janitor.clearAndReload();
+            Detective.clearAndReload();
+            TimeMaster.clearAndReload();
+            Medic.clearAndReload();
+            Shifter.clearAndReload();
+            Swapper.clearAndReload();
+            Lovers.clearAndReload();
+            Seer.clearAndReload();
+            Morphling.clearAndReload();
+            Camouflager.clearAndReload();
+            Spy.clearAndReload();
+            Child.clearAndReload();
+            BountyHunter.clearAndReload();
+            Tracker.clearAndReload();
+            Vampire.clearAndReload();
+            Snitch.clearAndReload();
+        }
+
+        public static class Jester {
+            public static PlayerControl jester;
+            public static Color color = new Color(255f / 255f, 84f / 255f, 167f / 255f, 1);
+
+            public static void clearAndReload() {
+                jester = null;
+            }
+        }
+
+        public static class Mayor {
+            public static PlayerControl mayor;
+            public static Color color = new Color(105f / 255f, 58f / 255f, 58f / 255f, 1);
+
+            public static void clearAndReload() {
+                mayor = null;
+            }
+        }
+
+        public static class Engineer {
+            public static PlayerControl engineer;
+            public static Color color = new Color(98f / 255f, 216f / 255f, 240f / 255f, 1);
+            public static bool usedRepair;
+            private static Sprite buttonSprite;
+
+            public static Sprite getButtonSprite() {
+                if (buttonSprite) return buttonSprite;
+                buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.RepairButton.png", 100f);
+                return buttonSprite;
+            }
+
+            public static void clearAndReload() {
+                engineer = null;
+                usedRepair = false;
+            }
+        }
+
+        public static class Godfather {
+            public static PlayerControl godfather;
+            public static Color color = Palette.ImpostorRed;
+
+            public static void clearAndReload() {
+                godfather = null;
+            }
+        }
+
+        public static class Mafioso {
+            public static PlayerControl mafioso;
+            public static Color color = Palette.ImpostorRed;
+
+            public static void clearAndReload() {
+                mafioso = null;
+            }
+        }
+
+
+        public static class Janitor {
+            public static PlayerControl janitor;
+            public static Color color = Palette.ImpostorRed;
+
+            public static float cooldown = float.MaxValue;
+
+            private static Sprite buttonSprite;
+            public static Sprite getButtonSprite() {
+                if (buttonSprite) return buttonSprite;
+                buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.CleanButton.png", 100f);
+                return buttonSprite;
+            }
+
+            public static void clearAndReload() {
+                janitor = null;
+                cooldown = TheOtherRolesPlugin.janitorCooldown.GetValue();
+            }
+        }
+
+        public static class Sheriff {
+            public static PlayerControl sheriff;
+            public static Color color = new Color(255f / 255f, 204f / 255f, 0f / 255f, 1);
+
+            public static float cooldown = float.MaxValue;
+            public static bool jesterCanDieToSheriff = false;
+
+            public static PlayerControl currentTarget;
+
+            public static void clearAndReload() {
+                sheriff = null;
+                currentTarget = null;
+                cooldown = TheOtherRolesPlugin.sheriffCooldown.GetValue();
+                jesterCanDieToSheriff = TheOtherRolesPlugin.jesterCanDieToSheriff.GetValue();
+            }
+        }
+
+        public static class Lighter {
+            public static PlayerControl lighter;
+            public static Color color = new Color(250f / 255f, 204f / 255f, 37f / 255f, 1);
+            
+            public static float lighterVision = 2f;
+
+            public static void clearAndReload() {
+                lighter = null;
+                lighterVision = TheOtherRolesPlugin.lighterVision.GetValue();
+            }
+        }
+
+        public static class Detective {
+            public static PlayerControl detective;
+            public static Color color = new Color(2f / 255f, 61f / 255f, 156f / 255f, 1);
+
+            public static float footprintIntervall = 1f;
+            public static float footprintDuration = 1f;
+            public static bool anonymousFootprints = false;
+
+            public static float timer = 1f;
+
+            public static void clearAndReload() {
+                detective = null;
+                anonymousFootprints = TheOtherRolesPlugin.detectiveAnonymousFootprints.GetValue();
+                footprintIntervall = TheOtherRolesPlugin.detectiveFootprintIntervall.GetValue();
+                footprintDuration = TheOtherRolesPlugin.detectiveFootprintDuration.GetValue();
+                timer = footprintIntervall;
+            }
+        }
+    }
+
+    public static class TimeMaster {
+        public static PlayerControl timeMaster;
+        public static Color color = new Color(114f / 255f, 234f / 255f, 247f / 255f, 1);
+
+        public static bool reviveDuringRewind = false;
+        public static float rewindTime = 3f;
+        public static float cooldown = float.MaxValue;
+
+        public static bool isRewinding = false;
+
+        private static Sprite buttonSprite;
+        public static Sprite getButtonSprite() {
+            if (buttonSprite) return buttonSprite;
+            buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.RewindButton.png", 100f);
+            return buttonSprite;
+        }
+
+        public static void clearAndReload() {
+            timeMaster = null;
+            isRewinding = false;
+            reviveDuringRewind = TheOtherRolesPlugin.timeMasterReviveDuringRewind.GetValue();
+            rewindTime = TheOtherRolesPlugin.timeMasterRewindTime.GetValue();
+            cooldown = TheOtherRolesPlugin.timeMasterCooldown.GetValue();
+        }
+    }
+
+    public static class Medic {
+        public static PlayerControl medic;
+        public static PlayerControl shielded;
+        public static Color color = new Color(0f / 255f, 80f / 255f, 105f / 255f, 1);
+        public static bool usedShield;
+
+        public static float reportNameDuration = 10f;
+        public static float reportColorDuration = 20f;
+        public static int showShielded = 0;
+        public static bool showAttemptToShielded = false;
+
+        public static Color shieldedColor = new Color(0f / 255f, 221f / 255f, 255f / 255f, 1);
+        public static PlayerControl currentTarget;
+
+        private static Sprite buttonSprite;
+        public static Sprite getButtonSprite() {
+            if (buttonSprite) return buttonSprite;
+            buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.ShieldButton.png", 100f);
+            return buttonSprite;
+        }
+
+        public static void clearAndReload() {
+            medic = null;
+            shielded = null;
+            currentTarget = null;
+            usedShield = false;
+            shieldedColor = new Color(0f / 255f, 221f / 255f, 255f / 255f, 1);
+            reportNameDuration = TheOtherRolesPlugin.medicReportNameDuration.GetValue();
+            reportColorDuration = TheOtherRolesPlugin.medicReportColorDuration.GetValue();
+            showShielded = TheOtherRolesPlugin.medicShowShielded.GetValue();
+            showAttemptToShielded = TheOtherRolesPlugin.medicShowAttemptToShielded.GetValue();
+        }
+    }
+
+    public static class Shifter {
+        public static PlayerControl shifter;
+        public static Color color = new Color(90f / 255f, 90f / 255f, 90f / 255f, 1);
+    
+        public static float cooldown = float.MaxValue;
+
+        public static PlayerControl currentTarget;
+
+        private static Sprite buttonSprite;
+        public static Sprite getButtonSprite() {
+            if (buttonSprite) return buttonSprite;
+            buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.ShiftButton.png", 100f);
+            return buttonSprite;
+        }
+
+        public static void clearAndReload() {
+            shifter = null;
+            currentTarget = null;
+            cooldown = TheOtherRolesPlugin.shifterCooldown.GetValue();
+        }
+    }
+
+    public static class Swapper {
+        public static PlayerControl swapper;
+        public static Color color = new Color(240f / 255f, 128f / 255f, 72f / 255f, 1);
+        private static Sprite spriteCheck;
+
+        public static byte playerId1 = Byte.MaxValue;
+        public static byte playerId2 = Byte.MaxValue;
+
+        public static Sprite getCheckSprite() {
+            if (spriteCheck) return spriteCheck;
+            spriteCheck = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.SwapperCheck.png", 150f);
+            return spriteCheck;
+        }
+
+        public static void clearAndReload() {
+            swapper = null;
+            playerId1 = Byte.MaxValue;
+            playerId2 = Byte.MaxValue;
+        }
+    }
+
+    public static class Lovers {
+        public static PlayerControl lover1;
+        public static PlayerControl lover2;
+        public static Color color = new Color(252f / 255f, 3f / 255f, 190f / 255f, 1);
+
+        public static bool bothDie = true;
+        // Lovers save if next to be exiled is a lover, because RPC of ending game comes before RPC of exiled
+        public static bool notAckedExiledIsLover = false;
+
+        public static bool existingAndAlive() {
+            return lover1 != null && lover2 != null && !lover1.Data.IsDead && !lover2.Data.IsDead && !notAckedExiledIsLover; // ADD NOT ACKED IS LOVER
+        }
+
+        public static bool existingWithImpLover() {
+            return lover1 != null && lover2 != null && (lover1.Data.IsImpostor || lover2.Data.IsImpostor);
+        }
+
+        public static void clearAndReload() {
+            lover1 = null;
+            lover2 = null;
+            notAckedExiledIsLover = false;
+            bothDie = TheOtherRolesPlugin.loversBothDie.GetValue();
+        }
+    }
+
+    public static class Seer {
+        public static PlayerControl seer;
+        public static Color color = new Color(60f / 255f, 181f / 255f, 100f / 255f, 1);
+        public static Dictionary<PlayerControl, PlayerControl> revealedPlayers = new Dictionary<PlayerControl, PlayerControl>();
+
+        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;
+        
+        private static Sprite buttonSprite;
+        public static Sprite getButtonSprite() {
+            if (buttonSprite) return buttonSprite;
+            buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.SeerButton.png", 100f);
+            return buttonSprite;
+        }
+
+        public static void clearAndReload() {
+            seer = null;
+            revealedPlayers = new Dictionary<PlayerControl, PlayerControl>();
+            cooldown = TheOtherRolesPlugin.seerCooldown.GetValue();
+            kindOfInfo = TheOtherRolesPlugin.seerKindOfInfo.GetValue();
+            playersWithNotification = TheOtherRolesPlugin.seerPlayersWithNotification.GetValue();
+            chanceOfSeeingRight = TheOtherRolesPlugin.seerChanceOfSeeingRight.GetValue();
+        }
+    }
+
+    public static class Morphling {
+        public static PlayerControl morphling;
+        public static Color color = Palette.ImpostorRed;
+        private static Sprite sampleSprite;
+        private static Sprite morphSprite;
+    
+        public static float cooldown = float.MaxValue;
+
+        public static PlayerControl currentTarget;
+        public static PlayerControl sampledTarget;
+        public static PlayerControl morphTarget;
+        public static float morphTimer = 0f;
+
+
+        public static void clearAndReload() {
+            morphling = null;
+            currentTarget = null;
+            sampledTarget = null;
+            morphTarget = null;
+            morphTimer = 0f;
+            cooldown = TheOtherRolesPlugin.morphlingCooldown.GetValue();
+        }
+
+        public static Sprite getSampleSprite() {
+            if (sampleSprite) return sampleSprite;
+            sampleSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.SampleButton.png", 100f);
+            return sampleSprite;
+        }
+
+        public static Sprite getMorphSprite() {
+            if (morphSprite) return morphSprite;
+            morphSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.MorphButton.png", 100f);
+            return morphSprite;
+        }
+    }
+
+    public static class Camouflager {
+        public static PlayerControl camouflager;
+        public static Color color = Palette.ImpostorRed;
+    
+        public static float cooldown = float.MaxValue;
+        public static float camouflageTimer = 0f;
+
+        private static Sprite buttonSprite;
+        public static Sprite getButtonSprite() {
+            if (buttonSprite) return buttonSprite;
+            buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.CamoButton.png", 100f);
+            return buttonSprite;
+        }
+
+        public static void clearAndReload() {
+            camouflager = null;
+            camouflageTimer = 0f;
+            cooldown = TheOtherRolesPlugin.camouflagerCooldown.GetValue();
+        }
+    }
+
+    public static class Spy {
+        public static PlayerControl spy;
+        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("TheOtherRoles.Resources.AdminTableIcon.png", 350f);
+            return adminTableIcon;
+        }
+
+        private static Sprite buttonSprite;
+        public static Sprite getButtonSprite() {
+            if (buttonSprite) return buttonSprite;
+            buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.SpyButton.png", 100f);
+            return buttonSprite;
+        }
+
+        public static void clearAndReload() {
+            spy = null;
+            spyTimer = 0f;
+            cooldown = TheOtherRolesPlugin.spyCooldown.GetValue();
+            duration = TheOtherRolesPlugin.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 = TheOtherRolesPlugin.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;
+        }
+    }
+
+    public static class BountyHunter {
+        public static PlayerControl bountyHunter;
+        public static Color color = new Color(237f / 255f, 101f / 255f, 59f / 255f, 1);
+
+        public static bool notifyBounty = true;
+        public static PlayerControl target;
+    
+        public static void clearAndReload() {
+            bountyHunter = null;
+            target = null;
+            // notifyBounty = TheOtherRolesPlugin.bountyHunterNotifyBounty.GetValue();
+        }
+    }
+
+    public static class Tracker {
+        public static PlayerControl tracker;
+        public static Color color = new Color(117f / 255f, 209f / 255f, 255f / 255f, 1);
+
+        public static float updateIntervall = 5f;
+
+        public static PlayerControl currentTarget;
+        public static PlayerControl tracked;
+        public static bool usedTracker = false;
+        public static float timeUntilUpdate = 0f;
+        public static Arrow arrow = new Arrow(Color.blue);
+
+        private static Sprite buttonSprite;
+        public static Sprite getButtonSprite() {
+            if (buttonSprite) return buttonSprite;
+            buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.TrackerButton.png", 100f);
+            return buttonSprite;
+        }
+
+        public static void clearAndReload() {
+            tracker = null;
+            currentTarget = null;
+            tracked = null;
+            usedTracker = false;
+            timeUntilUpdate = 0f;
+            updateIntervall = TheOtherRolesPlugin.trackerUpdateIntervall.GetValue();
+            arrow = new Arrow(Color.blue);
+            if (arrow.arrow != null) arrow.arrow.SetActive(false);
+        }
+    }
+
+    public static class Vampire {
+        public static PlayerControl vampire;
+        public static Color color = Palette.ImpostorRed;
+
+        public static float delay = 10f;
+        public static bool localPlacedGarlic = false;
+        public static bool garlicsActive = true;
+
+        public static PlayerControl currentTarget;
+        public static PlayerControl bitten;
+        public static bool targetNearGarlic = false;
+
+        private static Sprite buttonSprite;
+        public static Sprite getButtonSprite() {
+            if (buttonSprite) return buttonSprite;
+            buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.VampireButton.png", 100f);
+            return buttonSprite;
+        }
+
+        private static Sprite garlicButtonSprite;
+        public static Sprite getGarlicButtonSprite() {
+            if (garlicButtonSprite) return garlicButtonSprite;
+            garlicButtonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.GarlicButton.png", 100f);
+            return garlicButtonSprite;
+        }
+
+        public static IEnumerator killWithDelay() {
+            yield return new WaitForSeconds(delay);
+            MessageWriter killWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.VampireTryKill, Hazel.SendOption.None, -1);
+            AmongUsClient.Instance.FinishRpcImmediately(killWriter);
+            RPCProcedure.vampireTryKill();
+        }
+
+        public static void clearAndReload() {
+            vampire = null;
+            bitten = null;
+            targetNearGarlic = false;
+            localPlacedGarlic = false;
+            currentTarget = null;
+            garlicsActive = TheOtherRolesPlugin.vampireSpawnRate.GetValue() > 0;
+            delay = TheOtherRolesPlugin.vampireKillDelay.GetValue();
+        }
+    }
+
+    public static class Snitch {
+        public static PlayerControl snitch;
+        public static Color color = new Color(227f / 255f, 251f / 255f, 47f / 255f, 1);
+
+        public static List<Arrow> localArrows = new List<Arrow>();
+        public static int taskCountForImpostors = 1;
+
+        public static void clearAndReload() {
+            localArrows = new List<Arrow>();
+            taskCountForImpostors = Mathf.RoundToInt(TheOtherRolesPlugin.snitchLeftTasksForImpostors.GetValue());
+            snitch = null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Source Code/TheOtherRoles.csproj b/Source Code/TheOtherRoles.csproj
new file mode 100644 (file)
index 0000000..c0e534e
--- /dev/null
@@ -0,0 +1,58 @@
+<Project Sdk="Microsoft.NET.Sdk">
+    <PropertyGroup>
+        <TargetFramework>netstandard2.1</TargetFramework>
+        <Version>1.0.0</Version>
+        <Mappings>NuclearPowered/Mappings:0.2.0</Mappings>
+
+        <Description>TheOtherRoles</Description>
+        <Authors>Eibison</Authors>
+    </PropertyGroup>
+
+    <PropertyGroup Condition="'$(GamePlatform)' == 'Steam'">
+        <GameVersion>2021.3.5s</GameVersion>
+        <DefineConstants>$(DefineConstants);STEAM</DefineConstants>
+    </PropertyGroup>
+
+    <PropertyGroup Condition="'$(GamePlatform)' == 'Itch'">
+        <GameVersion>2021.3.5i</GameVersion>
+        <DefineConstants>$(DefineConstants);ITCH</DefineConstants>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <Deobfuscate Include="$(AmongUs)\BepInEx\plugins\Reactor-$(GameVersion).dll" />
+
+        <PackageReference Include="Reactor.OxygenFilter.MSBuild" Version="0.2.9" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <Reference Include="Essentials">
+        <HintPath>$(AmongUs)\BepInEx\plugins\Essentials-$(GameVersion).dll</HintPath>
+      </Reference>
+    </ItemGroup>
+
+    <ItemGroup>
+        <EmbeddedResource Include="Resources\RepairButton.png"/>
+        <EmbeddedResource Include="Resources\CleanButton.png"/>
+        <EmbeddedResource Include="Resources\RewindButton.png"/>
+        <EmbeddedResource Include="Resources\ShieldButton.png"/>
+        <EmbeddedResource Include="Resources\ShiftButton.png"/>
+        <EmbeddedResource Include="Resources\SeerButton.png"/>
+        <EmbeddedResource Include="Resources\MorphButton.png"/>
+        <EmbeddedResource Include="Resources\SampleButton.png"/>
+        <EmbeddedResource Include="Resources\CamoButton.png"/>
+        <EmbeddedResource Include="Resources\SpyButton.png"/>
+        <EmbeddedResource Include="Resources\TrackerButton.png"/>
+        <EmbeddedResource Include="Resources\VampireButton.png"/>
+        <EmbeddedResource Include="Resources\GarlicButton.png"/>
+        <EmbeddedResource Include="Resources\Arrow.png"/>
+        <EmbeddedResource Include="Resources\Garlic.png"/>
+        <EmbeddedResource Include="Resources\GarlicBackground.png"/>
+        <EmbeddedResource Include="Resources\Footprint.png"/>
+        <EmbeddedResource Include="Resources\AdminTableIcon.png"/>
+        <EmbeddedResource Include="Resources\SwapperCheck.png"/>
+    </ItemGroup>
+
+    <Target Name="Copy" AfterTargets="Reobfuscate">
+        <Copy SourceFiles="$(OutputPath)reobfuscated/$(AssemblyName)-$(GameVersion).dll" DestinationFolder="$(AmongUs)/BepInEx/plugins/" Condition="'$(Configuration)' == 'Debug'" />
+    </Target>
+</Project>
\ No newline at end of file
index d5dc2e64dac08e8b36bd9886edaca221a2680f18..6d20ef667d0feb2ed922494f6ca5fd339b50a4a8 100644 (file)
@@ -3,12 +3,12 @@ using System;
 using System.IO;
 using System.Net.Http;
 using UnityEngine;
-using static BonusRoles.BonusRoles;
+using static TheOtherRoles.TheOtherRoles;
 using Reactor.Extensions;
 using System.Collections.Generic;
 using System.Linq;
 
-namespace BonusRoles
+namespace TheOtherRoles
 {
     [HarmonyPatch(typeof(HudManager), nameof(HudManager.Update))]
     class HudManagerUpdatePatch
@@ -89,9 +89,17 @@ namespace BonusRoles
                 setPlayerNameColor(Seer.seer, Seer.color);  
             else if (Spy.spy != null && Spy.spy == PlayerControl.LocalPlayer) 
                 setPlayerNameColor(Spy.spy, Spy.color);
+            else if (BountyHunter.bountyHunter != null && BountyHunter.target != null && BountyHunter.bountyHunter == PlayerControl.LocalPlayer) {
+                setPlayerNameColor(BountyHunter.bountyHunter, BountyHunter.color);
+                setPlayerNameColor(BountyHunter.target, BountyHunter.color);
+            }
+            else if (Tracker.tracker != null && Tracker.tracker == PlayerControl.LocalPlayer) 
+                setPlayerNameColor(Tracker.tracker, Tracker.color);
+            else if (Snitch.snitch != null && Snitch.snitch == PlayerControl.LocalPlayer) 
+                setPlayerNameColor(Snitch.snitch, Snitch.color);
 
             // Crewmate roles with no changes: Child
-            // Impostor roles with no changes: Morphling, Camouflager, Godfather, Janitor and Mafioso
+            // Impostor roles with no changes: Morphling, Camouflager, Vampire, Godfather, Janitor and Mafioso
         }
 
         static void setMafiaNameTags() {
@@ -333,6 +341,61 @@ namespace BonusRoles
             }
         }
 
+        public static void bountyHunterUpdate() {
+            if (BountyHunter.bountyHunter == null || BountyHunter.target == null) return;
+
+            if (BountyHunter.notifyBounty && PlayerControl.LocalPlayer == BountyHunter.target) {
+                string suffix = "[AD653BFF] (Bounty)[FFFFFFFF]";
+                PlayerControl.LocalPlayer.nameText.Text += suffix;
+                if (MeetingHud.Instance != null)
+                    foreach (PlayerVoteArea player in MeetingHud.Instance.playerStates)
+                        if (player.NameText != null && PlayerControl.LocalPlayer.PlayerId == player.TargetPlayerId)
+                            player.NameText.Text += suffix;
+            }
+        }
+
+        static void vampireDeactivateKillButton(HudManager __instance) {
+            if (Vampire.vampire != null && Vampire.vampire == PlayerControl.LocalPlayer) {
+                __instance.KillButton.gameObject.SetActive(false);
+                __instance.KillButton.renderer.enabled = false;
+                __instance.KillButton.isActive = false;
+                __instance.KillButton.enabled = false;
+            }
+        }
+
+        static void snitchUpdate() {
+            if (Snitch.snitch == null) return;
+
+            foreach (Arrow arrow in Snitch.localArrows) arrow.arrow.SetActive(false);
+            if (Snitch.snitch.Data.IsDead) return;
+
+            int numberOfTasks = 0;
+            foreach (PlayerTask t in Snitch.snitch.myTasks) {
+                if (t.TaskType != TaskTypes.FixComms && t.TaskType != TaskTypes.FixLights && t.TaskType != TaskTypes.ResetReactor && t.TaskType != TaskTypes.ResetSeismic && t.TaskType != TaskTypes.RestoreOxy)
+                    if (!t.IsComplete) numberOfTasks++;
+            }
+
+            if (PlayerControl.LocalPlayer.Data.IsImpostor && numberOfTasks <= Snitch.taskCountForImpostors) {
+                if (Snitch.localArrows.Count == 0) Snitch.localArrows.Add(new Arrow(Color.blue));
+                if (Snitch.localArrows.Count != 0 && Snitch.localArrows[0] != null) {
+                    Snitch.localArrows[0].arrow.SetActive(true);
+                    Snitch.localArrows[0].Update(Snitch.snitch.transform.position);
+                }
+            } else if (PlayerControl.LocalPlayer == Snitch.snitch && numberOfTasks == 0) { 
+                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) {
+                            Snitch.localArrows[arrowIndex].arrow.SetActive(true);
+                            Snitch.localArrows[arrowIndex].Update(p.transform.position);
+                        }
+                        arrowIndex++;
+                    }
+                }
+            }
+        }
+
         static void Postfix(HudManager __instance)
         {
             if (AmongUsClient.Instance.GameState != InnerNet.InnerNetClient.GameStates.Started) return;
@@ -356,9 +419,16 @@ namespace BonusRoles
             seerUpdate();
             // Spy update();
             spyUpdate();
-
+            // Camouflager and Morphling
             camouflageAndMorphActions();
+            // Child
             childUpdate();
+            // Bounty Hunter
+            bountyHunterUpdate();
+            // Vampire
+            vampireDeactivateKillButton(__instance);
+            // Snitch
+            snitchUpdate();
         }
     }
 }
\ No newline at end of file
index 5146a4975797f2da140278b6c5833873180d3b7c..fb0415f4ec0fe815001f3855b5e553517b3cb947 100644 (file)
@@ -4,12 +4,12 @@ using System.IO;
 using System.Net.Http;
 using UnityEngine;
 using System.Linq;
-using static BonusRoles.BonusRoles;
-using static BonusRoles.GameHistory;
+using static TheOtherRoles.TheOtherRoles;
+using static TheOtherRoles.GameHistory;
 using Reactor.Extensions;
 using System.Collections.Generic;
 
-namespace BonusRoles
+namespace TheOtherRoles
 {
 
     [HarmonyPatch(typeof(Vent), "CanUse")]