From: Eisbison Date: Sun, 14 Nov 2021 20:10:31 +0000 (+0100) Subject: v3.0.0 X-Git-Url: https://git.deb.at/?a=commitdiff_plain;h=5294204e467f5e9db647d4852327a134febf81e2;p=rhonda%2Ftheotherroles.git v3.0.0 --- diff --git a/README.md b/README.md index 95a5881..e1876e1 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein # Releases | Among Us - Version| Mod Version | Link | |----------|-------------|-----------------| +| 2021.11.9.5s| v3.0.0| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v3.0.0/TheOtherRoles.zip) | 2021.6.30s| v2.9.2| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.9.2/TheOtherRoles.zip) | 2021.6.30s| v2.9.1| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.9.1/TheOtherRoles.zip) | 2021.6.30s| v2.9.0| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.9.0/TheOtherRoles.zip) @@ -79,6 +80,13 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein
Click to show the Changelog +**Version 3.0.0** +- Update to Among Us version v2021.11.9.5s +- **Note:** We wanted to update as fast as possible, that's why you can't use both the Innersloth and mod roles at the same time. We'll make that possible in the future, but there are various things that need to be modified (e.g. Shifter, Guesser, ...) to make that work, so that'll take a little longer. Also, be aware that this version might contain more bugs than usual because Innersloth changed a lot of things and we might have missed some of them. +- Ability buttons are now bind to the Q key (if it's a killing ability) or to the F key (otherwise). We'll make the binds adaptable in the future. +- For now we removed the option "Jester Can Sabotage" +- The Sheriff now always dies, when he tries to kill a not fully grown Mini + **Hotfix 2.9.2** - Fixed a bug where the names of all players were visible during camouflage - Fixed a bug where the Morphling didn't take over the name of its target @@ -520,8 +528,8 @@ Note: Changing the settings to Hacker: 20%, Tracker: 60% would statistically res ### **Team: Impostors** The Mafia are a group of three Impostors.\ The Godfather works like a normal Impostor.\ -The Mafioso is an Impostor who cannot kill nor sabotage until the Godfather is dead.\ -The Janitor is an Impostor who cannot kill nor sabotage, but they can hide dead bodies instead.\ +The Mafioso is an Impostor who cannot kill until the Godfather is dead.\ +The Janitor is an Impostor who cannot kill, but they can hide dead bodies instead.\ \ **NOTE:** - There have to be 3 Impostors activated for the mafia to spawn. @@ -780,8 +788,7 @@ The Jester does not have any tasks. They win the game as a solo, if they get vot | Name | Description | |----------|:-------------:| | Jester Spawn Chance | - -| Jester can call emergency meeting | Option to disable the emergency button for the Jester -| Jester can sabotage | Option to allow the Jester to sabotage +| Jester Can Call Emergency Meeting | Option to disable the emergency button for the Jester ----------------------- ## Arsonist @@ -816,11 +823,10 @@ The Seer gets a blue flash on his screen, if a player dies somewhere on the map. ## Engineer ### **Team: Crewmates** -The Engineer (if alive) can fix one sabotage per game from anywhere on the map.\ -The Engineer can use vents. If the Engineer is inside a vent, the Impostors will see a blue outline -around all vents on the map (in order to warn them). -Because of the vents the Engineer might not be able to start some tasks using the "Use" button, -you can double-click on the tasks instead. +The Engineer (if alive) can fix a certain amount of sabotages per game from anywhere on the map.\ +The Engineer can use vents.\ +If the Engineer is inside a vent, depending on the options the members of the team Jackal/Impostors will see a blue outline around all vents on the map (in order to warn them). +Because of the vents the Engineer might not be able to start some tasks using the "Use" button, you can double-click on the tasks instead.\ \ **NOTE:** - The kill button of Impostors activates if they stand next to a vent where the Engineer is. They can also kill them there. No other action (e.g. Morphling sample, Shifter shift, ...) can affect players inside vents. @@ -829,6 +835,9 @@ you can double-click on the tasks instead. | Name | Description | |----------|:-------------:| | Engineer Spawn Chance | - +| Number Of Sabotage Fixes| - +| Impostors See Vents Highlighted | - +| Jackal and Sidekick See Vents Highlighted | - ----------------------- ## Detective @@ -880,7 +889,7 @@ The Mini cannot be killed until it turns 18 years old, however it can be voted o - If it gets thrown out of the ship before it turns 18, everyone loses. So think twice before you vote out a Mini. **NOTE:** -- Impostors can't kill the Mini (the button does not work) until it turns 18 +- If the Sheriff tries to kill the Mini before it's fully grown, he dies, no matter if the Mini is a Crewmate or Impostor - The Sheriff can kill the Impostor Mini, but only if it's fully grown up ### Game Options @@ -1082,7 +1091,6 @@ If both Impostors and Jackals are in the game the game, continues even if all Cr | Jackals promoted from Sidekick can create a Sidekick | Yes/No (to prevent the Jackal team from growing) | | Jackals can make an Impostor to his Sidekick | Yes/No (to prevent a Jackal from turning an Impostor into a Sidekick, if he uses the ability on an Impostor he sees the Impostor as Sidekick, but the Impostor isn't converted to Sidekick. If this option is set to "No" Jackal and Sidekick can kill each other.) | | Jackal and Sidekick have Impostor vision | - | -| Jackal and Sidekick Can See Engineer Vent | - | If set to true, team Jackal will see vents highlited like Impostors if an Engineer is inside one ----------------------- ## Sidekick diff --git a/TheOtherRoles/Buttons.cs b/TheOtherRoles/Buttons.cs index f51b10f..a3e5f91 100644 --- a/TheOtherRoles/Buttons.cs +++ b/TheOtherRoles/Buttons.cs @@ -81,7 +81,7 @@ namespace TheOtherRoles public static void resetTimeMasterButton() { timeMasterShieldButton.Timer = timeMasterShieldButton.MaxTimer; timeMasterShieldButton.isEffectActive = false; - timeMasterShieldButton.killButtonManager.TimerText.color = Palette.EnabledColor; + timeMasterShieldButton.actionButton.cooldownTimerText.color = Palette.EnabledColor; } public static void Postfix(HudManager __instance) @@ -116,19 +116,19 @@ namespace TheOtherRoles } } }, - () => { return Engineer.engineer != null && Engineer.engineer == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, + () => { return Engineer.engineer != null && Engineer.engineer == PlayerControl.LocalPlayer && Engineer.remainingFixes > 0 && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { bool sabotageActive = false; foreach (PlayerTask task in PlayerControl.LocalPlayer.myTasks) if (task.TaskType == TaskTypes.FixLights || task.TaskType == TaskTypes.RestoreOxy || task.TaskType == TaskTypes.ResetReactor || task.TaskType == TaskTypes.ResetSeismic || task.TaskType == TaskTypes.FixComms || task.TaskType == TaskTypes.StopCharles) sabotageActive = true; - return sabotageActive && !Engineer.usedRepair && PlayerControl.LocalPlayer.CanMove; + return sabotageActive && Engineer.remainingFixes > 0 && PlayerControl.LocalPlayer.CanMove; }, () => {}, Engineer.getButtonSprite(), - new Vector3(-1.3f, 0, 0), + new Vector3(-1.8f, -0.06f, 0), __instance, - KeyCode.Q + KeyCode.F ); // Janitor Clean @@ -159,12 +159,12 @@ namespace TheOtherRoles } }, () => { return Janitor.janitor != null && Janitor.janitor == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, - () => { return __instance.ReportButton.renderer.color == Palette.EnabledColor && PlayerControl.LocalPlayer.CanMove; }, + () => { return __instance.ReportButton.graphic.color == Palette.EnabledColor && PlayerControl.LocalPlayer.CanMove; }, () => { janitorCleanButton.Timer = janitorCleanButton.MaxTimer; }, Janitor.getButtonSprite(), - new Vector3(-1.3f, 0, 0), + new Vector3(-1.8f, -0.06f, 0), __instance, - KeyCode.Q + KeyCode.F ); // Sheriff Kill @@ -178,7 +178,7 @@ namespace TheOtherRoles } byte targetId = 0; - if ((Sheriff.currentTarget.Data.IsImpostor && (Sheriff.currentTarget != Mini.mini || Mini.isGrownUp())) || + if ((Sheriff.currentTarget.Data.Role.IsImpostor && (Sheriff.currentTarget != Mini.mini || Mini.isGrownUp())) || (Sheriff.spyCanDieToSheriff && Spy.spy == Sheriff.currentTarget) || (Sheriff.canKillNeutrals && (Arsonist.arsonist == Sheriff.currentTarget || Jester.jester == Sheriff.currentTarget || Vulture.vulture == Sheriff.currentTarget)) || (Jackal.jackal == Sheriff.currentTarget || Sidekick.sidekick == Sheriff.currentTarget)) { @@ -198,8 +198,8 @@ namespace TheOtherRoles () => { return Sheriff.sheriff != null && Sheriff.sheriff == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { return Sheriff.currentTarget && PlayerControl.LocalPlayer.CanMove; }, () => { sheriffKillButton.Timer = sheriffKillButton.MaxTimer;}, - __instance.KillButton.renderer.sprite, - new Vector3(-1.3f, 0, 0), + __instance.KillButton.graphic.sprite, + new Vector3(0f, 1f, 0), __instance, KeyCode.Q ); @@ -216,12 +216,12 @@ namespace TheOtherRoles () => { timeMasterShieldButton.Timer = timeMasterShieldButton.MaxTimer; timeMasterShieldButton.isEffectActive = false; - timeMasterShieldButton.killButtonManager.TimerText.color = Palette.EnabledColor; + timeMasterShieldButton.actionButton.cooldownTimerText.color = Palette.EnabledColor; }, TimeMaster.getButtonSprite(), - new Vector3(-1.3f, 0, 0), + new Vector3(-1.8f, -0.06f, 0), __instance, - KeyCode.Q, + KeyCode.F, true, TimeMaster.shieldDuration, () => { timeMasterShieldButton.Timer = timeMasterShieldButton.MaxTimer; } @@ -244,9 +244,9 @@ namespace TheOtherRoles () => { return !Medic.usedShield && Medic.currentTarget && PlayerControl.LocalPlayer.CanMove; }, () => {}, Medic.getButtonSprite(), - new Vector3(-1.3f, 0, 0), + new Vector3(-1.8f, -0.06f, 0), __instance, - KeyCode.Q + KeyCode.F ); @@ -262,9 +262,9 @@ namespace TheOtherRoles () => { return Shifter.currentTarget && Shifter.futureShift == null && PlayerControl.LocalPlayer.CanMove; }, () => { }, Shifter.getButtonSprite(), - new Vector3(-1.3f, 0, 0), + new Vector3(-1.8f, -0.06f, 0), __instance, - KeyCode.Q + KeyCode.F ); // Morphling morph @@ -289,11 +289,11 @@ namespace TheOtherRoles morphlingButton.Timer = morphlingButton.MaxTimer; morphlingButton.Sprite = Morphling.getSampleSprite(); morphlingButton.isEffectActive = false; - morphlingButton.killButtonManager.TimerText.color = Palette.EnabledColor; + morphlingButton.actionButton.cooldownTimerText.color = Palette.EnabledColor; Morphling.sampledTarget = null; }, Morphling.getSampleSprite(), - new Vector3(-1.3f, 1.3f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, KeyCode.F, true, @@ -318,10 +318,10 @@ namespace TheOtherRoles () => { camouflagerButton.Timer = camouflagerButton.MaxTimer; camouflagerButton.isEffectActive = false; - camouflagerButton.killButtonManager.TimerText.color = Palette.EnabledColor; + camouflagerButton.actionButton.cooldownTimerText.color = Palette.EnabledColor; }, Camouflager.getButtonSprite(), - new Vector3(-1.3f, 1.3f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, KeyCode.F, true, @@ -339,12 +339,12 @@ namespace TheOtherRoles () => { hackerButton.Timer = hackerButton.MaxTimer; hackerButton.isEffectActive = false; - hackerButton.killButtonManager.TimerText.color = Palette.EnabledColor; + hackerButton.actionButton.cooldownTimerText.color = Palette.EnabledColor; }, Hacker.getButtonSprite(), - new Vector3(-1.3f, 0, 0), + new Vector3(-1.8f, -0.06f, 0), __instance, - KeyCode.Q, + KeyCode.F, true, 0f, () => { @@ -364,14 +364,14 @@ namespace TheOtherRoles () => { return PlayerControl.LocalPlayer.CanMove && Tracker.currentTarget != null && !Tracker.usedTracker; }, () => { if(Tracker.resetTargetAfterMeeting) Tracker.resetTracked(); }, Tracker.getButtonSprite(), - new Vector3(-1.3f, 0, 0), + new Vector3(-1.8f, -0.06f, 0), __instance, - KeyCode.Q + KeyCode.F ); vampireKillButton = new CustomButton( () => { - if (Helpers.handleMurderAttempt(Vampire.currentTarget)) { + if (Helpers.handleMurderAttempt(Vampire.vampire, Vampire.currentTarget)) { if (Vampire.targetNearGarlic) { MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.UncheckedMurderPlayer, Hazel.SendOption.Reliable, -1); writer.Write(Vampire.vampire.PlayerId); @@ -392,7 +392,7 @@ namespace TheOtherRoles HudManager.Instance.StartCoroutine(Effects.Lerp(Vampire.delay, new Action((p) => { // Delayed action if (p == 1f) { - if (Vampire.bitten != null && !Vampire.bitten.Data.IsDead && Helpers.handleMurderAttempt(Vampire.bitten)) { + if (Vampire.bitten != null && !Vampire.bitten.Data.IsDead && Helpers.handleMurderAttempt(Vampire.vampire, Vampire.bitten)) { // Perform kill MessageWriter killWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.VampireTryKill, Hazel.SendOption.Reliable, -1); AmongUsClient.Instance.FinishRpcImmediately(killWriter); @@ -416,19 +416,23 @@ namespace TheOtherRoles }, () => { return Vampire.vampire != null && Vampire.vampire == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { - if (Vampire.targetNearGarlic && Vampire.canKillNearGarlics) - vampireKillButton.killButtonManager.renderer.sprite = __instance.KillButton.renderer.sprite; - else - vampireKillButton.killButtonManager.renderer.sprite = Vampire.getButtonSprite(); + if (Vampire.targetNearGarlic && Vampire.canKillNearGarlics) { + vampireKillButton.actionButton.graphic.sprite = __instance.KillButton.graphic.sprite; + vampireKillButton.showButtonText = true; + } + else { + vampireKillButton.actionButton.graphic.sprite = Vampire.getButtonSprite(); + vampireKillButton.showButtonText = false; + } return Vampire.currentTarget != null && PlayerControl.LocalPlayer.CanMove && (!Vampire.targetNearGarlic || Vampire.canKillNearGarlics); }, () => { vampireKillButton.Timer = vampireKillButton.MaxTimer; vampireKillButton.isEffectActive = false; - vampireKillButton.killButtonManager.TimerText.color = Palette.EnabledColor; + vampireKillButton.actionButton.cooldownTimerText.color = Palette.EnabledColor; }, Vampire.getButtonSprite(), - new Vector3(-1.3f, 0, 0), + new Vector3(0, 1f, 0), __instance, KeyCode.Q, false, @@ -455,7 +459,7 @@ namespace TheOtherRoles () => { return PlayerControl.LocalPlayer.CanMove && !Vampire.localPlacedGarlic; }, () => { }, Vampire.getGarlicButtonSprite(), - Vector3.zero, + new Vector3(0, -0.06f, 0), __instance, null, true @@ -474,7 +478,7 @@ namespace TheOtherRoles () => { return Jackal.canCreateSidekick && Jackal.currentTarget != null && PlayerControl.LocalPlayer.CanMove; }, () => { jackalSidekickButton.Timer = jackalSidekickButton.MaxTimer;}, Jackal.getSidekickButtonSprite(), - new Vector3(-1.3f, 1.3f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, KeyCode.F ); @@ -482,7 +486,7 @@ namespace TheOtherRoles // Jackal Kill jackalKillButton = new CustomButton( () => { - if (!Helpers.handleMurderAttempt(Jackal.currentTarget)) return; + if (!Helpers.handleMurderAttempt(Jackal.jackal, Jackal.currentTarget)) return; byte targetId = Jackal.currentTarget.PlayerId; MessageWriter killWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.JackalKill, Hazel.SendOption.Reliable, -1); killWriter.Write(targetId); @@ -494,8 +498,8 @@ namespace TheOtherRoles () => { return Jackal.jackal != null && Jackal.jackal == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { return Jackal.currentTarget && PlayerControl.LocalPlayer.CanMove; }, () => { jackalKillButton.Timer = jackalKillButton.MaxTimer;}, - __instance.KillButton.renderer.sprite, - new Vector3(-1.3f, 0, 0), + __instance.KillButton.graphic.sprite, + new Vector3(0, 1f, 0), __instance, KeyCode.Q ); @@ -503,7 +507,7 @@ namespace TheOtherRoles // Sidekick Kill sidekickKillButton = new CustomButton( () => { - if (!Helpers.handleMurderAttempt(Sidekick.currentTarget)) return; + if (!Helpers.handleMurderAttempt(Sidekick.sidekick, Sidekick.currentTarget)) return; byte targetId = Sidekick.currentTarget.PlayerId; MessageWriter killWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SidekickKill, Hazel.SendOption.Reliable, -1); killWriter.Write(targetId); @@ -516,8 +520,8 @@ namespace TheOtherRoles () => { return Sidekick.canKill && Sidekick.sidekick != null && Sidekick.sidekick == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { return Sidekick.currentTarget && PlayerControl.LocalPlayer.CanMove; }, () => { sidekickKillButton.Timer = sidekickKillButton.MaxTimer;}, - __instance.KillButton.renderer.sprite, - new Vector3(-1.3f, 0, 0), + __instance.KillButton.graphic.sprite, + new Vector3(0, 1f, 0), __instance, KeyCode.Q ); @@ -532,12 +536,12 @@ namespace TheOtherRoles () => { lighterButton.Timer = lighterButton.MaxTimer; lighterButton.isEffectActive = false; - lighterButton.killButtonManager.TimerText.color = Palette.EnabledColor; + lighterButton.actionButton.graphic.color = Palette.EnabledColor; }, Lighter.getButtonSprite(), - new Vector3(-1.3f, 0f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, - KeyCode.Q, + KeyCode.F, true, Lighter.duration, () => { lighterButton.Timer = lighterButton.MaxTimer; } @@ -558,7 +562,7 @@ namespace TheOtherRoles () => { return PlayerControl.LocalPlayer.CanMove && Eraser.currentTarget != null; }, () => { eraserButton.Timer = eraserButton.MaxTimer;}, Eraser.getButtonSprite(), - new Vector3(-1.3f, 1.3f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, KeyCode.F ); @@ -581,7 +585,7 @@ namespace TheOtherRoles () => { return PlayerControl.LocalPlayer.CanMove && !JackInTheBox.hasJackInTheBoxLimitReached(); }, () => { placeJackInTheBoxButton.Timer = placeJackInTheBoxButton.MaxTimer;}, Trickster.getPlaceBoxButtonSprite(), - new Vector3(-1.3f, 1.3f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, KeyCode.F ); @@ -597,16 +601,17 @@ namespace TheOtherRoles () => { lightsOutButton.Timer = lightsOutButton.MaxTimer; lightsOutButton.isEffectActive = false; - lightsOutButton.killButtonManager.TimerText.color = Palette.EnabledColor; + lightsOutButton.actionButton.graphic.color = Palette.EnabledColor; }, Trickster.getLightsOutButtonSprite(), - new Vector3(-1.3f, 1.3f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, KeyCode.F, true, Trickster.lightsOutDuration, () => { lightsOutButton.Timer = lightsOutButton.MaxTimer; } ); + // Cleaner Clean cleanerCleanButton = new CustomButton( () => { @@ -635,10 +640,10 @@ namespace TheOtherRoles } }, () => { return Cleaner.cleaner != null && Cleaner.cleaner == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, - () => { return __instance.ReportButton.renderer.color == Palette.EnabledColor && PlayerControl.LocalPlayer.CanMove; }, + () => { return __instance.ReportButton.graphic.color == Palette.EnabledColor && PlayerControl.LocalPlayer.CanMove; }, () => { cleanerCleanButton.Timer = cleanerCleanButton.MaxTimer; }, Cleaner.getButtonSprite(), - new Vector3(-1.3f, 1.3f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, KeyCode.F ); @@ -651,7 +656,7 @@ namespace TheOtherRoles Warlock.curseVictim = Warlock.currentTarget; warlockCurseButton.Sprite = Warlock.getCurseKillButtonSprite(); warlockCurseButton.Timer = 1f; - } else if (Warlock.curseVictim != null && Warlock.curseVictimTarget != null && Helpers.handleMurderAttempt(Warlock.curseVictimTarget)) { + } else if (Warlock.curseVictim != null && Warlock.curseVictimTarget != null && Helpers.handleMurderAttempt(Warlock.warlock, Warlock.curseVictimTarget)) { // Curse Kill Warlock.curseKillTarget = Warlock.curseVictimTarget; @@ -685,7 +690,7 @@ namespace TheOtherRoles Warlock.curseVictimTarget = null; }, Warlock.getCurseButtonSprite(), - new Vector3(-1.3f, 1.3f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, KeyCode.F ); @@ -714,7 +719,7 @@ namespace TheOtherRoles }, () => { return SecurityGuard.securityGuard != null && SecurityGuard.securityGuard == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead && SecurityGuard.remainingScrews >= Mathf.Min(SecurityGuard.ventPrice, SecurityGuard.camPrice); }, () => { - securityGuardButton.killButtonManager.renderer.sprite = (SecurityGuard.ventTarget == null && PlayerControl.GameOptions.MapId != 1) ? SecurityGuard.getPlaceCameraButtonSprite() : SecurityGuard.getCloseVentButtonSprite(); + securityGuardButton.actionButton.graphic.sprite = (SecurityGuard.ventTarget == null && PlayerControl.GameOptions.MapId != 1) ? SecurityGuard.getPlaceCameraButtonSprite() : SecurityGuard.getCloseVentButtonSprite(); if (securityGuardButtonScrewsText != null) securityGuardButtonScrewsText.text = $"{SecurityGuard.remainingScrews}/{SecurityGuard.totalScrews}"; if (SecurityGuard.ventTarget != null) @@ -723,13 +728,13 @@ namespace TheOtherRoles }, () => { securityGuardButton.Timer = securityGuardButton.MaxTimer; }, SecurityGuard.getPlaceCameraButtonSprite(), - new Vector3(-1.3f, 0f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, - KeyCode.Q + KeyCode.F ); // Security Guard button screws counter - securityGuardButtonScrewsText = GameObject.Instantiate(securityGuardButton.killButtonManager.TimerText, securityGuardButton.killButtonManager.TimerText.transform.parent); + securityGuardButtonScrewsText = GameObject.Instantiate(securityGuardButton.actionButton.cooldownTimerText, securityGuardButton.actionButton.cooldownTimerText.transform.parent); securityGuardButtonScrewsText.text = ""; securityGuardButtonScrewsText.enableWordWrapping = false; securityGuardButtonScrewsText.transform.localScale = Vector3.one * 0.5f; @@ -752,7 +757,7 @@ namespace TheOtherRoles () => { return Arsonist.arsonist != null && Arsonist.arsonist == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, () => { bool dousedEveryoneAlive = Arsonist.dousedEveryoneAlive(); - if (dousedEveryoneAlive) arsonistButton.killButtonManager.renderer.sprite = Arsonist.getIgniteSprite(); + if (dousedEveryoneAlive) arsonistButton.actionButton.graphic.sprite = Arsonist.getIgniteSprite(); if (arsonistButton.isEffectActive && Arsonist.douseTarget != Arsonist.currentTarget) { Arsonist.douseTarget = null; @@ -768,9 +773,9 @@ namespace TheOtherRoles Arsonist.douseTarget = null; }, Arsonist.getDouseSprite(), - new Vector3(-1.3f, 0f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, - KeyCode.Q, + KeyCode.F, true, Arsonist.duration, () => { @@ -818,10 +823,10 @@ namespace TheOtherRoles } }, () => { return Vulture.vulture != null && Vulture.vulture == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; }, - () => { return __instance.ReportButton.renderer.color == Palette.EnabledColor && PlayerControl.LocalPlayer.CanMove; }, + () => { return __instance.ReportButton.graphic.color == Palette.EnabledColor && PlayerControl.LocalPlayer.CanMove; }, () => { vultureEatButton.Timer = vultureEatButton.MaxTimer; }, Vulture.getButtonSprite(), - new Vector3(-1.3f, 0f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, KeyCode.F ); @@ -849,9 +854,9 @@ namespace TheOtherRoles Medium.soulTarget = null; }, Medium.getQuestionSprite(), - new Vector3(-1.3f, 0f, 0f), + new Vector3(-1.8f, -0.06f, 0), __instance, - KeyCode.Q, + KeyCode.F, true, Medium.duration, () => { @@ -860,7 +865,7 @@ namespace TheOtherRoles string msg = ""; int randomNumber = Medium.target.killerIfExisting?.PlayerId == Mini.mini?.PlayerId ? TheOtherRoles.rnd.Next(3) : TheOtherRoles.rnd.Next(4); - string typeOfColor = Helpers.isLighterColor(Medium.target.killerIfExisting.Data.ColorId) ? "lighter" : "darker"; + string typeOfColor = Helpers.isLighterColor(Medium.target.killerIfExisting.Data.DefaultOutfit.ColorId) ? "lighter" : "darker"; float timeSinceDeath = ((float)(Medium.meetingStartTime - Medium.target.timeOfDeath).TotalMilliseconds); string name = " (" + Medium.target.player.Data.PlayerName + ")"; diff --git a/TheOtherRoles/CustomOptions.cs b/TheOtherRoles/CustomOptions.cs index 13e0ecc..30d9fee 100644 --- a/TheOtherRoles/CustomOptions.cs +++ b/TheOtherRoles/CustomOptions.cs @@ -15,6 +15,7 @@ namespace TheOtherRoles { public static string[] presets = new string[]{"Preset 1", "Preset 2", "Preset 3", "Preset 4", "Preset 5"}; public static CustomOption presetSelection; + public static CustomOption activateRoles; public static CustomOption crewmateRolesCountMin; public static CustomOption crewmateRolesCountMax; public static CustomOption neutralRolesCountMin; @@ -57,7 +58,6 @@ namespace TheOtherRoles { public static CustomOption jesterSpawnRate; public static CustomOption jesterCanCallEmergency; - public static CustomOption jesterCanSabotage; public static CustomOption arsonistSpawnRate; public static CustomOption arsonistCooldown; @@ -74,7 +74,6 @@ namespace TheOtherRoles { public static CustomOption jackalPromotedFromSidekickCanCreateSidekick; public static CustomOption jackalCanCreateSidekickFromImpostor; public static CustomOption jackalAndSidekickHaveImpostorVision; - public static CustomOption jackalCanSeeEngineerVent; public static CustomOption bountyHunterSpawnRate; public static CustomOption bountyHunterBountyDuration; @@ -89,6 +88,9 @@ namespace TheOtherRoles { public static CustomOption mayorSpawnRate; public static CustomOption engineerSpawnRate; + public static CustomOption engineerNumberOfFixes; + public static CustomOption engineerHighlightForImpostors; + public static CustomOption engineerHighlightForTeamJackal; public static CustomOption sheriffSpawnRate; public static CustomOption sheriffCooldown; @@ -199,6 +201,7 @@ namespace TheOtherRoles { // Role Options presetSelection = CustomOption.Create(0, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Preset"), presets, null, true); + activateRoles = CustomOption.Create(7, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Enable Mod Roles And Block Vanilla Roles"), true, null, true); // Using new id's for the options to not break compatibilty with older versions crewmateRolesCountMin = CustomOption.Create(300, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Minimum Crewmate Roles"), 0f, 0f, 15f, 1f, null, true); @@ -263,7 +266,6 @@ namespace TheOtherRoles { jesterSpawnRate = CustomOption.Create(60, cs(Jester.color, "Jester"), rates, null, true); jesterCanCallEmergency = CustomOption.Create(61, "Jester can call emergency meeting", true, jesterSpawnRate); - jesterCanSabotage = CustomOption.Create(62, "Jester can sabotage", true, jesterSpawnRate); arsonistSpawnRate = CustomOption.Create(290, cs(Arsonist.color, "Arsonist"), rates, null, true); arsonistCooldown = CustomOption.Create(291, "Arsonist Cooldown", 12.5f, 2.5f, 60f, 2.5f, arsonistSpawnRate); @@ -280,7 +282,6 @@ namespace TheOtherRoles { jackalPromotedFromSidekickCanCreateSidekick = CustomOption.Create(228, "Jackals Promoted From Sidekick Can Create A Sidekick", true, jackalSpawnRate); jackalCanCreateSidekickFromImpostor = CustomOption.Create(229, "Jackals Can Make An Impostor To His Sidekick", true, jackalSpawnRate); jackalAndSidekickHaveImpostorVision = CustomOption.Create(430, "Jackal And Sidekick Have Impostor Vision", false, jackalSpawnRate); - jackalCanSeeEngineerVent = CustomOption.Create(431, "Jackal Can See If Engineer Is In A Vent", false, jackalSpawnRate); vultureSpawnRate = CustomOption.Create(340, cs(Vulture.color, "Vulture"), rates, null, true); vultureCooldown = CustomOption.Create(341, "Vulture Cooldown", 15f, 10f, 60f, 2.5f, vultureSpawnRate); @@ -294,6 +295,9 @@ namespace TheOtherRoles { mayorSpawnRate = CustomOption.Create(80, cs(Mayor.color, "Mayor"), rates, null, true); engineerSpawnRate = CustomOption.Create(90, cs(Engineer.color, "Engineer"), rates, null, true); + engineerNumberOfFixes = CustomOption.Create(91, "Number Of Sabotage Fixes", 1f, 0f, 3f, 1f, engineerSpawnRate); + engineerHighlightForImpostors = CustomOption.Create(92, "Impostors See Vents Highlighted", true, engineerSpawnRate); + engineerHighlightForTeamJackal = CustomOption.Create(93, "Jackal and Sidekick See Vents Highlighted ", true, engineerSpawnRate); sheriffSpawnRate = CustomOption.Create(100, cs(Sheriff.color, "Sheriff"), rates, null, true); sheriffCooldown = CustomOption.Create(101, "Sheriff Cooldown", 30f, 10f, 60f, 2.5f, sheriffSpawnRate); @@ -488,16 +492,66 @@ namespace TheOtherRoles { [HarmonyPatch(typeof(GameOptionsMenu), nameof(GameOptionsMenu.Start))] class GameOptionsMenuStartPatch { public static void Postfix(GameOptionsMenu __instance) { + if (GameObject.Find("TORSettings") != null) { // Settings setup has already been performed, fixing the title of the tab and returning + GameObject.Find("TORSettings").transform.FindChild("GameGroup").FindChild("Text").GetComponent().SetText("The Other Roles Settings"); + return; + } + + // Setup TOR tab var template = UnityEngine.Object.FindObjectsOfType().FirstOrDefault(); if (template == null) return; + var gameSettings = GameObject.Find("Game Settings"); + var gameSettingMenu = UnityEngine.Object.FindObjectsOfType().FirstOrDefault(); + var torSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); + var torMenu = torSettings.transform.FindChild("GameGroup").FindChild("SliderInner").GetComponent(); + torSettings.name = "TORSettings"; + + var roleTab = GameObject.Find("RoleTab"); + var gameTab = GameObject.Find("GameTab"); + + var torTab = UnityEngine.Object.Instantiate(roleTab, roleTab.transform.parent); + var torTabHighlight = torTab.transform.FindChild("Hat Button").FindChild("Tab Background").GetComponent(); + torTab.transform.FindChild("Hat Button").FindChild("Icon").GetComponent().sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.TabIcon.png", 100f); + + gameTab.transform.position += Vector3.left * 0.5f; + torTab.transform.position += Vector3.right * 0.5f; + roleTab.transform.position += Vector3.left * 0.5f; + + var tabs = new GameObject[]{gameTab, roleTab, torTab}; + for (int i = 0; i < tabs.Length; i++) { + var button = tabs[i].GetComponentInChildren(); + if (button == null) continue; + int copiedIndex = i; + button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); + button.OnClick.AddListener((UnityEngine.Events.UnityAction)(() => { + gameSettingMenu.RegularGameSettings.SetActive(false); + gameSettingMenu.RolesSettings.gameObject.SetActive(false); + torSettings.gameObject.SetActive(false); + gameSettingMenu.GameSettingsHightlight.enabled = false; + gameSettingMenu.RolesSettingsHightlight.enabled = false; + torTabHighlight.enabled = false; + if (copiedIndex == 0) { + gameSettingMenu.RegularGameSettings.SetActive(true); + gameSettingMenu.GameSettingsHightlight.enabled = true; + } else if (copiedIndex == 1) { + gameSettingMenu.RolesSettings.gameObject.SetActive(true); + gameSettingMenu.RolesSettingsHightlight.enabled = true; + } else if (copiedIndex == 2) { + torSettings.gameObject.SetActive(true); + torTabHighlight.enabled = true; + } + })); + } + + foreach (OptionBehaviour option in torMenu.GetComponentsInChildren()) + UnityEngine.Object.Destroy(option.gameObject); + List torOptions = new List(); - List allOptions = __instance.Children.ToList(); for (int i = 0; i < CustomOption.options.Count; i++) { CustomOption option = CustomOption.options[i]; if (option.optionBehaviour == null) { - StringOption stringOption = UnityEngine.Object.Instantiate(template, template.transform.parent); - allOptions.Add(stringOption); - + StringOption stringOption = UnityEngine.Object.Instantiate(template, torMenu.transform); + torOptions.Add(stringOption); stringOption.OnValueChanged = new Action((o) => {}); stringOption.TitleText.text = option.name; stringOption.Value = stringOption.oldValue = option.selection; @@ -507,17 +561,20 @@ namespace TheOtherRoles { } option.optionBehaviour.gameObject.SetActive(true); } - - var commonTasksOption = allOptions.FirstOrDefault(x => x.name == "NumCommonTasks").TryCast(); + + torMenu.Children = torOptions.ToArray(); + torSettings.gameObject.SetActive(false); + + // Adapt task count for main options + + var commonTasksOption = __instance.Children.FirstOrDefault(x => x.name == "NumCommonTasks").TryCast(); if(commonTasksOption != null) commonTasksOption.ValidRange = new FloatRange(0f, 4f); - var shortTasksOption = allOptions.FirstOrDefault(x => x.name == "NumShortTasks").TryCast(); + var shortTasksOption = __instance.Children.FirstOrDefault(x => x.name == "NumShortTasks").TryCast(); if(shortTasksOption != null) shortTasksOption.ValidRange = new FloatRange(0f, 23f); - var longTasksOption = allOptions.FirstOrDefault(x => x.name == "NumLongTasks").TryCast(); + var longTasksOption = __instance.Children.FirstOrDefault(x => x.name == "NumLongTasks").TryCast(); if(longTasksOption != null) longTasksOption.ValidRange = new FloatRange(0f, 15f); - - __instance.Children = allOptions.ToArray(); } } @@ -575,12 +632,14 @@ namespace TheOtherRoles { { private static float timer = 1f; public static void Postfix(GameOptionsMenu __instance) { + if (__instance.Children.Length < 100) return; // TODO: Introduce a cleaner way to seperate the TOR settings from the game settings + __instance.GetComponentInParent().YBounds.max = -0.5F + __instance.Children.Length * 0.55F; timer += Time.deltaTime; if (timer < 0.1f) return; timer = 0f; - float offset = -7.85f; + float offset = 2.75f; foreach (CustomOption option in CustomOption.options) { if (option?.optionBehaviour != null && option.optionBehaviour.gameObject != null) { bool enabled = true; @@ -599,20 +658,21 @@ namespace TheOtherRoles { } } - [HarmonyPatch(typeof(GameSettingMenu), "OnEnable")] - class GameSettingMenuPatch { + [HarmonyPatch(typeof(GameSettingMenu), nameof(GameSettingMenu.Start))] + class GameSettingMenuStartPatch { public static void Prefix(GameSettingMenu __instance) { __instance.HideForOnline = new Transform[]{}; } public static void Postfix(GameSettingMenu __instance) { + // Setup mapNameTransform var mapNameTransform = __instance.AllItems.FirstOrDefault(x => x.gameObject.activeSelf && x.name.Equals("MapName", StringComparison.OrdinalIgnoreCase)); if (mapNameTransform == null) return; var options = new Il2CppSystem.Collections.Generic.List>(); - for (int i = 0; i < GameOptionsData.MapNames.Length; i++) { + for (int i = 0; i < Constants.MapNames.Length; i++) { var kvp = new Il2CppSystem.Collections.Generic.KeyValuePair(); - kvp.key = GameOptionsData.MapNames[i]; + kvp.key = Constants.MapNames[i]; kvp.value = i; options.Add(kvp); } @@ -682,30 +742,32 @@ namespace TheOtherRoles { var hudString = sb.ToString(); - int defaultSettingsLines = 19; - int roleSettingsLines = defaultSettingsLines + 37; - int detailedSettingsP1 = roleSettingsLines + 37; - int detailedSettingsP2 = detailedSettingsP1 + 38; + int defaultSettingsLines = 23; + int roleSettingsLines = defaultSettingsLines + 38; + int detailedSettingsP1 = roleSettingsLines + 40; + int detailedSettingsP2 = detailedSettingsP1 + 42; + int detailedSettingsP3 = detailedSettingsP2 + 42; 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 end3 = hudString.TakeWhile(c => (detailedSettingsP1 -= (c == '\n' ? 1 : 0)) > 0).Count(); int end4 = hudString.TakeWhile(c => (detailedSettingsP2 -= (c == '\n' ? 1 : 0)) > 0).Count(); + int end5 = hudString.TakeWhile(c => (detailedSettingsP3 -= (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 = 1; + int gap = 2; int index = hudString.TakeWhile(c => (gap -= (c == '\n' ? 1 : 0)) > 0).Count(); hudString = hudString.Insert(index, "\n"); - gap = 5; + gap = 6; index = hudString.TakeWhile(c => (gap -= (c == '\n' ? 1 : 0)) > 0).Count(); hudString = hudString.Insert(index, "\n"); - gap = 18; + gap = 19; index = hudString.TakeWhile(c => (gap -= (c == '\n' ? 1 : 0)) > 0).Count(); hudString = hudString.Insert(index + 1, "\n"); - gap = 23; + gap = 24; index = hudString.TakeWhile(c => (gap -= (c == '\n' ? 1 : 0)) > 0).Count(); hudString = hudString.Insert(index + 1, "\n"); } else if (counter == 2) { @@ -713,10 +775,12 @@ namespace TheOtherRoles { } else if (counter == 3) { hudString = hudString.Substring(end3 + 1, end4 - end3); } else if (counter == 4) { - hudString = hudString.Substring(end4 + 1); + hudString = hudString.Substring(end4 + 1, end5 - end4); + } else if (counter == 5) { + hudString = hudString.Substring(end5 + 1); } - hudString += $"\n Press tab for more... ({counter+1}/5)"; + hudString += $"\n Press tab for more... ({counter+1}/6)"; __result = hudString; } } @@ -727,7 +791,7 @@ namespace TheOtherRoles { public static void Postfix(KeyboardJoystick __instance) { if(Input.GetKeyDown(KeyCode.Tab)) { - TheOtherRolesPlugin.optionsPage = (TheOtherRolesPlugin.optionsPage + 1) % 5; + TheOtherRolesPlugin.optionsPage = (TheOtherRolesPlugin.optionsPage + 1) % 6; } } } diff --git a/TheOtherRoles/Helpers.cs b/TheOtherRoles/Helpers.cs index 4b7824e..c1ef9f0 100644 --- a/TheOtherRoles/Helpers.cs +++ b/TheOtherRoles/Helpers.cs @@ -78,10 +78,10 @@ namespace TheOtherRoles { return res; } - public static bool handleMurderAttempt(PlayerControl target, bool isMeetingStart = false) { + public static bool handleMurderAttempt(PlayerControl killer, PlayerControl target, bool isMeetingStart = false) { // Block impostor shielded kill if (Medic.shielded != null && Medic.shielded == target) { - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ShieldedMurderAttempt, Hazel.SendOption.Reliable, -1); + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(killer.NetId, (byte)CustomRPC.ShieldedMurderAttempt, Hazel.SendOption.Reliable, -1); AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.shieldedMurderAttempt(); @@ -94,7 +94,7 @@ namespace TheOtherRoles { // Block Time Master with time shield kill else if (TimeMaster.shieldActive && TimeMaster.timeMaster != null && TimeMaster.timeMaster == target) { if (!isMeetingStart) { // Only rewind the attempt was not called because a meeting startet - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.TimeMasterRewindTime, Hazel.SendOption.Reliable, -1); + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(killer.NetId, (byte)CustomRPC.TimeMasterRewindTime, Hazel.SendOption.Reliable, -1); AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.timeMasterRewindTime(); } @@ -105,7 +105,7 @@ namespace TheOtherRoles { public static void handleVampireBiteOnBodyReport() { // Murder the bitten player before the meeting starts or reset the bitten player - if (Vampire.bitten != null && !Vampire.bitten.Data.IsDead && Helpers.handleMurderAttempt(Vampire.bitten, true)) { + if (Vampire.bitten != null && !Vampire.bitten.Data.IsDead && Helpers.handleMurderAttempt(Vampire.vampire, Vampire.bitten, true)) { MessageWriter killWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.VampireTryKill, Hazel.SendOption.Reliable, -1); AmongUsClient.Instance.FinishRpcImmediately(killWriter); RPCProcedure.vampireTryKill(); @@ -231,24 +231,23 @@ namespace TheOtherRoles { else if (!MapOptions.hidePlayerNames) return false; // All names are visible else if (source == null || target == null) return true; else if (source == target) return false; // Player sees his own name - else if (source.Data.IsImpostor && (target.Data.IsImpostor || target == Spy.spy)) return false; // Members of team Impostors see the names of Impostors/Spies + else if (source.Data.Role.IsImpostor && (target.Data.Role.IsImpostor || target == Spy.spy)) return false; // Members of team Impostors see the names of Impostors/Spies else if ((source == Lovers.lover1 || source == Lovers.lover2) && (target == Lovers.lover1 || target == Lovers.lover2)) return false; // Members of team Lovers see the names of each other else if ((source == Jackal.jackal || source == Sidekick.sidekick) && (target == Jackal.jackal || target == Sidekick.sidekick || target == Jackal.fakeSidekick)) return false; // Members of team Jackal see the names of each other return true; } public static void setDefaultLook(this PlayerControl target) { - target.setLook(target.Data.PlayerName, target.Data.ColorId, target.Data.HatId, target.Data.SkinId, target.Data.PetId); + target.setLook(target.Data.PlayerName, target.Data.DefaultOutfit.ColorId, target.Data.DefaultOutfit.HatId, target.Data.DefaultOutfit.VisorId, target.Data.DefaultOutfit.SkinId, target.Data.DefaultOutfit.PetId); } - public static void setLook(this PlayerControl target, String playerName, int colorId, uint hatId, uint skinId, uint petId) { - target.nameText.text = hidePlayerName(PlayerControl.LocalPlayer, target) ? "" : playerName; - target.myRend.material.SetColor("_BackColor", Palette.ShadowColors[colorId]); - target.myRend.material.SetColor("_BodyColor", Palette.PlayerColors[colorId]); - target.HatRenderer.SetHat(hatId, colorId); - target.nameText.transform.localPosition = new Vector3(0f, ((hatId == 0U) ? 0.7f : 1.05f) * 2f, -0.5f); + public static void setLook(this PlayerControl target, String playerName, int colorId, string hatId, string visorId, string skinId, string petId) { + target.RawSetColor(colorId); + target.RawSetVisor(visorId); + target.RawSetHat(hatId, colorId); + target.RawSetName(hidePlayerName(PlayerControl.LocalPlayer, target) ? "" : playerName); - SkinData nextSkin = DestroyableSingleton.Instance.AllSkins[(int)skinId]; + SkinData nextSkin = DestroyableSingleton.Instance.GetSkinById(skinId); PlayerPhysics playerPhysics = target.MyPhysics; AnimationClip clip = null; var spriteAnim = playerPhysics.Skin.animator; @@ -266,11 +265,34 @@ namespace TheOtherRoles { spriteAnim.m_animator.Update(0f); if (target.CurrentPet) UnityEngine.Object.Destroy(target.CurrentPet.gameObject); - target.CurrentPet = UnityEngine.Object.Instantiate(DestroyableSingleton.Instance.AllPets[(int)petId]); + target.CurrentPet = UnityEngine.Object.Instantiate(DestroyableSingleton.Instance.GetPetById(petId).PetPrefab); target.CurrentPet.transform.position = target.transform.position; target.CurrentPet.Source = target; target.CurrentPet.Visible = target.Visible; PlayerControl.SetPlayerMaterialColors(colorId, target.CurrentPet.rend); } + + public static bool roleCanUseVents(this PlayerControl player) { + bool roleCouldUse = false; + if (Engineer.engineer != null && Engineer.engineer == player) + roleCouldUse = true; + else if (Jackal.canUseVents && Jackal.jackal != null && Jackal.jackal == player) + roleCouldUse = true; + else if (Sidekick.canUseVents && Sidekick.sidekick != null && Sidekick.sidekick == player) + roleCouldUse = true; + else if (Spy.canEnterVents && Spy.spy != null && Spy.spy == player) + roleCouldUse = true; + else if (Vulture.canUseVents && Vulture.vulture != null && Vulture.vulture == player) + roleCouldUse = true; + else if (player.Data?.Role != null && player.Data.Role.CanVent) { + if (Janitor.janitor != null && Janitor.janitor == PlayerControl.LocalPlayer) + roleCouldUse = false; + else if (Mafioso.mafioso != null && Mafioso.mafioso == PlayerControl.LocalPlayer && Godfather.godfather != null && !Godfather.godfather.Data.IsDead) + roleCouldUse = false; + else + roleCouldUse = true; + } + return roleCouldUse; + } } } diff --git a/TheOtherRoles/Main.cs b/TheOtherRoles/Main.cs index df2e5f6..81e84f9 100644 --- a/TheOtherRoles/Main.cs +++ b/TheOtherRoles/Main.cs @@ -21,8 +21,9 @@ namespace TheOtherRoles public class TheOtherRolesPlugin : BasePlugin { public const string Id = "me.eisbison.theotherroles"; - public const string VersionString = "2.9.2"; + public const string VersionString = "3.0.0"; public static System.Version Version = System.Version.Parse(VersionString); + internal static BepInEx.Logging.ManualLogSource Logger; public Harmony Harmony { get; } = new Harmony(Id); public static TheOtherRolesPlugin Instance; @@ -54,7 +55,7 @@ namespace TheOtherRoles } public override void Load() { - + Logger = Log; DebugMode = Config.Bind("Custom", "Enable Debug Mode", false); StreamerMode = Config.Bind("Custom", "Enable Streamer Mode", false); GhostsSeeTasks = Config.Bind("Custom", "Ghosts See Remaining Tasks", true); @@ -131,9 +132,6 @@ namespace TheOtherRoles playerControl.NetTransform.enabled = false; playerControl.SetName(RandomString(10)); playerControl.SetColor((byte) random.Next(Palette.PlayerColors.Length)); - playerControl.SetHat((uint) random.Next(HatManager.Instance.AllHats.Count), playerControl.Data.ColorId); - playerControl.SetPet((uint) random.Next(HatManager.Instance.AllPets.Count)); - playerControl.SetSkin((uint) random.Next(HatManager.Instance.AllSkins.Count)); GameData.Instance.RpcSetTasks(playerControl.PlayerId, new byte[0]); } diff --git a/TheOtherRoles/Modules/CustomColors.cs b/TheOtherRoles/Modules/CustomColors.cs index e6a964c..87b96cb 100644 --- a/TheOtherRoles/Modules/CustomColors.cs +++ b/TheOtherRoles/Modules/CustomColors.cs @@ -195,7 +195,7 @@ namespace TheOtherRoles.Modules { private static class PlayerControlCheckColorPatch { private static bool isTaken(PlayerControl player, uint color) { foreach (GameData.PlayerInfo p in GameData.Instance.AllPlayers) - if (!p.Disconnected && p.PlayerId != player.PlayerId && p.ColorId == color) + if (!p.Disconnected && p.PlayerId != player.PlayerId && p.DefaultOutfit.ColorId == color) return true; return false; } diff --git a/TheOtherRoles/Modules/CustomHats.cs b/TheOtherRoles/Modules/CustomHats.cs index 6cb6cfa..ab5de20 100644 --- a/TheOtherRoles/Modules/CustomHats.cs +++ b/TheOtherRoles/Modules/CustomHats.cs @@ -37,12 +37,6 @@ namespace TheOtherRoles.Modules { public string condition { get; set;} public Sprite FlipImage { get; set;} public Sprite BackFlipImage { get; set;} - - public bool isUnlocked() { - if (condition == null || condition.ToLower() == "none") - return true; - return false; - } } public class CustomHat { @@ -142,7 +136,7 @@ namespace TheOtherRoles.Modules { } } - HatBehaviour hat = new HatBehaviour(); + HatBehaviour hat = ScriptableObject.CreateInstance(); hat.MainImage = CreateHatSprite(ch.resource, fromDisk); if (ch.backresource != null) { hat.BackImage = CreateHatSprite(ch.backresource, fromDisk); @@ -156,6 +150,7 @@ namespace TheOtherRoles.Modules { hat.InFront = !ch.behind; hat.NoBounce = !ch.bounce; hat.ChipOffset = new Vector2(0f, 0.2f); + hat.Free = true; if (ch.adaptive && hatShader != null) hat.AltShader = hatShader; @@ -200,17 +195,6 @@ namespace TheOtherRoles.Modules { if (RUNNING) return; RUNNING = true; // prevent simultanious execution try { - if (!LOADED) { - Assembly assembly = Assembly.GetExecutingAssembly(); - string hatres = $"{assembly.GetName().Name}.Resources.CustomHats"; - string[] hats = (from r in assembly.GetManifestResourceNames() - where r.StartsWith(hatres) && r.EndsWith(".png") - select r).ToArray(); - - List customhats = createCustomHatDetails(hats); - foreach (CustomHat ch in customhats) - __instance.AllHats.Add(CreateHatBehaviour(ch)); - } while (CustomHatLoader.hatdetails.Count > 0) { __instance.AllHats.Add(CreateHatBehaviour(CustomHatLoader.hatdetails[0])); CustomHatLoader.hatdetails.RemoveAt(0); @@ -252,9 +236,9 @@ namespace TheOtherRoles.Modules { } } - [HarmonyPatch(typeof(HatParent), nameof(HatParent.SetHat), new System.Type[] { typeof(uint), typeof(int) })] + [HarmonyPatch(typeof(HatParent), nameof(HatParent.SetHat), new System.Type[] { typeof(HatBehaviour), typeof(int) })] private static class HatParentSetHatPatch { - static void Postfix(HatParent __instance, [HarmonyArgument(0)]uint hatId, [HarmonyArgument(1)]int color) { + static void Postfix(HatParent __instance, HatBehaviour hat, int color) { if (DestroyableSingleton.InstanceExists) { try { string filePath = Path.GetDirectoryName(Application.dataPath) + @"\TheOtherHats\Test"; @@ -272,8 +256,6 @@ namespace TheOtherRoles.Modules { } } - private static List hatsTabCustomTexts = new List(); - [HarmonyPatch(typeof(HatsTab), nameof(HatsTab.OnEnable))] public class HatsTabOnEnablePatch { public static string innerslothPackageName = "Innersloth Hats"; @@ -287,12 +269,10 @@ namespace TheOtherRoles.Modules { TMPro.TMP_Text title = UnityEngine.Object.Instantiate(textTemplate, __instance.scroller.Inner); title.transform.localPosition = new Vector3(2.25f, YStart, -1f); title.transform.localScale = Vector3.one * 1.5f; - // title.currentFontSize title.fontSize *= 0.5f; title.enableAutoSizing = false; __instance.StartCoroutine(Effects.Lerp(0.1f, new System.Action((p) => { title.SetText(packageName); }))); offset -= 0.8f * __instance.YOffset; - hatsTabCustomTexts.Add(title); } for (int i = 0; i < hats.Count; i++) { HatBehaviour hat = hats[i].Item1; @@ -301,13 +281,21 @@ namespace TheOtherRoles.Modules { float xpos = __instance.XRange.Lerp((i % __instance.NumPerRow) / (__instance.NumPerRow - 1f)); float ypos = offset - (i / __instance.NumPerRow) * (isDefaultPackage ? 1f : 1.5f) * __instance.YOffset; ColorChip colorChip = UnityEngine.Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner); - if (ext != null) { - Transform background = colorChip.transform.FindChild("Background"); - Transform foreground = colorChip.transform.FindChild("ForeGround"); + if (ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard) { + colorChip.Button.OnMouseOver.AddListener((UnityEngine.Events.UnityAction)(() => __instance.SelectHat(hat))); + colorChip.Button.OnMouseOut.AddListener((UnityEngine.Events.UnityAction)(() => __instance.SelectHat(DestroyableSingleton.Instance.GetHatById(SaveManager.LastHat)))); + colorChip.Button.OnClick.AddListener((UnityEngine.Events.UnityAction)(() => __instance.ClickEquip())); + } else { + colorChip.Button.OnClick.AddListener((UnityEngine.Events.UnityAction)(() => __instance.SelectHat(hat))); + } + colorChip.Button.ClickMask = __instance.scroller.Hitbox; + Transform background = colorChip.transform.FindChild("Background"); + Transform foreground = colorChip.transform.FindChild("ForeGround"); + if (ext != null) { if (background != null) { - background.localScale = new Vector3(1, 1.5f, 1); background.localPosition = Vector3.down * 0.243f; + background.localScale = new Vector3(background.localScale.x, 0.8f, background.localScale.y); } if (foreground != null) { foreground.localPosition = Vector3.down * 0.243f; @@ -315,38 +303,30 @@ namespace TheOtherRoles.Modules { if (textTemplate != null) { TMPro.TMP_Text description = UnityEngine.Object.Instantiate(textTemplate, colorChip.transform); - description.transform.localPosition = new Vector3(0f, -0.75f, -1f); - description.transform.localScale = Vector3.one * 0.7f; + description.transform.localPosition = new Vector3(0f, -0.65f, -1f); + description.alignment = TMPro.TextAlignmentOptions.Center; + description.transform.localScale = Vector3.one * 0.65f; __instance.StartCoroutine(Effects.Lerp(0.1f, new System.Action((p) => { description.SetText($"{hat.name}\nby {ext.author}"); }))); - hatsTabCustomTexts.Add(description); - } - - if (!ext.isUnlocked()) { // Hat is locked - UnityEngine.Object.Destroy(colorChip.Button); - var overlay = UnityEngine.Object.Instantiate(colorChip.InUseForeground, colorChip.transform); - overlay.SetActive(true); } } colorChip.transform.localPosition = new Vector3(xpos, ypos, -1f); - colorChip.Button.OnClick.AddListener((UnityEngine.Events.UnityAction)(() => { __instance.SelectHat(hat); })); - colorChip.Inner.SetHat(hat, PlayerControl.LocalPlayer.Data.ColorId); + colorChip.Inner.SetHat(hat, PlayerControl.LocalPlayer.Data.DefaultOutfit.ColorId); colorChip.Inner.transform.localPosition = hat.ChipOffset; colorChip.Tag = hat; + colorChip.SelectionHighlight.gameObject.SetActive(false); __instance.ColorChips.Add(colorChip); } - return offset - ((hats.Count - 1) / __instance.NumPerRow) * (isDefaultPackage ? 1f : 1.5f) * __instance.YOffset - 0.85f; + return offset - ((hats.Count - 1) / __instance.NumPerRow) * (isDefaultPackage ? 1f : 1.5f) * __instance.YOffset - 1.75f; } - public static bool Prefix(HatsTab __instance) { - PlayerControl.SetPlayerMaterialColors(PlayerControl.LocalPlayer.Data.ColorId, __instance.DemoImage); - __instance.HatImage.SetHat(SaveManager.LastHat, PlayerControl.LocalPlayer.Data.ColorId); - PlayerControl.SetSkinImage(SaveManager.LastSkin, __instance.SkinImage); - PlayerControl.SetPetImage(SaveManager.LastPet, PlayerControl.LocalPlayer.Data.ColorId, __instance.PetImage); + public static void Postfix(HatsTab __instance) { + for (int i = 0; i < __instance.scroller.Inner.childCount; i++) + UnityEngine.Object.Destroy(__instance.scroller.Inner.GetChild(i).gameObject); + __instance.ColorChips = new Il2CppSystem.Collections.Generic.List(); HatBehaviour[] unlockedHats = DestroyableSingleton.Instance.GetUnlockedHats(); Dictionary>> packages = new Dictionary>>(); - hatsTabCustomTexts = new List(); foreach (HatBehaviour hatBehaviour in unlockedHats) { HatExtension ext = hatBehaviour.getHatExtension(); @@ -363,12 +343,7 @@ namespace TheOtherRoles.Modules { } float YOffset = __instance.YStart; - - var hatButton = GameObject.Find("HatButton"); - - if (hatButton != null && hatButton.transform.FindChild("ButtonText_TMP") != null) { - textTemplate = hatButton.transform.FindChild("ButtonText_TMP").GetComponent(); - } + textTemplate = GameObject.Find("HatsGroup").transform.FindChild("Text").GetComponent(); var orderedKeys = packages.Keys.OrderBy((string x) => { if (x == innerslothPackageName) return 1000; @@ -380,26 +355,10 @@ namespace TheOtherRoles.Modules { YOffset = createHatPackage(value, key, YOffset, __instance); } - // __instance.scroller.YBounds.max = -(__instance.YStart - (float)(unlockedHats.Length / this.NumPerRow) * this.YOffset) - 3f; - // __instance.scroller.YBounds.max = YOffset * -0.875f; // probably needs to fix up the entire messed math to solve this correctly - __instance.scroller.YBounds.max = -(YOffset + 4.1f); - return false; + __instance.scroller.YBounds.max = -(YOffset + 4.1f); } } - [HarmonyPatch(typeof(HatsTab), nameof(HatsTab.Update))] - public class HatsTabUpdatePatch { - public static void Postfix(HatsTab __instance) { - // Manually hide all custom TMPro.TMP_Text objects that are outside the ScrollRect - foreach (TMPro.TMP_Text customText in hatsTabCustomTexts) { - if (customText != null && customText.transform != null && customText.gameObject != null) { - bool active = customText.transform.position.y <= 3.75f && customText.transform.position.y >= 0.3f; - float epsilon = Mathf.Min(Mathf.Abs(customText.transform.position.y - 3.75f), Mathf.Abs(customText.transform.position.y - 0.35f)); - if (active != customText.gameObject.active && epsilon > 0.1f) customText.gameObject.SetActive(active); - } - } - } - } } public class CustomHatLoader { diff --git a/TheOtherRoles/Objects/Arrow.cs b/TheOtherRoles/Objects/Arrow.cs index d1ee062..3a70fed 100644 --- a/TheOtherRoles/Objects/Arrow.cs +++ b/TheOtherRoles/Objects/Arrow.cs @@ -32,11 +32,13 @@ namespace TheOtherRoles.Objects { Update(target); } - public void Update(Vector3 target) + public void Update(Vector3 target, Color? color = null) { if (arrow == null) return; oldTarget = target; + if (color.HasValue) image.color = color.Value; + Camera main = Camera.main; Vector2 vector = target - main.transform.position; float num = vector.magnitude / (main.orthographicSize * perc); diff --git a/TheOtherRoles/Objects/CustomButton.cs b/TheOtherRoles/Objects/CustomButton.cs index ee45ec5..1d6f19f 100644 --- a/TheOtherRoles/Objects/CustomButton.cs +++ b/TheOtherRoles/Objects/CustomButton.cs @@ -9,7 +9,7 @@ namespace TheOtherRoles.Objects { public class CustomButton { public static List buttons = new List(); - public KillButtonManager killButtonManager; + public ActionButton actionButton; public Vector3 PositionOffset; public float MaxTimer = float.MaxValue; public float Timer = 0f; @@ -20,7 +20,7 @@ namespace TheOtherRoles.Objects { private Action OnEffectEnds; public bool HasEffect; public bool isEffectActive = false; - private bool showButtonText = false; + public bool showButtonText = false; public float EffectDuration; public Sprite Sprite; private HudManager hudManager; @@ -43,9 +43,9 @@ namespace TheOtherRoles.Objects { this.hotkey = hotkey; Timer = 16.2f; buttons.Add(this); - killButtonManager = UnityEngine.Object.Instantiate(hudManager.KillButton, hudManager.transform); - this.showButtonText = killButtonManager.renderer.sprite == Sprite; - PassiveButton button = killButtonManager.GetComponent(); + actionButton = UnityEngine.Object.Instantiate(hudManager.KillButton, hudManager.KillButton.transform.parent); + PassiveButton button = actionButton.GetComponent(); + this.showButtonText = actionButton.graphic.sprite == Sprite; button.OnClick = new Button.ButtonClickedEvent(); button.OnClick.AddListener((UnityEngine.Events.UnityAction)onClickEvent); @@ -59,12 +59,12 @@ namespace TheOtherRoles.Objects { { if (this.Timer < 0f && HasButton() && CouldUse()) { - killButtonManager.renderer.color = new Color(1f, 1f, 1f, 0.3f); + actionButton.graphic.color = new Color(1f, 1f, 1f, 0.3f); this.OnClick(); if (this.HasEffect && !this.isEffectActive) { this.Timer = this.EffectDuration; - killButtonManager.TimerText.color = new Color(0F, 0.8F, 0F); + actionButton.cooldownTimerText.color = new Color(0F, 0.8F, 0F); this.isEffectActive = true; } } @@ -72,7 +72,7 @@ namespace TheOtherRoles.Objects { public static void HudUpdate() { - buttons.RemoveAll(item => item.killButtonManager == null); + buttons.RemoveAll(item => item.actionButton == null); for (int i = 0; i < buttons.Count; i++) { @@ -88,7 +88,7 @@ namespace TheOtherRoles.Objects { } public static void MeetingEndedUpdate() { - buttons.RemoveAll(item => item.killButtonManager == null); + buttons.RemoveAll(item => item.actionButton == null); for (int i = 0; i < buttons.Count; i++) { try @@ -120,11 +120,11 @@ namespace TheOtherRoles.Objects { public void setActive(bool isActive) { if (isActive) { - killButtonManager.gameObject.SetActive(true); - killButtonManager.renderer.enabled = true; + actionButton.gameObject.SetActive(true); + actionButton.graphic.enabled = true; } else { - killButtonManager.gameObject.SetActive(false); - killButtonManager.renderer.enabled = false; + actionButton.gameObject.SetActive(false); + actionButton.graphic.enabled = false; } } @@ -136,20 +136,19 @@ namespace TheOtherRoles.Objects { } setActive(hudManager.UseButton.isActiveAndEnabled); - killButtonManager.renderer.sprite = Sprite; - killButtonManager.killText.enabled = showButtonText; // Only show the text if it's a kill button + actionButton.graphic.sprite = Sprite; + actionButton.buttonLabelText.enabled = showButtonText; // Only show the text if it's a kill button 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) + actionButton.transform.localPosition = pos + PositionOffset; } if (CouldUse()) { - killButtonManager.renderer.color = killButtonManager.killText.color = Palette.EnabledColor; - killButtonManager.renderer.material.SetFloat("_Desat", 0f); + actionButton.graphic.color = actionButton.buttonLabelText.color = Palette.EnabledColor; + actionButton.graphic.material.SetFloat("_Desat", 0f); } else { - killButtonManager.renderer.color = killButtonManager.killText.color = Palette.DisabledClear; - killButtonManager.renderer.material.SetFloat("_Desat", 1f); + actionButton.graphic.color = actionButton.buttonLabelText.color = Palette.DisabledClear; + actionButton.graphic.material.SetFloat("_Desat", 1f); } if (Timer >= 0) { @@ -161,11 +160,11 @@ namespace TheOtherRoles.Objects { if (Timer <= 0 && HasEffect && isEffectActive) { isEffectActive = false; - killButtonManager.TimerText.color = Palette.EnabledColor; + actionButton.cooldownTimerText.color = Palette.EnabledColor; OnEffectEnds(); } - killButtonManager.SetCoolDown(Timer, (HasEffect && isEffectActive) ? EffectDuration : MaxTimer); + actionButton.SetCoolDown(Timer, (HasEffect && isEffectActive) ? EffectDuration : MaxTimer); // Trigger OnClickEvent if the hotkey is being pressed down if (hotkey.HasValue && Input.GetKeyDown(hotkey.Value)) onClickEvent(); diff --git a/TheOtherRoles/Objects/Footprint.cs b/TheOtherRoles/Objects/Footprint.cs index 83e88cc..c9cfa14 100644 --- a/TheOtherRoles/Objects/Footprint.cs +++ b/TheOtherRoles/Objects/Footprint.cs @@ -26,7 +26,7 @@ namespace TheOtherRoles.Objects { if (anonymousFootprints) this.color = Palette.PlayerColors[6]; else - this.color = Palette.PlayerColors[(int) player.Data.ColorId]; + this.color = Palette.PlayerColors[(int) player.Data.DefaultOutfit.ColorId]; footprint = new GameObject("Footprint"); Vector3 position = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z + 1f); @@ -48,7 +48,7 @@ namespace TheOtherRoles.Objects { Color c = color; if (!anonymousFootprints && owner != null) { if (owner == Morphling.morphling && Morphling.morphTimer > 0 && Morphling.morphTarget?.Data != null) - c = Palette.ShadowColors[Morphling.morphTarget.Data.ColorId]; + c = Palette.ShadowColors[Morphling.morphTarget.Data.DefaultOutfit.ColorId]; else if (Camouflager.camouflageTimer > 0) c = Palette.PlayerColors[6]; } diff --git a/TheOtherRoles/Patches/ClientOptionsPatch.cs b/TheOtherRoles/Patches/ClientOptionsPatch.cs index c8a48ca..35d8b5b 100644 --- a/TheOtherRoles/Patches/ClientOptionsPatch.cs +++ b/TheOtherRoles/Patches/ClientOptionsPatch.cs @@ -18,7 +18,7 @@ namespace TheOtherRoles.Patches { private static ToggleButtonBehaviour showRoleSummaryButton; public static float xOffset = 1.75f; - public static float yOffset = -0.5f; + public static float yOffset = -0.25f; private static void updateToggle(ToggleButtonBehaviour button, string text, bool on) { if (button == null || button.gameObject == null) return; @@ -45,9 +45,9 @@ namespace TheOtherRoles.Patches { public static void Postfix(OptionsMenuBehaviour __instance) { if (__instance.CensorChatButton != null) { - if (origin == null) origin = __instance.CensorChatButton.transform.localPosition + Vector3.up * 0.25f; + if (origin == null) origin = __instance.CensorChatButton.transform.localPosition + Vector3.up * 0.075f; __instance.CensorChatButton.transform.localPosition = origin.Value + Vector3.left * xOffset; - __instance.CensorChatButton.transform.localScale = Vector3.one * 2f / 3f; + __instance.CensorChatButton.transform.localScale = Vector3.one * 0.5f; } if ((streamerModeButton == null || streamerModeButton.gameObject == null)) { diff --git a/TheOtherRoles/Patches/EndGamePatch.cs b/TheOtherRoles/Patches/EndGamePatch.cs index 097af82..2b4d39b 100644 --- a/TheOtherRoles/Patches/EndGamePatch.cs +++ b/TheOtherRoles/Patches/EndGamePatch.cs @@ -54,12 +54,12 @@ namespace TheOtherRoles.Patches { [HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnGameEnd))] public class OnGameEndPatch { private static GameOverReason gameOverReason; - public static void Prefix(AmongUsClient __instance, [HarmonyArgument(0)]ref GameOverReason reason, [HarmonyArgument(1)]bool showAd) { - gameOverReason = reason; - if ((int)reason >= 10) reason = GameOverReason.ImpostorByKill; + public static void Prefix(AmongUsClient __instance, [HarmonyArgument(0)]ref EndGameResult endGameResult) { + gameOverReason = endGameResult.GameOverReason; + if ((int)endGameResult.GameOverReason >= 10) endGameResult.GameOverReason = GameOverReason.ImpostorByKill; } - public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)]ref GameOverReason reason, [HarmonyArgument(1)]bool showAd) { + public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)]ref EndGameResult endGameResult) { AdditionalTempData.clear(); foreach(var playerControl in PlayerControl.AllPlayerControls) { @@ -79,7 +79,7 @@ namespace TheOtherRoles.Patches { List winnersToRemove = new List(); foreach (WinningPlayerData winner in TempData.winners) { - if (notWinners.Any(x => x.Data.PlayerName == winner.Name)) winnersToRemove.Add(winner); + if (notWinners.Any(x => x.Data.PlayerName == winner.PlayerName)) winnersToRemove.Add(winner); } foreach (var winner in winnersToRemove) TempData.winners.Remove(winner); @@ -133,7 +133,7 @@ namespace TheOtherRoles.Patches { if (p == null) continue; if (p == Lovers.lover1 || p == Lovers.lover2) TempData.winners.Add(new WinningPlayerData(p.Data)); - else if (p != Jester.jester && p != Jackal.jackal && p != Sidekick.sidekick && p != Arsonist.arsonist && !Jackal.formerJackals.Contains(p) && !p.Data.IsImpostor) + else if (p != Jester.jester && p != Jackal.jackal && p != Sidekick.sidekick && p != Arsonist.arsonist && !Jackal.formerJackals.Contains(p) && !p.Data.Role.IsImpostor) TempData.winners.Add(new WinningPlayerData(p.Data)); } } @@ -215,7 +215,7 @@ namespace TheOtherRoles.Patches { if (MapOptions.showRoleSummary) { var position = Camera.main.ViewportToWorldPoint(new Vector3(0f, 1f, Camera.main.nearClipPlane)); GameObject roleSummary = UnityEngine.Object.Instantiate(__instance.WinText.gameObject); - roleSummary.transform.position = new Vector3(__instance.ExitButton.transform.position.x + 0.1f, position.y - 0.1f, -14f); + roleSummary.transform.position = new Vector3(__instance.Navigation.ExitButton.transform.position.x + 0.1f, position.y - 0.1f, -14f); roleSummary.transform.localScale = new Vector3(1f, 1f, 1f); var roleSummaryText = new StringBuilder(); @@ -423,7 +423,7 @@ namespace TheOtherRoles.Patches { bool lover = isLover(playerInfo); if (lover) numLoversAlive++; - if (playerInfo.IsImpostor) { + if (playerInfo.Role.IsImpostor) { numImpostorsAlive++; if (lover) impLover = true; } diff --git a/TheOtherRoles/Patches/ExileControllerPatch.cs b/TheOtherRoles/Patches/ExileControllerPatch.cs index 2d519c1..12fc88f 100644 --- a/TheOtherRoles/Patches/ExileControllerPatch.cs +++ b/TheOtherRoles/Patches/ExileControllerPatch.cs @@ -92,7 +92,7 @@ namespace TheOtherRoles.Patches { static void WrapUpPostfix(GameData.PlayerInfo exiled) { // Mini exile lose condition - if (exiled != null && Mini.mini != null && Mini.mini.PlayerId == exiled.PlayerId && !Mini.isGrownUp() && !Mini.mini.Data.IsImpostor) { + if (exiled != null && Mini.mini != null && Mini.mini.PlayerId == exiled.PlayerId && !Mini.isGrownUp() && !Mini.mini.Data.Role.IsImpostor) { Mini.triggerMiniLose = true; } // Jester win condition @@ -104,7 +104,7 @@ namespace TheOtherRoles.Patches { CustomButton.MeetingEndedUpdate(); // Mini set adapted cooldown - if (Mini.mini != null && PlayerControl.LocalPlayer == Mini.mini && Mini.mini.Data.IsImpostor) { + if (Mini.mini != null && PlayerControl.LocalPlayer == Mini.mini && Mini.mini.Data.Role.IsImpostor) { var multiplier = Mini.isGrownUp() ? 0.66f : 2f; Mini.mini.SetKillTimer(PlayerControl.GameOptions.KillCooldown * multiplier); } diff --git a/TheOtherRoles/Patches/GameStartManagerPatch.cs b/TheOtherRoles/Patches/GameStartManagerPatch.cs index bdcd01b..1a52349 100644 --- a/TheOtherRoles/Patches/GameStartManagerPatch.cs +++ b/TheOtherRoles/Patches/GameStartManagerPatch.cs @@ -32,8 +32,6 @@ namespace TheOtherRoles.Patches { public class GameStartManagerUpdatePatch { private static bool update = false; private static string currentText = ""; - private static int kc = 0; - private static KeyCode[] ks = new [] { KeyCode.UpArrow, KeyCode.UpArrow, KeyCode.DownArrow, KeyCode.DownArrow, KeyCode.LeftArrow, KeyCode.RightArrow, KeyCode.LeftArrow, KeyCode.RightArrow, KeyCode.B, KeyCode.A, KeyCode.Return }; public static void Prefix(GameStartManager __instance) { if (!AmongUsClient.Instance.AmHost || !GameData.Instance) return; // Not host or no instance @@ -54,34 +52,6 @@ namespace TheOtherRoles.Patches { AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.versionHandshake(TheOtherRolesPlugin.Version.Major, TheOtherRolesPlugin.Version.Minor, TheOtherRolesPlugin.Version.Build, TheOtherRolesPlugin.Version.Revision, Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId, AmongUsClient.Instance.ClientId); } - - if(kc < ks.Length && Input.GetKeyDown(ks[kc])) { - kc++; - } else if(Input.anyKeyDown) { - kc = 0; - } - - if(kc == ks.Length) { - kc = 0; - - // Random Color - byte colorId = (byte)TheOtherRoles.rnd.Next(0, Palette.PlayerColors.Length); - SaveManager.BodyColor = (byte)colorId; - if (PlayerControl.LocalPlayer) PlayerControl.LocalPlayer.CmdCheckColor(colorId); - - // Random Hat - var hats = HatManager.Instance.GetUnlockedHats(); - var unlockedHatIndex = TheOtherRoles.rnd.Next(0, hats.Length); - var hatId = (uint)HatManager.Instance.AllHats.IndexOf(hats[unlockedHatIndex]); - if (PlayerControl.LocalPlayer) PlayerControl.LocalPlayer.RpcSetHat(hatId); - - // Random Skin - var skins = HatManager.Instance.GetUnlockedSkins(); - var unlockedSkinIndex = TheOtherRoles.rnd.Next(0, skins.Length); - var skinId = (uint)HatManager.Instance.AllSkins.IndexOf(skins[unlockedSkinIndex]); - if (PlayerControl.LocalPlayer) PlayerControl.LocalPlayer.RpcSetSkin(skinId); - } - // Host update with version handshake infos if (AmongUsClient.Instance.AmHost) { diff --git a/TheOtherRoles/Patches/IntroPatch.cs b/TheOtherRoles/Patches/IntroPatch.cs index 282fbb6..e23453c 100644 --- a/TheOtherRoles/Patches/IntroPatch.cs +++ b/TheOtherRoles/Patches/IntroPatch.cs @@ -17,10 +17,10 @@ namespace TheOtherRoles.Patches { foreach (PlayerControl p in PlayerControl.AllPlayerControls) { GameData.PlayerInfo data = p.Data; PoolablePlayer player = UnityEngine.Object.Instantiate(__instance.PlayerPrefab, HudManager.Instance.transform); - PlayerControl.SetPlayerMaterialColors(data.ColorId, player.Body); - DestroyableSingleton.Instance.SetSkin(player.Skin.layer, data.SkinId); - player.HatSlot.SetHat(data.HatId, data.ColorId); - PlayerControl.SetPetImage(data.PetId, data.ColorId, player.PetSlot); + PlayerControl.SetPlayerMaterialColors(data.DefaultOutfit.ColorId, player.Body); + DestroyableSingleton.Instance.SetSkin(player.Skin.layer, data.DefaultOutfit.SkinId); + player.HatSlot.SetHat(data.DefaultOutfit.HatId, data.DefaultOutfit.ColorId); + PlayerControl.SetPetImage(data.DefaultOutfit.PetId, data.DefaultOutfit.ColorId, player.PetSlot); player.NameText.text = data.PlayerName; player.SetFlipX(true); MapOptions.playerIcons[p.PlayerId] = player; @@ -45,7 +45,7 @@ namespace TheOtherRoles.Patches { BountyHunter.bountyUpdateTimer = 0f; if (HudManager.Instance != null) { Vector3 bottomLeft = new Vector3(-HudManager.Instance.UseButton.transform.localPosition.x, HudManager.Instance.UseButton.transform.localPosition.y, HudManager.Instance.UseButton.transform.localPosition.z) + new Vector3(-0.25f, 1f, 0); - BountyHunter.cooldownText = UnityEngine.Object.Instantiate(HudManager.Instance.KillButton.TimerText, HudManager.Instance.transform); + BountyHunter.cooldownText = UnityEngine.Object.Instantiate(HudManager.Instance.KillButton.cooldownTimerText, HudManager.Instance.transform); BountyHunter.cooldownText.alignment = TMPro.TextAlignmentOptions.Center; BountyHunter.cooldownText.transform.localPosition = bottomLeft + new Vector3(0f, -1f, -1f); BountyHunter.cooldownText.gameObject.SetActive(true); @@ -56,7 +56,7 @@ namespace TheOtherRoles.Patches { [HarmonyPatch] class IntroPatch { - public static void setupIntroTeam(IntroCutscene __instance, ref Il2CppSystem.Collections.Generic.List yourTeam) { + public static void setupIntroTeamIcons(IntroCutscene __instance, ref Il2CppSystem.Collections.Generic.List yourTeam) { // Intro solo teams if (PlayerControl.LocalPlayer == Jester.jester || PlayerControl.LocalPlayer == Jackal.jackal || PlayerControl.LocalPlayer == Arsonist.arsonist || PlayerControl.LocalPlayer == Vulture.vulture) { var soloTeam = new Il2CppSystem.Collections.Generic.List(); @@ -65,59 +65,70 @@ namespace TheOtherRoles.Patches { } // Add the Spy to the Impostor team (for the Impostors) - if (Spy.spy != null && PlayerControl.LocalPlayer.Data.IsImpostor) { + if (Spy.spy != null && PlayerControl.LocalPlayer.Data.Role.IsImpostor) { List players = PlayerControl.AllPlayerControls.ToArray().ToList().OrderBy(x => Guid.NewGuid()).ToList(); var fakeImpostorTeam = new Il2CppSystem.Collections.Generic.List(); foreach (PlayerControl p in players) { - if (p == Spy.spy || p.Data.IsImpostor) + if (p == Spy.spy || p.Data.Role.IsImpostor) fakeImpostorTeam.Add(p); } yourTeam = fakeImpostorTeam; } } - public static void setupIntroRole(IntroCutscene __instance, ref Il2CppSystem.Collections.Generic.List yourTeam) { + public static void setupIntroTeam(IntroCutscene __instance, ref Il2CppSystem.Collections.Generic.List yourTeam) { List infos = RoleInfo.getRoleInfoForPlayer(PlayerControl.LocalPlayer); RoleInfo roleInfo = infos.Where(info => info.roleId != RoleId.Lover).FirstOrDefault(); + if (roleInfo == null) return; + if (roleInfo.isNeutral) { + var neutralColor = new Color32(76, 84, 78, 255); + __instance.BackgroundBar.material.color = neutralColor; + __instance.TeamTitle.text = "Neutral"; + __instance.TeamTitle.color = neutralColor; + } + } + + [HarmonyPatch(typeof(IntroCutscene), nameof(IntroCutscene.SetUpRoleText))] + class SetUpRoleTextPatch { + public static void Postfix(IntroCutscene __instance) { + if (!CustomOptionHolder.activateRoles.getBool()) return; // Don't override the intro of the vanilla roles + + List infos = RoleInfo.getRoleInfoForPlayer(PlayerControl.LocalPlayer); + RoleInfo roleInfo = infos.Where(info => info.roleId != RoleId.Lover).FirstOrDefault(); - if (roleInfo != null) { - __instance.Title.text = roleInfo.name; - __instance.ImpostorText.gameObject.SetActive(true); - __instance.ImpostorText.text = roleInfo.introDescription; - if (roleInfo.roleId != RoleId.Crewmate && roleInfo.roleId != RoleId.Impostor) { - // For native Crewmate or Impostor do not modify the colors - __instance.Title.color = roleInfo.color; - __instance.BackgroundBar.material.color = roleInfo.color; + if (roleInfo != null) { + __instance.RoleText.text = roleInfo.name; + __instance.RoleText.color = roleInfo.color; + __instance.RoleBlurbText.text = roleInfo.introDescription; + __instance.RoleBlurbText.color = roleInfo.color; } - } - if (infos.Any(info => info.roleId == RoleId.Lover)) { - var loversText = UnityEngine.Object.Instantiate(__instance.ImpostorText, __instance.ImpostorText.transform.parent); - loversText.transform.localPosition += Vector3.down * 3f; - PlayerControl otherLover = PlayerControl.LocalPlayer == Lovers.lover1 ? Lovers.lover2 : Lovers.lover1; - loversText.text = Helpers.cs(Lovers.color, $"♥ You are in love with {otherLover?.Data?.PlayerName ?? ""} ♥"); + if (infos.Any(info => info.roleId == RoleId.Lover)) { + PlayerControl otherLover = PlayerControl.LocalPlayer == Lovers.lover1 ? Lovers.lover2 : Lovers.lover1; + __instance.RoleBlurbText.text += Helpers.cs(Lovers.color, $"\n♥ You are in love with {otherLover?.Data?.PlayerName ?? ""} ♥"); + } } } [HarmonyPatch(typeof(IntroCutscene), nameof(IntroCutscene.BeginCrewmate))] class BeginCrewmatePatch { public static void Prefix(IntroCutscene __instance, ref Il2CppSystem.Collections.Generic.List yourTeam) { - setupIntroTeam(__instance, ref yourTeam); + setupIntroTeamIcons(__instance, ref yourTeam); } public static void Postfix(IntroCutscene __instance, ref Il2CppSystem.Collections.Generic.List yourTeam) { - setupIntroRole(__instance, ref yourTeam); + setupIntroTeam(__instance, ref yourTeam); } } [HarmonyPatch(typeof(IntroCutscene), nameof(IntroCutscene.BeginImpostor))] class BeginImpostorPatch { public static void Prefix(IntroCutscene __instance, ref Il2CppSystem.Collections.Generic.List yourTeam) { - setupIntroTeam(__instance, ref yourTeam); + setupIntroTeamIcons(__instance, ref yourTeam); } public static void Postfix(IntroCutscene __instance, ref Il2CppSystem.Collections.Generic.List yourTeam) { - setupIntroRole(__instance, ref yourTeam); + setupIntroTeam(__instance, ref yourTeam); } } } diff --git a/TheOtherRoles/Patches/MeetingPatch.cs b/TheOtherRoles/Patches/MeetingPatch.cs index 764b6a6..5b58012 100644 --- a/TheOtherRoles/Patches/MeetingPatch.cs +++ b/TheOtherRoles/Patches/MeetingPatch.cs @@ -95,7 +95,7 @@ namespace TheOtherRoles.Patches { public static bool Prefix(MeetingHud __instance, [HarmonyArgument(0)]GameData.PlayerInfo voterPlayer, [HarmonyArgument(1)]int index, [HarmonyArgument(2)]Transform parent) { SpriteRenderer spriteRenderer = UnityEngine.Object.Instantiate(__instance.PlayerVotePrefab); if (!PlayerControl.GameOptions.AnonymousVotes || (PlayerControl.LocalPlayer.Data.IsDead && MapOptions.ghostsSeeVotes)) - PlayerControl.SetPlayerMaterialColors(voterPlayer.ColorId, spriteRenderer); + PlayerControl.SetPlayerMaterialColors(voterPlayer.DefaultOutfit.ColorId, spriteRenderer); else PlayerControl.SetPlayerMaterialColors(Palette.DisabledGrey, spriteRenderer); spriteRenderer.transform.SetParent(parent); @@ -226,8 +226,7 @@ namespace TheOtherRoles.Patches { if (guesserUI != null || !(__instance.state == MeetingHud.VoteStates.Voted || __instance.state == MeetingHud.VoteStates.NotVoted)) return; __instance.playerStates.ToList().ForEach(x => x.gameObject.SetActive(false)); - Transform container = UnityEngine.Object.Instantiate(__instance.transform.FindChild("Background"), __instance.transform); - container.FindChild("BlackBG").gameObject.SetActive(false); + Transform container = UnityEngine.Object.Instantiate(__instance.transform.FindChild("PhoneUI"), __instance.transform); container.transform.localPosition = new Vector3(0, 0, -5f); guesserUI = container.gameObject; diff --git a/TheOtherRoles/Patches/PlayerControlPatch.cs b/TheOtherRoles/Patches/PlayerControlPatch.cs index b1fddf9..055fa6d 100644 --- a/TheOtherRoles/Patches/PlayerControlPatch.cs +++ b/TheOtherRoles/Patches/PlayerControlPatch.cs @@ -28,7 +28,7 @@ namespace TheOtherRoles.Patches { for (int i = 0; i < allPlayers.Count; i++) { GameData.PlayerInfo playerInfo = allPlayers[i]; - if (!playerInfo.Disconnected && playerInfo.PlayerId != targetingPlayer.PlayerId && !playerInfo.IsDead && (!onlyCrewmates || !playerInfo.IsImpostor)) + if (!playerInfo.Disconnected && playerInfo.PlayerId != targetingPlayer.PlayerId && !playerInfo.IsDead && (!onlyCrewmates || !playerInfo.Role.IsImpostor)) { PlayerControl @object = playerInfo.Object; if(untargetablePlayers != null && untargetablePlayers.Any(x => x == @object)) { @@ -232,7 +232,9 @@ namespace TheOtherRoles.Patches { } static void engineerUpdate() { - if ((Jackal.canSeeEngineerVent && (PlayerControl.LocalPlayer == Jackal.jackal || PlayerControl.LocalPlayer == Sidekick.sidekick)) || PlayerControl.LocalPlayer.Data.IsImpostor && ShipStatus.Instance?.AllVents != null) { + bool jackalHighlight = Engineer.highlightForTeamJackal && (PlayerControl.LocalPlayer == Jackal.jackal || PlayerControl.LocalPlayer == Sidekick.sidekick); + bool impostorHighlight = Engineer.highlightForImpostors && PlayerControl.LocalPlayer.Data.Role.IsImpostor; + if ((jackalHighlight || impostorHighlight) && ShipStatus.Instance?.AllVents != null) { foreach (Vent vent in ShipStatus.Instance.AllVents) { try { if (vent?.myRend?.material != null) { @@ -249,7 +251,7 @@ namespace TheOtherRoles.Patches { } static void impostorSetTarget() { - if (!PlayerControl.LocalPlayer.Data.IsImpostor ||!PlayerControl.LocalPlayer.CanMove || PlayerControl.LocalPlayer.Data.IsDead) { // !isImpostor || !canMove || isDead + if (!PlayerControl.LocalPlayer.Data.Role.IsImpostor ||!PlayerControl.LocalPlayer.CanMove || PlayerControl.LocalPlayer.Data.IsDead) { // !isImpostor || !canMove || isDead HudManager.Instance.KillButton.SetTarget(null); return; } @@ -435,7 +437,7 @@ namespace TheOtherRoles.Patches { var (playerCompleted, playerTotal) = TasksHandler.taskInfo(Snitch.snitch.Data); int numberOfTasks = playerTotal - playerCompleted; - if (numberOfTasks <= Snitch.taskCountForReveal && (PlayerControl.LocalPlayer.Data.IsImpostor || (Snitch.includeTeamJackal && (PlayerControl.LocalPlayer == Jackal.jackal || PlayerControl.LocalPlayer == Sidekick.sidekick)))) { + if (numberOfTasks <= Snitch.taskCountForReveal && (PlayerControl.LocalPlayer.Data.Role.IsImpostor || (Snitch.includeTeamJackal && (PlayerControl.LocalPlayer == Jackal.jackal || PlayerControl.LocalPlayer == Sidekick.sidekick)))) { if (Snitch.localArrows.Count == 0) Snitch.localArrows.Add(new Arrow(Color.blue)); if (Snitch.localArrows.Count != 0 && Snitch.localArrows[0] != null) { @@ -448,14 +450,16 @@ namespace TheOtherRoles.Patches { int arrowIndex = 0; foreach (PlayerControl p in PlayerControl.AllPlayerControls) { - if (!p.Data.IsDead && (p.Data.IsImpostor || (Snitch.includeTeamJackal && (p == Jackal.jackal || p == Sidekick.sidekick)))) { + bool arrowForImp = p.Data.Role.IsImpostor; + bool arrowForTeamJackal = Snitch.includeTeamJackal && (p == Jackal.jackal || p == Sidekick.sidekick); + + if (!p.Data.IsDead && (arrowForImp || arrowForTeamJackal)) { if (arrowIndex >= Snitch.localArrows.Count) { - if (Snitch.teamJackalUseDifferentArrowColor && (p == Jackal.jackal || p == Sidekick.sidekick)) Snitch.localArrows.Add(new Arrow(Jackal.color)); - else Snitch.localArrows.Add(new Arrow(Palette.ImpostorRed)); + Snitch.localArrows.Add(new Arrow(Palette.ImpostorRed)); } if (arrowIndex < Snitch.localArrows.Count && Snitch.localArrows[arrowIndex] != null) { Snitch.localArrows[arrowIndex].arrow.SetActive(true); - Snitch.localArrows[arrowIndex].Update(p.transform.position); + Snitch.localArrows[arrowIndex].Update(p.transform.position, (arrowForTeamJackal && Snitch.teamJackalUseDifferentArrowColor ? Jackal.color : Palette.ImpostorRed)); } arrowIndex++; } @@ -488,7 +492,7 @@ namespace TheOtherRoles.Patches { BountyHunter.bountyUpdateTimer = BountyHunter.bountyDuration; var possibleTargets = new List(); foreach (PlayerControl p in PlayerControl.AllPlayerControls) { - if (!p.Data.IsDead && !p.Data.Disconnected && p != p.Data.IsImpostor && p != Spy.spy && (p != Mini.mini || Mini.isGrownUp())) possibleTargets.Add(p); + if (!p.Data.IsDead && !p.Data.Disconnected && p != p.Data.Role.IsImpostor && p != Spy.spy && (p != Mini.mini || Mini.isGrownUp())) possibleTargets.Add(p); } BountyHunter.bounty = possibleTargets[TheOtherRoles.rnd.Next(0, possibleTargets.Count)]; if (BountyHunter.bounty == null) return; @@ -614,7 +618,7 @@ namespace TheOtherRoles.Patches { Camouflager.resetCamouflage(); if (Morphling.morphTimer > 0f && Morphling.morphling != null && Morphling.morphTarget != null) { PlayerControl target = Morphling.morphTarget; - Morphling.morphling.setLook(target.Data.PlayerName, target.Data.ColorId, target.Data.HatId, target.Data.SkinId, target.Data.PetId); + Morphling.morphling.setLook(target.Data.PlayerName, target.Data.DefaultOutfit.ColorId, target.Data.DefaultOutfit.HatId, target.Data.DefaultOutfit.VisorId, target.Data.DefaultOutfit.SkinId, target.Data.DefaultOutfit.PetId); } } @@ -710,18 +714,23 @@ namespace TheOtherRoles.Patches { Helpers.handleVampireBiteOnBodyReport(); } } - [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.RpcMurderPlayer))] + [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.CheckMurder))] class RpcMurderPlayer { - public static bool Prefix([HarmonyArgument(0)]PlayerControl target) { - if (Helpers.handleMurderAttempt(target)) { // Custom checks - if (Mini.mini != null && PlayerControl.LocalPlayer == Mini.mini || BountyHunter.bountyHunter != null && PlayerControl.LocalPlayer == BountyHunter.bountyHunter) { // Not checked by official servers - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.UncheckedMurderPlayer, Hazel.SendOption.Reliable, -1); - writer.Write(PlayerControl.LocalPlayer.PlayerId); + public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)]PlayerControl target) { + PlayerControl killer = __instance; + if (AmongUsClient.Instance.IsGameOver || !AmongUsClient.Instance.AmHost) return false; + if (!target || killer.Data.IsDead || killer.Data.Disconnected) return false; // Allow non Impostor kills compared to vanilla code + if (target.Data == null || target.Data.IsDead) return false; // Allow killing players in vents compared to vanilla code + + if (Helpers.handleMurderAttempt(killer, target)) { // Custom checks + if (Mini.mini != null && killer == Mini.mini || BountyHunter.bountyHunter != null && killer == BountyHunter.bountyHunter) { // Not checked by official servers + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(killer.NetId, (byte)CustomRPC.UncheckedMurderPlayer, Hazel.SendOption.Reliable, -1); + writer.Write(killer.PlayerId); writer.Write(target.PlayerId); AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.uncheckedMurderPlayer(PlayerControl.LocalPlayer.PlayerId, target.PlayerId); + RPCProcedure.uncheckedMurderPlayer(killer.PlayerId, target.PlayerId); } else { // Checked by official servers - return true; + killer.RpcMurderPlayer(target); } } return false; @@ -750,7 +759,7 @@ namespace TheOtherRoles.Patches { if (timeSinceDeath < Detective.reportNameDuration * 1000) { msg = $"Body Report: The killer appears to be {deadPlayer.killerIfExisting.name}!"; } else if (timeSinceDeath < Detective.reportColorDuration * 1000) { - var typeOfColor = Helpers.isLighterColor(deadPlayer.killerIfExisting.Data.ColorId) ? "lighter" : "darker"; + var typeOfColor = Helpers.isLighterColor(deadPlayer.killerIfExisting.Data.DefaultOutfit.ColorId) ? "lighter" : "darker"; msg = $"Body Report: The killer appears to be a {typeOfColor} color!"; } else { msg = $"Body Report: The corpse is too old to gain information from!"; @@ -782,9 +791,9 @@ namespace TheOtherRoles.Patches { public static void Prefix(PlayerControl __instance, [HarmonyArgument(0)]PlayerControl target) { // Allow everyone to murder players - resetToCrewmate = !__instance.Data.IsImpostor; + resetToCrewmate = !__instance.Data.Role.IsImpostor; resetToDead = __instance.Data.IsDead; - __instance.Data.IsImpostor = true; + __instance.Data.Role.TeamType = RoleTeamTypes.Impostor; __instance.Data.IsDead = false; } @@ -795,7 +804,7 @@ namespace TheOtherRoles.Patches { GameHistory.deadPlayers.Add(deadPlayer); // Reset killer to crewmate if resetToCrewmate - if (resetToCrewmate) __instance.Data.IsImpostor = false; + if (resetToCrewmate) __instance.Data.Role.TeamType = RoleTeamTypes.Crewmate; if (resetToDead) __instance.Data.IsDead = true; // Remove fake tasks when player dies @@ -851,7 +860,7 @@ namespace TheOtherRoles.Patches { } // Mini set adapted kill cooldown - if (Mini.mini != null && PlayerControl.LocalPlayer == Mini.mini && Mini.mini.Data.IsImpostor && Mini.mini == __instance) { + if (Mini.mini != null && PlayerControl.LocalPlayer == Mini.mini && Mini.mini.Data.Role.IsImpostor && Mini.mini == __instance) { var multiplier = Mini.isGrownUp() ? 0.66f : 2f; Mini.mini.SetKillTimer(PlayerControl.GameOptions.KillCooldown * multiplier); } @@ -874,7 +883,7 @@ namespace TheOtherRoles.Patches { if (PlayerControl.GameOptions.KillCooldown <= 0f) return false; float multiplier = 1f; float addition = 0f; - if (Mini.mini != null && PlayerControl.LocalPlayer == Mini.mini && Mini.mini.Data.IsImpostor) multiplier = Mini.isGrownUp() ? 0.66f : 2f; + if (Mini.mini != null && PlayerControl.LocalPlayer == Mini.mini && Mini.mini.Data.Role.IsImpostor) multiplier = Mini.isGrownUp() ? 0.66f : 2f; if (BountyHunter.bountyHunter != null && PlayerControl.LocalPlayer == BountyHunter.bountyHunter) addition = BountyHunter.punishmentTime; __instance.killTimer = Mathf.Clamp(time, 0f, PlayerControl.GameOptions.KillCooldown * multiplier + addition); @@ -924,20 +933,4 @@ namespace TheOtherRoles.Patches { } } } - - [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.CanMove), MethodType.Getter)] - class PlayerControlCanMovePatch { - public static bool Prefix(PlayerControl __instance, ref bool __result) - { - __result = __instance.moveable && - !Minigame.Instance && - (!DestroyableSingleton.InstanceExists || (!DestroyableSingleton.Instance.Chat.IsOpen && !DestroyableSingleton.Instance.KillOverlay.IsOpen && !DestroyableSingleton.Instance.GameMenu.IsOpen)) && - (!MapBehaviour.Instance || !MapBehaviour.Instance.IsOpenStopped) && - !MeetingHud.Instance && - !CustomPlayerMenu.Instance && - !ExileController.Instance && - !IntroCutscene.Instance; - return false; - } - } } diff --git a/TheOtherRoles/Patches/RegionMenuPatch.cs b/TheOtherRoles/Patches/RegionMenuPatch.cs index 5c5e956..caef306 100644 --- a/TheOtherRoles/Patches/RegionMenuPatch.cs +++ b/TheOtherRoles/Patches/RegionMenuPatch.cs @@ -36,14 +36,16 @@ namespace TheOtherRoles.Patches { private static TextBoxTMP ipField; private static TextBoxTMP portField; - public static void Postfix(RegionMenu __instance) - { + public static void Postfix(RegionMenu __instance) { var template = DestroyableSingleton.Instance; + if (template == null || template.GameIdText == null) return; if (ipField == null || ipField.gameObject == null) { ipField = UnityEngine.Object.Instantiate(template.GameIdText, __instance.transform); ipField.gameObject.name = "IpTextBox"; - UnityEngine.Object.DestroyImmediate(ipField.transform.FindChild("arrowEnter").gameObject); + var arrow = ipField.transform.FindChild("arrowEnter"); + if (arrow == null || arrow.gameObject == null) return; + UnityEngine.Object.DestroyImmediate(arrow.gameObject); ipField.transform.localPosition = new Vector3(0, -1f, -100f); ipField.characterLimit = 30; @@ -70,10 +72,13 @@ namespace TheOtherRoles.Patches { __instance.ChooseOption(ServerManager.DefaultRegions[ServerManager.DefaultRegions.Length - 1]); } } + if (portField == null || portField.gameObject == null) { portField = UnityEngine.Object.Instantiate(template.GameIdText, __instance.transform); portField.gameObject.name = "PortTextBox"; - UnityEngine.Object.DestroyImmediate(portField.transform.FindChild("arrowEnter").gameObject); + var arrow = portField.transform.FindChild("arrowEnter"); + if (arrow == null || arrow.gameObject == null) return; + UnityEngine.Object.DestroyImmediate(arrow.gameObject); portField.transform.localPosition = new Vector3(0, -1.75f, -100f); portField.characterLimit = 5; diff --git a/TheOtherRoles/Patches/RoleAssignmentPatch.cs b/TheOtherRoles/Patches/RoleAssignmentPatch.cs index 6be6579..95db273 100644 --- a/TheOtherRoles/Patches/RoleAssignmentPatch.cs +++ b/TheOtherRoles/Patches/RoleAssignmentPatch.cs @@ -8,17 +8,21 @@ using System; using static TheOtherRoles.TheOtherRoles; namespace TheOtherRoles.Patches { - [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.RpcSetInfected))] - class SetInfectedPatch - { + [HarmonyPatch(typeof(RoleOptionsData), nameof(RoleOptionsData.GetNumPerGame))] + class RoleOptionsDataGetNumPerGamePatch{ + public static void Postfix(ref int __result) { + if (CustomOptionHolder.activateRoles.getBool()) __result = 0; // Deactivate Vanilla Roles if the mod roles are active + } + } - public static void Postfix([HarmonyArgument(0)]Il2CppReferenceArray infected) - { + [HarmonyPatch(typeof(RoleManager), nameof(RoleManager.SelectRoles))] + class RoleManagerSelectRolesPatch { + public static void Postfix() { MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ResetVaribles, Hazel.SendOption.Reliable, -1); AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.resetVariables(); - if (!DestroyableSingleton.InstanceExists) // Don't assign Roles in Tutorial + if (!DestroyableSingleton.InstanceExists && CustomOptionHolder.activateRoles.getBool()) // Don't assign Roles in Tutorial or if deactivated assignRoles(); } @@ -33,9 +37,9 @@ namespace TheOtherRoles.Patches { private static RoleAssignmentData getRoleAssignmentData() { // Get the players that we want to assign the roles to. Crewmate and Neutral roles are assigned to natural crewmates. Impostor roles to impostors. List crewmates = PlayerControl.AllPlayerControls.ToArray().ToList().OrderBy(x => Guid.NewGuid()).ToList(); - crewmates.RemoveAll(x => x.Data.IsImpostor); + crewmates.RemoveAll(x => x.Data.Role.IsImpostor); List impostors = PlayerControl.AllPlayerControls.ToArray().ToList().OrderBy(x => Guid.NewGuid()).ToList(); - impostors.RemoveAll(x => !x.Data.IsImpostor); + impostors.RemoveAll(x => !x.Data.Role.IsImpostor); var crewmateMin = CustomOptionHolder.crewmateRolesCountMin.getSelection(); var crewmateMax = CustomOptionHolder.crewmateRolesCountMax.getSelection(); diff --git a/TheOtherRoles/Patches/ShipStatusPatch.cs b/TheOtherRoles/Patches/ShipStatusPatch.cs index 446db39..64f2b12 100644 --- a/TheOtherRoles/Patches/ShipStatusPatch.cs +++ b/TheOtherRoles/Patches/ShipStatusPatch.cs @@ -19,7 +19,7 @@ namespace TheOtherRoles.Patches { if (player == null || player.IsDead) // IsDead __result = __instance.MaxLightRadius; - else if (player.IsImpostor + else if (player.Role.IsImpostor || (Jackal.jackal != null && Jackal.jackal.PlayerId == player.PlayerId && Jackal.hasImpostorVision) || (Sidekick.sidekick != null && Sidekick.sidekick.PlayerId == player.PlayerId && Sidekick.hasImpostorVision) || (Spy.spy != null && Spy.spy.PlayerId == player.PlayerId && Spy.hasImpostorVision)) // Impostor, Jackal/Sidekick or Spy with Impostor vision diff --git a/TheOtherRoles/Patches/UpdatePatch.cs b/TheOtherRoles/Patches/UpdatePatch.cs index 0160484..62d5085 100644 --- a/TheOtherRoles/Patches/UpdatePatch.cs +++ b/TheOtherRoles/Patches/UpdatePatch.cs @@ -20,7 +20,7 @@ namespace TheOtherRoles.Patches { if (Morphling.morphTimer > 0f && Morphling.morphling == player && Morphling.morphTarget != null) playerName = Morphling.morphTarget.Data.PlayerName; // Temporary hotfix for the Morphling's name player.nameText.text = Helpers.hidePlayerName(PlayerControl.LocalPlayer, player) ? "" : playerName; - if (PlayerControl.LocalPlayer.Data.IsImpostor && player.Data.IsImpostor) { + if (PlayerControl.LocalPlayer.Data.Role.IsImpostor && player.Data.Role.IsImpostor) { player.nameText.color = Palette.ImpostorRed; } else { player.nameText.color = Color.white; @@ -31,7 +31,7 @@ namespace TheOtherRoles.Patches { PlayerControl playerControl = playersById.ContainsKey((byte)player.TargetPlayerId) ? playersById[(byte)player.TargetPlayerId] : null; if (playerControl != null) { player.NameText.text = playerControl.Data.PlayerName; - if (PlayerControl.LocalPlayer.Data.IsImpostor && playerControl.Data.IsImpostor) { + if (PlayerControl.LocalPlayer.Data.Role.IsImpostor && playerControl.Data.Role.IsImpostor) { player.NameText.color = Palette.ImpostorRed; } else { player.NameText.color = Color.white; @@ -39,15 +39,15 @@ namespace TheOtherRoles.Patches { } } } - if (PlayerControl.LocalPlayer.Data.IsImpostor) { + if (PlayerControl.LocalPlayer.Data.Role.IsImpostor) { List impostors = PlayerControl.AllPlayerControls.ToArray().ToList(); - impostors.RemoveAll(x => !x.Data.IsImpostor); + impostors.RemoveAll(x => !x.Data.Role.IsImpostor); foreach (PlayerControl player in impostors) player.nameText.color = Palette.ImpostorRed; if (MeetingHud.Instance != null) foreach (PlayerVoteArea player in MeetingHud.Instance.playerStates) { PlayerControl playerControl = Helpers.playerById((byte)player.TargetPlayerId); - if (playerControl != null && playerControl.Data.IsImpostor) + if (playerControl != null && playerControl.Data.Role.IsImpostor) player.NameText.color = Palette.ImpostorRed; } } @@ -108,7 +108,7 @@ namespace TheOtherRoles.Patches { } else if (Arsonist.arsonist != null && Arsonist.arsonist == PlayerControl.LocalPlayer) { setPlayerNameColor(Arsonist.arsonist, Arsonist.color); } else if (Guesser.guesser != null && Guesser.guesser == PlayerControl.LocalPlayer) { - setPlayerNameColor(Guesser.guesser, Guesser.guesser.Data.IsImpostor ? Palette.ImpostorRed : Guesser.color); + setPlayerNameColor(Guesser.guesser, Guesser.guesser.Data.Role.IsImpostor ? Palette.ImpostorRed : Guesser.color); } else if (Bait.bait != null && Bait.bait == PlayerControl.LocalPlayer) { setPlayerNameColor(Bait.bait, Bait.color); } else if (Vulture.vulture != null && Vulture.vulture == PlayerControl.LocalPlayer) { @@ -127,7 +127,7 @@ namespace TheOtherRoles.Patches { } // No else if here, as the Impostors need the Spy name to be colored - if (Spy.spy != null && PlayerControl.LocalPlayer.Data.IsImpostor) { + if (Spy.spy != null && PlayerControl.LocalPlayer.Data.Role.IsImpostor) { setPlayerNameColor(Spy.spy, Spy.color); } @@ -137,7 +137,7 @@ namespace TheOtherRoles.Patches { static void setNameTags() { // Mafia - if (PlayerControl.LocalPlayer != null && PlayerControl.LocalPlayer.Data.IsImpostor) { + if (PlayerControl.LocalPlayer != null && PlayerControl.LocalPlayer.Data.Role.IsImpostor) { foreach (PlayerControl player in PlayerControl.AllPlayerControls) if (Godfather.godfather != null && Godfather.godfather == player) player.nameText.text = player.Data.PlayerName + " (G)"; @@ -203,7 +203,7 @@ namespace TheOtherRoles.Patches { } static void updateImpostorKillButton(HudManager __instance) { - if (!PlayerControl.LocalPlayer.Data.IsImpostor) return; + if (!PlayerControl.LocalPlayer.Data.Role.IsImpostor) return; bool enabled = true; if (Vampire.vampire != null && Vampire.vampire == PlayerControl.LocalPlayer) enabled = false; @@ -211,12 +211,9 @@ namespace TheOtherRoles.Patches { enabled = false; else if (Janitor.janitor != null && Janitor.janitor == PlayerControl.LocalPlayer) enabled = false; - enabled &= __instance.UseButton.isActiveAndEnabled; - __instance.KillButton.gameObject.SetActive(enabled); - __instance.KillButton.renderer.enabled = enabled; - __instance.KillButton.isActive = enabled; - __instance.KillButton.enabled = enabled; + if (enabled) __instance.KillButton.Show(); + else __instance.KillButton.Hide(); } static void Postfix(HudManager __instance) diff --git a/TheOtherRoles/Patches/UsablesPatch.cs b/TheOtherRoles/Patches/UsablesPatch.cs index 083ef96..f269301 100644 --- a/TheOtherRoles/Patches/UsablesPatch.cs +++ b/TheOtherRoles/Patches/UsablesPatch.cs @@ -11,7 +11,7 @@ using System.Collections.Generic; namespace TheOtherRoles.Patches { - [HarmonyPatch(typeof(Vent), "CanUse")] + [HarmonyPatch(typeof(Vent), nameof(Vent.CanUse))] public static class VentCanUsePatch { public static bool Prefix(Vent __instance, ref float __result, [HarmonyArgument(0)] GameData.PlayerInfo pc, [HarmonyArgument(1)] out bool canUse, [HarmonyArgument(2)] out bool couldUse) @@ -19,26 +19,7 @@ namespace TheOtherRoles.Patches { float num = float.MaxValue; PlayerControl @object = pc.Object; - - bool roleCouldUse = false; - if (Engineer.engineer != null && Engineer.engineer == @object) - roleCouldUse = true; - else if (Jackal.canUseVents && Jackal.jackal != null && Jackal.jackal == @object) - roleCouldUse = true; - else if (Sidekick.canUseVents && Sidekick.sidekick != null && Sidekick.sidekick == @object) - roleCouldUse = true; - else if (Spy.canEnterVents && Spy.spy != null && Spy.spy == @object) - roleCouldUse = true; - else if (Vulture.canUseVents && Vulture.vulture != null && Vulture.vulture == @object) - roleCouldUse = true; - else if (pc.IsImpostor) { - if (Janitor.janitor != null && Janitor.janitor == PlayerControl.LocalPlayer) - roleCouldUse = false; - else if (Mafioso.mafioso != null && Mafioso.mafioso == PlayerControl.LocalPlayer && Godfather.godfather != null && !Godfather.godfather.Data.IsDead) - roleCouldUse = false; - else - roleCouldUse = true; - } + bool roleCouldUse = @object.roleCanUseVents(); var usableDistance = __instance.UsableDistance; if (__instance.name.StartsWith("JackInTheBoxVent_")) { @@ -73,7 +54,7 @@ namespace TheOtherRoles.Patches { } } - [HarmonyPatch(typeof(Vent), "Use")] + [HarmonyPatch(typeof(Vent), nameof(Vent.Use))] public static class VentUsePatch { public static bool Prefix(Vent __instance) { bool canUse; @@ -107,66 +88,37 @@ namespace TheOtherRoles.Patches { } } - [HarmonyPatch(typeof(UseButtonManager), nameof(UseButtonManager.SetTarget))] - class UseButtonSetTargetPatch { - static void Postfix(UseButtonManager __instance) { - // Trickster render special vent button - if (__instance.currentTarget != null && Trickster.trickster != null && Trickster.trickster == PlayerControl.LocalPlayer) { - Vent possibleVent = __instance.currentTarget.TryCast(); - if (possibleVent != null && possibleVent.gameObject != null) { - var useButton = __instance.currentButtonShown; - if (possibleVent.gameObject.name.StartsWith("JackInTheBoxVent_")) { - useButton.graphic.sprite = Trickster.getTricksterVentButtonSprite(); - useButton.text.enabled = false; // clear text; - } else { - useButton.graphic.sprite = DestroyableSingleton.Instance.GetImage(ImageNames.VentButton); - useButton.text.enabled = false; - } - } - } - - // Jester sabotage - if (Jester.canSabotage && Jester.jester != null && Jester.jester == PlayerControl.LocalPlayer && PlayerControl.LocalPlayer.CanMove) { - var useButton = __instance.currentButtonShown; - if (!Jester.jester.Data.IsDead && __instance.currentTarget == null) { // no target, so sabotage - useButton.graphic.sprite = DestroyableSingleton.Instance.GetImage(ImageNames.SabotageButton); - useButton.graphic.color = UseButtonManager.EnabledColor; - useButton.text.enabled = false; - } else { - useButton.graphic.sprite = DestroyableSingleton.Instance.GetImage(ImageNames.UseButton); - useButton.text.enabled = false; - } + [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))] + class VentButtonVisibilityPatch { + static void Postfix(PlayerControl __instance) { + if (__instance.AmOwner && __instance.roleCanUseVents() && HudManager.Instance.ReportButton.isActiveAndEnabled) { + HudManager.Instance.ImpostorVentButton.Show(); } + } + } - // Mafia sabotage button render patch - bool blockSabotageJanitor = (Janitor.janitor != null && Janitor.janitor == PlayerControl.LocalPlayer); - bool blockSabotageMafioso = (Mafioso.mafioso != null && Mafioso.mafioso == PlayerControl.LocalPlayer && Godfather.godfather != null && !Godfather.godfather.Data.IsDead); - if (__instance.currentTarget == null && (blockSabotageJanitor || blockSabotageMafioso)) { - var useButton = __instance.currentButtonShown; - useButton.graphic.sprite = DestroyableSingleton.Instance.GetImage(ImageNames.UseButton); - useButton.graphic.color = UseButtonManager.DisabledColor; - useButton.text.enabled = false; + [HarmonyPatch(typeof(VentButton), nameof(VentButton.SetTarget))] + class VentButtonSetTargetPatch { + static void Postfix(VentButton __instance) { + // Trickster render special vent button + if (Trickster.trickster != null && Trickster.trickster == PlayerControl.LocalPlayer) { + if (__instance.currentTarget != null && __instance.currentTarget.gameObject != null && __instance.currentTarget.gameObject.name.StartsWith("JackInTheBoxVent_")) + __instance.graphic.sprite = Trickster.getTricksterVentButtonSprite(); + else + __instance.graphic.sprite = DestroyableSingleton.Instance.GetImage(ImageNames.VentButton); } - } } - - [HarmonyPatch(typeof(UseButtonManager), nameof(UseButtonManager.DoClick))] - class UseButtonDoClickPatch { - static bool Prefix(UseButtonManager __instance) { - if (__instance.currentTarget != null) return true; - // Jester sabotage - if (Jester.canSabotage && Jester.jester != null && Jester.jester == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead) { - Action action = m => m.ShowInfectedMap() ; - DestroyableSingleton.Instance.ShowMap(action); - return false; - } - // Mafia sabotage button click patch + + [HarmonyPatch(typeof(SabotageButton), nameof(SabotageButton.Refresh))] + class SabotageButtonRefreshPatch { + static void Postfix() { + // Mafia disable sabotage button for Janitor and sometimes for Mafioso bool blockSabotageJanitor = (Janitor.janitor != null && Janitor.janitor == PlayerControl.LocalPlayer); bool blockSabotageMafioso = (Mafioso.mafioso != null && Mafioso.mafioso == PlayerControl.LocalPlayer && Godfather.godfather != null && !Godfather.godfather.Data.IsDead); - if (blockSabotageJanitor || blockSabotageMafioso) return false; - - return true; + if (blockSabotageJanitor || blockSabotageMafioso) { + HudManager.Instance.SabotageButton.SetDisabled(); + } } } @@ -366,9 +318,9 @@ namespace TheOtherRoles.Patches { if (component) { GameData.PlayerInfo playerInfo = GameData.Instance.GetPlayerById(component.ParentId); if (playerInfo != null) { - var color = Palette.PlayerColors[playerInfo.ColorId]; + var color = Palette.PlayerColors[playerInfo.DefaultOutfit.ColorId]; if (Hacker.onlyColorType) - color = Helpers.isLighterColor(playerInfo.ColorId) ? Palette.PlayerColors[7] : Palette.PlayerColors[6]; + color = Helpers.isLighterColor(playerInfo.DefaultOutfit.ColorId) ? Palette.PlayerColors[7] : Palette.PlayerColors[6]; roomColors.Add(color); } } diff --git a/TheOtherRoles/RPC.cs b/TheOtherRoles/RPC.cs index 3469019..602a823 100644 --- a/TheOtherRoles/RPC.cs +++ b/TheOtherRoles/RPC.cs @@ -124,7 +124,7 @@ namespace TheOtherRoles public static void forceEnd() { foreach (PlayerControl player in PlayerControl.AllPlayerControls) { - if (!player.Data.IsImpostor) + if (!player.Data.Role.IsImpostor) { player.RemoveInfected(); player.MurderPlayer(player); @@ -296,7 +296,7 @@ namespace TheOtherRoles } public static void engineerUsedRepair() { - Engineer.usedRepair = true; + Engineer.remainingFixes--; } public static void cleanBody(byte playerId) { @@ -381,7 +381,7 @@ namespace TheOtherRoles Shifter.clearAndReload(); // Suicide (exile) when impostor or impostor variants - if (player.Data.IsImpostor || player == Jackal.jackal || player == Sidekick.sidekick || Jackal.formerJackals.Contains(player) || player == Jester.jester || player == Arsonist.arsonist || player == Vulture.vulture) { + if (player.Data.Role.IsImpostor || player == Jackal.jackal || player == Sidekick.sidekick || Jackal.formerJackals.Contains(player) || player == Jester.jester || player == Arsonist.arsonist || player == Vulture.vulture) { oldShifter.Exiled(); return; } @@ -461,7 +461,7 @@ namespace TheOtherRoles Morphling.morphTimer = Morphling.duration; Morphling.morphTarget = target; if (Camouflager.camouflageTimer <= 0f) - Morphling.morphling.setLook(target.Data.PlayerName, target.Data.ColorId, target.Data.HatId, target.Data.SkinId, target.Data.PetId); + Morphling.morphling.setLook(target.Data.PlayerName, target.Data.DefaultOutfit.ColorId, target.Data.DefaultOutfit.HatId, target.Data.DefaultOutfit.VisorId, target.Data.DefaultOutfit.SkinId, target.Data.DefaultOutfit.PetId); } public static void camouflagerCamouflage() { @@ -469,7 +469,7 @@ namespace TheOtherRoles Camouflager.camouflageTimer = Camouflager.duration; foreach (PlayerControl player in PlayerControl.AllPlayerControls) - player.setLook("", 6, 0, 0, 0); + player.setLook("", 6, "", "", "", ""); } public static void vampireSetBitten(byte targetId, byte reset) { @@ -534,7 +534,7 @@ namespace TheOtherRoles { if (player.PlayerId == targetId) { - if (!Jackal.canCreateSidekickFromImpostor && player.Data.IsImpostor) { + if (!Jackal.canCreateSidekickFromImpostor && player.Data.Role.IsImpostor) { Jackal.fakeSidekick = player; } else { player.RemoveInfected(); @@ -638,7 +638,7 @@ namespace TheOtherRoles public static void lightsOut() { Trickster.lightsOutTimer = Trickster.lightsOutDuration; // If the local player is impostor indicate lights out - if(PlayerControl.LocalPlayer.Data.IsImpostor) { + if(PlayerControl.LocalPlayer.Data.Role.IsImpostor) { new CustomMessage("Lights are out", Trickster.lightsOutDuration); } } diff --git a/TheOtherRoles/Resources/TabIcon.png b/TheOtherRoles/Resources/TabIcon.png new file mode 100644 index 0000000..1ac410b Binary files /dev/null and b/TheOtherRoles/Resources/TabIcon.png differ diff --git a/TheOtherRoles/RoleInfo.cs b/TheOtherRoles/RoleInfo.cs index b2d8679..6f99102 100644 --- a/TheOtherRoles/RoleInfo.cs +++ b/TheOtherRoles/RoleInfo.cs @@ -13,16 +13,18 @@ namespace TheOtherRoles public string introDescription; public string shortDescription; public RoleId roleId; + public bool isNeutral; - RoleInfo(string name, Color color, string introDescription, string shortDescription, RoleId roleId) { + RoleInfo(string name, Color color, string introDescription, string shortDescription, RoleId roleId, bool isNeutral = false) { this.color = color; this.name = name; this.introDescription = introDescription; this.shortDescription = shortDescription; this.roleId = roleId; + this.isNeutral = isNeutral; } - public static RoleInfo jester = new RoleInfo("Jester", Jester.color, "Get voted out", "Get voted out", RoleId.Jester); + public static RoleInfo jester = new RoleInfo("Jester", Jester.color, "Get voted out", "Get voted out", RoleId.Jester, true); public static RoleInfo mayor = new RoleInfo("Mayor", Mayor.color, "Your vote counts twice", "Your vote counts twice", RoleId.Mayor); public static RoleInfo engineer = new RoleInfo("Engineer", Engineer.color, "Maintain important systems on the ship", "Repair the ship", RoleId.Engineer); public static RoleInfo sheriff = new RoleInfo("Sheriff", Sheriff.color, "Shoot the Impostors", "Shoot the Impostors", RoleId.Sheriff); @@ -49,15 +51,15 @@ namespace TheOtherRoles public static RoleInfo evilMini = new RoleInfo("Evil Mini", Palette.ImpostorRed, "No one will harm you until you grow up", "No one will harm you", RoleId.Mini); public static RoleInfo tracker = new RoleInfo("Tracker", Tracker.color, "Track the Impostors down", "Track the Impostors down", RoleId.Tracker); public static RoleInfo snitch = new RoleInfo("Snitch", Snitch.color, "Finish your tasks to find the Impostors", "Finish your tasks", RoleId.Snitch); - public static RoleInfo jackal = new RoleInfo("Jackal", Jackal.color, "Kill all Crewmates and Impostors to win", "Kill everyone", RoleId.Jackal); - public static RoleInfo sidekick = new RoleInfo("Sidekick", Sidekick.color, "Help your Jackal to kill everyone", "Help your Jackal to kill everyone", RoleId.Sidekick); + public static RoleInfo jackal = new RoleInfo("Jackal", Jackal.color, "Kill all Crewmates and Impostors to win", "Kill everyone", RoleId.Jackal, true); + public static RoleInfo sidekick = new RoleInfo("Sidekick", Sidekick.color, "Help your Jackal to kill everyone", "Help your Jackal to kill everyone", RoleId.Sidekick, true); public static RoleInfo spy = new RoleInfo("Spy", Spy.color, "Confuse the Impostors", "Confuse the Impostors", RoleId.Spy); public static RoleInfo securityGuard = new RoleInfo("Security Guard", SecurityGuard.color, "Seal vents and place cameras", "Seal vents and place cameras", RoleId.SecurityGuard); - public static RoleInfo arsonist = new RoleInfo("Arsonist", Arsonist.color, "Let them burn", "Let them burn", RoleId.Arsonist); + public static RoleInfo arsonist = new RoleInfo("Arsonist", Arsonist.color, "Let them burn", "Let them burn", RoleId.Arsonist, true); public static RoleInfo goodGuesser = new RoleInfo("Nice Guesser", Guesser.color, "Guess and shoot", "Guess and shoot", RoleId.Guesser); public static RoleInfo badGuesser = new RoleInfo("Evil Guesser", Palette.ImpostorRed, "Guess and shoot", "Guess and shoot", RoleId.Guesser); public static RoleInfo bait = new RoleInfo("Bait", Bait.color, "Bait your enemies", "Bait your enemies", RoleId.Bait); - public static RoleInfo vulture = new RoleInfo("Vulture", Vulture.color, "Eat Corpses to win", "Eat dead bodies", RoleId.Vulture); + public static RoleInfo vulture = new RoleInfo("Vulture", Vulture.color, "Eat Corpses to win", "Eat dead bodies", RoleId.Vulture, true); public static RoleInfo medium = new RoleInfo("Medium", Medium.color, "Question the souls of the dead to gain informations", "Question the souls", RoleId.Medium); public static RoleInfo impostor = new RoleInfo("Impostor", Palette.ImpostorRed, Helpers.cs(Palette.ImpostorRed, "Sabotage and kill everyone"), "Sabotage and kill everyone", RoleId.Impostor); public static RoleInfo crewmate = new RoleInfo("Crewmate", Color.white, "Find the Impostors", "Find the Impostors", RoleId.Crewmate); @@ -133,7 +135,7 @@ namespace TheOtherRoles if (p == Swapper.swapper) infos.Add(swapper); if (p == Seer.seer) infos.Add(seer); if (p == Hacker.hacker) infos.Add(hacker); - if (p == Mini.mini) infos.Add(p.Data.IsImpostor ? evilMini : niceMini); + if (p == Mini.mini) infos.Add(p.Data.Role.IsImpostor ? evilMini : niceMini); if (p == Tracker.tracker) infos.Add(tracker); if (p == Snitch.snitch) infos.Add(snitch); if (p == Jackal.jackal || (Jackal.formerJackals != null && Jackal.formerJackals.Any(x => x.PlayerId == p.PlayerId))) infos.Add(jackal); @@ -141,15 +143,15 @@ namespace TheOtherRoles if (p == Spy.spy) infos.Add(spy); if (p == SecurityGuard.securityGuard) infos.Add(securityGuard); if (p == Arsonist.arsonist) infos.Add(arsonist); - if (p == Guesser.guesser) infos.Add(p.Data.IsImpostor ? badGuesser : goodGuesser); + if (p == Guesser.guesser) infos.Add(p.Data.Role.IsImpostor ? badGuesser : goodGuesser); if (p == BountyHunter.bountyHunter) infos.Add(bountyHunter); if (p == Bait.bait) infos.Add(bait); if (p == Vulture.vulture) infos.Add(vulture); if (p == Medium.medium) infos.Add(medium); // Default roles - if (infos.Count == 0 && p.Data.IsImpostor) infos.Add(impostor); // Just Impostor - if (infos.Count == 0 && !p.Data.IsImpostor) infos.Add(crewmate); // Just Crewmate + if (infos.Count == 0 && p.Data.Role.IsImpostor) infos.Add(impostor); // Just Impostor + if (infos.Count == 0 && !p.Data.Role.IsImpostor) infos.Add(crewmate); // Just Crewmate // Modifier if (p == Lovers.lover1|| p == Lovers.lover2) infos.Add(lover); diff --git a/TheOtherRoles/TasksHandler.cs b/TheOtherRoles/TasksHandler.cs index fc00fe9..8b5aa12 100644 --- a/TheOtherRoles/TasksHandler.cs +++ b/TheOtherRoles/TasksHandler.cs @@ -14,7 +14,7 @@ namespace TheOtherRoles { if (!playerInfo.Disconnected && playerInfo.Tasks != null && playerInfo.Object && (PlayerControl.GameOptions.GhostsDoTasks || !playerInfo.IsDead) && - !playerInfo.IsImpostor && + playerInfo.Role && playerInfo.Role.TasksCountTowardProgress && !playerInfo.Object.hasFakeTasks() ) { diff --git a/TheOtherRoles/TheOtherRoles.cs b/TheOtherRoles/TheOtherRoles.cs index 2313fdb..1fdbc26 100644 --- a/TheOtherRoles/TheOtherRoles.cs +++ b/TheOtherRoles/TheOtherRoles.cs @@ -64,13 +64,11 @@ namespace TheOtherRoles public static bool triggerJesterWin = false; public static bool canCallEmergency = true; - public static bool canSabotage = true; public static void clearAndReload() { jester = null; triggerJesterWin = false; canCallEmergency = CustomOptionHolder.jesterCanCallEmergency.getBool(); - canSabotage = CustomOptionHolder.jesterCanSabotage.getBool(); } } @@ -86,9 +84,12 @@ namespace TheOtherRoles public static class Engineer { public static PlayerControl engineer; public static Color color = new Color32(0, 40, 245, byte.MaxValue); - public static bool usedRepair; private static Sprite buttonSprite; + public static int remainingFixes = 1; + public static bool highlightForImpostors = true; + public static bool highlightForTeamJackal = true; + public static Sprite getButtonSprite() { if (buttonSprite) return buttonSprite; buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.RepairButton.png", 115f); @@ -97,7 +98,9 @@ namespace TheOtherRoles public static void clearAndReload() { engineer = null; - usedRepair = false; + remainingFixes = Mathf.RoundToInt(CustomOptionHolder.engineerNumberOfFixes.getFloat()); + highlightForImpostors = CustomOptionHolder.engineerHighlightForImpostors.getBool(); + highlightForTeamJackal = CustomOptionHolder.engineerHighlightForTeamJackal.getBool(); } } @@ -341,7 +344,7 @@ namespace TheOtherRoles public static bool existingWithKiller() { return existing() && (lover1 == Jackal.jackal || lover2 == Jackal.jackal || lover1 == Sidekick.sidekick || lover2 == Sidekick.sidekick - || lover1.Data.IsImpostor || lover2.Data.IsImpostor); + || lover1.Data.Role.IsImpostor || lover2.Data.Role.IsImpostor); } public static bool hasAliveKillingLover(this PlayerControl player) { @@ -639,7 +642,6 @@ namespace TheOtherRoles public static bool jackalPromotedFromSidekickCanCreateSidekick = true; public static bool canCreateSidekickFromImpostor = true; public static bool hasImpostorVision = false; - public static bool canSeeEngineerVent = false; public static Sprite getSidekickButtonSprite() { if (buttonSprite) return buttonSprite; @@ -668,7 +670,6 @@ namespace TheOtherRoles canCreateSidekickFromImpostor = CustomOptionHolder.jackalCanCreateSidekickFromImpostor.getBool(); formerJackals.Clear(); hasImpostorVision = CustomOptionHolder.jackalAndSidekickHaveImpostorVision.getBool(); - canSeeEngineerVent = CustomOptionHolder.jackalCanSeeEngineerVent.getBool(); } } @@ -838,7 +839,7 @@ namespace TheOtherRoles public static void resetCurse() { HudManagerStartPatch.warlockCurseButton.Timer = HudManagerStartPatch.warlockCurseButton.MaxTimer; HudManagerStartPatch.warlockCurseButton.Sprite = Warlock.getCurseButtonSprite(); - HudManagerStartPatch.warlockCurseButton.killButtonManager.TimerText.color = Palette.EnabledColor; + HudManagerStartPatch.warlockCurseButton.actionButton.cooldownTimerText.color = Palette.EnabledColor; currentTarget = null; curseVictim = null; curseVictimTarget = null; diff --git a/TheOtherRoles/TheOtherRoles.csproj b/TheOtherRoles/TheOtherRoles.csproj index c59decf..49971c9 100644 --- a/TheOtherRoles/TheOtherRoles.csproj +++ b/TheOtherRoles/TheOtherRoles.csproj @@ -1,7 +1,7 @@ netstandard2.1 - 2.9.2 + 3.0.0 TheOtherRoles Eisbison