diff --git a/README.md b/README.md index 95a588190..e1876e141 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 f51b10faa..a3e5f91af 100644 --- a/TheOtherRoles/Buttons.cs +++ b/TheOtherRoles/Buttons.cs @@ -81,7 +81,7 @@ public static void setCustomButtonCooldowns() { 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 @@ public static void Postfix(HudManager __instance) } } }, - () => { 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 @@ public static void Postfix(HudManager __instance) } }, () => { 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 @@ public static void Postfix(HudManager __instance) } 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) 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 @@ public static void Postfix(HudManager __instance) }, () => { 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 @@ public static void Postfix(HudManager __instance) () => { return PlayerControl.LocalPlayer.CanMove && !Vampire.localPlacedGarlic; }, () => { }, Vampire.getGarlicButtonSprite(), - Vector3.zero, + new Vector3(0, -0.06f, 0), __instance, null, true @@ -474,7 +478,7 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) // 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) // 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) } }, () => { 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 @@ public static void Postfix(HudManager __instance) 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 @@ public static void Postfix(HudManager __instance) 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 @@ public static void Postfix(HudManager __instance) }, () => { 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 @@ public static void Postfix(HudManager __instance) }, () => { 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 @@ public static void Postfix(HudManager __instance) () => { 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 @@ public static void Postfix(HudManager __instance) 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 @@ public static void Postfix(HudManager __instance) } }, () => { 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 @@ public static void Postfix(HudManager __instance) 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 @@ public static void Postfix(HudManager __instance) 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 13e0eccd4..30d9feebb 100644 --- a/TheOtherRoles/CustomOptions.cs +++ b/TheOtherRoles/CustomOptions.cs @@ -15,6 +15,7 @@ public class CustomOptionHolder { 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 @@ public class CustomOptionHolder { public static CustomOption jesterSpawnRate; public static CustomOption jesterCanCallEmergency; - public static CustomOption jesterCanSabotage; public static CustomOption arsonistSpawnRate; public static CustomOption arsonistCooldown; @@ -74,7 +74,6 @@ public class CustomOptionHolder { 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 @@ public class CustomOptionHolder { 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 @@ public static void Load() { // 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 @@ public static void Load() { 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 @@ public static void Load() { 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 @@ public static void Load() { 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 @@ public void updateSelection(int newSelection) { [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 @@ public static void Postfix(GameOptionsMenu __instance) { } 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 @@ class GameOptionsMenuUpdatePatch { 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 @@ public static void Postfix(GameOptionsMenu __instance) { } } - [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 @@ private static void Postfix(ref string __result) 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 @@ private static void Postfix(ref string __result) } 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 @@ public static class GameOptionsNextPagePatch 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 4b7824e69..c1ef9f0f4 100644 --- a/TheOtherRoles/Helpers.cs +++ b/TheOtherRoles/Helpers.cs @@ -78,10 +78,10 @@ public static Dictionary allPlayersById() 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 @@ public static bool handleMurderAttempt(PlayerControl target, bool isMeetingStart // 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 @@ public static bool handleMurderAttempt(PlayerControl target, bool isMeetingStart 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 @@ public static bool hidePlayerName(PlayerControl source, PlayerControl target) { 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 @@ public static void setLook(this PlayerControl target, String playerName, int col 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 df2e5f6bf..81e84f9b0 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 @@ public static void UpdateRegions() { } 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 @@ public static void Postfix(KeyboardJoystick __instance) 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 e6a964c55..87b96cb17 100644 --- a/TheOtherRoles/Modules/CustomColors.cs +++ b/TheOtherRoles/Modules/CustomColors.cs @@ -195,7 +195,7 @@ public static void Postfix() { 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 6cb6cfa42..ab5de20e1 100644 --- a/TheOtherRoles/Modules/CustomHats.cs +++ b/TheOtherRoles/Modules/CustomHats.cs @@ -37,12 +37,6 @@ public class HatExtension { 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 @@ private static HatBehaviour CreateHatBehaviour(CustomHat ch, bool fromDisk = fal } } - 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 @@ private static HatBehaviour CreateHatBehaviour(CustomHat ch, bool fromDisk = fal 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 @@ static void Prefix(HatManager __instance) { 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 @@ private static void Postfix(PlayerPhysics __instance) { } } - [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 @@ static void Postfix(HatParent __instance, [HarmonyArgument(0)]uint hatId, [Harmo } } - 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 @@ public static float createHatPackage(List(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 @@ public static float createHatPackage(List(__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 @@ public static float createHatPackage(List(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 @@ public static bool Prefix(HatsTab __instance) { } 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 @@ public static bool Prefix(HatsTab __instance) { 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 d1ee062d9..3a70feddb 100644 --- a/TheOtherRoles/Objects/Arrow.cs +++ b/TheOtherRoles/Objects/Arrow.cs @@ -32,11 +32,13 @@ public void Update() { 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 ee45ec53a..1d6f19fe4 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 @@ public class CustomButton 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 @@ public CustomButton(Action OnClick, Func HasButton, Func CouldUse, A 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 @@ void onClickEvent() { 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 @@ void onClickEvent() 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 @@ public static void HudUpdate() } 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 @@ public static void ResetAllCooldowns() { 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 @@ private void Update() } 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 @@ private void Update() 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 83e88cca1..c9cfa148a 100644 --- a/TheOtherRoles/Objects/Footprint.cs +++ b/TheOtherRoles/Objects/Footprint.cs @@ -26,7 +26,7 @@ public Footprint(float footprintDuration, bool anonymousFootprints, PlayerContro 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 @@ public Footprint(float footprintDuration, bool anonymousFootprints, PlayerContro 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 c8a48ca54..35d8b5b89 100644 --- a/TheOtherRoles/Patches/ClientOptionsPatch.cs +++ b/TheOtherRoles/Patches/ClientOptionsPatch.cs @@ -18,7 +18,7 @@ public class OptionsMenuBehaviourStartPatch { 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 @@ private static ToggleButtonBehaviour createCustomToggle(string text, bool on, Ve 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 097af82e7..2b4d39b64 100644 --- a/TheOtherRoles/Patches/EndGamePatch.cs +++ b/TheOtherRoles/Patches/EndGamePatch.cs @@ -54,12 +54,12 @@ internal class PlayerRoleInfo { [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 @@ public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)]ref Gam 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 @@ public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)]ref Gam 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 @@ public static void Postfix(EndGameManager __instance) { 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 @@ private void GetPlayerCounts() { 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 2d519c181..12fc88f4a 100644 --- a/TheOtherRoles/Patches/ExileControllerPatch.cs +++ b/TheOtherRoles/Patches/ExileControllerPatch.cs @@ -92,7 +92,7 @@ public static void Postfix(AirshipExileController __instance) { 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 @@ static void WrapUpPostfix(GameData.PlayerInfo exiled) { 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 bdcd01b0f..1a5234930 100644 --- a/TheOtherRoles/Patches/GameStartManagerPatch.cs +++ b/TheOtherRoles/Patches/GameStartManagerPatch.cs @@ -32,8 +32,6 @@ public static void Postfix(GameStartManager __instance) { 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 @@ public static void Postfix(GameStartManager __instance) { 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 282fbb6e0..e23453cdf 100644 --- a/TheOtherRoles/Patches/IntroPatch.cs +++ b/TheOtherRoles/Patches/IntroPatch.cs @@ -17,10 +17,10 @@ public static void Prefix(IntroCutscene __instance) { 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 @@ public static void Prefix(IntroCutscene __instance) { 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 @@ public static void Prefix(IntroCutscene __instance) { [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 @@ public static void setupIntroTeam(IntroCutscene __instance, ref Il2CppSystem.Co } // 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 764b6a6ff..5b58012a3 100644 --- a/TheOtherRoles/Patches/MeetingPatch.cs +++ b/TheOtherRoles/Patches/MeetingPatch.cs @@ -95,7 +95,7 @@ class MeetingHudBloopAVoteIconPatch { 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 @@ static void guesserOnClick(int buttonTarget, MeetingHud __instance) { 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 b1fddf974..055fa6d68 100644 --- a/TheOtherRoles/Patches/PlayerControlPatch.cs +++ b/TheOtherRoles/Patches/PlayerControlPatch.cs @@ -28,7 +28,7 @@ static PlayerControl setTarget(bool onlyCrewmates = false, bool targetPlayersInV 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 @@ static void eraserSetTarget() { } 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 @@ static void engineerUpdate() { } 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 @@ static void snitchUpdate() 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 @@ static void snitchUpdate() 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 @@ static void bountyHunterUpdate() { 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 @@ static void morphlingAndCamouflagerUpdate() { 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 @@ public static void Prefix(PlayerControl __instance) { 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 @@ static void Postfix(PlayerControl __instance, [HarmonyArgument(0)]GameData.Playe 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 @@ public static class MurderPlayerPatch 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 @@ public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)]PlayerC 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 @@ public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)]PlayerC } // 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 @@ public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)]float ti 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 @@ public static void Postfix(PlayerControl __instance) } } } - - [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 5c5e95614..caef3065a 100644 --- a/TheOtherRoles/Patches/RegionMenuPatch.cs +++ b/TheOtherRoles/Patches/RegionMenuPatch.cs @@ -36,14 +36,16 @@ public static class RegionMenuOpenPatch 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 @@ void onFocusLost() { __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 6be6579f7..95db273c0 100644 --- a/TheOtherRoles/Patches/RoleAssignmentPatch.cs +++ b/TheOtherRoles/Patches/RoleAssignmentPatch.cs @@ -8,17 +8,21 @@ 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 @@ private static void assignRoles() { 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 446db3972..64f2b12b8 100644 --- a/TheOtherRoles/Patches/ShipStatusPatch.cs +++ b/TheOtherRoles/Patches/ShipStatusPatch.cs @@ -19,7 +19,7 @@ public static bool Prefix(ref float __result, ShipStatus __instance, [HarmonyArg 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 0160484ed..62d50853b 100644 --- a/TheOtherRoles/Patches/UpdatePatch.cs +++ b/TheOtherRoles/Patches/UpdatePatch.cs @@ -20,7 +20,7 @@ static void resetNameTagsAndColors() { 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 @@ static void resetNameTagsAndColors() { 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 @@ static void resetNameTagsAndColors() { } } } - 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 @@ static void setNameColors() { } 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 @@ static void setNameColors() { } // 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 @@ static void setNameColors() { 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 @@ public static void miniUpdate() { } 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 @@ static void updateImpostorKillButton(HudManager __instance) { 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 083ef96c9..f2693016f 100644 --- a/TheOtherRoles/Patches/UsablesPatch.cs +++ b/TheOtherRoles/Patches/UsablesPatch.cs @@ -11,7 +11,7 @@ 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 @@ public static bool Prefix(Vent __instance, ref float __result, [HarmonyArgument( 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 @@ public static bool Prefix(Vent __instance, ref float __result, [HarmonyArgument( } } - [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 @@ public static bool Prefix(Vent __instance) { } } - [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 @@ static bool Prefix(MapCountOverlay __instance) { 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 3469019f7..602a823c9 100644 --- a/TheOtherRoles/RPC.cs +++ b/TheOtherRoles/RPC.cs @@ -124,7 +124,7 @@ public static void shareOptionSelection(uint id, uint selection) { 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 @@ public static void engineerFixLights() { } public static void engineerUsedRepair() { - Engineer.usedRepair = true; + Engineer.remainingFixes--; } public static void cleanBody(byte playerId) { @@ -381,7 +381,7 @@ public static void shifterShift(byte targetId) { 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 @@ public static void morphlingMorph(byte playerId) { 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 @@ public static void camouflagerCamouflage() { 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 @@ public static void jackalCreatesSidekick(byte targetId) { { 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 @@ public static void placeJackInTheBox(byte[] buff) { 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 000000000..1ac410bef Binary files /dev/null and b/TheOtherRoles/Resources/TabIcon.png differ diff --git a/TheOtherRoles/RoleInfo.cs b/TheOtherRoles/RoleInfo.cs index b2d8679d0..6f9910295 100644 --- a/TheOtherRoles/RoleInfo.cs +++ b/TheOtherRoles/RoleInfo.cs @@ -13,16 +13,18 @@ class RoleInfo { 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 @@ class RoleInfo { 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 @@ public static List getRoleInfoForPlayer(PlayerControl p) { 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 @@ public static List getRoleInfoForPlayer(PlayerControl p) { 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 fc00fe9e3..8b5aa12e3 100644 --- a/TheOtherRoles/TasksHandler.cs +++ b/TheOtherRoles/TasksHandler.cs @@ -14,7 +14,7 @@ public static Tuple taskInfo(GameData.PlayerInfo playerInfo) { 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 2313fdb9c..1fdbc26b8 100644 --- a/TheOtherRoles/TheOtherRoles.cs +++ b/TheOtherRoles/TheOtherRoles.cs @@ -64,13 +64,11 @@ public static class Jester { 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 @@ public static void clearAndReload() { 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 @@ public static Sprite getButtonSprite() { 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 @@ public static bool existingAndAlive() { 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 @@ public static class Jackal { 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 @@ public static void clearAndReload() { canCreateSidekickFromImpostor = CustomOptionHolder.jackalCanCreateSidekickFromImpostor.getBool(); formerJackals.Clear(); hasImpostorVision = CustomOptionHolder.jackalAndSidekickHaveImpostorVision.getBool(); - canSeeEngineerVent = CustomOptionHolder.jackalCanSeeEngineerVent.getBool(); } } @@ -838,7 +839,7 @@ public static void clearAndReload() { 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 c59decfda..49971c940 100644 --- a/TheOtherRoles/TheOtherRoles.csproj +++ b/TheOtherRoles/TheOtherRoles.csproj @@ -1,7 +1,7 @@ netstandard2.1 - 2.9.2 + 3.0.0 TheOtherRoles Eisbison