- [Snitch](#snitch)\r
- [Jackal](#jackal)\r
- [Sidekick](#sidekick)\r
+- [Spy](#spy)\r
\r
The [Role Assignment](#role-assignment) sections explains how the roles are being distributed among the players.\r
\r
# Releases\r
| Among Us - Version| Mod Version | Link |\r
|----------|-------------|-----------------|\r
-| **2021.31.3s** | v2.0.1 | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.0.1/TheOtherRoles.zip)\r
-| 2021.31.3s | v2.0.0 beta | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.0.0/TheOtherRoles.zip)\r
+| 2021.3.31.3s| v2.1.0| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.1.0/TheOtherRoles.zip)\r
+| 2021.3.31.3s| v2.0.1 beta| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.0.1/TheOtherRoles.zip)\r
+| 2021.3.31.3s | v2.0.0 beta | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.0.0/TheOtherRoles.zip)\r
| 2021.3.5s | v1.8.2 | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v1.8.2/TheOtherRoles.zip)\r
| 2021.3.5s | v1.8.1 | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v1.8.1/TheOtherRoles.zip)\r
| 2021.3.5s | v1.8 | [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v1.8/TheOtherRoles.zip)\r
<details>\r
<summary>Click to show the Changelog</summary>\r
\r
+**Version 2.1.0**\r
+- **New Role:** [Spy](#spy)\r
+- **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.\r
+- **Camouflager:** The Child age/size will now also be hidden, to allow the Child Impostor to kill during camouflage\r
+\r
**Hotfix 2.0.1**\r
- Fixed a bug where camouflaged players would get stuck on ladders/platforms on the airship\r
- Introduced a one second cooldown after the Morphling sampled another player\r
If you have problems with the AmongUsModUpdater, make sure To [contact us](https://discord.gg/csa3pHYdPU).\r
\r
## Windows Manual Installation\r
-1. Download and unzip the newest [release](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.0.1/TheOtherRoles.zip)\r
+1. Download and unzip the newest [release](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.1.0/TheOtherRoles.zip)\r
2. Find the folder of your game, for steams players you can right click in steam, on the game, a menu will appear proposing you to go to the folders.\r
3. Make a copy of your game, it's not obligatory but advise, put it where you want.\r
4. Drag or extract the files from the zip into your game, at the .exe level.\r
\r
**Linux Manual**\r
1. Install Among Us via steam\r
-2. Download newest [release](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.0.1/TheOtherRoles.zip) and extract it to ~/.steam/steam/steamapps/common/Among Us\r
+2. Download newest [release](https://github.com/Eisbison/TheOtherRoles/releases/download/v2.1.0/TheOtherRoles.zip) and extract it to ~/.steam/steam/steamapps/common/Among Us\r
3. Enable winhttp.dll via the proton winecfg (https://bepinex.github.io/bepinex_docs/master/articles/advanced/steam_interop.html#protonwine)\r
4. Launch the game via Steam\r
\r
are hidden and all players have the same color.\\r
\\r
**NOTE**\r
-- The Child (and Morphling when copying the child) remains small and show its age.\r
+- The Child will look like all the other players\r
- The color of the footprints turns gray (also the ones that were already on the ground).\r
- The Spy sees gray icons on the admin table\r
- The shield is not visible anymore\r
\r
## Eraser\r
### **Team: Impostors**\r
-The Eraser is an Impostor that can erase the role of every player that is not an Impostor.\\r
+The Eraser is an Impostor that can erase the role of every player.\\r
The targeted players will lose their role after the meeting right before a player is exiled.\\r
After every erase, the cooldown increases by 10 seconds.\\r
The erase will be performed, even if the Eraser or his target die before the next meeting.\\r
- Erasing a Jackal that has a Sidekick, triggers the Sidekick promotion if it's activated in the settings\r
- As the erasing is being triggered before the eject of a player, erasing and voting out a Lover in the same round, would result in the\r
ex lover surviving as the partnership was erased before. Also a Jester win would not happen, as the erase will be triggered before.\r
+- The Eraser can erase the role of other Impostors to reveal if they are a real Impostor or a Spy. When erasing a real Impostor, the Impostor loses its special ability.\r
\r
### Game Options\r
| Name | Description |\r
| Sidekick can use vents | Yes/No |\r
-----------------------\r
\r
+## Spy\r
+### **Team: Crewmates**\r
+The Spy is a Crewmate, which has no special abilities.\\r
+The Spy looks like an additional Impostor to the Impostors, they can't tell the difference.\\r
+There are two possibilities (depending on the set options):\r
+- The Impostors can't kill the Spy (because otherwise their kill button would reveal, who the Spy is)\r
+- The Impostors can kill the Spy but they can also kill their Impostor partner (if they mistake another Impostor for the Spy)\r
+You can set whether the Sheriff can kill the Spy or not (in order to keep the lie alive).\r
+\r
+### Game Options\r
+| Name | Description\r
+|----------|:-------------:|\r
+| Spy Spawn Chance |\r
+| Spy Can Die To Sheriff | \r
+| Impostors Can Kill Anyone If There Is A Spy | This allows the Impostors to kill both the Spy and their Impostor partners\r
+-----------------------\r
+\r
+\r
# Source code\r
It's bad I know, this is a side project and my second week of modding. So there are no best practices around here.\r
You can use parts of the codes but don't copy paste the whole thing. Make sure you give credits to the other developers, because some parts of the code are based on theirs.\r
if ((Sheriff.currentTarget.IDOFAMCIJKE.CIDDOFDJHJH && (Sheriff.currentTarget != Child.child || Child.isGrownUp())) ||
Sheriff.currentTarget == Jackal.jackal ||
Sheriff.currentTarget == Sidekick.sidekick ||
- (Sheriff.jesterCanDieToSheriff && Jester.jester != null && Jester.jester == Sheriff.currentTarget))
- {
+ (Sheriff.spyCanDieToSheriff && Spy.spy != null && Spy.spy == Sheriff.currentTarget) ||
+ (Sheriff.jesterCanDieToSheriff && Jester.jester != null && Jester.jester == Sheriff.currentTarget)) {
targetId = Sheriff.currentTarget.PlayerId;
}
else {
{
static void Postfix(VersionShower __instance) {
string spacer = new String('\n', 21);
- string fullCredentials = "[FCCE03FF]TheOtherRoles[] v2.0.1 beta:\n- Modded by [FCCE03FF]Eisbison[] and [FFEB91FF]Thunderstorm584[]\n- Balanced with [FFEB91FF]Dhalucard[]\n- Button design by [FFEB91FF]Bavari[]";
+ string fullCredentials = "[FCCE03FF]TheOtherRoles[] v2.1.0:\n- Modded by [FCCE03FF]Eisbison[] and [FFEB91FF]Thunderstorm584[]\n- Balanced with [FFEB91FF]Dhalucard[]\n- Button design by [FFEB91FF]Bavari[]";
if (__instance.text.Text.Contains(spacer))
__instance.text.Text += "\n" + fullCredentials;
else
public static CustomOption presetSelection, crewmateRolesCount, impostorRolesCount;
public static CustomOption mafiaSpawnRate, morphlingSpawnRate, camouflagerSpawnRate, vampireSpawnRate, eraserSpawnRate;
public static CustomOption childSpawnRate, loversSpawnRate, jesterSpawnRate;
- public static CustomOption shifterSpawnRate, mayorSpawnRate, engineerSpawnRate, sheriffSpawnRate, lighterSpawnRate, detectiveSpawnRate, timeMasterSpawnRate, medicSpawnRate, swapperSpawnRate, seerSpawnRate, hackerSpawnRate, trackerSpawnRate, snitchSpawnRate, jackalSpawnRate;
+ public static CustomOption shifterSpawnRate, mayorSpawnRate, engineerSpawnRate, sheriffSpawnRate, lighterSpawnRate, detectiveSpawnRate, timeMasterSpawnRate, medicSpawnRate, swapperSpawnRate, seerSpawnRate, hackerSpawnRate, trackerSpawnRate, snitchSpawnRate, jackalSpawnRate, spySpawnRate;
public static CustomOption maxNumberOfMeetings;
public static CustomOption janitorCooldown, morphlingCooldown, camouflagerCooldown, vampireKillDelay, vampireCooldown, vampireCanKillNearGarlics, eraserCooldown;
public static CustomOption childGrowingUpDuration, loversImpLoverRate, loversBothDie;
- public static CustomOption sheriffCooldown, jesterCanDieToSheriff, lighterModeLightsOnVision, lighterModeLightsOffVision, lighterCooldown, lighterDuration, detectiveAnonymousFootprints, detectiveFootprintIntervall, detectiveFootprintDuration, detectiveReportNameDuration, detectiveReportColorDuration, timeMasterCooldown, timeMasterRewindTime, medicShowShielded, medicShowAttemptToShielded, seerMode, seerSoulDuration, hackerCooldown, hackerHackeringDuration, hackerOnlyColorType, trackerUpdateIntervall, snitchLeftTasksForImpostors, jackalKillCooldown, jackalCreateSidekickCooldown, jackalCanUseVents, jackalCanCreateSidekick, sidekickPromotesToJackal, sidekickCanKill, sidekickCanUseVents, jackalPromotedFromSidekickCanCreateSidekick, jackalCanCreateSidekickFromImpostor;
-
+ public static CustomOption sheriffCooldown, jesterCanDieToSheriff, lighterModeLightsOnVision, lighterModeLightsOffVision, lighterCooldown,
+ lighterDuration, detectiveAnonymousFootprints, detectiveFootprintIntervall, detectiveFootprintDuration, detectiveReportNameDuration, detectiveReportColorDuration,
+ timeMasterCooldown, timeMasterRewindTime, medicShowShielded, medicShowAttemptToShielded, seerMode, seerSoulDuration, hackerCooldown, hackerHackeringDuration, hackerOnlyColorType,
+ trackerUpdateIntervall, snitchLeftTasksForImpostors, jackalKillCooldown, jackalCreateSidekickCooldown, jackalCanUseVents, jackalCanCreateSidekick, sidekickPromotesToJackal, sidekickCanKill,
+ sidekickCanUseVents, jackalPromotedFromSidekickCanCreateSidekick, jackalCanCreateSidekickFromImpostor, spyCanDieToSheriff, spyImpostorsCanKillAnyone;
+
public static string cs(Color c, string s) {
return string.Format("[{0:X2}{1:X2}{2:X2}{3:X2}]{4}[]", ToByte(c.r), ToByte(c.g), ToByte(c.b), ToByte(c.a), s);
}
trackerSpawnRate = CustomOption.Create(200, cs(Tracker.color, "Tracker"), rates);
snitchSpawnRate = CustomOption.Create(210, cs(Snitch.color, "Snitch"), rates);
jackalSpawnRate = CustomOption.Create(220, cs(Jackal.color, "Jackal"), rates);
+ spySpawnRate = CustomOption.Create(240, cs(Spy.color, "Spy"), rates);
// Map settings
maxNumberOfMeetings = CustomOption.Create(3, "Number Of Meetings (excluding Mayor meeting)", 10, 0, 15, 1);
snitchLeftTasksForImpostors = CustomOption.Create(211, "Task Count Where Impostors See Snitch", 1f, 0f, 5f, 1f);
jackalKillCooldown = CustomOption.Create(221, "Jackal/Sidekick Kill Cooldown", 30f, 10f, 60f, 2.5f);
jackalCreateSidekickCooldown = CustomOption.Create(222, "Jackal Create Sidekick Cooldown", 30f, 10f, 60f, 2.5f);
- jackalCanUseVents = CustomOption.Create(223, "Jackal can use vents", true);
- jackalCanCreateSidekick = CustomOption.Create(224, "Jackal can create a sidekick", false);
- sidekickPromotesToJackal = CustomOption.Create(225, "Sidekick gets promoted to Jackal on Jackal death", false);
- sidekickCanKill = CustomOption.Create(226, "Sidekick can kill", false);
- sidekickCanUseVents = CustomOption.Create(227, "Sidekick can use vents", true);
- jackalPromotedFromSidekickCanCreateSidekick = CustomOption.Create(228, "Jackals promoted from Sidekick can create a Sidekick", true);
- jackalCanCreateSidekickFromImpostor = CustomOption.Create(229, "Jackals can make an Impostor to his Sidekick", true);
+ jackalCanUseVents = CustomOption.Create(223, "Jackal Can Use Vents", true);
+ jackalCanCreateSidekick = CustomOption.Create(224, "Jackal Can Create A Sidekick", false);
+ sidekickPromotesToJackal = CustomOption.Create(225, "Sidekick Gets Promoted To Jackal On Jackal Death", false);
+ sidekickCanKill = CustomOption.Create(226, "Sidekick Can Kill", false);
+ sidekickCanUseVents = CustomOption.Create(227, "Sidekick Can Use Vents", true);
+ jackalPromotedFromSidekickCanCreateSidekick = CustomOption.Create(228, "Jackals Promoted From Sidekick Can Create A Sidekick", true);
+ jackalCanCreateSidekickFromImpostor = CustomOption.Create(229, "Jackals Can Make An Impostor To His Sidekick", true);
+ spyCanDieToSheriff = CustomOption.Create(241, "Spy Can Die To Sheriff", false);
+ spyImpostorsCanKillAnyone = CustomOption.Create(242, "Impostors Can Kill Anyone If There Is A Spy", true);
}
}
var hudString = sb.ToString();
int defaultSettingsLines = 19;
- int roleSettingsLines = 19 + 25;
- int detailedSettingsLines = 19 + 25 + 25;
+ int roleSettingsLines = 19 + 26;
+ int detailedSettingsLines = 19 + 26 + 25;
int end1 = hudString.TakeWhile(c => (defaultSettingsLines -= (c == '\n' ? 1 : 0)) > 0).Count();
int end2 = hudString.TakeWhile(c => (roleSettingsLines -= (c == '\n' ? 1 : 0)) > 0).Count();
int end3 = hudString.TakeWhile(c => (detailedSettingsLines -= (c == '\n' ? 1 : 0)) > 0).Count();
using static TheOtherRoles.TheOtherRoles;
using UnityEngine;
using System.Collections.Generic;
+using System.Linq;
namespace TheOtherRoles
{
jackalTeam.Add(PlayerControl.LocalPlayer);
__instance.yourTeam = jackalTeam;
}
+
+ // Add the Spy to the Impostor team (for the Impostors)
+ if (Spy.spy != null && PlayerControl.LocalPlayer.IDOFAMCIJKE.CIDDOFDJHJH) {
+ List<PlayerControl> players = PlayerControl.AllPlayerControls.ToArray().ToList().OrderBy(x => Guid.NewGuid()).ToList();
+ var fakeImpostorTeam = new Il2CppSystem.Collections.Generic.List<PlayerControl>();
+ foreach (PlayerControl p in players) {
+ if (p == Spy.spy || p.IDOFAMCIJKE.CIDDOFDJHJH)
+ fakeImpostorTeam.Add(p);
+ }
+ __instance.yourTeam = fakeImpostorTeam;
+ }
}
// Intro display role
namespace TheOtherRoles
{
- [BepInPlugin(Id, "The Other Roles", "2.0.1")]
+ [BepInPlugin(Id, "The Other Roles", "2.1.0")]
[BepInProcess("Among Us.exe")]
public class TheOtherRolesPlugin : BasePlugin
{
__result = ExileController.Instance.KLHCDCKJHKC.HGGCLJHCDBM + " was The Jackal.";
else if(Sidekick.sidekick != null && ExileController.Instance.KLHCDCKJHKC.GPBBCHGPABL.PlayerId == Sidekick.sidekick.PlayerId)
__result = ExileController.Instance.KLHCDCKJHKC.HGGCLJHCDBM + " was The Sidekick.";
+ else if(Spy.spy != null && ExileController.Instance.KLHCDCKJHKC.GPBBCHGPABL.PlayerId == Spy.spy.PlayerId)
+ __result = ExileController.Instance.KLHCDCKJHKC.HGGCLJHCDBM + " was The Spy.";
else
__result = ExileController.Instance.KLHCDCKJHKC.HGGCLJHCDBM + " was not The Impostor.";
}
static void vampireSetTarget() {
if (Vampire.vampire == null || Vampire.vampire != PlayerControl.LocalPlayer) return;
- PlayerControl target = setTarget(true, true);
+
+ PlayerControl target = null;
+ if (Spy.spy != null) {
+ if (Spy.impostorsCanKillAnyone) {
+ target = setTarget(false, true);
+ } else {
+ target = setTarget(true, true, new List<PlayerControl>() { Spy.spy });
+ }
+ } else {
+ target = setTarget(true, true);
+ }
+
bool targetNearGarlic = false;
if (target != null) {
foreach (Garlic garlic in Garlic.garlics) {
static void eraserSetTarget() {
if (Eraser.eraser == null || Eraser.eraser != PlayerControl.LocalPlayer) return;
- Eraser.currentTarget = setTarget(true);
+ Eraser.currentTarget = setTarget();
}
static void engineerUpdate() {
}
}
+ static void impostorSetTarget() {
+ if (!PlayerControl.LocalPlayer.IDOFAMCIJKE.CIDDOFDJHJH ||!PlayerControl.LocalPlayer.AMDJMEEHNIG || PlayerControl.LocalPlayer.IDOFAMCIJKE.FGNJJFABIHJ) { // !isImpostor || !canMove || isDead
+ HudManager.CMJOLNCMAPD.KillButton.SetTarget(null);
+ return;
+ }
+
+ PlayerControl target = null;
+ if (Spy.spy != null) {
+ if (Spy.impostorsCanKillAnyone) {
+ target = setTarget(false, true);
+ } else {
+ target = setTarget(true, true, new List<PlayerControl>() { Spy.spy });
+ }
+ } else {
+ target = setTarget(true, true);
+ }
+
+ HudManager.CMJOLNCMAPD.KillButton.SetTarget(target);
+ }
+
static void trackerUpdate() {
if (Tracker.arrow?.arrow == null) return;
}
public static void playerSizeUpdate(PlayerControl p) {
- if (Child.child == null) return;
+ if (Child.child == null || Camouflager.camouflageTimer > 0f) return;
float growingProgress = Child.growingProgress();
float scale = growingProgress * 0.35f + 0.35f;
jackalSetTarget();
// Sidekick
sidekickSetTarget();
+ // Impostor
+ impostorSetTarget();
}
}
}
Snitch,
Jackal,
Sidekick,
- Eraser
+ Eraser,
+ Spy
}
enum CustomRPC
case RoleId.Eraser:
Eraser.eraser = player;
break;
+ case RoleId.Spy:
+ Spy.spy = player;
+ break;
}
}
}
Tracker.tracker = oldShifter;
} else if (Snitch.snitch != null && Snitch.snitch == player) {
Snitch.snitch = oldShifter;
- }else { // Crewmate
+ } else if (Spy.spy != null && Spy.spy == player) {
+ Spy.spy = oldShifter;
+ } else { // Crewmate
}
// Set cooldowns to max for both players
if (player == Tracker.tracker) Tracker.clearAndReload();
if (player == Snitch.snitch) Snitch.clearAndReload();
if (player == Swapper.swapper) Swapper.clearAndReload();
+ if (player == Spy.spy) Spy.clearAndReload();
// Impostor roles
if (player == Morphling.morphling) Morphling.clearAndReload();
crewSettings.Add((byte)RoleId.Tracker, CustomOptionHolder.trackerSpawnRate.getSelection());
crewSettings.Add((byte)RoleId.Snitch, CustomOptionHolder.snitchSpawnRate.getSelection());
crewSettings.Add((byte)RoleId.Jackal, CustomOptionHolder.jackalSpawnRate.getSelection());
+ crewSettings.Add((byte)RoleId.Spy, CustomOptionHolder.spySpawnRate.getSelection());
// Set special roles
if (impostors.Count >= 3 && maxImpostorRoles >= 3 && (rnd.Next(1, 101) <= CustomOptionHolder.mafiaSpawnRate.getSelection() * 10)) {
if (Jackal.jackal != null && p == Jackal.jackal) {
infos.Add(new RoleInfo("Jackal",
Jackal.color,
- "Kill all Crewmates and [FF1919FF]Impostors[FFFFFFFF] to win",
+ "Kill all Crewmates and [FF1919FF]Impostors[] to win",
"Kill everyone"));
}
if (Sidekick.sidekick != null && p == Sidekick.sidekick) {
"You are in [FC03BEFF]Love[]",
"You are in love"));
}
-
+ if (Spy.spy != null && p == Spy.spy) {
+ infos.Add(new RoleInfo("Spy",
+ Spy.color,
+ "Confuse the [FF1919FF]Impostors[]",
+ "Confuse the Impostors"));
+ }
if (infos.Count == 0 && p.IDOFAMCIJKE.CIDDOFDJHJH) { // Just Impostor
infos.Add(new RoleInfo("Impostor",
Palette.LDCHDOFJPGH,
Jackal.clearAndReload();
Sidekick.clearAndReload();
Eraser.clearAndReload();
+ Spy.clearAndReload();
}
public static class Jester {
public static float cooldown = 30f;
public static bool jesterCanDieToSheriff = false;
+ public static bool spyCanDieToSheriff = false;
public static PlayerControl currentTarget;
currentTarget = null;
cooldown = CustomOptionHolder.sheriffCooldown.getFloat();
jesterCanDieToSheriff = CustomOptionHolder.jesterCanDieToSheriff.getBool();
+ spyCanDieToSheriff = CustomOptionHolder.spyCanDieToSheriff.getBool();
}
}
cooldown = CustomOptionHolder.eraserCooldown.getFloat();
}
}
+
+ public static class Spy {
+ public static PlayerControl spy;
+ public static Color color = Palette.LDCHDOFJPGH;
+
+ public static bool impostorsCanKillAnyone = true;
+
+ public static void clearAndReload() {
+ spy = null;
+ impostorsCanKillAnyone = CustomOptionHolder.spyImpostorsCanKillAnyone.getBool();
+ }
+ }
}
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
- <Version>2.0.1</Version>
+ <Version>2.1.0</Version>
<Description>TheOtherRoles</Description>
<Authors>Eisbison</Authors>
setPlayerNameColor(Jackal.fakeSidekick, Jackal.color);
}
}
+ else if (Spy.spy != null && Spy.spy == PlayerControl.LocalPlayer) {
+ setPlayerNameColor(Spy.spy, Spy.color);
+ }
// No else if here, as a Lover of team Jackal needs the colors
if (Sidekick.sidekick != null && Sidekick.sidekick == PlayerControl.LocalPlayer) {
}
}
+ // No else if here, as the Impostors need the Spy name to be colored
+ if (Spy.spy != null && PlayerControl.LocalPlayer.IDOFAMCIJKE.CIDDOFDJHJH) {
+ setPlayerNameColor(Spy.spy, Spy.color);
+ }
+
// Crewmate roles with no changes: Child
// Impostor roles with no changes: Morphling, Camouflager, Vampire, Godfather, Eraser, Janitor and Mafioso
}
}
public static void childUpdate() {
- if (Child.child == null) return;
+ if (Child.child == null || Camouflager.camouflageTimer > 0f) return;
float growingProgress = Child.growingProgress();
float scale = growingProgress * 0.35f + 0.35f;