Skip to content

Commit

Permalink
Merge pull request #342 from Eisbison/Version_4.1.0
Browse files Browse the repository at this point in the history
Version 4.1.0
  • Loading branch information
Mallaris authored May 1, 2022
2 parents a5de718 + 6ccba0f commit 322de56
Show file tree
Hide file tree
Showing 31 changed files with 709 additions and 400 deletions.
32 changes: 28 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein
# Releases
| Among Us - Version| Mod Version | Link |
|----------|-------------|-----------------|
| 2022.3.29| v4.0.0| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.0.0/TheOtherRoles.zip)
| 2022.3.29| v4.1.0| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.1.0/TheOtherRoles.zip)
| 2022.3.29s| v4.0.0| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.0.0/TheOtherRoles.zip)
| 2022.3.29s| v3.4.5| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v3.4.5/TheOtherRoles.zip)
| 2022.2.23s| v3.4.4| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v3.4.4/TheOtherRoles.zip)
| 2021.12.15s| v3.4.3| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v3.4.3/TheOtherRoles.zip)
Expand Down Expand Up @@ -101,6 +102,13 @@ The [Role Assignment](#role-assignment) sections explains how the roles are bein
<details>
<summary>Click to show the Changelog</summary>

**Version 4.1.0**
- Added support for the Submerged map (https://github.com/SubmergedAmongUs/Submerged), which can be downloaded ingame with the Update button. If there is also a mod update, it will be prioritized
- Added an option to give the Mayor a portable Meeting Button
- Fixed a bug where the Lawyer didn't die with their client when voted out
- Removed Lawyer option "Wins after Meetings"
- Changed Medium question if the killer of a body is the Mini, the medium can now randomly ask the role question

**Version 4.0.0**
- Added new role [Ninja](#ninja) thanks [gendelo3](https://github.com/gendelo3)
- Added new role [Portalmaker](#portalmaker) thanks [gendelo3](https://github.com/gendelo3)
Expand Down Expand Up @@ -917,6 +925,7 @@ If the Ninja uses its ability, it will leave a trace (leaves) for a configurable
- The mark on the marked player will reset after a meeting or after using the ability to kill the marked player. Performing a normal kill will **NOT** reset the mark
- If the Ninja tries to kill a shielded player (e.g. Medic shield, Shield last game first kill ), the kill will not be performed
- If the Ninja tries to kill the Time Master while the shield is active, the Ninja won't teleport to the players position, but the Time Master shield will still be activated
- If the marked target is on a different floor on Submerged, the arrow will always point to the elevator

### Game Options
| Name | Description |
Expand Down Expand Up @@ -1056,6 +1065,9 @@ The Vulture is a neutral role that must eat a specified number of corpses (depen
Depending on the options, when a player dies, the Vulture gets an arrow pointing to the corpse.\
If there is a Vulture in the game, there can't be a Cleaner.

**NOTE**
- If the corpse is on a different floor on Submerged, the arrow will always point to the elevator

### Game Options
| Name | Description |
|----------|:-------------:|
Expand Down Expand Up @@ -1133,14 +1145,16 @@ can only use them, if the previous player did not use them before)
### **Team: Crewmates**
The Mayor leads the Crewmates by having a vote that counts twice.\
The Mayor can always use their meeting, even if the maximum number of meetings was reached.\
The Mayor has a portable Meeting Button, depending on the options.\
The Mayor can see the vote colors after completing a configurable amount of tasks, depending on the options.

### Game Options
| Name | Description |
|----------|:-------------:|
| Mayor Spawn Chance | -
| Mayor Can See Vote Colors | -
| Mayor Completed Tasks Needed To See Vote Colors | -
| Completed Tasks Needed To See Vote Colors | -
| Mobile Emergency Button | -
-----------------------

## Engineer
Expand Down Expand Up @@ -1358,6 +1372,9 @@ An arrow points to the last tracked position of the player.\
The arrow updates its position every few seconds (configurable).\
Depending on the options, the Tracker has another ability: They can track all corpses on the map for a set amount of time. They will keep tracking corpses, even if they were cleaned or eaten by the Vulture.

**NOTE**
- If the tracked player is on a different floor on Submerged, the arrow will always point to the elevator

### Game Options
| Name | Description
|----------|:-------------:|
Expand All @@ -1374,6 +1391,9 @@ Depending on the options, the Tracker has another ability: They can track all co
When the Snitch finishes all the tasks, arrows will appear (only visible to the Snitch) that point to the Impostors (depending on the options also to members of team Jackal).\
When the Snitch has one task left (configurable) the Snitch will be revealed to the Impostors (depending on the options also to members of team Jackal) with an arrow pointing to the Snitch.

**NOTE**
- If the Impostor(s)/Jackal(s) is/are on a different floor on Submerged when the Snitch finished their tasks, the arrow will always point to the elevator

### Game Options
| Name | Description
|----------|:-------------:|
Expand Down Expand Up @@ -1418,7 +1438,7 @@ Additionally to that, the Portalmaker gets information about who used the portal
- While one player uses a portal, it is blocked for any other player until the player got teleported.
- All ghosts can still use the portals, but won't block any living player from using it and the Portalmaker won't get any information about it in chat.
- If a morphed person uses a portal it will show the morphed name/color depending on the options.
- If a comouflaged person uses a portal it will show "A comouflaged person used the portal."
- If a camouflaged person uses a portal it will show "A comouflaged person used the portal."

### Game Options
| Name | Description
Expand Down Expand Up @@ -1469,7 +1489,7 @@ Questions:
What is your Role?
What is your killer's color type?
When did you die?
What is your killers role? (mini exluded)
What is your killers role?

### Game Options
| Name | Description
Expand All @@ -1482,6 +1502,7 @@ What is your killers role? (mini exluded)

# Modifier
A modifier is an addition to your Impostor/Neutral/Crewmate role.
Some modifiers can be ingame more than once (Quantity option).

## Bloody
### Bloody
Expand Down Expand Up @@ -1525,6 +1546,9 @@ Everyone will know if the Tie Breaker was involved in the Meeting or not.
The Bait forces the killer to self report the body (you can configure a delay in the options).\
There can be more than one Bait.

**NOTE:**
- If the Sheriff has the Bait modifier and dies while trying to kill a Crewmate, the Sheriff will *NOT* report themself.

### Game Options
| Name | Description
|----------|:-------------:|
Expand Down
68 changes: 58 additions & 10 deletions TheOtherRoles/Buttons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ static class HudManagerStartPatch
public static CustomButton pursuerButton;
public static CustomButton witchSpellButton;
public static CustomButton ninjaButton;
public static CustomButton mayorMeetingButton;

public static Dictionary<byte, List<CustomButton>> deputyHandcuffedButtons = null;

Expand Down Expand Up @@ -93,6 +94,7 @@ public static void setCustomButtonCooldowns() {
trackerTrackCorpsesButton.MaxTimer = Tracker.corpsesTrackingCooldown;
witchSpellButton.MaxTimer = Witch.cooldown;
ninjaButton.MaxTimer = Ninja.cooldown;
mayorMeetingButton.MaxTimer = PlayerControl.GameOptions.EmergencyCooldown;

timeMasterShieldButton.EffectDuration = TimeMaster.shieldDuration;
hackerButton.EffectDuration = Hacker.duration;
Expand Down Expand Up @@ -213,14 +215,20 @@ public static void Postfix(HudManager __instance)
} else if (task.TaskType == TaskTypes.StopCharles) {
ShipStatus.Instance.RpcRepairSystem(SystemTypes.Reactor, 0 | 16);
ShipStatus.Instance.RpcRepairSystem(SystemTypes.Reactor, 1 | 16);
} else if (SubmergedCompatibility.isSubmerged() && task.TaskType == SubmergedCompatibility.RetrieveOxygenMask) {
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.EngineerFixSubmergedOxygen, Hazel.SendOption.Reliable, -1);
AmongUsClient.Instance.FinishRpcImmediately(writer);
RPCProcedure.engineerFixSubmergedOxygen();
}

}
},
() => { return Engineer.engineer != null && Engineer.engineer == PlayerControl.LocalPlayer && Engineer.remainingFixes > 0 && !PlayerControl.LocalPlayer.Data.IsDead; },
() => {
bool sabotageActive = false;
foreach (PlayerTask task in PlayerControl.LocalPlayer.myTasks)
if (task.TaskType == TaskTypes.FixLights || task.TaskType == TaskTypes.RestoreOxy || task.TaskType == TaskTypes.ResetReactor || task.TaskType == TaskTypes.ResetSeismic || task.TaskType == TaskTypes.FixComms || task.TaskType == TaskTypes.StopCharles)
if (task.TaskType == TaskTypes.FixLights || task.TaskType == TaskTypes.RestoreOxy || task.TaskType == TaskTypes.ResetReactor || task.TaskType == TaskTypes.ResetSeismic || task.TaskType == TaskTypes.FixComms || task.TaskType == TaskTypes.StopCharles
|| SubmergedCompatibility.isSubmerged() && task.TaskType == SubmergedCompatibility.RetrieveOxygenMask)
sabotageActive = true;
return sabotageActive && Engineer.remainingFixes > 0 && PlayerControl.LocalPlayer.CanMove;
},
Expand Down Expand Up @@ -746,9 +754,9 @@ public static void Postfix(HudManager __instance)
usePortalButton = new CustomButton(
() => {
bool didTeleport = false;
Vector2 exit = Portal.findExit(PlayerControl.LocalPlayer.transform.position);
Vector2 entry = Portal.findEntry(PlayerControl.LocalPlayer.transform.position);
PlayerControl.LocalPlayer.NetTransform.RpcSnapTo(entry); // TODO: check for bans on servers
Vector3 exit = Portal.findExit(PlayerControl.LocalPlayer.transform.position);
Vector3 entry = Portal.findEntry(PlayerControl.LocalPlayer.transform.position);
PlayerControl.LocalPlayer.NetTransform.RpcSnapTo(entry);

if (!PlayerControl.LocalPlayer.Data.IsDead) { // Ghosts can portal too, but non-blocking and only with a local animation
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.UsePortal, Hazel.SendOption.Reliable, -1);
Expand All @@ -761,6 +769,9 @@ public static void Postfix(HudManager __instance)
PlayerControl.LocalPlayer.moveable = false;
PlayerControl.LocalPlayer.NetTransform.Halt();
if (p >= 0.5f && p <= 0.53f && !didTeleport) {
if (SubmergedCompatibility.isSubmerged()) {
SubmergedCompatibility.ChangeFloor(exit.y > -7);
}
PlayerControl.LocalPlayer.NetTransform.RpcSnapTo(exit);
didTeleport = true;
}
Expand Down Expand Up @@ -1005,7 +1016,7 @@ public static void Postfix(HudManager __instance)
RPCProcedure.sealVent(SecurityGuard.ventTarget.Id);
SecurityGuard.ventTarget = null;

} else if (PlayerControl.GameOptions.MapId != 1) { // Place camera if there's no vent and it's not MiraHQ
} else if (PlayerControl.GameOptions.MapId != 1 && !SubmergedCompatibility.isSubmerged()) { // Place camera if there's no vent and it's not MiraHQ or Submerged
var pos = PlayerControl.LocalPlayer.transform.position;
byte[] buff = new byte[sizeof(float) * 2];
Buffer.BlockCopy(BitConverter.GetBytes(pos.x), 0, buff, 0*sizeof(float), sizeof(float));
Expand All @@ -1020,12 +1031,12 @@ public static void Postfix(HudManager __instance)
},
() => { return SecurityGuard.securityGuard != null && SecurityGuard.securityGuard == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead && SecurityGuard.remainingScrews >= Mathf.Min(SecurityGuard.ventPrice, SecurityGuard.camPrice); },
() => {
securityGuardButton.actionButton.graphic.sprite = (SecurityGuard.ventTarget == null && PlayerControl.GameOptions.MapId != 1) ? SecurityGuard.getPlaceCameraButtonSprite() : SecurityGuard.getCloseVentButtonSprite();
securityGuardButton.actionButton.graphic.sprite = (SecurityGuard.ventTarget == null && PlayerControl.GameOptions.MapId != 1 && !SubmergedCompatibility.isSubmerged()) ? SecurityGuard.getPlaceCameraButtonSprite() : SecurityGuard.getCloseVentButtonSprite();
if (securityGuardButtonScrewsText != null) securityGuardButtonScrewsText.text = $"{SecurityGuard.remainingScrews}/{SecurityGuard.totalScrews}";

if (SecurityGuard.ventTarget != null)
return SecurityGuard.remainingScrews >= SecurityGuard.ventPrice && PlayerControl.LocalPlayer.CanMove;
return PlayerControl.GameOptions.MapId != 1 && SecurityGuard.remainingScrews >= SecurityGuard.camPrice && PlayerControl.LocalPlayer.CanMove;
return PlayerControl.GameOptions.MapId != 1 && !SubmergedCompatibility.isSubmerged() && SecurityGuard.remainingScrews >= SecurityGuard.camPrice && PlayerControl.LocalPlayer.CanMove;
},
() => { securityGuardButton.Timer = securityGuardButton.MaxTimer; },
SecurityGuard.getPlaceCameraButtonSprite(),
Expand Down Expand Up @@ -1070,7 +1081,8 @@ public static void Postfix(HudManager __instance)
if (SecurityGuard.cantMove) PlayerControl.LocalPlayer.moveable = false;
PlayerControl.LocalPlayer.NetTransform.Halt(); // Stop current movement
},
() => { return SecurityGuard.securityGuard != null && SecurityGuard.securityGuard == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead && SecurityGuard.remainingScrews < Mathf.Min(SecurityGuard.ventPrice, SecurityGuard.camPrice); },
() => { return SecurityGuard.securityGuard != null && SecurityGuard.securityGuard == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead && SecurityGuard.remainingScrews < Mathf.Min(SecurityGuard.ventPrice, SecurityGuard.camPrice)
&& !SubmergedCompatibility.isSubmerged(); },
() => {
if (securityGuardChargesText != null) securityGuardChargesText.text = $"{SecurityGuard.charges} / {SecurityGuard.maxCharges}";
securityGuardCamButton.actionButton.graphic.sprite = PlayerControl.GameOptions.MapId == 1 ? SecurityGuard.getLogSprite() : SecurityGuard.getCamSprite();
Expand Down Expand Up @@ -1230,7 +1242,7 @@ public static void Postfix(HudManager __instance)
if (Medium.target == null || Medium.target.player == null) return;
string msg = "";

int randomNumber = Medium.target.killerIfExisting?.PlayerId == Mini.mini?.PlayerId ? TheOtherRoles.rnd.Next(3) : TheOtherRoles.rnd.Next(4);
int randomNumber = TheOtherRoles.rnd.Next(4);
string typeOfColor = Helpers.isLighterColor(Medium.target.killerIfExisting.Data.DefaultOutfit.ColorId) ? "lighter" : "darker";
float timeSinceDeath = ((float)(Medium.meetingStartTime - Medium.target.timeOfDeath).TotalMilliseconds);
string name = " (" + Medium.target.player.Data.PlayerName + ")";
Expand All @@ -1239,7 +1251,7 @@ public static void Postfix(HudManager __instance)
if (randomNumber == 0) msg = "What is your role? My role is " + RoleInfo.GetRolesString(Medium.target.player, false) + name;
else if (randomNumber == 1) msg = "What is your killer`s color type? My killer is a " + typeOfColor + " color" + name;
else if (randomNumber == 2) msg = "When did you die? I have died " + Math.Round(timeSinceDeath / 1000) + "s before meeting started" + name;
else msg = "What is your killer`s role? My killer is " + RoleInfo.GetRolesString(Medium.target.killerIfExisting, false) + name; //exlude mini
else msg = "What is your killer`s role? My killer is " + RoleInfo.GetRolesString(Medium.target.killerIfExisting, false) + name;

DestroyableSingleton<HudManager>.Instance.Chat.AddChat(PlayerControl.LocalPlayer, $"{msg}");

Expand Down Expand Up @@ -1392,6 +1404,9 @@ public static void Postfix(HudManager __instance)
writer2.Write(Ninja.ninjaMarked.PlayerId);
writer2.Write(byte.MaxValue);
AmongUsClient.Instance.FinishRpcImmediately(writer2);
if (SubmergedCompatibility.isSubmerged()) {
SubmergedCompatibility.ChangeFloor(Ninja.ninjaMarked.transform.localPosition.y > -7);
}
RPCProcedure.uncheckedMurderPlayer(PlayerControl.LocalPlayer.PlayerId, Ninja.ninjaMarked.PlayerId, byte.MaxValue);

// Create Second trace after killing
Expand Down Expand Up @@ -1435,6 +1450,39 @@ public static void Postfix(HudManager __instance)
KeyCode.F
);

mayorMeetingButton = new CustomButton(
() => {
PlayerControl.LocalPlayer.NetTransform.Halt(); // Stop current movement
Helpers.handleVampireBiteOnBodyReport(); // Manually call Vampire handling, since the CmdReportDeadBody Prefix won't be called
RPCProcedure.uncheckedCmdReportDeadBody(PlayerControl.LocalPlayer.PlayerId, Byte.MinValue);

MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.UncheckedCmdReportDeadBody, Hazel.SendOption.Reliable, -1);
writer.Write(PlayerControl.LocalPlayer.PlayerId);
writer.Write(Byte.MaxValue);
AmongUsClient.Instance.FinishRpcImmediately(writer);
},
() => { return Mayor.mayor != null && Mayor.mayor == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead ;},
() => {
mayorMeetingButton.actionButton.OverrideText("Emergency");
bool sabotageActive = false;
foreach (PlayerTask task in PlayerControl.LocalPlayer.myTasks)
if (task.TaskType == TaskTypes.FixLights || task.TaskType == TaskTypes.RestoreOxy || task.TaskType == TaskTypes.ResetReactor || task.TaskType == TaskTypes.ResetSeismic || task.TaskType == TaskTypes.FixComms || task.TaskType == TaskTypes.StopCharles
|| SubmergedCompatibility.isSubmerged() && task.TaskType == SubmergedCompatibility.RetrieveOxygenMask)
sabotageActive = true;
return !sabotageActive && PlayerControl.LocalPlayer.CanMove;
},
() => { mayorMeetingButton.Timer = mayorMeetingButton.MaxTimer; },
Mayor.getMeetingSprite(),
new Vector3(-1.8f, -0.06f, 0),
__instance,
KeyCode.F,
true,
0f,
() => {},
false,
"Meeting"
);

// Set the default (or settings from the previous game) timers / durations when spawning the buttons
setCustomButtonCooldowns();
deputyHandcuffedButtons = null;
Expand Down
Loading

0 comments on commit 322de56

Please sign in to comment.