From c3680a52a2f29067d783505d26110cb14e6fb4c4 Mon Sep 17 00:00:00 2001 From: EoF-1141 <61620805+EoF-1141@users.noreply.github.com> Date: Sun, 30 May 2021 18:07:27 +0200 Subject: [PATCH] Version 2.6.7 - New Role: Guesser - Changed the colors of some roles - Changed name of Child to Mini - Changed Version check to allow to check for modified versions of the mod. (Forks don't need to use different versioning now) - Changed Role Assignment to make Spy incompatible with Mini (former child) - Fixed a bug where a Jester win was triggered, when the partner of a Jester Lover was voted out - Fixed a bug where a Mini lose was triggered, when the partner of a Crew Mini Lover was voted out --- README.md | 105 +++++--- Source Code/Buttons.cs | 2 +- Source Code/CustomOptions.cs | 22 +- Source Code/EndGamePatch.cs | 30 +-- Source Code/ExileControllerPatch.cs | 149 ++++++++++++ Source Code/GameStartManagerPatch.cs | 31 ++- Source Code/Helpers.cs | 4 +- Source Code/Main.cs | 4 +- Source Code/MeetingPatch.cs | 291 +++++++++++------------ Source Code/PlayerControlPatch.cs | 51 ++-- Source Code/RPC.cs | 71 ++++-- Source Code/RoleAssignmentPatch.cs | 23 +- Source Code/RoleInfo.cs | 343 +++++++++------------------ Source Code/TheOtherRoles.cs | 80 ++++--- Source Code/TheOtherRoles.csproj | 2 +- Source Code/UpdatePatch.cs | 20 +- 16 files changed, 679 insertions(+), 549 deletions(-) create mode 100644 Source Code/ExileControllerPatch.cs diff --git a/README.md b/README.md index a59d025b5..f22095ac6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![eisbison banner](./Images/TOR_logo.png) [![Discord](./Images/TOR_server.png)](https://discord.gg/77RkMJHWsM) -![eisbison infographic](./Images/TOR_roles.png) +![eisbison infographic](./Images/TOR_roles.jpg) # The Other Roles @@ -10,7 +10,7 @@ Even more roles are coming soon :) | Impostors | Crewmates | Neutral | |----------|-------------|-----------------| -| [Bad Child](#child) | [Child](#child) | [Arsonist](#arsonist) | +| [Evil Mini](#mini) | [Nice Mini](#mini) | [Arsonist](#arsonist) | | [Camouflager](#camouflager) | [Detective](#detective) | [Jester](#jester) | | [Cleaner](#cleaner) | [Engineer](#engineer) | [Jackal](#jackal) | | [Eraser](#eraser) | [Hacker](#hacker) | [Sidekick](#sidekick) | @@ -22,6 +22,7 @@ Even more roles are coming soon :) | [Trickster](#trickster) | [Seer](#seer) | | | [Vampire](#vampire) | [Sheriff](#sheriff) | | | [Warlock](#warlock) | [Shifter](#shifter) | | +| [Evil Guesser](#guesser) | [Nice Guesser](#guesser) | | | | [Snitch](#snitch) | | | | [Spy](#spy) | | | | [Swapper](#swapper) | | @@ -33,6 +34,7 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein # Releases | Among Us - Version| Mod Version | Link | |----------|-------------|-----------------| +| 2021.5.25.2s| v2.6.7| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.6.7/TheOtherRoles.zip) | 2021.5.10s| v2.6.6| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.6.6/TheOtherRoles.zip) | 2021.5.10s| v2.6.5| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.6.5/TheOtherRoles.zip) | 2021.5.10s| v2.6.4| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.6.4/TheOtherRoles.zip) @@ -65,6 +67,15 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein
Click to show the Changelog +**Version 2.6.7** +- **New Role:** [Guesser](#guesser) +- Changed the colors of some roles +- Changed name of Child to [Mini](#mini) +- Changed Version check to allow to check for modified versions of the mod. (Forks don't need to use different versioning now) +- Changed Role Assignment to make [Spy](#spy) incompatible with [Mini](#mini) (former child) +- Fixed a bug where a [Jester](#jester) win was triggered, when the partner of a [Jester Lover](#lovers) was voted out +- Fixed a bug where a [Mini](#mini) lose was triggered, when the partner of a Crew [Mini Lover](#lovers) was voted out + **Version 2.6.6** - Fixed a bug introduced in v2.6.5 that caused all player to be able to use vents when the new option for spy was enabled @@ -161,9 +172,9 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein **Version 2.2.1** - Trickster: The vent button now has a custom texture. Fixed a bug where the Trickster could clip out of bounds when his box was close to a wall. -- Fixed a bug where the Bad Child's kill button went on cooldown when someone else performed a kill +- Fixed a bug where the Bad Mini's kill button went on cooldown when someone else performed a kill - Fixed a few bugs with footprints, Seer souls and the Vampire delayed kill -- Fixed a bug where the Child was banned for hacking (because of its reduced kill cooldown) +- Fixed a bug where the Mini was banned for hacking (because of its reduced kill cooldown) - Improved the version handshake **Version 2.2.0** @@ -178,22 +189,22 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein - Fixed a bug where a Lovers win wasn't displayed properly - Fixed the Among Us bug where people were unable to move after meetings - We added a version checking system: The host can only start the game if everyone in his lobby has the same version of the mod installed (he will see, who is using a wrong version). This prevents hacking in public lobbies and bugs because of version missmatches. -- Fixed a bug where the Child Impostor had the same cooldowns as normal Impostors +- Fixed a bug where the Mini Impostor had the same cooldowns as normal Impostors - Fixed a bug where the Vampire/Janitor/Mafioso would lose their kill button after being erased -- The Child is now able to use ladders and it can do all the tasks right away +- The Mini is now able to use ladders and it can do all the tasks right away **Version 2.1.0** - **New Role:** [Spy](#spy) - **Eraser:** The Eraser can now also remove the role of other Impostors. This enables him to reveal the Spy, but might result in removing the special ability of his partner. -- **Camouflager:** The Child age/size will now also be hidden, to allow the Child Impostor to kill during camouflage +- **Camouflager:** The Mini age/size will now also be hidden, to allow the Mini Impostor to kill during camouflage **Hotfix 2.0.1** - Fixed a bug where camouflaged players would get stuck on ladders/platforms on the airship - Introduced a one second cooldown after the Morphling sampled another player -- The Child can now always reach all usables (ladders, tasks, ...) +- The Mini can now always reach all usables (ladders, tasks, ...) - We removed a bug, where some footprints remained on the ground forever - We removed a bug, where the Detective didn't see the right color type when reporting a player -- We changed the Jester win and Child lose conditions, they're not being affected by server delays anymore +- We changed the Jester win and Mini lose conditions, they're not being affected by server delays anymore **Changes in 2.0.0** - **New button art** created by **Bavari** @@ -208,7 +219,7 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein - **Hacker:** The Hacker is basically the old Spy. We added the option to only show the color type instead of the color on the admin table. - **Camouflager:** Now also overrides the information of other roles, check the [Camouflager](#camouflager) section for more details. - **Morphling:** Now also overrides the information of other roles, check the [Morphling](#morphling) section for more details -- **Child:** The Child can now be a Crewmate Child or an Impostor Child, check the [Child](#child) section for more details +- **Mini:** The Mini can now be a Crewmate Mini or an Impostor Mini, check the [Mini](#mini) section for more details - **Eraser:** The Eraser, a new Impostor role, is now part of the mod. Check the [Eraser](#eraser) section for more details - **New options:** - You can now set the maximum number of meetings in a game: Every player still only has one meeting. The Mayor can always use his meeting (even if the maximum number of meetings was reached). Impostor/Jackal meetings also count. @@ -238,13 +249,13 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein \ **Changes in v1.6:** - This update is a small hotfix, fixing the bug where some people were unable to join lobbies. -- The Child can't be voted out anymore before it turns 18, hence games can't end anymore because the Child died. +- The Mini can't be voted out anymore before it turns 18, hence games can't end anymore because the Mini died. - Footprints are no longer visible to the Detective, if players are inside vents. \ **Changes in v1.5:** - Time Master - Buff: He is not affected by his rewind anymore, which gives him more utility. Players will now be rewinded out of vents. -- Child - Nerf: The Child now grows up (see [Child](#child)) and becomes a normal Crewmate at some point. A growing Child is not killable anymore. Some tasks are still not doable for the small Child, we are working on that. But eventually when growing up it can do all the tasks as it's size increases. +- Mini - Nerf: The Mini now grows up (see [Mini](#mini)) and becomes a normal Crewmate at some point. A growing Mini is not killable anymore. Some tasks are still not doable for the small Mini, we are working on that. But eventually when growing up it can do all the tasks as it's size increases. - Seer - Nerf: Added an option that sets how often the Seer mistakes the player for another. - Hacker - Nerf: The Hacker now only sees the additional information when he activates his "Hacker mode". That should stops the Hacker from camping the admin table/vitals. - Other: Camouflager/Morphling cooldowns were fixed. Custom regions code was removed to enable 3rd party tools. Some minor bugfixes. @@ -262,7 +273,7 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein **Changes in v1.1:** - Morphling: The color of pet now also morphs. The skin animation now starts at the right point. -- The game over screen now shows if the Jester/Child/Lovers won. +- The game over screen now shows if the Jester/Mini/Lovers won. - A bug was removed where the Jester won together with the Crewmates. - A bug was removed where the game of the Lovers crashed if they were the last players killed by the host of the lobby.
@@ -366,11 +377,11 @@ The mod adds a few new settings to Among Us (in addition to the role settings): - **Map:** The map can be changed inside a lobby - **Maximum Number Of Meetings:** You can set the maximum number of meetings that can be called in total (every player still has personal maximum of buttons, but if the maximum number of meetings is reached you can't use your meetings even if you have some left, Impostor and Jackal meetings also count) - **Allow Skips On Emergency Meetings:** If set to false, there will not be a skip button in emergency meetings. If a player does not vote, he'll vote himself. -- **Hide Player Names:** Hides the names of all players that have role which is unknown to you. Team Lovers/Impostors/Jackal still see the names of their teammates. Impostors can alse see the name of the Spy and everyone can still see the age of the child. +- **Hide Player Names:** Hides the names of all players that have role which is unknown to you. Team Lovers/Impostors/Jackal still see the names of their teammates. Impostors can alse see the name of the Spy and everyone can still see the age of the mini. - **Ghosts Can See Roles** - **Ghosts Can See Votes** - **Ghosts Can See The Number Of Remaining Tasks** -- **Dleks:** You are now able to select the Dleks map. +- **Dleks:** You are now able to select the Dleks map. - **Task Counts:** You are now able to select more tasks. - **Role Summary:** When a game ends there will be a list of all players and their roles and their task progress @@ -433,7 +444,7 @@ First you need to choose how many special roles of each kind (Impostor/Neutral/C The count you set will only be reached, if there are enough Crewmates/Impostors in the game and if enough roles are set to be in the game (i.e. they are set to > 0%). The roles are then being distributed as follows: - First all roles that are set to 100% are being assigned to arbitrary players - After that each role that has 10%-90% selected adds 1-9 tickets to a ticket pool (there exists a ticket pool for Crewmates, Neutrals and Impostors). Then the roles will be selected randomly from the pools as long it's possible (until the selected number is reached, until there are no more Crewmates/Impostors or until there are no more tickets). If a role is selected from the pool, obviously all the tickets of that role are being removed. -- The Mafia, Lovers and Child are being selected independently (without using the ticket system) according to the spawn chance you selected. After that the Crewmate, Neutral and Impostor roles are selected and assigned in a random order. +- The Mafia, Lovers and Mini are being selected independently (without using the ticket system) according to the spawn chance you selected. After that the Crewmate, Neutral and Impostor roles are selected and assigned in a random order. **Example:**\ Settings: 2 special Crewmate roles, Snitch: 100%, Hacker: 10%, Tracker: 30%\ @@ -463,7 +474,7 @@ The Janitor is an Impostor who cannot kill nor sabotage, but they can hide dead The Morphling is an Impostor which can additionally scan the appearance of a player. After an arbitrary time they can take on that appearance for 10s. \ **NOTE:** -- They shrink to the size of the Child when they copies its look. +- They shrink to the size of the Mini when they copies its look. - The Hacker sees the new color on the admin table. - The color of the footprints changes accordingly (also the ones that were already on the ground). - The other Impostor still sees that they are an Impostor (the name remains red). @@ -485,7 +496,7 @@ The camouflage mode lasts for 10s and while it is active, all player names/pets/ are hidden and all players have the same color.\ \ **NOTE:** -- The Child will look like all the other players +- The Mini will look like all the other players - The color of the footprints turns gray (also the ones that were already on the ground). - The Hacker sees gray icons on the admin table - The shield is not visible anymore @@ -582,7 +593,7 @@ The Cleaner is an Impostor who has the ability to clean up dead bodies. \ ### **Team: Impostors** The Warlock is an Impostor, that can curse another player (the cursed player doesn't get notified).\ If the cursed person stands next to another player, the Warlock is able to kill that player (no matter how far away he is).\ -Performing a kill with the help of a cursed player, will lift the curse and it will result in the Warlock being unable to move for a configurable amount of time. +Performing a kill with the help of a cursed player, will lift the curse and it will result in the Warlock being unable to move for a configurable amount of time.\ The Warlock can still perform normal kills, but the two buttons share the same cooldown. \ @@ -599,6 +610,30 @@ The Warlock can still perform normal kills, but the two buttons share the same c | Warlock Root Time | Time the Warlock is rooted in place after killing using the curse ----------------------- +## Guesser +### **Team: Crewmates or Impostors** +The Guesser can be a Crewmate or an Impostor (depending on the settings).\ +The Guesser can shoot a player during the meeting, by guessing its role. If the guess is wrong, the Guesser dies instead.\ +Only one person can be shot per meeting and you can set a maximum number of shots.\ +The guesses Impostor and Crewmate are only right, if the player is part of the corresponding team and has no special role.\ +You can only shoot during the voting time. + +\ +**NOTE:** +- The vote of a shot player, won't be counted +- You can't guess the role **Nice Mini** for obvious reasons +- You can't guess the role **Lover**, you'll have to guess the primary role of one of the Lovers, to kill both of them +- Jester wins won't be triggered, if the Guesser shoots the Jester before the Jester gets voted out + +### Game Options +| Name | Description | +|----------|:-------------:| +| Guesser Spawn Chance | - +| Chance That The Guesser Is An Impostor | - +| Guesser Number Of Shots | - + +----------------------- + ## Lovers ### **Team: Lovers (and secondary team)** There are always two Lovers which are linked together.\ @@ -633,7 +668,7 @@ If they try to kill a Crewmate, they die instead. **NOTE:** - If the Sheriff shoots the person the Medic shielded, the Sheriff and the shielded person **both remain unharmed**. -- If the Sheriff shoots a Child Impostor, the Sheriff dies if the Child is still growing up. If it's 18, the Child Impostor dies. +- If the Sheriff shoots a Mini Impostor, the Sheriff dies if the Mini is still growing up. If it's 18, the Mini Impostor dies. ### Game Options | Name | Description | @@ -737,28 +772,28 @@ The Lighter can turn on his Lighter every now and then, which increases his visi | Lighter Duration | - ----------------------- -## Child +## Mini ### **Team: Crewmates or Impostors** -The Child can be a Crewmate (67% chance) or an Impostor (33% chance).\ -The Child's character is smaller and hence visible to everyone in the game.\ -The Child cannot be killed until it turns 18 years old, however it can be voted out.\ -**Impostor Child:** +The Mini can be a Crewmate (67% chance) or an Impostor (33% chance).\ +The Mini's character is smaller and hence visible to everyone in the game.\ +The Mini cannot be killed until it turns 18 years old, however it can be voted out.\ +**Impostor Mini:** - While growing up the kill cooldown is doubled. When it's fully grown up its kill cooldown is 2/3 of the default one. - If it gets thrown out of the ship, everything is fine. -**Crewmate Child:** - - The Crewmate Child aims to play out the strength its invincibility in the early game. - - If it gets thrown out of the ship before it turns 18, everyone loses. So think twice before you vote out a Child. +**Crewmate Mini:** + - The Crewmate Mini aims to play out the strength its invincibility in the early game. + - 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 Child (the button does not work) until it turns 18 -- The Sheriff can kill the Impostor Child, but only if it's fully grown up +- Impostors can't kill the Mini (the button does not work) until it turns 18 +- The Sheriff can kill the Impostor Mini, but only if it's fully grown up ### Game Options | Name | Description | |----------|:-------------:| -| Child Spawn Chance | - -| Child | Child Growing Up Duration +| Mini Spawn Chance | - +| Mini | Mini Growing Up Duration ----------------------- ## Medic @@ -916,7 +951,7 @@ The team Jackal enables multiple new outcomes of the game, listing some examples - The Crew could be eliminated, then the Team Jackal fight against the Impostors (the Crew can still make a task win in this scenario) The priority of the win conditions is the following: -1. Crewmate Child lose by vote +1. Crewmate Mini lose by vote 2. Jester wins by vote 3. Arsonist win 4. Team Impostor wins by sabotage @@ -928,7 +963,7 @@ The priority of the win conditions is the following: **NOTE:** - The Jackal (and his Sidekick) may be killed by a Sheriff. -- A Jackal cannot target the Child, while it's growing up. After that he can kill it or select it as its Sidekick +- A Jackal cannot target the Mini, while it's growing up. After that he can kill it or select it as its Sidekick - The Crew can still win, even if all of their members are dead, if they finish their tasks fast enough (that's why converting the last Crewmate with tasks left into a Sidekick results in a task win for the crew) If both Impostors and Jackals are in the game the game continues even if all Crewmates are dead. Crewmates may still win in this case by completing their tasks. Jackal and Impostor have to kill each other. @@ -956,7 +991,7 @@ Upon the death of the Jackal (depending on the options), he might get promoted t **NOTE:** - A player that converts into a Sidekick loses his previous role and tasks (if he had one), except the Lover role. - The Sidekick may be killed by a Sheriff. -- The Sidekick cannot target the Child, while it's growing up. +- The Sidekick cannot target the Mini, while it's growing up. ### Game Options | Name | Description diff --git a/Source Code/Buttons.cs b/Source Code/Buttons.cs index bfb463883..1174c868e 100644 --- a/Source Code/Buttons.cs +++ b/Source Code/Buttons.cs @@ -172,7 +172,7 @@ public static void Postfix(HudManager __instance) } byte targetId = 0; - if ((Sheriff.currentTarget.Data.IsImpostor && (Sheriff.currentTarget != Child.child || Child.isGrownUp())) || + if ((Sheriff.currentTarget.Data.IsImpostor && (Sheriff.currentTarget != Mini.mini || Mini.isGrownUp())) || (Sheriff.spyCanDieToSheriff && Spy.spy == Sheriff.currentTarget) || (Sheriff.canKillNeutrals && (Arsonist.arsonist == Sheriff.currentTarget || Jester.jester == Sheriff.currentTarget || Jackal.jackal == Sheriff.currentTarget || Sidekick.sidekick == Sheriff.currentTarget))) { targetId = Sheriff.currentTarget.PlayerId; diff --git a/Source Code/CustomOptions.cs b/Source Code/CustomOptions.cs index f7fa2db86..864497eae 100644 --- a/Source Code/CustomOptions.cs +++ b/Source Code/CustomOptions.cs @@ -42,14 +42,18 @@ public class CustomOptionHolder { public static CustomOption eraserCooldown; public static CustomOption eraserCanEraseAnyone; - public static CustomOption childSpawnRate; - public static CustomOption childGrowingUpDuration; + public static CustomOption miniSpawnRate; + public static CustomOption miniGrowingUpDuration; public static CustomOption loversSpawnRate; public static CustomOption loversImpLoverRate; public static CustomOption loversBothDie; public static CustomOption loversCanHaveAnotherRole; + public static CustomOption guesserSpawnRate; + public static CustomOption guesserIsImpGuesserRate; + public static CustomOption guesserNumberOfShots; + public static CustomOption jesterSpawnRate; public static CustomOption jesterCanCallEmergency; public static CustomOption jesterCanSabotage; @@ -207,14 +211,18 @@ public static void Load() { warlockCooldown = CustomOption.Create(271, "Warlock Cooldown", 30f, 10f, 60f, 2.5f, warlockSpawnRate); warlockRootTime = CustomOption.Create(272, "Warlock Root Time", 5f, 0f, 15f, 1f, warlockSpawnRate); - childSpawnRate = CustomOption.Create(180, cs(Child.color, "Child"), rates, null, true); - childGrowingUpDuration = CustomOption.Create(181, "Child Growing Up Duration", 400f, 100f, 1500f, 100f, childSpawnRate); + miniSpawnRate = CustomOption.Create(180, cs(Mini.color, "Mini"), rates, null, true); + miniGrowingUpDuration = CustomOption.Create(181, "Mini Growing Up Duration", 400f, 100f, 1500f, 100f, miniSpawnRate); loversSpawnRate = CustomOption.Create(50, cs(Lovers.color, "Lovers"), rates, null, true); loversImpLoverRate = CustomOption.Create(51, "Chance That One Lover Is Impostor", rates, loversSpawnRate); loversBothDie = CustomOption.Create(52, "Both Lovers Die", true, loversSpawnRate); loversCanHaveAnotherRole = CustomOption.Create(53, "Lovers Can Have Another Role", true, loversSpawnRate); + guesserSpawnRate = CustomOption.Create(310, cs(Guesser.color, "Guesser"), rates, null, true); + guesserIsImpGuesserRate = CustomOption.Create(311, "Chance That The Guesser Is An Impostor", rates, guesserSpawnRate); + guesserNumberOfShots = CustomOption.Create(312, "Guesser Number Of Shots", 2f, 1f, 15f, 1f, guesserSpawnRate); + 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); @@ -616,7 +624,7 @@ private static void Postfix(ref string __result) var hudString = sb.ToString(); int defaultSettingsLines = 19; - int roleSettingsLines = defaultSettingsLines + 32; + int roleSettingsLines = defaultSettingsLines + 33; int detailedSettingsP1 = roleSettingsLines + 34; int detailedSettingsP2 = detailedSettingsP1 + 35; int end1 = hudString.TakeWhile(c => (defaultSettingsLines -= (c == '\n' ? 1 : 0)) > 0).Count(); @@ -635,10 +643,10 @@ private static void Postfix(ref string __result) gap = 5; index = hudString.TakeWhile(c => (gap -= (c == '\n' ? 1 : 0)) > 0).Count(); hudString = hudString.Insert(index, "\n"); - gap = 16; + gap = 17; index = hudString.TakeWhile(c => (gap -= (c == '\n' ? 1 : 0)) > 0).Count(); hudString = hudString.Insert(index + 1, "\n"); - gap = 20; + gap = 21; index = hudString.TakeWhile(c => (gap -= (c == '\n' ? 1 : 0)) > 0).Count(); hudString = hudString.Insert(index + 1, "\n"); } else if (counter == 2) { diff --git a/Source Code/EndGamePatch.cs b/Source Code/EndGamePatch.cs index ebbc97407..f0cfea937 100644 --- a/Source Code/EndGamePatch.cs +++ b/Source Code/EndGamePatch.cs @@ -15,7 +15,7 @@ namespace TheOtherRoles { enum CustomGameOverReason { LoversWin = 10, TeamJackalWin = 11, - ChildLose = 12, + MiniLose = 12, JesterWin = 13, ArsonistWin = 14 } @@ -26,7 +26,7 @@ enum WinCondition { LoversSoloWin, JesterWin, JackalWin, - ChildLose, + MiniLose, ArsonistWin } @@ -82,17 +82,17 @@ public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)]ref Gam bool jesterWin = Jester.jester != null && gameOverReason == (GameOverReason)CustomGameOverReason.JesterWin; bool arsonistWin = Arsonist.arsonist != null && gameOverReason == (GameOverReason)CustomGameOverReason.ArsonistWin; - bool childLose = Child.child != null && gameOverReason == (GameOverReason)CustomGameOverReason.ChildLose; + bool miniLose = Mini.mini != null && gameOverReason == (GameOverReason)CustomGameOverReason.MiniLose; bool loversWin = Lovers.existingAndAlive() && (gameOverReason == (GameOverReason)CustomGameOverReason.LoversWin || (TempData.DidHumansWin(gameOverReason) && !Lovers.existingWithKiller())); // Either they win if they are among the last 3 players, or they win if they are both Crewmates and both alive and the Crew wins (Team Imp/Jackal Lovers can only win solo wins) bool teamJackalWin = gameOverReason == (GameOverReason)CustomGameOverReason.TeamJackalWin && ((Jackal.jackal != null && !Jackal.jackal.Data.IsDead) || (Sidekick.sidekick != null && !Sidekick.sidekick.Data.IsDead)); - // Child lose - if (childLose) { + // Mini lose + if (miniLose) { TempData.winners = new Il2CppSystem.Collections.Generic.List(); - WinningPlayerData wpd = new WinningPlayerData(Child.child.Data); - wpd.IsYou = false; // If "no one is the Child", it will display the Child, but also show defeat to everyone + WinningPlayerData wpd = new WinningPlayerData(Mini.mini.Data); + wpd.IsYou = false; // If "no one is the Mini", it will display the Mini, but also show defeat to everyone TempData.winners.Add(wpd); - AdditionalTempData.winCondition = WinCondition.ChildLose; + AdditionalTempData.winCondition = WinCondition.MiniLose; } // Jester win @@ -191,9 +191,9 @@ public static void Postfix(EndGameManager __instance) { textRenderer.text = "Team Jackal Wins"; textRenderer.color = Jackal.color; } - else if (AdditionalTempData.winCondition == WinCondition.ChildLose) { - textRenderer.text = "Child died"; - textRenderer.color = Child.color; + else if (AdditionalTempData.winCondition == WinCondition.MiniLose) { + textRenderer.text = "Mini died"; + textRenderer.color = Mini.color; } if(MapOptions.showRoleSummary) { @@ -231,7 +231,7 @@ public static bool Prefix(ShipStatus __instance) { if (DestroyableSingleton.InstanceExists) // InstanceExists | Don't check Custom Criteria when in Tutorial return true; var statistics = new PlayerStatistics(__instance); - if (CheckAndEndGameForChildLose(__instance)) return false; + if (CheckAndEndGameForMiniLose(__instance)) return false; if (CheckAndEndGameForJesterWin(__instance)) return false; if (CheckAndEndGameForArsonistWin(__instance)) return false; if (CheckAndEndGameForSabotageWin(__instance)) return false; @@ -243,10 +243,10 @@ public static bool Prefix(ShipStatus __instance) { return false; } - private static bool CheckAndEndGameForChildLose(ShipStatus __instance) { - if (Child.triggerChildLose) { + private static bool CheckAndEndGameForMiniLose(ShipStatus __instance) { + if (Mini.triggerMiniLose) { __instance.enabled = false; - ShipStatus.RpcEndGame((GameOverReason)CustomGameOverReason.ChildLose, false); + ShipStatus.RpcEndGame((GameOverReason)CustomGameOverReason.MiniLose, false); return true; } return false; diff --git a/Source Code/ExileControllerPatch.cs b/Source Code/ExileControllerPatch.cs new file mode 100644 index 000000000..4f3d139b7 --- /dev/null +++ b/Source Code/ExileControllerPatch.cs @@ -0,0 +1,149 @@ +using HarmonyLib; +using Hazel; +using System.Collections.Generic; +using System.Linq; +using UnhollowerBaseLib; +using static TheOtherRoles.TheOtherRoles; +using static TheOtherRoles.MapOptions; +using System.Collections; +using System; +using System.Text; +using UnityEngine; +using System.Reflection; + +namespace TheOtherRoles { + [HarmonyPatch(typeof(ExileController), "Begin")] + class ExileControllerBeginPatch { + public static void Prefix(ExileController __instance, [HarmonyArgument(0)]ref GameData.PlayerInfo exiled, [HarmonyArgument(1)]bool tie) { + // Shifter shift + if (Shifter.shifter != null && AmongUsClient.Instance.AmHost && Shifter.futureShift != null) { // We need to send the RPC from the host here, to make sure that the order of shifting and erasing is correct (for that reason the futureShifted and futureErased are being synced) + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ShifterShift, Hazel.SendOption.Reliable, -1); + writer.Write(Shifter.futureShift.PlayerId); + AmongUsClient.Instance.FinishRpcImmediately(writer); + RPCProcedure.shifterShift(Shifter.futureShift.PlayerId); + } + Shifter.futureShift = null; + + // Eraser erase + if (Eraser.eraser != null && AmongUsClient.Instance.AmHost && Eraser.futureErased != null) { // We need to send the RPC from the host here, to make sure that the order of shifting and erasing is correct (for that reason the futureShifted and futureErased are being synced) + foreach (PlayerControl target in Eraser.futureErased) { + if (target != null) { + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ErasePlayerRoles, Hazel.SendOption.Reliable, -1); + writer.Write(target.PlayerId); + AmongUsClient.Instance.FinishRpcImmediately(writer); + RPCProcedure.erasePlayerRoles(target.PlayerId); + } + } + } + Eraser.futureErased = new List(); + + // Trickster boxes + if (Trickster.trickster != null && JackInTheBox.hasJackInTheBoxLimitReached()) { + JackInTheBox.convertToVents(); + } + + // SecurityGuard vents and cameras + var allCameras = ShipStatus.Instance.AllCameras.ToList(); + MapOptions.camerasToAdd.ForEach(camera => { + camera.gameObject.SetActive(true); + camera.gameObject.GetComponent().color = Color.white; + allCameras.Add(camera); + }); + ShipStatus.Instance.AllCameras = allCameras.ToArray(); + MapOptions.camerasToAdd = new List(); + + foreach (Vent vent in MapOptions.ventsToSeal) { + PowerTools.SpriteAnim animator = vent.GetComponent(); + animator?.Stop(); + vent.EnterVentAnim = vent.ExitVentAnim = null; + vent.myRend.sprite = animator == null ? SecurityGuard.getStaticVentSealedSprite() : SecurityGuard.getAnimatedVentSealedSprite(); + vent.myRend.color = Color.white; + vent.name = "SealedVent_" + vent.name; + } + MapOptions.ventsToSeal = new List(); + } + } + + [HarmonyPatch(typeof(UnityEngine.Object), nameof(UnityEngine.Object.Destroy), new Type[] { typeof(UnityEngine.Object) })] + class ExileControllerDestroyPatch { + static void Prefix(UnityEngine.Object obj) { + if (ExileController.Instance == null || obj != ExileController.Instance.gameObject) return; + var exiled = ExileController.Instance.exiled; + + // Mini exile lose condition + if (exiled != null && Mini.mini != null && Mini.mini.PlayerId == exiled.PlayerId && !Mini.isGrownUp() && !Mini.mini.Data.IsImpostor) { + Mini.triggerMiniLose = true; + } + // Jester win condition + else if (exiled != null && Jester.jester != null && Jester.jester.PlayerId == exiled.PlayerId) { + Jester.triggerJesterWin = true; + } + + // Reset custom button timers where necessary + CustomButton.MeetingEndedUpdate(); + + // Mini set adapted cooldown + if (Mini.mini != null && PlayerControl.LocalPlayer == Mini.mini && Mini.mini.Data.IsImpostor) { + var multiplier = Mini.isGrownUp() ? 0.66f : 2f; + Mini.mini.SetKillTimer(PlayerControl.GameOptions.KillCooldown * multiplier); + } + + // Seer spawn souls + if (Seer.deadBodyPositions != null && Seer.seer != null && PlayerControl.LocalPlayer == Seer.seer && (Seer.mode == 0 || Seer.mode == 2)) { + foreach (Vector3 pos in Seer.deadBodyPositions) { + GameObject soul = new GameObject(); + soul.transform.position = pos; + soul.layer = 5; + var rend = soul.AddComponent(); + rend.sprite = Seer.getSoulSprite(); + + if(Seer.limitSoulDuration) { + HudManager.Instance.StartCoroutine(Effects.Lerp(Seer.soulDuration, new Action((p) => { + if (rend != null) { + var tmp = rend.color; + tmp.a = Mathf.Clamp01(1 - p); + rend.color = tmp; + } + if (p == 1f && rend != null && rend.gameObject != null) UnityEngine.Object.Destroy(rend.gameObject); + }))); + } + } + Seer.deadBodyPositions = new List(); + } + + // Arsonist deactivate dead poolable players + if (Arsonist.arsonist != null && Arsonist.arsonist == PlayerControl.LocalPlayer) { + int visibleCounter = 0; + Vector3 bottomLeft = new Vector3(-HudManager.Instance.UseButton.transform.localPosition.x, HudManager.Instance.UseButton.transform.localPosition.y, HudManager.Instance.UseButton.transform.localPosition.z); + bottomLeft += new Vector3(-0.25f, -0.25f, 0); + foreach (PlayerControl p in PlayerControl.AllPlayerControls) { + if (!Arsonist.dousedIcons.ContainsKey(p.PlayerId)) continue; + if (p.Data.IsDead || p.Data.Disconnected) { + Arsonist.dousedIcons[p.PlayerId].gameObject.SetActive(false); + } else { + Arsonist.dousedIcons[p.PlayerId].transform.localPosition = bottomLeft + Vector3.right * visibleCounter * 0.35f; + visibleCounter++; + } + } + } + } + } + + [HarmonyPatch(typeof(TranslationController), nameof(TranslationController.GetString), new Type[] { typeof(StringNames), typeof(Il2CppReferenceArray) })] + class ExileControllerMessagePatch { + static void Postfix(ref string __result, [HarmonyArgument(0)]StringNames id, [HarmonyArgument(1)]Il2CppReferenceArray parts) { + if (ExileController.Instance != null && ExileController.Instance.exiled != null) { + PlayerControl player = Helpers.playerById(ExileController.Instance.exiled.Object.PlayerId); + if (player == null) return; + // Exile role text + if (id == StringNames.ExileTextPN || id == StringNames.ExileTextSN || id == StringNames.ExileTextPP || id == StringNames.ExileTextSP) { + __result = player.Data.PlayerName + " was The " + String.Join(" ", RoleInfo.getRoleInfoForPlayer(player).Select(x => x.name).ToArray()); + } + // Hide number of remaining impostors on Jester win + if (id == StringNames.ImpostorsRemainP || id == StringNames.ImpostorsRemainS) { + if (Jester.jester != null && player.PlayerId == Jester.jester.PlayerId) __result = ""; + } + } + } + } +} \ No newline at end of file diff --git a/Source Code/GameStartManagerPatch.cs b/Source Code/GameStartManagerPatch.cs index 45430d60b..84a3a446b 100644 --- a/Source Code/GameStartManagerPatch.cs +++ b/Source Code/GameStartManagerPatch.cs @@ -1,6 +1,7 @@ using HarmonyLib; using UnityEngine; +using System.Reflection; using System.Collections.Generic; using Hazel; using System; @@ -8,7 +9,7 @@ namespace TheOtherRoles { public class GameStartManagerPatch { - public static Dictionary playerVersions = new Dictionary(); + public static Dictionary playerVersions = new Dictionary(); private static float timer = 600f; private static bool versionSent = false; private static string lobbyCodeText = ""; @@ -48,8 +49,10 @@ public static void Postfix(GameStartManager __instance) { writer.Write((byte)TheOtherRolesPlugin.Version.Minor); writer.Write((byte)TheOtherRolesPlugin.Version.Build); writer.WritePacked(AmongUsClient.Instance.ClientId); + writer.Write((byte)(TheOtherRolesPlugin.Version.Revision < 0 ? 0xFF : TheOtherRolesPlugin.Version.Revision)); + writer.Write(Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId.ToByteArray()); AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.versionHandshake(TheOtherRolesPlugin.Version.Major, TheOtherRolesPlugin.Version.Minor, TheOtherRolesPlugin.Version.Build, AmongUsClient.Instance.ClientId); + 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])) { @@ -93,12 +96,16 @@ public static void Postfix(GameStartManager __instance) { blockStart = true; message += $"{client.Character.Data.PlayerName} has a different or no version of The Other Roles\n"; } else { - int diff = TheOtherRolesPlugin.Version.CompareTo(playerVersions[client.Id]); + PlayerVersion PV = playerVersions[client.Id]; + int diff = TheOtherRolesPlugin.Version.CompareTo(PV.version); if (diff > 0) { - message += $"{client.Character.Data.PlayerName} has an older version of The Other Roles (v{playerVersions[client.Id].ToString()})\n"; + message += $"{client.Character.Data.PlayerName} has an older version of The Other Roles (v{playerVersions[client.Id].version.ToString()})\n"; blockStart = true; } else if (diff < 0) { - message += $"{client.Character.Data.PlayerName} has a newer version of The Other Roles (v{playerVersions[client.Id].ToString()}) \n"; + message += $"{client.Character.Data.PlayerName} has a newer version of The Other Roles (v{playerVersions[client.Id].version.ToString()})\n"; + blockStart = true; + } else if (!PV.GuidMatches()) { // version presumably matches, check if Guid matches + message += $"{client.Character.Data.PlayerName} has a modified version of TOR v{playerVersions[client.Id].version.ToString()} ({PV.guid.ToString()})\n"; blockStart = true; } } @@ -151,5 +158,19 @@ public static bool Prefix(GameStartManager __instance) { return continueStart; } } + + public class PlayerVersion { + public readonly Version version; + public readonly Guid guid; + + public PlayerVersion(Version version, Guid guid) { + this.version = version; + this.guid = guid; + } + + public bool GuidMatches() { + return Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId.Equals(this.guid); + } + } } } diff --git a/Source Code/Helpers.cs b/Source Code/Helpers.cs index 8e0f346ad..a2727f079 100644 --- a/Source Code/Helpers.cs +++ b/Source Code/Helpers.cs @@ -109,8 +109,8 @@ public static bool handleMurderAttempt(PlayerControl target, bool isMeetingStart return false; } - // Block impostor not fully grown child kill - else if (Child.child != null && target == Child.child && !Child.isGrownUp()) { + // Block impostor not fully grown mini kill + else if (Mini.mini != null && target == Mini.mini && !Mini.isGrownUp()) { return false; } // Block Time Master with time shield kill diff --git a/Source Code/Main.cs b/Source Code/Main.cs index 3c39aa8dd..65fa6ce80 100644 --- a/Source Code/Main.cs +++ b/Source Code/Main.cs @@ -4,6 +4,7 @@ using HarmonyLib; using Hazel; using System.Collections.Generic; +using System.Security.Cryptography; using System.Linq; using System.Net; using System.IO; @@ -19,7 +20,7 @@ namespace TheOtherRoles public class TheOtherRolesPlugin : BasePlugin { public const string Id = "me.eisbison.theotherroles"; - public const string VersionString = "2.6.6"; + public const string VersionString = "2.6.7"; public static System.Version Version = System.Version.Parse(VersionString); public Harmony Harmony { get; } = new Harmony(Id); @@ -39,7 +40,6 @@ public class TheOtherRolesPlugin : BasePlugin public static ConfigEntry Port { get; set; } public static IRegionInfo[] defaultRegions; - public static void UpdateRegions() { ServerManager serverManager = DestroyableSingleton.Instance; IRegionInfo[] regions = defaultRegions; diff --git a/Source Code/MeetingPatch.cs b/Source Code/MeetingPatch.cs index 4e5389de4..b64458f64 100644 --- a/Source Code/MeetingPatch.cs +++ b/Source Code/MeetingPatch.cs @@ -96,7 +96,7 @@ static bool Prefix(MeetingHud __instance) { int maxIdx = IndexOfMax(self, (byte p) => (int)p, out tie) - 1; GameData.PlayerInfo exiled = null; foreach (GameData.PlayerInfo pi in GameData.Instance.AllPlayers) { - if (pi.PlayerId == maxIdx) { + if (pi.PlayerId == maxIdx && !pi.IsDead) { exiled = pi; break; } @@ -226,7 +226,7 @@ static void Postfix(MeetingHud __instance, [HarmonyArgument(0)]byte[] states, [H } - static void onClick(int i, MeetingHud __instance) { + static void swapperOnClick(int i, MeetingHud __instance) { if (__instance.state == MeetingHud.VoteStates.Results) return; if (__instance.playerStates[i].isDead) return; @@ -269,7 +269,137 @@ static void onClick(int i, MeetingHud __instance) { } } + private static GameObject guesserUI; + static void guesserOnClick(int buttonTarget, MeetingHud __instance) { + if (guesserUI != null || !(__instance.state == MeetingHud.VoteStates.Voted || __instance.state == MeetingHud.VoteStates.NotVoted)) return; + + Transform container = UnityEngine.Object.Instantiate(__instance.transform.FindChild("Background"), __instance.transform); + container.transform.localPosition = new Vector3(0, 0, -5f); + guesserUI = container.gameObject; + + int i = 0; + var buttonTemplate = __instance.playerStates[0].transform.FindChild("votePlayerBase"); + var smallButtonTemplate = __instance.playerStates[0].Buttons.transform.Find("CancelButton"); + var textTemplate = __instance.playerStates[0].NameText; + + + Transform exitButton = UnityEngine.Object.Instantiate(smallButtonTemplate.transform, container); + exitButton.transform.localPosition = new Vector3(2.5f, 2.25f, -5); + exitButton.GetComponent().OnClick.RemoveAllListeners(); + exitButton.GetComponent().OnClick.AddListener((UnityEngine.Events.UnityAction)(() => { + UnityEngine.Object.Destroy(container.gameObject); + })); + + List confirmButtons = new List(); + + foreach (RoleInfo roleInfo in RoleInfo.allRoleInfos) { + if (roleInfo.roleId == RoleId.Lover || roleInfo.roleId == RoleId.Guesser || roleInfo == RoleInfo.niceMini) continue; // Not guessable roles + + Transform button = UnityEngine.Object.Instantiate(buttonTemplate.transform, container); + Transform confirm = UnityEngine.Object.Instantiate(smallButtonTemplate.transform, button); + confirmButtons.Add(confirm); + TMPro.TextMeshPro label = UnityEngine.Object.Instantiate(textTemplate, button); + int row = i/4, col = i%4; + button.localPosition = new Vector3(-3 + 1.83f * col, 1.5f - 0.4f * row, -5); + button.localScale = new Vector3(0.4f, 0.4f, 1f); + confirm.localScale = new Vector3(1.5f, 1.5f, 1f); + confirm.localPosition = new Vector3(0, 0, confirm.localPosition.z); + confirm.GetComponent().sprite = Guesser.getTargetSprite(); + confirm.GetComponent().color = Color.black; + confirm.gameObject.SetActive(false); + label.text = Helpers.cs(roleInfo.color, roleInfo.name); + label.alignment = TMPro.TextAlignmentOptions.Center; + label.transform.localPosition = new Vector3(0, 0, label.transform.localPosition.z); + label.transform.localScale *= 2; + int copiedIndex = i; + + button.GetComponent().OnClick.RemoveAllListeners(); + button.GetComponent().OnClick.AddListener((UnityEngine.Events.UnityAction)(() => { + confirmButtons.ForEach(x => x.gameObject.SetActive(false)); + confirm.gameObject.SetActive(true); + })); + + + confirm.GetComponent().OnClick.RemoveAllListeners(); + confirm.GetComponent().OnClick.AddListener((UnityEngine.Events.UnityAction)(() => { + PlayerControl target = Helpers.playerById((byte)__instance.playerStates[buttonTarget].TargetPlayerId); + if (!(__instance.state == MeetingHud.VoteStates.Voted || __instance.state == MeetingHud.VoteStates.NotVoted) || target == null || Guesser.remainingShots <= 0 ) return; + + var mainRoleInfo = RoleInfo.getRoleInfoForPlayer(target).FirstOrDefault(); + if (mainRoleInfo == null) return; + + target = (mainRoleInfo == roleInfo) ? target : PlayerControl.LocalPlayer; + + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.GuesserShoot, Hazel.SendOption.Reliable, -1); + writer.Write(target.PlayerId); + AmongUsClient.Instance.FinishRpcImmediately(writer); + RPCProcedure.guesserShoot(target.PlayerId); + + UnityEngine.Object.Destroy(container.gameObject); + __instance.playerStates.ToList().ForEach(x => { if (x.transform.FindChild("ShootButton") != null) UnityEngine.Object.Destroy(x.transform.FindChild("ShootButton").gameObject); }); + })); + + i++; + } + container.transform.localScale *= 0.75f; + } + + [HarmonyPatch(typeof(PlayerVoteArea), nameof(PlayerVoteArea.Select))] + class PlayerVoteAreaSelectPatch { + static bool Prefix(MeetingHud __instance) { + return !(PlayerControl.LocalPlayer != null && PlayerControl.LocalPlayer == Guesser.guesser && guesserUI != null); + } + } + + static void populateButtonsPostfix(MeetingHud __instance) { + // Add Swapper Buttons + if (Swapper.swapper != null && PlayerControl.LocalPlayer == Swapper.swapper && !Swapper.swapper.Data.IsDead) { + selections = new bool[__instance.playerStates.Length]; + renderers = new SpriteRenderer[__instance.playerStates.Length]; + + for (int i = 0; i < __instance.playerStates.Length; i++) { + PlayerVoteArea playerVoteArea = __instance.playerStates[i]; + if (playerVoteArea.isDead || (playerVoteArea.TargetPlayerId == Swapper.swapper.PlayerId && Swapper.canOnlySwapOthers)) continue; + + GameObject template = playerVoteArea.Buttons.transform.Find("CancelButton").gameObject; + GameObject checkbox = UnityEngine.Object.Instantiate(template); + checkbox.transform.SetParent(playerVoteArea.transform); + checkbox.transform.position = template.transform.position; + checkbox.transform.localPosition = new Vector3(0f, 0.03f, template.transform.localPosition.z); + SpriteRenderer renderer = checkbox.GetComponent(); + renderer.sprite = Swapper.getCheckSprite(); + renderer.color = Color.red; + + PassiveButton button = checkbox.GetComponent(); + button.OnClick.RemoveAllListeners(); + int copiedIndex = i; + button.OnClick.AddListener((UnityEngine.Events.UnityAction)(() => swapperOnClick(copiedIndex, __instance))); + + selections[i] = false; + renderers[i] = renderer; + } + } + + // Add Guesser Buttons + if (Guesser.guesser != null && PlayerControl.LocalPlayer == Guesser.guesser && !Guesser.guesser.Data.IsDead && Guesser.remainingShots >= 0) { + for (int i = 0; i < __instance.playerStates.Length; i++) { + PlayerVoteArea playerVoteArea = __instance.playerStates[i]; + if (playerVoteArea.isDead || playerVoteArea.TargetPlayerId == Guesser.guesser.PlayerId) continue; + + GameObject template = playerVoteArea.Buttons.transform.Find("CancelButton").gameObject; + GameObject targetBox = UnityEngine.Object.Instantiate(template, playerVoteArea.transform); + targetBox.name = "ShootButton"; + targetBox.transform.localPosition = new Vector3(0f, 0.03f, template.transform.localPosition.z); + SpriteRenderer renderer = targetBox.GetComponent(); + renderer.sprite = Guesser.getTargetSprite(); + PassiveButton button = targetBox.GetComponent(); + button.OnClick.RemoveAllListeners(); + int copiedIndex = i; + button.OnClick.AddListener((UnityEngine.Events.UnityAction)(() => guesserOnClick(copiedIndex, __instance))); + } + } + // Change buttons if there are more than 10 players if (__instance.playerStates != null && __instance.playerStates.Length > 10) { PlayerVoteArea[] playerStates = __instance.playerStates.OrderBy((PlayerVoteArea p) => p.isDead ? 50 : 0) @@ -295,35 +425,6 @@ static void populateButtonsPostfix(MeetingHud __instance) { area.transform.localPosition = new Vector3(-3.63f + 2.43f * col, 1.5f - 0.76f * row, -0.9f - 0.01f * row); } } - - // Add Swapper Buttons - if (Swapper.swapper == null || PlayerControl.LocalPlayer != Swapper.swapper || Swapper.swapper.Data.IsDead) return; - selections = new bool[__instance.playerStates.Length]; - renderers = new SpriteRenderer[__instance.playerStates.Length]; - - for (int i = 0; i < __instance.playerStates.Length; i++) { - PlayerVoteArea playerVoteArea = __instance.playerStates[i]; - if (playerVoteArea.isDead || (playerVoteArea.TargetPlayerId == Swapper.swapper.PlayerId && Swapper.canOnlySwapOthers)) continue; - - GameObject template = playerVoteArea.Buttons.transform.Find("CancelButton").gameObject; - GameObject checkbox = UnityEngine.Object.Instantiate(template); - checkbox.transform.SetParent(playerVoteArea.transform); - checkbox.transform.position = template.transform.position; - checkbox.transform.localPosition = new Vector3(0f, 0.03f, template.transform.localPosition.z); - SpriteRenderer renderer = checkbox.GetComponent(); - renderer.sprite = Swapper.getCheckSprite(); - renderer.color = Color.red; - - PassiveButton button = checkbox.GetComponent(); - button.OnClick.RemoveAllListeners(); - int copiedIndex = i; - button.OnClick.AddListener((UnityEngine.Events.UnityAction)(() => onClick(copiedIndex, __instance))); - - - - selections[i] = false; - renderers[i] = renderer; - } } [HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.ServerStart))] @@ -366,132 +467,4 @@ static void Postfix(MeetingHud __instance) { } } } - - [HarmonyPatch(typeof(ExileController), "Begin")] - class ExileBeginPatch { - public static void Prefix(ExileController __instance, [HarmonyArgument(0)]ref GameData.PlayerInfo exiled, [HarmonyArgument(1)]bool tie) { - // Shifter shift - if (Shifter.shifter != null && AmongUsClient.Instance.AmHost && Shifter.futureShift != null) { // We need to send the RPC from the host here, to make sure that the order of shifting and erasing is correct (for that reason the futureShifted and futureErased are being synced) - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ShifterShift, Hazel.SendOption.Reliable, -1); - writer.Write(Shifter.futureShift.PlayerId); - AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.shifterShift(Shifter.futureShift.PlayerId); - } - Shifter.futureShift = null; - - // Eraser erase - if (Eraser.eraser != null && AmongUsClient.Instance.AmHost && Eraser.futureErased != null) { // We need to send the RPC from the host here, to make sure that the order of shifting and erasing is correct (for that reason the futureShifted and futureErased are being synced) - foreach (PlayerControl target in Eraser.futureErased) { - if (target != null) { - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ErasePlayerRoles, Hazel.SendOption.Reliable, -1); - writer.Write(target.PlayerId); - AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.erasePlayerRoles(target.PlayerId); - } - } - } - Eraser.futureErased = new List(); - - // Trickster boxes - if (Trickster.trickster != null && JackInTheBox.hasJackInTheBoxLimitReached()) { - JackInTheBox.convertToVents(); - } - - // SecurityGuard vents and cameras - var allCameras = ShipStatus.Instance.AllCameras.ToList(); - MapOptions.camerasToAdd.ForEach(camera => { - camera.gameObject.SetActive(true); - camera.gameObject.GetComponent().color = Color.white; - allCameras.Add(camera); - }); - ShipStatus.Instance.AllCameras = allCameras.ToArray(); - MapOptions.camerasToAdd = new List(); - - foreach (Vent vent in MapOptions.ventsToSeal) { - PowerTools.SpriteAnim animator = vent.GetComponent(); - animator?.Stop(); - vent.EnterVentAnim = vent.ExitVentAnim = null; - vent.myRend.sprite = animator == null ? SecurityGuard.getStaticVentSealedSprite() : SecurityGuard.getAnimatedVentSealedSprite(); - vent.myRend.color = Color.white; - vent.name = "SealedVent_" + vent.name; - } - MapOptions.ventsToSeal = new List(); - } - } - - - [HarmonyPatch(typeof(UnityEngine.Object), nameof(UnityEngine.Object.Destroy), new Type[] { typeof(UnityEngine.Object) })] - class MeetingExiledEndPatch - { - static void Prefix(UnityEngine.Object obj) - { - if (ExileController.Instance != null && obj == ExileController.Instance.gameObject) - { - // Reset custom button timers where necessary - CustomButton.MeetingEndedUpdate(); - // Child set adapted cooldown - if (Child.child != null && PlayerControl.LocalPlayer == Child.child && Child.child.Data.IsImpostor) { - var multiplier = Child.isGrownUp() ? 0.66f : 2f; - Child.child.SetKillTimer(PlayerControl.GameOptions.KillCooldown * multiplier); - } - - // Seer spawn souls - if (Seer.deadBodyPositions != null && Seer.seer != null && PlayerControl.LocalPlayer == Seer.seer && (Seer.mode == 0 || Seer.mode == 2)) { - foreach (Vector3 pos in Seer.deadBodyPositions) { - GameObject soul = new GameObject(); - soul.transform.position = pos; - soul.layer = 5; - var rend = soul.AddComponent(); - rend.sprite = Seer.getSoulSprite(); - - if(Seer.limitSoulDuration) { - HudManager.Instance.StartCoroutine(Effects.Lerp(Seer.soulDuration, new Action((p) => { - if (rend != null) { - var tmp = rend.color; - tmp.a = Mathf.Clamp01(1 - p); - rend.color = tmp; - } - if (p == 1f && rend != null && rend.gameObject != null) UnityEngine.Object.Destroy(rend.gameObject); - }))); - } - } - Seer.deadBodyPositions = new List(); - } - - // Arsonist deactivate dead poolable players - if (Arsonist.arsonist != null && Arsonist.arsonist == PlayerControl.LocalPlayer) { - int visibleCounter = 0; - Vector3 bottomLeft = new Vector3(-HudManager.Instance.UseButton.transform.localPosition.x, HudManager.Instance.UseButton.transform.localPosition.y, HudManager.Instance.UseButton.transform.localPosition.z); - bottomLeft += new Vector3(-0.25f, -0.25f, 0); - foreach (PlayerControl p in PlayerControl.AllPlayerControls) { - if (!Arsonist.dousedIcons.ContainsKey(p.PlayerId)) continue; - if (p.Data.IsDead || p.Data.Disconnected) { - Arsonist.dousedIcons[p.PlayerId].gameObject.SetActive(false); - } else { - Arsonist.dousedIcons[p.PlayerId].transform.localPosition = bottomLeft + Vector3.right * visibleCounter * 0.35f; - visibleCounter++; - } - } - } - } - } - } - - [HarmonyPatch(typeof(TranslationController), nameof(TranslationController.GetString), new Type[] { typeof(StringNames), typeof(Il2CppReferenceArray) })] - class ExileControllerMessagePatch { - static void Postfix(ref string __result, [HarmonyArgument(0)]StringNames id, [HarmonyArgument(1)]Il2CppReferenceArray parts) { - if (ExileController.Instance != null && ExileController.Instance.exiled != null) { - PlayerControl player = Helpers.playerById(ExileController.Instance.exiled.Object.PlayerId); - if (player == null) return; - // Exile role text - if (id == StringNames.ExileTextPN || id == StringNames.ExileTextSN || id == StringNames.ExileTextPP || id == StringNames.ExileTextSP) { - __result = player.Data.PlayerName + " was The " + String.Join(" ", RoleInfo.getRoleInfoForPlayer(player).Select(x => x.name).ToArray()); - } - // Hide number of remaining impostors on Jester win - if (id == StringNames.ImpostorsRemainP || id == StringNames.ImpostorsRemainS) { - if (Jester.jester != null && player.PlayerId == Jester.jester.PlayerId) __result = ""; - } - } - } - } } \ No newline at end of file diff --git a/Source Code/PlayerControlPatch.cs b/Source Code/PlayerControlPatch.cs index b1e2e24dd..55a68888f 100644 --- a/Source Code/PlayerControlPatch.cs +++ b/Source Code/PlayerControlPatch.cs @@ -196,7 +196,7 @@ static void jackalSetTarget() { // Only exclude sidekick from beeing targeted if the jackal can create sidekicks from impostors if(Sidekick.sidekick != null) untargetablePlayers.Add(Sidekick.sidekick); } - if(Child.child != null && !Child.isGrownUp()) untargetablePlayers.Add(Child.child); // Exclude Jackal from targeting the Child unless it has grown up + if(Mini.mini != null && !Mini.isGrownUp()) untargetablePlayers.Add(Mini.mini); // Exclude Jackal from targeting the Mini unless it has grown up Jackal.currentTarget = setTarget(untargetablePlayers : untargetablePlayers); setPlayerOutline(Jackal.currentTarget, Palette.ImpostorRed); } @@ -205,7 +205,7 @@ static void sidekickSetTarget() { if (Sidekick.sidekick == null || Sidekick.sidekick != PlayerControl.LocalPlayer) return; var untargetablePlayers = new List(); if(Jackal.jackal != null) untargetablePlayers.Add(Jackal.jackal); - if(Child.child != null && !Child.isGrownUp()) untargetablePlayers.Add(Child.child); // Exclude Sidekick from targeting the Child unless it has grown up + if(Mini.mini != null && !Mini.isGrownUp()) untargetablePlayers.Add(Mini.mini); // Exclude Sidekick from targeting the Mini unless it has grown up Sidekick.currentTarget = setTarget(untargetablePlayers : untargetablePlayers); if (Sidekick.canKill) setPlayerOutline(Sidekick.currentTarget, Palette.ImpostorRed); } @@ -318,21 +318,21 @@ public static void playerSizeUpdate(PlayerControl p) { CircleCollider2D collider = p.GetComponent(); p.transform.localScale = new Vector3(0.7f, 0.7f, 1f); - collider.radius = Child.defaultColliderRadius; - collider.offset = Child.defaultColliderOffset * Vector2.down; + collider.radius = Mini.defaultColliderRadius; + collider.offset = Mini.defaultColliderOffset * Vector2.down; - // Set adapted player size to Child and Morphling - if (Child.child == null || Camouflager.camouflageTimer > 0f) return; + // Set adapted player size to Mini and Morphling + if (Mini.mini == null || Camouflager.camouflageTimer > 0f) return; - float growingProgress = Child.growingProgress(); + float growingProgress = Mini.growingProgress(); float scale = growingProgress * 0.35f + 0.35f; - float correctedColliderRadius = Child.defaultColliderRadius * 0.7f / scale; // scale / 0.7f is the factor by which we decrease the player size, hence we need to increase the collider size by 0.7f / scale + float correctedColliderRadius = Mini.defaultColliderRadius * 0.7f / scale; // scale / 0.7f is the factor by which we decrease the player size, hence we need to increase the collider size by 0.7f / scale - if (p == Child.child) { + if (p == Mini.mini) { p.transform.localScale = new Vector3(scale, scale, 1f); collider.radius = correctedColliderRadius; } - if (Morphling.morphling != null && p == Morphling.morphling && Morphling.morphTarget == Child.child && Morphling.morphTimer > 0f) { + if (Morphling.morphling != null && p == Morphling.morphling && Morphling.morphTarget == Mini.mini && Morphling.morphTimer > 0f) { p.transform.localScale = new Vector3(scale, scale, 1f); collider.radius = correctedColliderRadius; } @@ -426,7 +426,7 @@ public static void arsonistSetTarget() { public static void Postfix(PlayerControl __instance) { if (AmongUsClient.Instance.GameState != InnerNet.InnerNetClient.GameStates.Started) return; - // Child and Morphling shrink + // Mini and Morphling shrink playerSizeUpdate(__instance); if (PlayerControl.LocalPlayer == __instance) { @@ -484,10 +484,10 @@ public static void Postfix(PlayerControl __instance) { class PlayerPhysicsWalkPlayerToPatch { private static Vector2 offset = Vector2.zero; public static void Prefix(PlayerPhysics __instance) { - bool correctOffset = Camouflager.camouflageTimer <= 0f && (__instance.myPlayer == Child.child || (Morphling.morphling != null && __instance.myPlayer == Morphling.morphling && Morphling.morphTarget == Child.child && Morphling.morphTimer > 0f)); + bool correctOffset = Camouflager.camouflageTimer <= 0f && (__instance.myPlayer == Mini.mini || (Morphling.morphling != null && __instance.myPlayer == Morphling.morphling && Morphling.morphTarget == Mini.mini && Morphling.morphTimer > 0f)); if (correctOffset) { - float currentScaling = (Child.growingProgress() + 1) * 0.5f; - __instance.myPlayer.Collider.offset = currentScaling * Child.defaultColliderOffset * Vector2.down; + float currentScaling = (Mini.growingProgress() + 1) * 0.5f; + __instance.myPlayer.Collider.offset = currentScaling * Mini.defaultColliderOffset * Vector2.down; } } } @@ -513,7 +513,7 @@ public static void Prefix(PlayerControl __instance) { class RpcMurderPlayer { public static bool Prefix([HarmonyArgument(0)]PlayerControl target) { if (Helpers.handleMurderAttempt(target)) { // Custom checks - if (Child.child != null && PlayerControl.LocalPlayer == Child.child) { // Not checked by official servers + if (Mini.mini != null && PlayerControl.LocalPlayer == Mini.mini) { // 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); writer.Write(target.PlayerId); @@ -644,10 +644,10 @@ public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)]PlayerC } if (Seer.deadBodyPositions != null) Seer.deadBodyPositions.Add(target.transform.position); - // Child set adapted kill cooldown - if (Child.child != null && PlayerControl.LocalPlayer == Child.child && Child.child.Data.IsImpostor && Child.child == __instance) { - var multiplier = Child.isGrownUp() ? 0.66f : 2f; - Child.child.SetKillTimer(PlayerControl.GameOptions.KillCooldown * multiplier); + // Mini set adapted kill cooldown + if (Mini.mini != null && PlayerControl.LocalPlayer == Mini.mini && Mini.mini.Data.IsImpostor && Mini.mini == __instance) { + var multiplier = Mini.isGrownUp() ? 0.66f : 2f; + Mini.mini.SetKillTimer(PlayerControl.GameOptions.KillCooldown * multiplier); } } } @@ -657,7 +657,7 @@ class PlayerControlSetCoolDownPatch { public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)]float time) { if (PlayerControl.GameOptions.KillCooldown <= 0f) return false; float multiplier = 1f; - if (Child.child != null && PlayerControl.LocalPlayer == Child.child && Child.child.Data.IsImpostor) multiplier = Child.isGrownUp() ? 0.66f : 2f; + if (Mini.mini != null && PlayerControl.LocalPlayer == Mini.mini && Mini.mini.Data.IsImpostor) multiplier = Mini.isGrownUp() ? 0.66f : 2f; __instance.killTimer = Mathf.Clamp(time, 0f, PlayerControl.GameOptions.KillCooldown * multiplier); DestroyableSingleton.Instance.KillButton.SetCoolDown(__instance.killTimer, PlayerControl.GameOptions.KillCooldown * multiplier); @@ -681,17 +681,6 @@ public static void Prefix(KillAnimation __instance, [HarmonyArgument(0)]ref Play [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.Exiled))] public static class ExilePlayerPatch { - public static void Prefix(PlayerControl __instance) { - // Child exile lose condition - if (Child.child != null && Child.child == __instance && !Child.isGrownUp() && !Child.child.Data.IsImpostor) { - Child.triggerChildLose = true; - } - // Jester win condition - else if (Jester.jester != null && Jester.jester == __instance) { - Jester.triggerJesterWin = true; - } - } - public static void Postfix(PlayerControl __instance) { // Collect dead player info diff --git a/Source Code/RPC.cs b/Source Code/RPC.cs index ae643f2fa..da98b4002 100644 --- a/Source Code/RPC.cs +++ b/Source Code/RPC.cs @@ -30,7 +30,7 @@ enum RoleId { Morphling, Camouflager, Hacker, - Child, + Mini, Tracker, Vampire, Snitch, @@ -43,6 +43,7 @@ enum RoleId { Warlock, SecurityGuard, Arsonist, + Guesser, Crewmate, Impostor } @@ -90,7 +91,8 @@ enum CustomRPC WarlockCurseKill, PlaceCamera, SealVent, - ArsonistWin + ArsonistWin, + GuesserShoot } public static class RPCProcedure { @@ -182,8 +184,8 @@ public static void setRole(byte roleId, byte playerId, byte flag) { case RoleId.Hacker: Hacker.hacker = player; break; - case RoleId.Child: - Child.child = player; + case RoleId.Mini: + Mini.mini = player; break; case RoleId.Tracker: Tracker.tracker = player; @@ -221,6 +223,9 @@ public static void setRole(byte roleId, byte playerId, byte flag) { case RoleId.Arsonist: Arsonist.arsonist = player; break; + case RoleId.Guesser: + Guesser.guesser = player; + break; } } } @@ -230,8 +235,14 @@ public static void setUncheckedColor(byte colorId, byte playerId) { if (player != null) player.SetColor(colorId); } - public static void versionHandshake(int major, int minor, int build, int clientId) { - GameStartManagerPatch.playerVersions[clientId] = new System.Version(major, minor, build); + public static void versionHandshake(int major, int minor, int build, int revision, Guid guid, int clientId) { + System.Version ver; + if (revision < 0) + ver = new System.Version(major, minor, build); + else + ver = new System.Version(major, minor, build, revision); + + GameStartManagerPatch.playerVersions[clientId] = new GameStartManagerPatch.PlayerVersion(ver, guid); } public static void useUncheckedVent(int ventId, byte playerId, byte isEnter) { @@ -368,8 +379,6 @@ public static void shifterShift(byte targetId) { else if (Lovers.lover2 != null && player == Lovers.lover2) Lovers.lover2 = oldShifter; // Shift role - if (Jester.jester != null && Jester.jester == player) - Jester.jester = oldShifter; if (Mayor.mayor != null && Mayor.mayor == player) Mayor.mayor = oldShifter; if (Engineer.engineer != null && Engineer.engineer == player) @@ -390,8 +399,8 @@ public static void shifterShift(byte targetId) { Seer.seer = oldShifter; if (Hacker.hacker != null && Hacker.hacker == player) Hacker.hacker = oldShifter; - if (Child.child != null && Child.child == player) - Child.child = oldShifter; + if (Mini.mini != null && Mini.mini == player) + Mini.mini = oldShifter; if (Tracker.tracker != null && Tracker.tracker == player) Tracker.tracker = oldShifter; if (Snitch.snitch != null && Snitch.snitch == player) @@ -400,8 +409,8 @@ public static void shifterShift(byte targetId) { Spy.spy = oldShifter; if (SecurityGuard.securityGuard != null && SecurityGuard.securityGuard == player) SecurityGuard.securityGuard = oldShifter; - if (Arsonist.arsonist != null && Arsonist.arsonist == player) - Arsonist.arsonist = oldShifter; + if (Guesser.guesser != null && Guesser.guesser == player) + Guesser.guesser = oldShifter; // Set cooldowns to max for both players if (PlayerControl.LocalPlayer == oldShifter || PlayerControl.LocalPlayer == player) @@ -530,7 +539,7 @@ public static void erasePlayerRoles(byte playerId, bool ignoreLovers = false) { if (player == Shifter.shifter) Shifter.clearAndReload(); if (player == Seer.seer) Seer.clearAndReload(); if (player == Hacker.hacker) Hacker.clearAndReload(); - if (player == Child.child) Child.clearAndReload(); + if (player == Mini.mini) Mini.clearAndReload(); if (player == Tracker.tracker) Tracker.clearAndReload(); if (player == Snitch.snitch) Snitch.clearAndReload(); if (player == Swapper.swapper) Swapper.clearAndReload(); @@ -552,6 +561,7 @@ public static void erasePlayerRoles(byte playerId, bool ignoreLovers = false) { // Other roles if (player == Jester.jester) Jester.clearAndReload(); if (player == Arsonist.arsonist) Arsonist.clearAndReload(); + if (player == Guesser.guesser) Guesser.clearAndReload(); if (!ignoreLovers && (player == Lovers.lover1 || player == Lovers.lover2)) { // The whole Lover couple is being erased Lovers.clearAndReload(); } @@ -646,6 +656,26 @@ public static void sealVent(int ventId) { public static void arsonistWin() { Arsonist.triggerArsonistWin = true; } + + public static void guesserShoot(byte playerId) { + PlayerControl target = Helpers.playerById(playerId); + if (target == null) return; + target.Exiled(); + Guesser.remainingShots = Mathf.Max(0, Guesser.remainingShots - 1); + if (Constants.ShouldPlaySfx()) SoundManager.Instance.PlaySound(target.KillSfx, false, 0.8f); + if (MeetingHud.Instance) { + foreach (PlayerVoteArea pva in MeetingHud.Instance.playerStates) { + if (pva.TargetPlayerId == playerId) { + pva.SetDead(playerId == PlayerControl.LocalPlayer.PlayerId, pva.didReport, true); + pva.Overlay.gameObject.SetActive(true); + pva.Overlay.transform.GetChild(0).gameObject.SetActive(true); + } + } + if (AmongUsClient.Instance.AmHost) MeetingHud.Instance.CheckForEndVoting(); + } + if (HudManager.Instance != null && Guesser.guesser != null && PlayerControl.LocalPlayer == target) + HudManager.Instance.KillOverlay.ShowOne(Guesser.guesser.Data, target.Data); + } } [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.HandleRpc))] @@ -685,7 +715,17 @@ static void Postfix([HarmonyArgument(0)]byte callId, [HarmonyArgument(1)]Message byte minor = reader.ReadByte(); byte patch = reader.ReadByte(); int versionOwnerId = reader.ReadPackedInt32(); - RPCProcedure.versionHandshake(major, minor, patch, versionOwnerId); + byte revision = 0xFF; + Guid guid; + if (reader.Length >= 24) { + revision = reader.ReadByte(); + // GUID + byte[] gbytes = reader.ReadBytes(16); + guid = new Guid(gbytes); + } else { + guid = new Guid(new byte[16]); + } + RPCProcedure.versionHandshake(major, minor, patch, revision == 0xFF ? -1 : revision, guid, versionOwnerId); break; case (byte)CustomRPC.UseUncheckedVent: int ventId = reader.ReadPackedInt32(); @@ -800,6 +840,9 @@ static void Postfix([HarmonyArgument(0)]byte callId, [HarmonyArgument(1)]Message case (byte)CustomRPC.ArsonistWin: RPCProcedure.arsonistWin(); break; + case (byte)CustomRPC.GuesserShoot: + RPCProcedure.guesserShoot(reader.ReadByte()); + break; } } } diff --git a/Source Code/RoleAssignmentPatch.cs b/Source Code/RoleAssignmentPatch.cs index e4a257391..4d3759704 100644 --- a/Source Code/RoleAssignmentPatch.cs +++ b/Source Code/RoleAssignmentPatch.cs @@ -26,6 +26,8 @@ public static void Postfix([HarmonyArgument(0)]Il2CppReferenceArray p.PlayerId == firstLoverId); setRoleToRandomPlayer((byte)RoleId.Lover, crewmatesWithoutFirstLover, 1, false); - System.Console.WriteLine(crewmatesWithoutFirstLover.Count); - } } } @@ -141,13 +141,24 @@ private static void assignSpecialRoles(RoleAssignmentData data) { data.maxImpostorRoles -= 3; } - // Assign Child - if (rnd.Next(1, 101) <= CustomOptionHolder.childSpawnRate.getSelection() * 10) { + // Assign Mini + if (rnd.Next(1, 101) <= CustomOptionHolder.miniSpawnRate.getSelection() * 10) { if (data.impostors.Count > 0 && data.maxImpostorRoles > 0 && rnd.Next(1, 101) <= 33) { - setRoleToRandomPlayer((byte)RoleId.Child, data.impostors); + setRoleToRandomPlayer((byte)RoleId.Mini, data.impostors); + data.maxImpostorRoles--; + } else if (data.crewmates.Count > 0 && data.maxCrewmateRoles > 0) { + setRoleToRandomPlayer((byte)RoleId.Mini, data.crewmates); + data.maxCrewmateRoles--; + } + } + + // Assign Guesser + if (rnd.Next(1, 101) <= CustomOptionHolder.guesserSpawnRate.getSelection() * 10) { + if (data.impostors.Count > 0 && data.maxImpostorRoles > 0 && rnd.Next(1, 101) <= CustomOptionHolder.guesserIsImpGuesserRate.getSelection() * 10) { + setRoleToRandomPlayer((byte)RoleId.Guesser, data.impostors); data.maxImpostorRoles--; } else if (data.crewmates.Count > 0 && data.maxCrewmateRoles > 0) { - setRoleToRandomPlayer((byte)RoleId.Child, data.crewmates); + setRoleToRandomPlayer((byte)RoleId.Guesser, data.crewmates); data.maxCrewmateRoles--; } } diff --git a/Source Code/RoleInfo.cs b/Source Code/RoleInfo.cs index 1ead17e8f..9db07c984 100644 --- a/Source Code/RoleInfo.cs +++ b/Source Code/RoleInfo.cs @@ -22,246 +22,125 @@ class RoleInfo { this.roleId = roleId; } + public static RoleInfo jester = new RoleInfo("Jester", Jester.color, "Get voted out", "Get voted out", RoleId.Jester); + 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); + public static RoleInfo lighter = new RoleInfo("Lighter", Lighter.color, "Your light never goes out", "Your light never goes out", RoleId.Lighter); + public static RoleInfo godfather = new RoleInfo("Godfather", Godfather.color, "Kill all Crewmates", "Kill all Crewmates", RoleId.Godfather); + public static RoleInfo mafioso = new RoleInfo("Mafioso", Mafioso.color, "Work with the Mafia to kill the Crewmates", "Kill all Crewmates", RoleId.Mafioso); + public static RoleInfo janitor = new RoleInfo("Janitor", Janitor.color, "Work with the Mafia by hiding dead bodies", "Hide dead bodies", RoleId.Janitor); + public static RoleInfo morphling = new RoleInfo("Morphling", Morphling.color, "Change your look to not get caught", "Change your look", RoleId.Morphling); + public static RoleInfo camouflager = new RoleInfo("Camouflager", Camouflager.color, "Camouflage and kill the Crewmates", "Hide among others", RoleId.Camouflager); + public static RoleInfo vampire = new RoleInfo("Vampire", Vampire.color, "Kill the Crewmates with your bites", "Bite your enemies", RoleId.Vampire); + public static RoleInfo eraser = new RoleInfo("Eraser", Eraser.color, "Kill the Crewmates and erase their roles", "Erase the roles of your enemies", RoleId.Eraser); + public static RoleInfo trickster = new RoleInfo("Trickster", Trickster.color, "Use your jack-in-the-boxes to surprise others", "Surprise your enemies", RoleId.Trickster); + public static RoleInfo cleaner = new RoleInfo("Cleaner", Cleaner.color, "Kill everyone and leave no traces", "Clean up dead bodies", RoleId.Cleaner); + public static RoleInfo warlock = new RoleInfo("Warlock", Warlock.color, "Curse other players and kill everyone", "Curse and kill everyone", RoleId.Warlock); + public static RoleInfo detective = new RoleInfo("Detective", Detective.color, "Find the Impostors by examining footprints", "Examine footprints", RoleId.Detective); + public static RoleInfo timeMaster = new RoleInfo("Time Master", TimeMaster.color, "Save yourself with your time shield", "Use your time shield", RoleId.TimeMaster); + public static RoleInfo medic = new RoleInfo("Medic", Medic.color, "Protect someone with your shield", "Protect other players", RoleId.Medic); + public static RoleInfo shifter = new RoleInfo("Shifter", Shifter.color, "Shift your role", "Shift your role", RoleId.Shifter); + public static RoleInfo swapper = new RoleInfo("Swapper", Swapper.color, "Swap votes to exile the Impostors", "Swap votes", RoleId.Swapper); + public static RoleInfo seer = new RoleInfo("Seer", Seer.color, "You will see players die", "You will see players die", RoleId.Seer); + public static RoleInfo hacker = new RoleInfo("Hacker", Hacker.color, "Hack systems to find the Impostors", "Hack to find the Impostors", RoleId.Hacker); + public static RoleInfo niceMini = new RoleInfo("Nice Mini", Mini.color, "No one will harm you until you grow up", "No one will harm you", RoleId.Mini); + 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 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 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 impostor = new RoleInfo("Impostor", Palette.ImpostorRed, Helpers.cs(Palette.ImpostorRed, "Sabotage and kill everyone"), "Sabotage and kill everyone", RoleId.Impostor); + public static RoleInfo crewmate = new RoleInfo("Crewmate", Color.white, "Find the Impostors", "Find the Impostors", RoleId.Crewmate); + public static RoleInfo lover = new RoleInfo("Lover", Lovers.color, $"You are in love", $"You are in love", RoleId.Lover); + + public static List allRoleInfos = new List() { + impostor, + godfather, + mafioso, + janitor, + morphling, + camouflager, + vampire, + eraser, + trickster, + cleaner, + warlock, + niceMini, + evilMini, + goodGuesser, + badGuesser, + lover, + jester, + arsonist, + jackal, + sidekick, + crewmate, + shifter, + mayor, + engineer, + sheriff, + lighter, + detective, + timeMaster, + medic, + swapper, + seer, + hacker, + tracker, + snitch, + spy, + securityGuard + }; + public static List getRoleInfoForPlayer(PlayerControl p) { List infos = new List(); + if (p == null) return infos; // Special roles - if (Jester.jester != null && p == Jester.jester) { - infos.Add(new RoleInfo("Jester", - Jester.color, - "Get voted out", - "Get voted out", - RoleId.Jester)); - } - if (Mayor.mayor != null && p == Mayor.mayor) { - infos.Add(new RoleInfo("Mayor", - Mayor.color, - "Your vote counts twice", - "Your vote counts twice", - RoleId.Mayor)); - } - if (Engineer.engineer != null && p == Engineer.engineer) { - infos.Add(new RoleInfo("Engineer", - Engineer.color, - "Maintain important systems on the ship", - "Repair the ship", - RoleId.Engineer)); - } - if (Sheriff.sheriff != null && p == Sheriff.sheriff) { - infos.Add(new RoleInfo("Sheriff", - Sheriff.color, - "Shoot the Impostors", - "Shoot the Impostors", - RoleId.Sheriff)); - } - if (Lighter.lighter != null && p == Lighter.lighter) { - infos.Add(new RoleInfo("Lighter", - Lighter.color, - "Your light never goes out", - "Your light never goes out", - RoleId.Lighter)); - } - if (Godfather.godfather != null && p == Godfather.godfather) { - infos.Add(new RoleInfo("Godfather", - Godfather.color, - "Kill all Crewmates", - "Kill all Crewmates", - RoleId.Godfather)); - ; - } - if (Mafioso.mafioso != null && p == Mafioso.mafioso) { - infos.Add(new RoleInfo("Mafioso", - Mafioso.color, - "Work with the Mafia to kill the Crewmates", - "Kill all Crewmates", - RoleId.Mafioso)); - } - if (Janitor.janitor != null && p == Janitor.janitor) { - infos.Add(new RoleInfo("Janitor", - Janitor.color, - "Work with the Mafia by hiding dead bodies", - "Hide dead bodies", - RoleId.Janitor)); - } - if (Morphling.morphling != null && p == Morphling.morphling) { - infos.Add(new RoleInfo("Morphling", - Morphling.color, - "Change your look to not get caught", - "Change your look", - RoleId.Morphling)); - } - if (Camouflager.camouflager != null && p == Camouflager.camouflager) { - infos.Add(new RoleInfo("Camouflager", - Camouflager.color, - "Camouflage and kill the Crewmates", - "Hide among others", - RoleId.Camouflager)); - } - if (Vampire.vampire != null && p == Vampire.vampire) { - infos.Add(new RoleInfo("Vampire", - Vampire.color, - "Kill the Crewmates with your bites", - "Bite your enemies", - RoleId.Vampire)); - } - if (Eraser.eraser != null && p == Eraser.eraser) { - infos.Add(new RoleInfo("Eraser", - Eraser.color, - "Kill the Crewmates and erase their roles", - "Erase the roles of your enemies", - RoleId.Eraser)); - } - if (Trickster.trickster != null && p == Trickster.trickster) { - infos.Add(new RoleInfo("Trickster", - Trickster.color, - "Use your jack-in-the-boxes to surprise others", - "Surprise your enemies", - RoleId.Trickster)); - } - if (Cleaner.cleaner != null && p == Cleaner.cleaner) { - infos.Add(new RoleInfo("Cleaner", - Cleaner.color, - "Kill everyone and leave no traces", - "Clean up dead bodies", - RoleId.Cleaner)); - } - if (Warlock.warlock != null && p == Warlock.warlock) { - infos.Add(new RoleInfo("Warlock", - Warlock.color, - "Curse other players and kill everyone", - "Curse and kill everyone", - RoleId.Warlock)); - } - if (Detective.detective != null && p == Detective.detective) { - infos.Add(new RoleInfo("Detective", - Detective.color, - "Find the Impostors by examining footprints", - "Examine footprints", - RoleId.Detective)); - } - if (TimeMaster.timeMaster != null && p == TimeMaster.timeMaster) { - infos.Add(new RoleInfo("Time Master", - TimeMaster.color, - "Save yourself with your time shield", - "Use your time shield", - RoleId.TimeMaster)); - } - if (Medic.medic != null && p == Medic.medic) { - infos.Add(new RoleInfo("Medic", - Medic.color, - "Protect someone with your shield", - "Protect other players", - RoleId.Medic)); - } - if (Shifter.shifter != null && p == Shifter.shifter) { - infos.Add(new RoleInfo("Shifter", - Shifter.color, - "Shift your role", - "Shift your role", - RoleId.Shifter)); - } - if (Swapper.swapper != null && p == Swapper.swapper) { - infos.Add(new RoleInfo("Swapper", - Swapper.color, - "Swap votes to exile the Impostors", - "Swap votes", - RoleId.Swapper)); - } - if (Seer.seer != null && p == Seer.seer) { - infos.Add(new RoleInfo("Seer", - Seer.color, - "You will see players die", - "You will see players die", - RoleId.Seer)); - } - if (Hacker.hacker != null && p == Hacker.hacker) { - infos.Add(new RoleInfo("Hacker", - Hacker.color, - "Hack to find the Impostors", - "Hack to find the Impostors", - RoleId.Hacker)); - } - if (Child.child != null && p == Child.child) { - infos.Add(new RoleInfo(p.Data.IsImpostor ? "Bad Child" : "Good Child", - p.Data.IsImpostor ? Palette.ImpostorRed : Child.color, - "No one will harm you until you grow up", - "No one will harm you", - RoleId.Child)); - } - if (Tracker.tracker != null && p == Tracker.tracker) { - infos.Add(new RoleInfo("Tracker", - Tracker.color, - "Track the Impostors down", - "Track the Impostors down", - RoleId.Tracker)); - } - if (Snitch.snitch != null && p == Snitch.snitch) { - infos.Add(new RoleInfo("Snitch", - Snitch.color, - "Finish your tasks to find the Impostors", - "Finish your tasks", - RoleId.Snitch)); - } - if ((Jackal.jackal != null && p == Jackal.jackal) || (Jackal.formerJackals != null && Jackal.formerJackals.Any(x => x.PlayerId == p.PlayerId))) { - infos.Add(new RoleInfo("Jackal", - Jackal.color, - "Kill all Crewmates and Impostors to win", - "Kill everyone", - RoleId.Jackal)); - } - if (Sidekick.sidekick != null && p == Sidekick.sidekick) { - infos.Add(new RoleInfo("Sidekick", - Sidekick.color, - "", - "Help your Jackal to kill everyone", - RoleId.Sidekick)); - } - if (Spy.spy != null && p == Spy.spy) { - infos.Add(new RoleInfo("Spy", - Spy.color, - "Confuse the Impostors", - "Confuse the Impostors", - RoleId.Spy)); - } - if (SecurityGuard.securityGuard != null && p == SecurityGuard.securityGuard) { - infos.Add(new RoleInfo("Security Guard", - SecurityGuard.color, - "Seal vents and place cameras", - "Seal vents and place cameras", - RoleId.SecurityGuard)); - } - if (Arsonist.arsonist != null && p == Arsonist.arsonist) { - infos.Add(new RoleInfo("Arsonist", - Arsonist.color, - "Let them burn", - "Let them burn", - RoleId.Arsonist)); - } + if (p == Jester.jester) infos.Add(jester); + if (p == Mayor.mayor) infos.Add(mayor); + if (p == Engineer.engineer) infos.Add(engineer); + if (p == Sheriff.sheriff) infos.Add(sheriff); + if (p == Lighter.lighter) infos.Add(lighter); + if (p == Godfather.godfather) infos.Add(godfather); + if (p == Mafioso.mafioso) infos.Add(mafioso); + if (p == Janitor.janitor) infos.Add(janitor); + if (p == Morphling.morphling) infos.Add(morphling); + if (p == Camouflager.camouflager) infos.Add(camouflager); + if (p == Vampire.vampire) infos.Add(vampire); + if (p == Eraser.eraser) infos.Add(eraser); + if (p == Trickster.trickster) infos.Add(trickster); + if (p == Cleaner.cleaner) infos.Add(cleaner); + if (p == Warlock.warlock) infos.Add(warlock); + if (p == Detective.detective) infos.Add(detective); + if (p == TimeMaster.timeMaster) infos.Add(timeMaster); + if (p == Medic.medic) infos.Add(medic); + if (p == Shifter.shifter) infos.Add(shifter); + 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 == 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); + if (p == Sidekick.sidekick) infos.Add(sidekick); + 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); // Default roles - if (infos.Count == 0 && p.Data.IsImpostor) { // Just Impostor - infos.Add(new RoleInfo("Impostor", - Palette.ImpostorRed, - Helpers.cs(Palette.ImpostorRed, "Sabotage and kill everyone"), - "Sabotage and kill everyone", - RoleId.Impostor)); - } else if (infos.Count == 0) { // Just Crewmate - infos.Add(new RoleInfo("Crewmate", - Color.white, - "Find the Impostors", - "Find the Impostors", - RoleId.Crewmate)); - } + if (infos.Count == 0 && p.Data.IsImpostor) infos.Add(impostor); // Just Impostor + if (infos.Count == 0 && !p.Data.IsImpostor) infos.Add(crewmate); // Just Crewmate // Modifier - if ((Lovers.lover1 != null && p == Lovers.lover1) || (Lovers.lover2 != null && p == Lovers.lover2)) { - var partnerName = (p == Lovers.lover1) ? Lovers.lover2.Data.PlayerName : Lovers.lover1.Data.PlayerName; - infos.Add(new RoleInfo("Lover", - Lovers.color, - $"You are in Love with {partnerName}", - $"You are in love with {partnerName}", - RoleId.Lover)); - } + if (p == Lovers.lover1|| p == Lovers.lover2) infos.Add(lover); return infos; } diff --git a/Source Code/TheOtherRoles.cs b/Source Code/TheOtherRoles.cs index 848664f59..74e0d472a 100644 --- a/Source Code/TheOtherRoles.cs +++ b/Source Code/TheOtherRoles.cs @@ -37,7 +37,7 @@ public static void clearAndReloadRoles() { Morphling.clearAndReload(); Camouflager.clearAndReload(); Hacker.clearAndReload(); - Child.clearAndReload(); + Mini.clearAndReload(); Tracker.clearAndReload(); Vampire.clearAndReload(); Snitch.clearAndReload(); @@ -50,11 +50,12 @@ public static void clearAndReloadRoles() { Warlock.clearAndReload(); SecurityGuard.clearAndReload(); Arsonist.clearAndReload(); + Guesser.clearAndReload(); } public static class Jester { public static PlayerControl jester; - public static Color color = new Color(255f / 255f, 84f / 255f, 167f / 255f, 1); + public static Color color = new Color32(236, 98, 165, byte.MaxValue); public static bool triggerJesterWin = false; public static bool canCallEmergency = true; @@ -70,7 +71,7 @@ public static void clearAndReload() { public static class Mayor { public static PlayerControl mayor; - public static Color color = new Color(105f / 255f, 58f / 255f, 58f / 255f, 1); + public static Color color = new Color32(32, 77, 66, byte.MaxValue); public static void clearAndReload() { mayor = null; @@ -79,7 +80,7 @@ public static void clearAndReload() { public static class Engineer { public static PlayerControl engineer; - public static Color color = new Color(98f / 255f, 216f / 255f, 240f / 255f, 1); + public static Color color = new Color32(0, 40, 245, byte.MaxValue); public static bool usedRepair; private static Sprite buttonSprite; @@ -135,7 +136,7 @@ public static void clearAndReload() { public static class Sheriff { public static PlayerControl sheriff; - public static Color color = new Color(255f / 255f, 204f / 255f, 0f / 255f, 1); + public static Color color = new Color32(248, 205, 70, byte.MaxValue); public static float cooldown = 30f; public static bool canKillNeutrals = false; @@ -154,7 +155,7 @@ public static void clearAndReload() { public static class Lighter { public static PlayerControl lighter; - public static Color color = new Color(250f / 255f, 204f / 255f, 37f / 255f, 1); + public static Color color = new Color32(238, 229, 190, byte.MaxValue); public static float lighterModeLightsOnVision = 2f; public static float lighterModeLightsOffVision = 0.75f; @@ -183,7 +184,7 @@ public static void clearAndReload() { public static class Detective { public static PlayerControl detective; - public static Color color = new Color(2f / 255f, 61f / 255f, 156f / 255f, 1); + public static Color color = new Color32(45, 106, 165, byte.MaxValue); public static float footprintIntervall = 1f; public static float footprintDuration = 1f; @@ -206,7 +207,7 @@ public static void clearAndReload() { public static class TimeMaster { public static PlayerControl timeMaster; - public static Color color = new Color(114f / 255f, 234f / 255f, 247f / 255f, 1); + public static Color color = new Color32(112, 142, 239, byte.MaxValue); public static bool reviveDuringRewind = false; public static float rewindTime = 3f; @@ -236,13 +237,13 @@ public static void clearAndReload() { public static class Medic { public static PlayerControl medic; public static PlayerControl shielded; - public static Color color = new Color(0f / 255f, 80f / 255f, 105f / 255f, 1); + public static Color color = new Color32(126, 251, 194, byte.MaxValue); public static bool usedShield; public static int showShielded = 0; public static bool showAttemptToShielded = false; - public static Color shieldedColor = new Color(0f / 255f, 221f / 255f, 255f / 255f, 1); + public static Color shieldedColor = new Color32(0, 221, 255, byte.MaxValue); public static PlayerControl currentTarget; private static Sprite buttonSprite; @@ -257,7 +258,6 @@ public static void clearAndReload() { shielded = null; currentTarget = null; usedShield = false; - shieldedColor = new Color(0f / 255f, 221f / 255f, 255f / 255f, 1); showShielded = CustomOptionHolder.medicShowShielded.getSelection(); showAttemptToShielded = CustomOptionHolder.medicShowAttemptToShielded.getBool(); } @@ -265,7 +265,7 @@ public static void clearAndReload() { public static class Shifter { public static PlayerControl shifter; - public static Color color = new Color(90f / 255f, 90f / 255f, 90f / 255f, 1); + public static Color color = new Color32(102, 102, 102, byte.MaxValue); public static PlayerControl futureShift; public static PlayerControl currentTarget; @@ -286,7 +286,7 @@ public static void clearAndReload() { public static class Swapper { public static PlayerControl swapper; - public static Color color = new Color(240f / 255f, 128f / 255f, 72f / 255f, 1); + public static Color color = new Color32(134, 55, 86, byte.MaxValue); private static Sprite spriteCheck; public static bool canCallEmergency = false; public static bool canOnlySwapOthers = false; @@ -312,7 +312,7 @@ public static void clearAndReload() { public static class Lovers { public static PlayerControl lover1; public static PlayerControl lover2; - public static Color color = new Color(252f / 255f, 3f / 255f, 190f / 255f, 1); + public static Color color = new Color32(232, 57, 185, byte.MaxValue); public static bool bothDie = true; // Lovers save if next to be exiled is a lover, because RPC of ending game comes before RPC of exiled @@ -348,7 +348,7 @@ public static void clearAndReload() { public static class Seer { public static PlayerControl seer; - public static Color color = new Color(60f / 255f, 181f / 255f, 100f / 255f, 1); + public static Color color = new Color32(97, 178, 108, byte.MaxValue); public static List deadBodyPositions = new List(); public static float soulDuration = 15f; @@ -462,7 +462,7 @@ public static void clearAndReload() { public static class Hacker { public static PlayerControl hacker; - public static Color color = new Color(252f / 255f, 90f / 255f, 30f / 255f, 1); + public static Color color = new Color32(117, 250, 76, byte.MaxValue); public static float cooldown = 30f; public static float duration = 10f; @@ -485,20 +485,20 @@ public static void clearAndReload() { } } - public static class Child { - public static PlayerControl child; + public static class Mini { + public static PlayerControl mini; public static Color color = Color.white; public const float defaultColliderRadius = 0.2233912f; public const float defaultColliderOffset = 0.3636057f; public static float growingUpDuration = 400f; public static DateTime timeOfGrowthStart = DateTime.UtcNow; - public static bool triggerChildLose = false; + public static bool triggerMiniLose = false; public static void clearAndReload() { - child = null; - triggerChildLose = false; - growingUpDuration = CustomOptionHolder.childGrowingUpDuration.getFloat(); + mini = null; + triggerMiniLose = false; + growingUpDuration = CustomOptionHolder.miniGrowingUpDuration.getFloat(); timeOfGrowthStart = DateTime.UtcNow; } @@ -516,7 +516,7 @@ public static bool isGrownUp() { public static class Tracker { public static PlayerControl tracker; - public static Color color = new Color(117f / 255f, 209f / 255f, 255f / 255f, 1); + public static Color color = new Color32(100, 58, 220, byte.MaxValue); public static float updateIntervall = 5f; @@ -589,7 +589,7 @@ public static void clearAndReload() { public static class Snitch { public static PlayerControl snitch; - public static Color color = new Color(227f / 255f, 251f / 255f, 47f / 255f, 1); + public static Color color = new Color32(184, 251, 79, byte.MaxValue); public static List localArrows = new List(); public static int taskCountForImpostors = 1; @@ -608,7 +608,7 @@ public static void clearAndReload() { public static class Jackal { public static PlayerControl jackal; - public static Color color = new Color(0f / 255f, 180f / 255f, 235f / 255f, 1); + public static Color color = new Color32(0, 180, 235, byte.MaxValue); public static PlayerControl fakeSidekick; public static PlayerControl currentTarget; @@ -656,7 +656,7 @@ public static void clearAndReload() { public static class Sidekick { public static PlayerControl sidekick; - public static Color color = new Color(0f / 255f, 180f / 255f, 235f / 255f, 1); + public static Color color = new Color32(0, 180, 235, byte.MaxValue); public static PlayerControl currentTarget; @@ -829,7 +829,7 @@ public static void resetCurse() { public static class SecurityGuard { public static PlayerControl securityGuard; - public static Color color = new Color(171/255f, 159f/255f, 55f/255f, 1f); + public static Color color = new Color32(195, 178, 95, byte.MaxValue); public static float cooldown = 30f; public static int remainingScrews = 7; @@ -880,7 +880,7 @@ public static void clearAndReload() { public static class Arsonist { public static PlayerControl arsonist; - public static Color color = new Color(1, 200f/255f, 0, 1f); + public static Color color = new Color32(238, 112, 46, byte.MaxValue); public static float cooldown = 30f; public static float duration = 3f; @@ -912,7 +912,7 @@ public static bool dousedEveryoneAlive() { public static void clearAndReload() { arsonist = null; currentTarget = null; - douseTarget = null; + douseTarget = null; triggerArsonistWin = false; dousedPlayers = new List(); foreach (PoolablePlayer p in dousedIcons.Values) { @@ -925,4 +925,24 @@ public static void clearAndReload() { duration = CustomOptionHolder.arsonistDuration.getFloat(); } } -} + + public static class Guesser { + public static PlayerControl guesser; + public static Color color = new Color32(255, 255, 0, byte.MaxValue); + private static Sprite targetSprite; + + public static int remainingShots = 2; + + public static Sprite getTargetSprite() { + if (targetSprite) return targetSprite; + targetSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.TargetIcon.png", 150f); + return targetSprite; + } + + public static void clearAndReload() { + guesser = null; + + remainingShots = Mathf.RoundToInt(CustomOptionHolder.guesserNumberOfShots.getFloat()); + } + } +} \ No newline at end of file diff --git a/Source Code/TheOtherRoles.csproj b/Source Code/TheOtherRoles.csproj index 87afba8c5..d9ed49784 100644 --- a/Source Code/TheOtherRoles.csproj +++ b/Source Code/TheOtherRoles.csproj @@ -1,7 +1,7 @@ netstandard2.1 - 2.6.6 + 2.6.7 TheOtherRoles Eisbison diff --git a/Source Code/UpdatePatch.cs b/Source Code/UpdatePatch.cs index 7156a5e15..57c2608ed 100644 --- a/Source Code/UpdatePatch.cs +++ b/Source Code/UpdatePatch.cs @@ -114,6 +114,8 @@ static void setNameColors() { setPlayerNameColor(SecurityGuard.securityGuard, SecurityGuard.color); } else if (Arsonist.arsonist != null && Arsonist.arsonist == PlayerControl.LocalPlayer) { setPlayerNameColor(Arsonist.arsonist, Arsonist.color); + } else if (Guesser.guesser != null && Guesser.guesser == PlayerControl.LocalPlayer) { + setPlayerNameColor(Guesser.guesser, Guesser.guesser.Data.IsImpostor ? Palette.ImpostorRed : Guesser.color); } // No else if here, as a Lover of team Jackal needs the colors @@ -130,7 +132,7 @@ static void setNameColors() { setPlayerNameColor(Spy.spy, Spy.color); } - // Crewmate roles with no changes: Child + // Crewmate roles with no changes: Mini // Impostor roles with no changes: Morphling, Camouflager, Vampire, Godfather, Eraser, Janitor, Cleaner, Warlock and Mafioso } @@ -249,23 +251,23 @@ static void camouflageAndMorphActions() { } } - public static void childUpdate() { - if (Child.child == null || Camouflager.camouflageTimer > 0f) return; + public static void miniUpdate() { + if (Mini.mini == null || Camouflager.camouflageTimer > 0f) return; - float growingProgress = Child.growingProgress(); + float growingProgress = Mini.growingProgress(); float scale = growingProgress * 0.35f + 0.35f; string suffix = ""; if (growingProgress != 1f) suffix = " (" + Mathf.FloorToInt(growingProgress * 18) + ")"; - Child.child.nameText.text += suffix; + Mini.mini.nameText.text += suffix; if (MeetingHud.Instance != null) { foreach (PlayerVoteArea player in MeetingHud.Instance.playerStates) - if (player.NameText != null && Child.child.PlayerId == player.TargetPlayerId) + if (player.NameText != null && Mini.mini.PlayerId == player.TargetPlayerId) player.NameText.text += suffix; } - if (Morphling.morphling != null && Morphling.morphTarget == Child.child && Morphling.morphTimer > 0f) + if (Morphling.morphling != null && Morphling.morphTarget == Mini.mini && Morphling.morphTimer > 0f) Morphling.morphling.nameText.text += suffix; } @@ -333,8 +335,8 @@ static void Postfix(HudManager __instance) timerUpdate(); // Camouflager and Morphling camouflageAndMorphActions(); - // Child - childUpdate(); + // Mini + miniUpdate(); // Snitch snitchUpdate(); }