with:
name: TheOtherRoles.dll
path: TheOtherRoles/bin/Release/net6.0/TheOtherRoles.dll
+
# Releases
| Among Us - Version| Mod Version | Link |
|----------|-------------|-----------------|
-| 2023.2.28s| v4.3.0| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.3.0/TheOtherRoles.zip)
+| 2023.03.28s| v4.3.1| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.3.1/TheOtherRoles.zip)
+| 2023.02.28s| v4.3.0| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.3.0/TheOtherRoles.zip)
| 2022.12.14s| v4.2.1| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.2.1/TheOtherRoles.zip)
<details>
<summary>Click to show older versions</summary>
<details>
<summary>Click to show the Changelog</summary>
+**Version 4.3.1**
+- Update to Among Us 2023.3.28
+- Added new options (Information mode - Chat/Map/Both, Roles - Evil Killing / Evil) to the snitch and fixed bugs affecting the snitch
+- Changed the maximum amount per modifier to 15 (where applicable)
+- Changed the colorblind text and player name to be behind certain objects again (rock on Polus etc.)
+- Fixed a bug where a sidekicked bomber would not lose the bomber role
+- Fixed a bug where enabled vanilla roles would be assigned even if "block vanilla roles" was turned on
+- Fixed several Bugs in both Hide N Seek modes (should be playable again)
+- Fixed a bug where Trickster JackInTheBoxes could sometimes not be used if they were placed behind objects
+- Fixed a bug where the cosmetics of an invisible Ninja became visible when the camouflage ended
+- Fixed a bug where the mini would show a "jump" in age at the end of a meeting due to the time spent in cutscenes
+- Fixed a bug where the mayor would always have the mobile emergency button, no matter which setting was selected
+
+
**Version 4.3.0**
- Updated to Among Us version 2023.2.28
- Added a new role: [Bomber](#bomber)
|----------|:-------------:|
| Snitch Spawn Chance | -
| Task Count Where The Snitch Will Be Revealed | -
+| Information Mode | Whether the snitch will get info on the map and/or in the chat
+| Targets | Snitch Will See All Evil Players or Killing Evil Players
-----------------------
## Spy
private static CustomButton huntedShieldButton;
public static Dictionary<byte, List<CustomButton>> deputyHandcuffedButtons = null;
- public static PoolablePlayer morphTargetDisplay;
+ public static PoolablePlayer targetDisplay;
public static TMPro.TMP_Text securityGuardButtonScrewsText;
public static TMPro.TMP_Text securityGuardChargesText;
}
}
+ private static void setButtonTargetDisplay(PlayerControl target, CustomButton button = null, Vector3? offset=null) {
+ if (target == null || button == null) {
+ if (targetDisplay != null) { // Reset the poolable player
+ targetDisplay.gameObject.SetActive(false);
+ GameObject.Destroy(targetDisplay.gameObject);
+ targetDisplay = null;
+ }
+ return;
+ }
+ // Add poolable player to the button so that the target outfit is shown
+ button.actionButton.cooldownTimerText.transform.localPosition = new Vector3(0, 0, -1f); // Before the poolable player
+ targetDisplay = UnityEngine.Object.Instantiate<PoolablePlayer>(Patches.IntroCutsceneOnDestroyPatch.playerPrefab, button.actionButton.transform);
+ GameData.PlayerInfo data = target.Data;
+ target.SetPlayerMaterialColors(targetDisplay.cosmetics.currentBodySprite.BodySprite);
+ targetDisplay.SetSkin(data.DefaultOutfit.SkinId, data.DefaultOutfit.ColorId);
+ targetDisplay.SetHat(data.DefaultOutfit.HatId, data.DefaultOutfit.ColorId);
+ targetDisplay.cosmetics.nameText.text = ""; // Hide the name!
+ targetDisplay.transform.localPosition = new Vector3(0f, 0.22f, -0.01f);
+ if (offset != null) targetDisplay.transform.localPosition += (Vector3)offset;
+ targetDisplay.transform.localScale = Vector3.one * 0.33f;
+ targetDisplay.setSemiTransparent(false);
+ targetDisplay.gameObject.SetActive(true);
+ }
+
public static void Postfix(HudManager __instance) {
initialized = false;
RPCProcedure.setFutureShifted(Shifter.currentTarget.PlayerId);
SoundEffectsManager.play("shifterShift");
},
- () => { return Shifter.shifter != null && Shifter.shifter == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
+ () => { return Shifter.shifter != null && Shifter.shifter == CachedPlayer.LocalPlayer.PlayerControl && Shifter.futureShift == null && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => { return Shifter.currentTarget && Shifter.futureShift == null && CachedPlayer.LocalPlayer.PlayerControl.CanMove; },
() => { },
Shifter.getButtonSprite(),
SoundEffectsManager.play("morphlingSample");
// Add poolable player to the button so that the target outfit is shown
- morphlingButton.actionButton.cooldownTimerText.transform.localPosition = new Vector3(0, 0, -1f); // Before the poolable player
- morphTargetDisplay = UnityEngine.Object.Instantiate<PoolablePlayer>(Patches.IntroCutsceneOnDestroyPatch.playerPrefab, morphlingButton.actionButton.transform);
- GameData.PlayerInfo data = Morphling.sampledTarget.Data;
- Morphling.sampledTarget.SetPlayerMaterialColors(morphTargetDisplay.cosmetics.currentBodySprite.BodySprite);
- morphTargetDisplay.SetSkin(data.DefaultOutfit.SkinId, data.DefaultOutfit.ColorId);
- morphTargetDisplay.SetHat(data.DefaultOutfit.HatId, data.DefaultOutfit.ColorId);
- // PlayerControl.SetPetImage(data.DefaultOutfit.PetId, data.DefaultOutfit.ColorId, morphTargetDisplay.PetSlot);
- morphTargetDisplay.cosmetics.nameText.text = ""; // Hide the name!
- morphTargetDisplay.transform.localPosition = new Vector3(0f, 0.22f, -0.01f);
- morphTargetDisplay.transform.localScale = Vector3.one * 0.33f;
- morphTargetDisplay.setSemiTransparent(false);
- morphTargetDisplay.gameObject.SetActive(true);
+ setButtonTargetDisplay(Morphling.sampledTarget, morphlingButton);
}
},
() => { return Morphling.morphling != null && Morphling.morphling == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
morphlingButton.isEffectActive = false;
morphlingButton.actionButton.cooldownTimerText.color = Palette.EnabledColor;
Morphling.sampledTarget = null;
- if (morphTargetDisplay != null) { // Reset the poolable player
- morphTargetDisplay.gameObject.SetActive(false);
- GameObject.Destroy(morphTargetDisplay.gameObject);
- morphTargetDisplay = null;
- }
+ setButtonTargetDisplay(null);
},
Morphling.getSampleSprite(),
CustomButton.ButtonPositions.upperRowLeft,
SoundEffectsManager.play("morphlingMorph");
// Reset the poolable player
- morphTargetDisplay.gameObject.SetActive(false);
- GameObject.Destroy(morphTargetDisplay.gameObject);
- morphTargetDisplay = null;
+ setButtonTargetDisplay(null);
}
}
);
AmongUsClient.Instance.FinishRpcImmediately(writer);
mayorMeetingButton.Timer = 1f;
},
- () => { return Mayor.mayor != null && Mayor.mayor == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
+ () => { return Mayor.mayor != null && Mayor.mayor == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead && Mayor.meetingButton; },
() => {
mayorMeetingButton.actionButton.OverrideText("Emergency ("+ Mayor.remoteMeetingsLeft + ")");
bool sabotageActive = false;
() => {
defuseButton.HasEffect = true;
},
- () => { return Bomber.bomb != null && Bomb.canDefuse && !CachedPlayer.LocalPlayer.Data.IsDead; },
+ () => {
+ if (shifterShiftButton.HasButton())
+ defuseButton.PositionOffset = new Vector3(0f, 2f, 0f);
+ else
+ defuseButton.PositionOffset = new Vector3(0f, 1f, 0f);
+ return Bomber.bomb != null && Bomb.canDefuse && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => {
if (defuseButton.isEffectActive && !Bomb.canDefuse) {
defuseButton.Timer = 0f;
defuseButton.isEffectActive = false;
},
Bomb.getDefuseSprite(),
- new Vector3(1f, 1f, 0),
+ new Vector3(0f, 1f, 0),
__instance,
null,
true,
AmongUsClient.Instance.FinishRpcImmediately(writer);
RPCProcedure.uncheckedMurderPlayer(thief.PlayerId, target.PlayerId, byte.MaxValue);
}
-
-
-
},
() => { return Thief.thief != null && CachedPlayer.LocalPlayer.PlayerControl == Thief.thief && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => { return Thief.currentTarget != null && CachedPlayer.LocalPlayer.PlayerControl.CanMove; },
hunterLighterButton.actionButton.graphic.color = Palette.EnabledColor;
},
Hunter.getLightSprite(),
- CustomButton.ButtonPositions.upperRowCenter,
+ CustomButton.ButtonPositions.upperRowFarLeft,
__instance,
KeyCode.F,
true,
public static float ArrowCooldown = 30f;
public static float ArrowDuration = 5f;
public static float ArrowPunish = 5f;
- private static Sprite buttonSprite;
+ private static Sprite buttonSpriteLight;
+ private static Sprite buttonSpriteArrow;
public static bool isLightActive (byte playerId) {
return lightActive.Contains(playerId);
}
public static Sprite getArrowSprite() {
- if (buttonSprite) return buttonSprite;
- buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.HideNSeekArrowButton.png", 115f);
- return buttonSprite;
+ if (buttonSpriteArrow) return buttonSpriteArrow;
+ buttonSpriteArrow = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.HideNSeekArrowButton.png", 115f);
+ return buttonSpriteArrow;
}
public static Sprite getLightSprite() {
- if (buttonSprite) return buttonSprite;
- buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.LighterButton.png", 115f);
- return buttonSprite;
+ if (buttonSpriteLight) return buttonSpriteLight;
+ buttonSpriteLight = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.LighterButton.png", 115f);
+ return buttonSpriteLight;
}
public static void clearAndReload() {
namespace TheOtherRoles {
public class CustomOptionHolder {
public static string[] rates = new string[]{"0%", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%"};
- public static string[] ratesModifier = new string[]{"1", "2", "3"};
+ public static string[] ratesModifier = new string[]{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
public static string[] presets = new string[]{"Preset 1", "Preset 2", "Random Preset Skeld", "Random Preset Mira HQ", "Random Preset Polus", "Random Preset Airship", "Random Preset Submerged" };
public static CustomOption presetSelection;
public static CustomOption modifiersCountMax;
public static CustomOption enableCodenameHorsemode;
+ public static CustomOption enableCodenameDisableHorses;
public static CustomOption mafiaSpawnRate;
public static CustomOption janitorCooldown;
public static CustomOption snitchSpawnRate;
public static CustomOption snitchLeftTasksForReveal;
+ public static CustomOption snitchMode;
+ public static CustomOption snitchTargets;
public static CustomOption spySpawnRate;
public static CustomOption spyCanDieToSheriff;
presetSelection = CustomOption.Create(0, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Preset"), presets, null, true);
activateRoles = CustomOption.Create(1, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Enable Mod Roles And Block Vanilla Roles"), true, null, true);
- if (Utilities.EventUtility.canBeEnabled) enableCodenameHorsemode = CustomOption.Create(10423, Types.General, cs(Color.green, "Enable Codename Horse"), false, null, true);
+ if (Utilities.EventUtility.canBeEnabled) enableCodenameHorsemode = CustomOption.Create(10423, Types.General, cs(Color.green, "Enable Codename Horsemode"), true, null, true);
+ if (Utilities.EventUtility.canBeEnabled) enableCodenameDisableHorses = CustomOption.Create(10424, Types.General, cs(Color.green, "Disable Horses"), false, enableCodenameHorsemode, false);
// Using new id's for the options to not break compatibilty with older versions
crewmateRolesCountMin = CustomOption.Create(300, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Minimum Crewmate Roles"), 15f, 0f, 15f, 1f, null, true);
snitchSpawnRate = CustomOption.Create(210, Types.Crewmate, cs(Snitch.color, "Snitch"), rates, null, true);
snitchLeftTasksForReveal = CustomOption.Create(219, Types.Crewmate, "Task Count Where The Snitch Will Be Revealed", 5f, 0f, 25f, 1f, snitchSpawnRate);
+ snitchMode = CustomOption.Create(211, Types.Crewmate, "Information Mode", new string[] { "Chat", "Map", "Chat & Map" }, snitchSpawnRate);
+ snitchTargets = CustomOption.Create(212, Types.Crewmate, "Targets", new string[] { "All Evil Players", "Killing Players" }, snitchSpawnRate);
spySpawnRate = CustomOption.Create(240, Types.Crewmate, cs(Spy.color, "Spy"), rates, null, true);
spyCanDieToSheriff = CustomOption.Create(241, Types.Crewmate, "Spy Can Die To Sheriff", false, spySpawnRate);
return false;
}
+ public static bool isKiller(PlayerControl player) {
+ return player.Data.Role.IsImpostor ||
+ (isNeutral(player) &&
+ player != Jester.jester &&
+ player != Arsonist.arsonist &&
+ player != Vulture.vulture &&
+ player != Lawyer.lawyer &&
+ player != Pursuer.pursuer);
+
+ }
+
+ public static bool isEvil(PlayerControl player) {
+ return player.Data.Role.IsImpostor || isNeutral(player);
+ }
public static bool zoomOutStatus = false;
public static void toggleZoom(bool reset=false) {
if (cam != null && cam.gameObject.name == "UI Camera") cam.orthographicSize = orthographicSize; // The UI is scaled too, else we cant click the buttons. Downside: map is super small.
}
- HudManagerStartPatch.zoomOutButton.Sprite = zoomOutStatus ? Helpers.loadSpriteFromResources("TheOtherRoles.Resources.PlusButton.png", 75f) : Helpers.loadSpriteFromResources("TheOtherRoles.Resources.MinusButton.png", 150f);
- HudManagerStartPatch.zoomOutButton.PositionOffset = zoomOutStatus ? new Vector3(0f, 3f, 0) : new Vector3(0.4f, 2.8f, 0);
+ if (HudManagerStartPatch.zoomOutButton != null) {
+ HudManagerStartPatch.zoomOutButton.Sprite = zoomOutStatus ? Helpers.loadSpriteFromResources("TheOtherRoles.Resources.PlusButton.png", 75f) : Helpers.loadSpriteFromResources("TheOtherRoles.Resources.MinusButton.png", 150f);
+ HudManagerStartPatch.zoomOutButton.PositionOffset = zoomOutStatus ? new Vector3(0f, 3f, 0) : new Vector3(0.4f, 2.8f, 0);
+ }
ResolutionManager.ResolutionChanged.Invoke((float)Screen.width / Screen.height); // This will move button positions to the correct position.
}
public class TheOtherRolesPlugin : BasePlugin
{
public const string Id = "me.eisbison.theotherroles";
- public const string VersionString = "4.3.0";
+ public const string VersionString = "4.3.1";
public static uint betaDays = 0; // amount of days for the build to be usable (0 for infinite!)
public static Version Version = Version.Parse(VersionString);
using static TheOtherRoles.TheOtherRoles;
using static TheOtherRoles.CustomOption;
using Reactor.Utilities.Extensions;
+using AmongUs.GameOptions;
namespace TheOtherRoles {
public class CustomOption {
[HarmonyPatch(typeof(PlayerPhysics), nameof(PlayerPhysics.CoSpawnPlayer))]
public class AmongUsClientOnPlayerJoinedPatch {
public static void Postfix() {
- if (PlayerControl.LocalPlayer != null) {
+ if (PlayerControl.LocalPlayer != null && AmongUsClient.Instance.AmHost) {
CustomOption.ShareOptionSelections();
}
}
private static TMPro.TextMeshPro[] settingsTMPs = new TMPro.TextMeshPro[3];
private static GameObject settingsBackground;
public static void OpenSettings(HudManager __instance) {
- if (__instance.FullScreen == null || MapBehaviour.Instance && MapBehaviour.Instance.IsOpen /*|| AmongUsClient.Instance.GameState != InnerNet.InnerNetClient.GameStates.Started*/) return;
+ if (__instance.FullScreen == null || MapBehaviour.Instance && MapBehaviour.Instance.IsOpen
+ /*|| AmongUsClient.Instance.GameState != InnerNet.InnerNetClient.GameStates.Started*/
+ || GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek) return;
settingsBackground = GameObject.Instantiate(__instance.FullScreen.gameObject, __instance.transform);
settingsBackground.SetActive(true);
var renderer = settingsBackground.GetComponent<SpriteRenderer>();
toggleSettingsButton.OnClick.RemoveAllListeners();
toggleSettingsButton.OnClick.AddListener((Action)(() => ToggleSettings(__instance)));
}
- toggleSettingsButtonObject.SetActive(__instance.MapButton.gameObject.active && !(MapBehaviour.Instance && MapBehaviour.Instance.IsOpen));
+ toggleSettingsButtonObject.SetActive(__instance.MapButton.gameObject.active && !(MapBehaviour.Instance && MapBehaviour.Instance.IsOpen) && GameOptionsManager.Instance.currentGameOptions.GameMode != GameModes.HideNSeek);
toggleSettingsButtonObject.transform.localPosition = __instance.MapButton.transform.localPosition + new Vector3(0, -0.66f, -500f);
}
}
if (TORMapOptions.showRoleSummary || HideNSeek.isHideNSeekGM) {
var position = Camera.main.ViewportToWorldPoint(new Vector3(0f, 1f, Camera.main.nearClipPlane));
GameObject roleSummary = UnityEngine.Object.Instantiate(__instance.WinText.gameObject);
- roleSummary.transform.position = new Vector3(__instance.Navigation.ExitButton.transform.position.x + 0.1f, position.y - 0.1f, -14f);
+ roleSummary.transform.position = new Vector3(__instance.Navigation.ExitButton.transform.position.x + 0.1f, position.y - 0.1f, -214f);
roleSummary.transform.localScale = new Vector3(1f, 1f, 1f);
var roleSummaryText = new StringBuilder();
}
static void WrapUpPostfix(GameData.PlayerInfo exiled) {
+ // Prosecutor win condition
+ if (exiled != null && Lawyer.lawyer != null && Lawyer.target != null && Lawyer.isProsecutor && Lawyer.target.PlayerId == exiled.PlayerId && !Lawyer.lawyer.Data.IsDead)
+ Lawyer.triggerProsecutorWin = true;
+
// Mini exile lose condition
- if (exiled != null && Mini.mini != null && Mini.mini.PlayerId == exiled.PlayerId && !Mini.isGrownUp() && !Mini.mini.Data.Role.IsImpostor && !RoleInfo.getRoleInfoForPlayer(Mini.mini).Any(x => x.isNeutral)) {
+ else if (exiled != null && Mini.mini != null && Mini.mini.PlayerId == exiled.PlayerId && !Mini.isGrownUp() && !Mini.mini.Data.Role.IsImpostor && !RoleInfo.getRoleInfoForPlayer(Mini.mini).Any(x => x.isNeutral)) {
Mini.triggerMiniLose = true;
}
// Jester win condition
else if (exiled != null && Lawyer.lawyer != null && Lawyer.target != null && Lawyer.isProsecutor && Lawyer.target.PlayerId == exiled.PlayerId && !Lawyer.lawyer.Data.IsDead)
Lawyer.triggerProsecutorWin = true;
+
// Reset custom button timers where necessary
CustomButton.MeetingEndedUpdate();
[HarmonyPatch(typeof(Constants), nameof(Constants.ShouldHorseAround))]
public static class ShouldAlwaysHorseAround {
public static bool Prefix(ref bool __result) {
- __result = false;
+ __result = EventUtility.isEnabled && !EventUtility.disableHorses;
return false;
}
}
using HarmonyLib;
+using Reactor.Utilities.Extensions;
+using System;
using System.Collections.Generic;
using System.Linq;
using TheOtherRoles.Objects;
[HarmonyPatch(typeof(MapBehaviour))]
class MapBehaviourPatch {
- private static Dictionary<PlayerControl, SpriteRenderer> herePoints = new Dictionary<PlayerControl, SpriteRenderer>();
+ public static Dictionary<PlayerControl, SpriteRenderer> herePoints = new Dictionary<PlayerControl, SpriteRenderer>();
[HarmonyPatch(typeof(MapBehaviour), nameof(MapBehaviour.FixedUpdate))]
static void Postfix(MapBehaviour __instance) {
UnityEngine.Object.Destroy(s.Value);
herePoints.Remove(s.Key);
}
+ } else if (Snitch.snitch != null && CachedPlayer.LocalPlayer.PlayerId == Snitch.snitch.PlayerId && !Snitch.snitch.Data.IsDead && Snitch.mode != Snitch.Mode.Chat) {
+ var (playerCompleted, playerTotal) = TasksHandler.taskInfo(Snitch.snitch.Data);
+ int numberOfTasks = playerTotal - playerCompleted;
+
+ if (numberOfTasks == 0) {
+ if (MeetingHud.Instance == null) {
+ foreach (PlayerControl player in CachedPlayer.AllPlayers) {
+ if (Snitch.targets == Snitch.Targets.EvilPlayers && !Helpers.isEvil(player)) continue;
+ else if (Snitch.targets == Snitch.Targets.Killers && !Helpers.isKiller(player)) continue;
+ if (player.Data.IsDead) continue;
+ Vector3 v = player.transform.position;
+ v /= MapUtilities.CachedShipStatus.MapScale;
+ v.x *= Mathf.Sign(MapUtilities.CachedShipStatus.transform.localScale.x);
+ v.z = -1f;
+ if (herePoints.ContainsKey(player)) {
+ herePoints[player].transform.localPosition = v;
+ continue;
+ }
+ var herePoint = UnityEngine.Object.Instantiate(__instance.HerePoint, __instance.HerePoint.transform.parent, true);
+ herePoint.transform.localPosition = v;
+ herePoint.enabled = true;
+ int colorId = player.CurrentOutfit.ColorId;
+ player.CurrentOutfit.ColorId = 6;
+ player.SetPlayerMaterialColors(herePoint);
+ player.CurrentOutfit.ColorId = colorId;
+ herePoints.Add(player, herePoint);
+ }
+ } else {
+ foreach (var s in herePoints) {
+ UnityEngine.Object.Destroy(s.Value);
+ herePoints.Remove(s.Key);
+ }
+ }
+ }
}
HudManagerUpdate.CloseSettings();
}
}
// Mini
- if (!Mini.isGrowingUpInMeeting) Mini.timeOfGrowthStart = Mini.timeOfGrowthStart.Add(DateTime.UtcNow.Subtract(Mini.timeOfMeetingStart));
+ if (!Mini.isGrowingUpInMeeting) Mini.timeOfGrowthStart = Mini.timeOfGrowthStart.Add(DateTime.UtcNow.Subtract(Mini.timeOfMeetingStart)).AddSeconds(10);
+
+ // Snitch
+ if (Snitch.snitch != null && !Snitch.needsUpdate && Snitch.snitch.Data.IsDead && Snitch.text != null) {
+ UnityEngine.Object.Destroy(Snitch.text);
+ }
}
}
class StartMeetingPatch {
public static void Prefix(PlayerControl __instance, [HarmonyArgument(0)]GameData.PlayerInfo meetingTarget) {
RoomTracker roomTracker = FastDestroyableSingleton<HudManager>.Instance?.roomTracker;
+ byte roomId = Byte.MinValue;
+ if (roomTracker != null && roomTracker.LastRoom != null) {
+ roomId = (byte)roomTracker.LastRoom?.RoomId;
+ }
if (Snitch.snitch != null && roomTracker != null) {
MessageWriter roomWriter = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.ShareRoom, Hazel.SendOption.Reliable, -1);
roomWriter.Write(CachedPlayer.LocalPlayer.PlayerId);
- roomWriter.Write(roomTracker.LastRoom ? (byte)roomTracker.LastRoom.RoomId : Byte.MinValue);
+ roomWriter.Write(roomId);
AmongUsClient.Instance.FinishRpcImmediately(roomWriter);
}
// Add Snitch info
string output = "";
- if (Snitch.snitch != null && (CachedPlayer.LocalPlayer.PlayerControl == Snitch.snitch || Helpers.shouldShowGhostInfo()) && !Snitch.snitch.Data.IsDead) {
+ if (Snitch.snitch != null && Snitch.mode != Snitch.Mode.Map && (CachedPlayer.LocalPlayer.PlayerControl == Snitch.snitch || Helpers.shouldShowGhostInfo()) && !Snitch.snitch.Data.IsDead) {
var (playerCompleted, playerTotal) = TasksHandler.taskInfo(Snitch.snitch.Data);
int numberOfTasks = playerTotal - playerCompleted;
if (numberOfTasks == 0) {
output = $"Bad alive roles in game: \n \n";
- FastDestroyableSingleton<HudManager>.Instance.StartCoroutine(Effects.Lerp(0.3f, new Action<float>((x) => {
+ FastDestroyableSingleton<HudManager>.Instance.StartCoroutine(Effects.Lerp(0.4f, new Action<float>((x) => {
if (x == 1f) {
foreach (PlayerControl p in CachedPlayer.AllPlayers) {
- if (!Helpers.isNeutral(p) && !p.Data.Role.IsImpostor) continue;
+ if (Snitch.targets == Snitch.Targets.Killers && !Helpers.isKiller(p)) continue;
+ else if (Snitch.targets == Snitch.Targets.EvilPlayers && !Helpers.isEvil(p)) continue;
if (!Snitch.playerRoomMap.ContainsKey(p.PlayerId)) continue;
if (p.Data.IsDead) continue;
var room = Snitch.playerRoomMap[p.PlayerId];
if (room != byte.MinValue) {
roomName = DestroyableSingleton<TranslationController>.Instance.GetString((SystemTypes)room);
}
- output += "- " + RoleInfo.GetRolesString(p, false, false, true) + ", was last seen in " + roomName + "\n";
+ output += "- " + RoleInfo.GetRolesString(p, false, false, true) + ", was last seen " + roomName + "\n";
}
FastDestroyableSingleton<HudManager>.Instance.Chat.AddChat(Snitch.snitch, $"{output}");
}
using TheOtherRoles.CustomGameModes;
using static UnityEngine.GraphicsBuffer;
using AmongUs.GameOptions;
+using Sentry.Internal.Extensions;
namespace TheOtherRoles.Patches {
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))]
// Colorblind Text During the round
if (p.cosmetics.colorBlindText != null && p.cosmetics.showColorBlindText && p.cosmetics.colorBlindText.gameObject.active) {
- p.cosmetics.colorBlindText.transform.localPosition = new Vector3(0, -1f, -0.001f);
+ p.cosmetics.colorBlindText.transform.localPosition = new Vector3(0, -1f, 0f);
}
+ p.cosmetics.nameText.transform.parent.SetLocalZ(-0.0001f); // This moves both the name AND the colorblindtext behind objects (if the player is behind the object), like the rock on polus
+
if ((Lawyer.lawyerKnowsRole && CachedPlayer.LocalPlayer.PlayerControl == Lawyer.lawyer && p == Lawyer.target) || p == CachedPlayer.LocalPlayer.PlayerControl || CachedPlayer.LocalPlayer.Data.IsDead) {
Transform playerInfoTransform = p.cosmetics.nameText.transform.parent.FindChild("Info");
TMPro.TextMeshPro playerInfo = playerInfoTransform != null ? playerInfoTransform.GetComponent<TMPro.TextMeshPro>() : null;
static void snitchUpdate() {
if (Snitch.snitch == null) return;
- bool snitchIsDead = Snitch.snitch.Data.IsDead;
+ if (!Snitch.needsUpdate) return;
+ bool snitchIsDead = Snitch.snitch.Data.IsDead;
var (playerCompleted, playerTotal) = TasksHandler.taskInfo(Snitch.snitch.Data);
+
+ if (playerTotal == 0) return;
+ PlayerControl local = CachedPlayer.LocalPlayer.PlayerControl;
+
int numberOfTasks = playerTotal - playerCompleted;
- if (Snitch.isRevealed && (CachedPlayer.LocalPlayer.PlayerControl.Data.Role.IsImpostor || Helpers.isNeutral(CachedPlayer.LocalPlayer.PlayerControl))) {
+ if (Snitch.isRevealed && ((Snitch.targets == Snitch.Targets.EvilPlayers && Helpers.isEvil(local)) || (Snitch.targets == Snitch.Targets.Killers && Helpers.isKiller(local)))) {
if (Snitch.text == null) {
Snitch.text = GameObject.Instantiate(FastDestroyableSingleton<HudManager>.Instance.KillButton.cooldownTimerText, FastDestroyableSingleton<HudManager>.Instance.transform);
Snitch.text.enableWordWrapping = false;
Snitch.text.text = $"Snitch is alive: " + playerCompleted + "/" + playerTotal;
if (snitchIsDead) Snitch.text.text = $"Snitch is dead!";
}
+ }
+ if (snitchIsDead) {
+ if (MeetingHud.Instance == null) Snitch.needsUpdate = false;
+ return;
}
- if (snitchIsDead) return;
if (numberOfTasks <= Snitch.taskCountForReveal) Snitch.isRevealed = true;
}
}
}
}
+
+ // Snitch
+ if (Snitch.snitch != null && CachedPlayer.LocalPlayer.PlayerId == Snitch.snitch.PlayerId && MapBehaviourPatch.herePoints.Keys.Any(x => x.PlayerId == target.PlayerId)) {
+ foreach (var a in MapBehaviourPatch.herePoints.Where(x => x.Key.PlayerId == target.PlayerId)) {
+ UnityEngine.Object.Destroy(a.Value);
+ MapBehaviourPatch.herePoints.Remove(a.Key);
+ }
+ }
}
}
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.IsFlashlightEnabled))]
public static class IsFlashlightEnabledPatch {
public static bool Prefix(ref bool __result) {
+ if (GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek)
+ return true;
__result = false;
if (!CachedPlayer.LocalPlayer.Data.IsDead && Lighter.lighter != null && Lighter.lighter.PlayerId == CachedPlayer.LocalPlayer.PlayerId) {
__result = true;
using TheOtherRoles.CustomGameModes;
namespace TheOtherRoles.Patches {
- [HarmonyPatch(typeof(RoleOptionsData), nameof(RoleOptionsData.GetNumPerGame))]
+ [HarmonyPatch(typeof(RoleOptionsCollectionV07), nameof(RoleOptionsCollectionV07.GetNumPerGame))]
class RoleOptionsDataGetNumPerGamePatch{
public static void Postfix(ref int __result) {
- if (CustomOptionHolder.activateRoles.getBool()) __result = 0; // Deactivate Vanilla Roles if the mod roles are active
+ if (CustomOptionHolder.activateRoles.getBool() && GameOptionsManager.Instance.CurrentGameOptions.GameMode == GameModes.Normal) __result = 0; // Deactivate Vanilla Roles if the mod roles are active
}
}
using TheOtherRoles.Players;
using TheOtherRoles.Utilities;
using TheOtherRoles.CustomGameModes;
+using AmongUs.GameOptions;
namespace TheOtherRoles.Patches {
[HarmonyPatch(typeof(HudManager), nameof(HudManager.Update))]
}
public static void miniUpdate() {
- if (Mini.mini == null || Camouflager.camouflageTimer > 0f || Mini.mini == Morphling.morphling && Morphling.morphTimer > 0f || Mini.mini == Ninja.ninja && Ninja.isInvisble) return;
+ if (Mini.mini == null || Camouflager.camouflageTimer > 0f || Mini.mini == Morphling.morphling && Morphling.morphTimer > 0f || Mini.mini == Ninja.ninja && Ninja.isInvisble || SurveillanceMinigamePatch.nightVisionIsActive) return;
float growingProgress = Mini.growingProgress();
float scale = growingProgress * 0.35f + 0.35f;
static void updateVentButton(HudManager __instance)
{
+ if (GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek) return;
if (Deputy.handcuffedKnows.ContainsKey(CachedPlayer.LocalPlayer.PlayerId) && Deputy.handcuffedKnows[CachedPlayer.LocalPlayer.PlayerId] > 0 || MeetingHud.Instance) __instance.ImpostorVentButton.Hide();
else if (CachedPlayer.LocalPlayer.PlayerControl.roleCanUseVents() && !__instance.ImpostorVentButton.isActiveAndEnabled) __instance.ImpostorVentButton.Show();
using TheOtherRoles.Objects;
using TheOtherRoles.CustomGameModes;
using Reactor.Utilities.Extensions;
+using AmongUs.GameOptions;
namespace TheOtherRoles.Patches {
[HarmonyPatch(typeof(Vent), nameof(Vent.CanUse))]
public static class VentCanUsePatch
{
- public static bool Prefix(Vent __instance, ref float __result, [HarmonyArgument(0)] GameData.PlayerInfo pc, [HarmonyArgument(1)] out bool canUse, [HarmonyArgument(2)] out bool couldUse) {
+ public static bool Prefix(Vent __instance, ref float __result, [HarmonyArgument(0)] GameData.PlayerInfo pc, [HarmonyArgument(1)] ref bool canUse, [HarmonyArgument(2)] ref bool couldUse) {
+ if (GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek) return true;
float num = float.MaxValue;
PlayerControl @object = pc.Object;
Vector3 center = @object.Collider.bounds.center;
Vector3 position = __instance.transform.position;
num = Vector2.Distance(center, position);
- canUse &= (num <= usableDistance && !PhysicsHelpers.AnythingBetween(@object.Collider, center, position, Constants.ShipOnlyMask, false));
+ canUse &= (num <= usableDistance && (!PhysicsHelpers.AnythingBetween(@object.Collider, center, position, Constants.ShipOnlyMask, false) || __instance.name.StartsWith("JackInTheBoxVent_")));
}
__result = num;
return false;
[HarmonyPatch(typeof(Vent), nameof(Vent.Use))]
public static class VentUsePatch {
public static bool Prefix(Vent __instance) {
+ if (GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek) return true;
// Deputy handcuff disables the vents
if (Deputy.handcuffedPlayers.Contains(CachedPlayer.LocalPlayer.PlayerId)) {
Deputy.setHandcuffedKnows();
}
}
- [HarmonyPatch(typeof(Vent), nameof(Vent.MoveToVent))]
+ [HarmonyPatch(typeof(Vent), nameof(Vent.TryMoveToVent))]
public static class MoveToVentPatch {
public static bool Prefix(Vent otherVent) {
return !Trapper.playersOnMap.Contains(CachedPlayer.LocalPlayer.PlayerControl);
}
// Dead Bodies
foreach (DeadBody deadBody in GameObject.FindObjectsOfType<DeadBody>()) {
- SpriteRenderer component = deadBody.bodyRenderer;
+ SpriteRenderer component = deadBody.bodyRenderers.FirstOrDefault();
component.material.SetColor("_BackColor", Palette.ShadowColors[11]);
component.material.SetColor("_BodyColor", Palette.PlayerColors[11]);
}
// Dead Bodies
foreach (DeadBody deadBody in GameObject.FindObjectsOfType<DeadBody>()) {
var colorId = GameData.Instance.GetPlayerById(deadBody.ParentId).Object.Data.DefaultOutfit.ColorId;
- SpriteRenderer component = deadBody.bodyRenderer;
+ SpriteRenderer component = deadBody.bodyRenderers.FirstOrDefault();
component.material.SetColor("_BackColor", Palette.ShadowColors[colorId]);
component.material.SetColor("_BodyColor", Palette.PlayerColors[colorId]);
}
public static void Postfix(PlayerControl __instance, SpriteRenderer rend) {
if (!nightVisionIsActive) return;
foreach (DeadBody deadBody in GameObject.FindObjectsOfType<DeadBody>()) {
- foreach (SpriteRenderer component in new SpriteRenderer[2] { deadBody.bodyRenderer, deadBody.bloodSplatter }) {
+ foreach (SpriteRenderer component in new SpriteRenderer[2] { deadBody.bodyRenderers.FirstOrDefault(), deadBody.bloodSplatter }) {
component.material.SetColor("_BackColor", Palette.ShadowColors[11]);
component.material.SetColor("_BodyColor", Palette.PlayerColors[11]);
}
if (player == Warlock.warlock) Warlock.clearAndReload();
if (player == Witch.witch) Witch.clearAndReload();
if (player == Ninja.ninja) Ninja.clearAndReload();
+ if (player == Bomber.bomber) Bomber.clearAndReload();
// Other roles
if (player == Jester.jester) Jester.clearAndReload();
public static void resetCamouflage() {
camouflageTimer = 0f;
- foreach (PlayerControl p in CachedPlayer.AllPlayers)
+ foreach (PlayerControl p in CachedPlayer.AllPlayers) {
+ if (p == Ninja.ninja && Ninja.isInvisble)
+ continue;
p.setDefaultLook();
}
+ }
public static void clearAndReload() {
resetCamouflage();
public static class Snitch {
public static PlayerControl snitch;
public static Color color = new Color32(184, 251, 79, byte.MaxValue);
+ public enum Mode {
+ Chat = 0,
+ Map = 1,
+ ChatAndMap = 2
+ }
+ public enum Targets {
+ EvilPlayers = 0,
+ Killers = 1
+ }
+ public static Mode mode = Mode.Chat;
+ public static Targets targets = Targets.EvilPlayers;
public static int taskCountForReveal = 1;
+
public static bool isRevealed = false;
public static Dictionary<byte, byte> playerRoomMap = new Dictionary<byte, byte>();
public static TMPro.TextMeshPro text = null;
+ public static bool needsUpdate = true;
public static void clearAndReload() {
taskCountForReveal = Mathf.RoundToInt(CustomOptionHolder.snitchLeftTasksForReveal.getFloat());
playerRoomMap = new Dictionary<byte, byte>();
if (text != null) UnityEngine.Object.Destroy(text);
text = null;
+ needsUpdate = true;
+ mode = (Mode) CustomOptionHolder.snitchMode.getSelection();
+ targets = (Targets) CustomOptionHolder.snitchTargets.getSelection();
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
- <Version>4.3.0</Version>
+ <Version>4.3.1</Version>
<Description>TheOtherRoles</Description>
<Authors>Eisbison</Authors>
<LangVersion>latest</LangVersion>
</ItemGroup>
<ItemGroup>
- </ItemGroup>
-
- <ItemGroup>
- <PackageReference Include="AmongUs.GameLibs.Steam" Version="2023.02.28" />
+ <PackageReference Include="AmongUs.GameLibs.Steam" Version="2023.03.28" />
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.664" />
<PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.0.1" />
<PackageReference Include="Reactor" Version="2.1.0" />
public static readonly float[] eventDurations = {0f, 1f, 5f, 0f};
public static double[] eventProbabilities;
private static bool knocked = false;
+ public static bool disableHorses = false;
public static void Load() {
if (!isEnabled) return;
public static void clearAndReload() {
eventQueue = new List<EventTypes>();
eventInvert = false;
-
+ if (canBeEnabled && CustomOptionHolder.enableCodenameDisableHorses != null)
+ disableHorses = CustomOptionHolder.enableCodenameDisableHorses.getBool();
}
public static void Update() {
if (!isEnabled) return;
defaultHat = PlayerControl.LocalPlayer.Data.DefaultOutfit.HatId;
meetingEndsUpdate();
+ List<CachedPlayer> relevantPlayers = CachedPlayer.AllPlayers.Where(x => !x.Data.IsDead && x != CachedPlayer.LocalPlayer).ToList();
+ foreach (CachedPlayer pc in relevantPlayers)
+ pc.PlayerControl.MyPhysics.SetBodyType(disableHorses ? PlayerBodyTypes.Normal : PlayerBodyTypes.Horse);
+
}
public static void gameEndsUpdate() {
switch (eventToStart) {
case EventTypes.Animation:
CachedPlayer animationPlayer = relevantPlayers[rnd.Next(relevantPlayers.Count)];
- animationPlayer.PlayerPhysics.SetBodyType(rnd.Next(2) > 0 ? PlayerBodyTypes.Horse : PlayerBodyTypes.Seeker);
+ animationPlayer.PlayerPhysics.SetBodyType(rnd.Next(2) > 0 ? (disableHorses ? PlayerBodyTypes.Horse : PlayerBodyTypes.Normal) : PlayerBodyTypes.Seeker);
FastDestroyableSingleton<HudManager>.Instance.StartCoroutine(Effects.Lerp(eventDurations[(int)EventTypes.Animation], new Action<float>((p) => {
if (p==1)
- animationPlayer.PlayerControl.MyPhysics.SetBodyType(PlayerBodyTypes.Normal);
+ animationPlayer.PlayerControl.MyPhysics.SetBodyType(disableHorses ? PlayerBodyTypes.Normal : PlayerBodyTypes.Horse);
})));
break;
case EventTypes.Communication:
"net6.0": {
"AmongUs.GameLibs.Steam": {
"type": "Direct",
- "requested": "[2023.2.28, )",
- "resolved": "2023.2.28",
- "contentHash": "GEcFs++dUrI+6Q+RTWvnm1a7l3Q/bUNNuCy9h8R17WISTOrV7g9QrZZmUiJka0JvVnj/J9Hygx5FaqApzFZKiQ=="
+ "requested": "[2023.3.28, )",
+ "resolved": "2023.3.28",
+ "contentHash": "EIaXNPlV9DMxlPfrC1yGZPXUIKbN7hAbmizYoL72gmxZVWiLLiJ8VbelM86OgyLocQ8+ma/UD7laPYIbePlrxw=="
},
"BepInEx.IL2CPP.MSBuild": {
"type": "Direct",