private static CustomButton trackerButton;
private static CustomButton vampireKillButton;
private static CustomButton garlicButton;
+ private static CustomButton jackalKillButton;
+ private static CustomButton sidekickKillButton;
+ private static CustomButton jackalSidekickButton;
public static void setCustomButtonCooldowns() {
engineerRepairButton.MaxTimer = 0f;
morphlingButton.MaxTimer = Morphling.cooldown;
camouflagerButton.MaxTimer = Camouflager.cooldown;
spyButton.MaxTimer = Spy.cooldown;
- vampireKillButton.MaxTimer = PlayerControl.GameOptions.KillCooldown;
+ vampireKillButton.MaxTimer = Vampire.cooldown;
trackerButton.MaxTimer = 0f;
garlicButton.MaxTimer = 0f;
spyButton.EffectDuration = Spy.duration;
vampireKillButton.EffectDuration= Vampire.delay;
+
+ jackalKillButton.MaxTimer = Jackal.cooldown;
+ sidekickKillButton.MaxTimer = Sidekick.cooldown;
+ jackalSidekickButton.MaxTimer = Jackal.createSidekickCooldown;
}
public static void Postfix(HudManager __instance)
MessageWriter attemptWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.ShieldedMurderAttempt, Hazel.SendOption.None, -1);
AmongUsClient.Instance.FinishRpcImmediately(attemptWriter);
RPCProcedure.shieldedMurderAttempt();
-
return;
}
byte targetId = 0;
- if (Sheriff.currentTarget.Data.IsImpostor || (Sheriff.jesterCanDieToSheriff && Jester.jester != null && Jester.jester == Sheriff.currentTarget))
+ if (Sheriff.currentTarget.Data.IsImpostor ||
+ Sheriff.currentTarget == Jackal.jackal ||
+ Sheriff.currentTarget == Sidekick.sidekick ||
+ (Sheriff.jesterCanDieToSheriff && Jester.jester != null && Jester.jester == Sheriff.currentTarget)) {
targetId = Sheriff.currentTarget.PlayerId;
- else
+ }
+ else {
targetId = PlayerControl.LocalPlayer.PlayerId;
-
+ }
MessageWriter killWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SheriffKill, Hazel.SendOption.None, -1);
killWriter.Write(targetId);
AmongUsClient.Instance.FinishRpcImmediately(killWriter);
},
() => { return Vampire.vampire != null && Vampire.vampire == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; },
() => {
- if (Vampire.targetNearGarlic)
+ if (Vampire.targetNearGarlic && Vampire.canKillNearGarlics)
vampireKillButton.killButtonManager.renderer.sprite = __instance.KillButton.renderer.sprite;
else
vampireKillButton.killButtonManager.renderer.sprite = Vampire.getButtonSprite();
- return Vampire.currentTarget != null && PlayerControl.LocalPlayer.CanMove;
+ return Vampire.currentTarget != null && PlayerControl.LocalPlayer.CanMove && (!Vampire.targetNearGarlic || Vampire.canKillNearGarlics);
},
() => {
vampireKillButton.Timer = vampireKillButton.MaxTimer;
__instance,
true
);
+
+
+ // Jackal Sidekick Button
+ jackalSidekickButton = new CustomButton(
+ () => {
+ MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.JackalCreatesSidekick, Hazel.SendOption.None, -1);
+ writer.Write(Jackal.currentTarget.PlayerId);
+ AmongUsClient.Instance.FinishRpcImmediately(writer);
+ RPCProcedure.jackalCreatesSidekick(Jackal.currentTarget.PlayerId);
+ },
+ () => { return Jackal.canCreateSidekick && Sidekick.sidekick == null && Jackal.fakeSidekick == null && Jackal.jackal != null && Jackal.jackal == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; },
+ () => { return Sidekick.sidekick == null && Jackal.fakeSidekick == null && Jackal.currentTarget != null && PlayerControl.LocalPlayer.CanMove; },
+ () => { jackalSidekickButton.Timer = jackalSidekickButton.MaxTimer;},
+ Jackal.getSidekickButtonSprite(),
+ new Vector3(-1.3f, 1.3f, 0f),
+ __instance
+ );
+
+ // Jackal Kill
+ jackalKillButton = new CustomButton(
+ () => {
+ if (!Helpers.handleMurderAttempt(Jackal.currentTarget)) return;
+ byte targetId = Jackal.currentTarget.PlayerId;
+ MessageWriter killWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.JackalKill, Hazel.SendOption.None, -1);
+ killWriter.Write(targetId);
+ AmongUsClient.Instance.FinishRpcImmediately(killWriter);
+ RPCProcedure.jackalKill(targetId);
+ jackalKillButton.Timer = jackalKillButton.MaxTimer;
+ Jackal.currentTarget = null;
+ },
+ () => { return Jackal.jackal != null && Jackal.jackal == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; },
+ () => { return Jackal.currentTarget && PlayerControl.LocalPlayer.CanMove; },
+ () => { jackalKillButton.Timer = jackalKillButton.MaxTimer;},
+ __instance.KillButton.renderer.sprite,
+ new Vector3(-1.3f, 0, 0),
+ __instance
+ );
+
+ // Sidekick Kill
+ sidekickKillButton = new CustomButton(
+ () => {
+ if (!Helpers.handleMurderAttempt(Sidekick.currentTarget)) return;
+ byte targetId = Sidekick.currentTarget.PlayerId;
+ MessageWriter killWriter = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SidekickKill, Hazel.SendOption.None, -1);
+ killWriter.Write(targetId);
+ AmongUsClient.Instance.FinishRpcImmediately(killWriter);
+ RPCProcedure.sidekickKill(targetId);
+
+ sidekickKillButton.Timer = sidekickKillButton.MaxTimer;
+ Sidekick.currentTarget = null;
+ },
+ () => { return Sidekick.canKill && Sidekick.sidekick != null && Sidekick.sidekick == PlayerControl.LocalPlayer && !PlayerControl.LocalPlayer.Data.IsDead; },
+ () => { return Sidekick.currentTarget && PlayerControl.LocalPlayer.CanMove; },
+ () => { sidekickKillButton.Timer = sidekickKillButton.MaxTimer;},
+ __instance.KillButton.renderer.sprite,
+ new Vector3(-1.3f, 0, 0),
+ __instance
+ );
}
}
}
\ No newline at end of file
{
static void Postfix(VersionShower __instance) {
string spacer = new String('\n', 15);
+ string text = "[FCCE03FF]TheOtherRoles[] v1.8.1:\n- Modded by [FCCE03FF]Eisbison[] and [FFEB91FF]Thunderstorm584[]\n- Balanced with [FFEB91FF]Dhalucard";
if (__instance.text.Text.Contains(spacer))
- __instance.text.Text += "\n[FFFFFFFF]- Loaded [FCCE03FF]TheOtherRoles[FFFFFFFF] v1.7\n by [FCCE03FF]Eisbison";
+ __instance.text.Text += "\n" + text;
else
- __instance.text.Text += spacer + "[FFFFFFFF]- Loaded [FCCE03FF]TheOtherRoles[FFFFFFFF] v1.7\n by [FCCE03FF]Eisbison";
+ __instance.text.Text += spacer + text;
}
}
static void Postfix(VersionShower __instance)
{
__instance.text.Text += "\n[FCCE03FF]TheOtherRoles[FFFFFFFF]";
- __instance.text.Text += "\nModded by [FCCE03FF]Eisbison";
+ __instance.text.Text += "\nModded by [FCCE03FF]Eisbison[FFFFFFFF]";
}
}
}
}
if (Timer >= 0) {
- Timer -= Time.deltaTime;
+ if (HasEffect && isEffectActive)
+ Timer -= Time.deltaTime;
+ else if (!PlayerControl.LocalPlayer.inVent)
+ Timer -= Time.deltaTime;
}
if (Timer <= 0 && HasEffect && isEffectActive) {
using System.Text;
namespace TheOtherRoles {
+ enum CustomGameOverReason {
+ LoversWin = 10,
+ TeamJackalWin = 11
+ }
+
enum WinCondition {
Default,
LoversTeamWin,
LoversSoloWin,
JesterWin,
BountyHunterWin,
- JesterAndBountyHunterWin
+ JesterAndBountyHunterWin,
+ JackalWin
}
static class AdditionalTempData {
[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnGameEnd))]
public class OnGameEndPatch {
+ private static GameOverReason gameOverReason;
+ public static void Prefix(AmongUsClient __instance, ref GameOverReason OFLKLGMHBEL, bool JFFPAKGPNJA) {
+ gameOverReason = OFLKLGMHBEL;
+ if ((int)OFLKLGMHBEL >= 10) OFLKLGMHBEL = GameOverReason.ImpostorByKill;
+ }
+
public static void Postfix(AmongUsClient __instance, GameOverReason OFLKLGMHBEL, bool JFFPAKGPNJA) {
AdditionalTempData.clear();
if (jesterWinner != null) TempData.winners.Remove(jesterWinner);
}
+ // Remove Jackal and Sidekick from winners (on Jackal win he will be added again, see below)
+ if (Jackal.jackal != null || Sidekick.sidekick != null) {
+ List<WinningPlayerData> winnersToRemove = new List<WinningPlayerData>();
+ foreach (WinningPlayerData winner in TempData.winners) {
+ if (winner.Name == Jackal.jackal?.Data?.PlayerName) winnersToRemove.Add(winner);
+ if (winner.Name == Sidekick.sidekick?.Data?.PlayerName) winnersToRemove.Add(winner);
+ foreach(var player in Jackal.formerJackals) {
+ if (winner.Name == player.Data.PlayerName) {
+ winnersToRemove.Add(winner);
+ }
+ }
+ }
+
+ foreach (var winner in winnersToRemove) {
+ TempData.winners.Remove(winner);
+ }
+ }
// Jester and Bounty Hunter win condition (should be implemented using a proper GameOverReason in the future)
bool jesterWin = Jester.jester != null && Jester.jester.Data.IsImpostor;
}
// Lovers win conditions (should be implemented using a proper GameOverReason in the future)
- else if (Lovers.existingAndAlive()) {
+ else if (Lovers.existingAndAlive() && gameOverReason == (GameOverReason)CustomGameOverReason.LoversWin) {
AdditionalTempData.localIsLover = (PlayerControl.LocalPlayer == Lovers.lover1 || PlayerControl.LocalPlayer == Lovers.lover2);
// Double win for lovers, crewmates also win
- if (TempData.DidHumansWin(OFLKLGMHBEL)) {
+ if (!Lovers.existingWithImpLover()) {
AdditionalTempData.winCondition = WinCondition.LoversTeamWin;
+ TempData.winners = new Il2CppSystem.Collections.Generic.List<WinningPlayerData>();
+ foreach (PlayerControl p in PlayerControl.AllPlayerControls) {
+ if (p == null) continue;
+ if (p == Lovers.lover1 || p == Lovers.lover2)
+ TempData.winners.Add(new WinningPlayerData(p.Data));
+ else if (p != Shifter.shifter && p != Jester.jester && p != Jackal.jackal && p != Sidekick.sidekick && !p.Data.IsImpostor)
+ TempData.winners.Add(new WinningPlayerData(p.Data));
+ }
}
// Lovers solo win
- else if (Lovers.existingWithImpLover()){
+ else {
AdditionalTempData.winCondition = WinCondition.LoversSoloWin;
TempData.winners = new Il2CppSystem.Collections.Generic.List<WinningPlayerData>();
TempData.winners.Add(new WinningPlayerData(Lovers.lover1.Data));
TempData.winners.Add(new WinningPlayerData(Lovers.lover2.Data));
}
}
+
+ // Jackal win condition (should be implemented using a proper GameOverReason in the future)
+ else if (gameOverReason == (GameOverReason)CustomGameOverReason.TeamJackalWin && (Jackal.jackal != null && !Jackal.jackal.Data.IsDead || Sidekick.sidekick != null && !Sidekick.sidekick.Data.IsDead)) {
+ // Jackal wins if nobody except jackal is alive
+ AdditionalTempData.winCondition = WinCondition.JackalWin;
+ TempData.winners = new Il2CppSystem.Collections.Generic.List<WinningPlayerData>();
+ WinningPlayerData wpd = new WinningPlayerData(Jackal.jackal.Data);
+ wpd.IsImpostor = false;
+ TempData.winners.Add(wpd);
+ // If there is a sidekick. The sidekick also wins
+ if (Sidekick.sidekick != null) {
+ WinningPlayerData wpdSidekick = new WinningPlayerData(Sidekick.sidekick.Data);
+ wpdSidekick.IsImpostor = false;
+ TempData.winners.Add(wpdSidekick);
+ }
+ foreach(var player in Jackal.formerJackals) {
+ WinningPlayerData wpdFormerJackal = new WinningPlayerData(player.Data);
+ wpdFormerJackal.IsImpostor = false;
+ TempData.winners.Add(wpdFormerJackal);
+ }
+ }
- // Reset Bonus Roles Settings
+ // Reset Role Settings
clearAndReloadRoles();
clearGameHistory();
}
textRenderer.Text = "Lovers And Crewmates Win";
textRenderer.Color = Lovers.color;
__instance.BackgroundBar.material.SetColor("_Color", Lovers.color);
- } else if (AdditionalTempData.winCondition == WinCondition.LoversSoloWin) {
+ }
+ else if (AdditionalTempData.winCondition == WinCondition.LoversSoloWin) {
textRenderer.Text = "Lovers Win";
textRenderer.Color = Lovers.color;
__instance.BackgroundBar.material.SetColor("_Color", Lovers.color);
}
+ else if (AdditionalTempData.winCondition == WinCondition.JackalWin) {
+ textRenderer.Text = "Team Jackal Wins";
+ textRenderer.Color = Jackal.color;
+ }
AdditionalTempData.clear();
}
[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.CheckEndCriteria))]
class CheckEndCriteriaPatch{
- private static void ReviveEveryone() {
- for (int i = 0; i < GameData.Instance.PlayerCount; i++)
- GameData.Instance.AllPlayers[i].Object.Revive();
- DeadBody[] array = UnityEngine.Object.FindObjectsOfType<DeadBody>();
- for (int i = 0; i < array.Length; i++) UnityEngine.Object.Destroy(array[i].gameObject);
- }
- private static void EndGameForSabotage(ShipStatus __instance)
- {
- if (!DestroyableSingleton<TutorialManager>.InstanceExists)
- {
- __instance.enabled = false;
- ShipStatus.RpcEndGame(GameOverReason.ImpostorBySabotage, false);
- return;
- }
- DestroyableSingleton<HudManager>.Instance.ShowPopUp(DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.GameOverSabotage, new Il2CppReferenceArray<Il2CppSystem.Object>(0)));
+ public static bool Prefix(ShipStatus __instance) {
+ if (!GameData.Instance) return false;
+ var statistics = new PlayerStatistics(__instance);
+ if(CheckAndEndGameForSabotageWin(__instance)) return false;
+ if(CheckAndEndGameForTaskWin(__instance)) return false;
+ if(CheckAndEndGameForLoverWin(__instance, statistics)) return false;
+ if(CheckAndEndGameForJackalWin(__instance, statistics)) return false;
+ if(CheckAndEndGameForImpostorWin(__instance, statistics)) return false;
+ if(CheckAndEndGameForCrewmateWin(__instance, statistics)) return false;
+ return false;
}
- public static bool Prefix(ShipStatus __instance) {
- if (!GameData.Instance)
- {
- return false;
- }
+ private static bool CheckAndEndGameForSabotageWin(ShipStatus __instance) {
ISystemType systemType = __instance.Systems.ContainsKey(SystemTypes.LifeSupp) ? __instance.Systems[SystemTypes.LifeSupp] : null;
- if (systemType != null)
- {
+ if (systemType != null) {
LifeSuppSystemType lifeSuppSystemType = systemType.TryCast<LifeSuppSystemType>();
- if (lifeSuppSystemType.Countdown < 0f)
- {
+ if (lifeSuppSystemType.Countdown < 0f) {
EndGameForSabotage(__instance);
lifeSuppSystemType.Countdown = 10000f;
+ return true;
}
}
ISystemType systemType2 = __instance.Systems.ContainsKey(SystemTypes.Reactor) ? __instance.Systems[SystemTypes.Reactor] : null;
- if (systemType2 == null) systemType2 = __instance.Systems.ContainsKey(SystemTypes.Laboratory) ? __instance.Systems[SystemTypes.Laboratory] : null;
- if (systemType2 != null)
- {
+ if (systemType2 == null) {
+ systemType2 = __instance.Systems.ContainsKey(SystemTypes.Laboratory) ? __instance.Systems[SystemTypes.Laboratory] : null;
+ }
+ if (systemType2 != null) {
ReactorSystemType reactorSystemType = systemType2.TryCast<ReactorSystemType>();
- if (reactorSystemType.Countdown < 0f)
- {
+ if (reactorSystemType.Countdown < 0f) {
EndGameForSabotage(__instance);
reactorSystemType.Countdown = 10000f;
+ return true;
}
}
- int num = 0;
- int num2 = 0;
- int num3 = 0;
- for (int i = 0; i < GameData.Instance.PlayerCount; i++)
+ return false;
+ }
+
+ private static bool CheckAndEndGameForTaskWin(ShipStatus __instance) {
+ bool localCompletedAllTasks = true;
+ foreach (PlayerTask t in PlayerControl.LocalPlayer.myTasks) {
+ localCompletedAllTasks = localCompletedAllTasks && t.IsComplete;
+ }
+
+ if (!DestroyableSingleton<TutorialManager>.InstanceExists)
{
- GameData.PlayerInfo playerInfo = GameData.Instance.AllPlayers[i];
- if (!playerInfo.Disconnected)
+ if (GameData.Instance.TotalTasks <= GameData.Instance.CompletedTasks)
{
- if (playerInfo.IsImpostor)
- {
- num3++;
- }
- if (!playerInfo.IsDead)
- {
- if (playerInfo.IsImpostor)
- {
- num2++;
- }
- else
- {
- num++;
- }
- }
+ __instance.enabled = false;
+ ShipStatus.RpcEndGame(GameOverReason.HumansByTask, false);
+ return true;
}
}
- if (num2 <= 0 && (!DestroyableSingleton<TutorialManager>.InstanceExists || num3 > 0))
+ else if (localCompletedAllTasks)
{
+ DestroyableSingleton<HudManager>.Instance.ShowPopUp(DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.GameOverTaskWin, new Il2CppReferenceArray<Il2CppSystem.Object>(0)));
+ __instance.Begin();
+ }
+ return false;
+ }
+
+ private static bool CheckAndEndGameForLoverWin(ShipStatus __instance, PlayerStatistics statistics) {
+ if (statistics.TeamLoversAlive == 2 && statistics.TotalAlive <= 3) {
if (!DestroyableSingleton<TutorialManager>.InstanceExists)
{
__instance.enabled = false;
- ShipStatus.RpcEndGame(GameOverReason.HumansByVote, false);
- return false;
+ ShipStatus.RpcEndGame((GameOverReason)CustomGameOverReason.LoversWin, false); // should be implemented using a proper GameOverReason in the future
+ return true;
}
DestroyableSingleton<HudManager>.Instance.ShowPopUp(DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.GameOverImpostorDead, new Il2CppReferenceArray<Il2CppSystem.Object>(0)));
ReviveEveryone();
- return false;
+ return true;
}
- else
- {
- if (num > num2)
- {
- bool localCompletedAllTasks = true;
- foreach (PlayerTask t in PlayerControl.LocalPlayer.myTasks) {
- localCompletedAllTasks = localCompletedAllTasks && t.IsComplete;
- }
+ return false;
+ }
- if (!DestroyableSingleton<TutorialManager>.InstanceExists)
- {
- if (GameData.Instance.TotalTasks <= GameData.Instance.CompletedTasks)
- {
- __instance.enabled = false;
- ShipStatus.RpcEndGame(GameOverReason.HumansByTask, false);
- return false;
- }
- }
- else if (localCompletedAllTasks)
- {
- DestroyableSingleton<HudManager>.Instance.ShowPopUp(DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.GameOverTaskWin, new Il2CppReferenceArray<Il2CppSystem.Object>(0)));
- __instance.Begin();
- }
- if (num + num2 == 3 && Lovers.existingAndAlive()) { // 3 players with 2 lovers is always a lover win (either shared with crewmates or solo for lovers, marked as impostor win)
- __instance.enabled = false;
- ShipStatus.RpcEndGame(Lovers.existingWithImpLover() ? GameOverReason.ImpostorByKill : GameOverReason.HumansByVote, false); // should be implemented using a proper GameOverReason in the future
- return false;
- }
- return false;
- }
- if (num == num2 && Lovers.existingAndAlive() && Lovers.existingWithImpLover()) { // 3 vs 3 or 2 vs 2 is not win if both lovers are alive and one is an impostor
- return false;
- }
+ private static bool CheckAndEndGameForJackalWin(ShipStatus __instance, PlayerStatistics statistics) {
+ if (statistics.TeamJackalAlive >= statistics.TotalAlive - statistics.TeamJackalAlive && statistics.TeamImpostorsAlive == 0 && !(statistics.TeamJackalHasAliveLover)) {
if (!DestroyableSingleton<TutorialManager>.InstanceExists)
{
+ __instance.enabled = false;
+ ShipStatus.RpcEndGame((GameOverReason)CustomGameOverReason.TeamJackalWin, false);
+ return true;
+ }
+ DestroyableSingleton<HudManager>.Instance.ShowPopUp(DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.GameOverImpostorKills, new Il2CppReferenceArray<Il2CppSystem.Object>(0)));
+ ReviveEveryone();
+ return true;
+ }
+ return false;
+ }
+
+ private static bool CheckAndEndGameForImpostorWin(ShipStatus __instance, PlayerStatistics statistics) {
+ if (statistics.TeamImpostorsAlive >= statistics.TotalAlive - statistics.TeamImpostorsAlive && statistics.TeamJackalAlive == 0 && !(statistics.TeamImpostorHasAliveLover)) {
+ if (!DestroyableSingleton<TutorialManager>.InstanceExists) {
__instance.enabled = false;
GameOverReason endReason;
- switch (TempData.LastDeathReason)
- {
- case DeathReason.Exile:
- endReason = GameOverReason.ImpostorByVote;
- break;
- case DeathReason.Kill:
- endReason = GameOverReason.ImpostorByKill;
- break;
- default:
- endReason = GameOverReason.ImpostorByVote;
- break;
+ switch (TempData.LastDeathReason) {
+ case DeathReason.Exile:
+ endReason = GameOverReason.ImpostorByVote;
+ break;
+ case DeathReason.Kill:
+ endReason = GameOverReason.ImpostorByKill;
+ break;
+ default:
+ endReason = GameOverReason.ImpostorByVote;
+ break;
}
ShipStatus.RpcEndGame(endReason, false);
- return false;
+ return true;
}
DestroyableSingleton<HudManager>.Instance.ShowPopUp(DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.GameOverImpostorKills, new Il2CppReferenceArray<Il2CppSystem.Object>(0)));
-
ReviveEveryone();
- return false;
+ return true;
+ }
+ return false;
+ }
+
+ private static bool CheckAndEndGameForCrewmateWin(ShipStatus __instance, PlayerStatistics statistics) {
+ if (statistics.TeamImpostorsAlive == 0 && statistics.TeamJackalAlive == 0) {
+ if (!DestroyableSingleton<TutorialManager>.InstanceExists) {
+ __instance.enabled = false;
+ ShipStatus.RpcEndGame(GameOverReason.HumansByVote, false);
+ return true;
+ }
+ DestroyableSingleton<HudManager>.Instance.ShowPopUp(DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.GameOverImpostorDead, new Il2CppReferenceArray<Il2CppSystem.Object>(0)));
+ ReviveEveryone();
+ return true;
}
+ return false;
+ }
+
+ private static void ReviveEveryone() {
+ for (int i = 0; i < GameData.Instance.PlayerCount; i++)
+ GameData.Instance.AllPlayers[i].Object.Revive();
+ DeadBody[] array = UnityEngine.Object.FindObjectsOfType<DeadBody>();
+ for (int i = 0; i < array.Length; i++) UnityEngine.Object.Destroy(array[i].gameObject);
+ }
+
+ private static void EndGameForSabotage(ShipStatus __instance)
+ {
+ if (!DestroyableSingleton<TutorialManager>.InstanceExists)
+ {
+ __instance.enabled = false;
+ ShipStatus.RpcEndGame(GameOverReason.ImpostorBySabotage, false);
+ return;
+ }
+ DestroyableSingleton<HudManager>.Instance.ShowPopUp(DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.GameOverSabotage, new Il2CppReferenceArray<Il2CppSystem.Object>(0)));
+ ReviveEveryone();
+ }
+
+ }
+
+ internal class PlayerStatistics {
+ public int TeamImpostorsAlive {get;set;}
+ public int TeamJackalAlive {get;set;}
+ public int TeamLoversAlive {get;set;}
+ public int TotalAlive {get;set;}
+ public bool TeamImpostorHasAliveLover {get;set;}
+ public bool TeamJackalHasAliveLover {get;set;}
+
+ public PlayerStatistics(ShipStatus __instance) {
+ GetPlayerCounts();
+ }
+
+ private bool isLover(GameData.PlayerInfo p) {
+ return (Lovers.lover1 != null && Lovers.lover1.PlayerId == p.PlayerId) || (Lovers.lover2 != null && Lovers.lover2.PlayerId == p.PlayerId);
+ }
+
+ private void GetPlayerCounts() {
+ int numJackalAlive = 0;
+ int numImpostorsAlive = 0;
+ int numLoversAlive = 0;
+ int numTotalAlive = 0;
+ bool impLover = false;
+ bool jackalLover = false;
+
+ for (int i = 0; i < GameData.Instance.PlayerCount; i++)
+ {
+ GameData.PlayerInfo playerInfo = GameData.Instance.AllPlayers[i];
+ if (!playerInfo.Disconnected)
+ {
+ if (!playerInfo.IsDead)
+ {
+ numTotalAlive++;
+
+ bool lover = isLover(playerInfo);
+ if (lover) numLoversAlive++;
+
+ if (playerInfo.IsImpostor) {
+ numImpostorsAlive++;
+ if (lover) impLover = true;
+ }
+ if (Jackal.jackal != null && Jackal.jackal.PlayerId == playerInfo.PlayerId) {
+ numJackalAlive++;
+ if (lover) jackalLover = true;
+ }
+ if (Sidekick.sidekick != null && Sidekick.sidekick.PlayerId == playerInfo.PlayerId) {
+ numJackalAlive++;
+ if (lover) jackalLover = true;
+ }
+ }
+ }
+ }
+
+ TeamJackalAlive = numJackalAlive;
+ TeamImpostorsAlive = numImpostorsAlive;
+ TeamLoversAlive = numLoversAlive;
+ TotalAlive = numTotalAlive;
+ TeamImpostorHasAliveLover = impLover;
+ TeamJackalHasAliveLover = jackalLover;
}
}
}
\ No newline at end of file
var hudString = stringBuilder.ToString();
int defaultSettingsLines = 19;
- int roleSettingsLines = 19 + 22;
+ int roleSettingsLines = 19 + 23;
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 counter = TheOtherRolesPlugin.optionsPage;
} else if (counter == 2) {
hudString = hudString.Substring(end2 + 1);
}
- hudString += "\n Press tab for more...";
+ hudString += "\n Press tab for more...\n\n\n";
__result = hudString;
}
}
return true;
}
+ public static void removeTasksFromPlayer(PlayerControl player, bool removeImportantTextTasks = false) {
+ if (player == null) return;
+ var toRemove = new List<PlayerTask>();
+ foreach (PlayerTask task in player.myTasks) {
+ if (!removeImportantTextTasks && task.gameObject.GetComponent<ImportantTextTask>() != null)
+ continue;
+ if (task.TaskType != TaskTypes.FixComms &&
+ task.TaskType != TaskTypes.FixLights &&
+ task.TaskType != TaskTypes.ResetReactor &&
+ task.TaskType != TaskTypes.ResetSeismic &&
+ task.TaskType != TaskTypes.RestoreOxy) {
+ toRemove.Add(task);
+ }
+ }
+ foreach (PlayerTask task in toRemove) {
+ player.RemoveTask(task);
+ }
+ }
+
public static IEnumerator Slide2D(Transform target, Vector2 source, Vector2 dest, float duration = 0.75f)
{
Vector3 temp = default(Vector3);
target.localPosition = temp;
yield break;
}
-
- public static string colorToHex(Color c) {
- return string.Format("[{0:X2}{1:X2}{2:X2}{3:X2}]", (int)c.r, (int)c.g, (int)c.b, (int)c.a);
- }
- }
-
- public class SeerInfo {
- public Color color;
- public string roleName;
- public bool isGood;
-
- public static SeerInfo getSeerInfoForPlayer(PlayerControl p) {
- string r = "";
- bool g = true;
- Color c = Color.white;
-
- if (Jester.jester != null && p == Jester.jester) {
- r = "Jester";
- c = Jester.color;
- g = false;
- }
- else if (Mayor.mayor != null && p == Mayor.mayor) {
- r = "Mayor";
- c = Mayor.color;
- }
- else if (Engineer.engineer != null && p == Engineer.engineer) {
- r = "Engineer";
- c = Engineer.color;
- }
- else if (Sheriff.sheriff != null && p == Sheriff.sheriff) {
- r = "Sheriff";
- c = Sheriff.color;
- }
- else if (Lighter.lighter != null && p == Lighter.lighter) {
- r = "Lighter";
- c = Lighter.color;
- }
- else if (Godfather.godfather != null && p == Godfather.godfather) {
- r = "Godfather";
- c = Godfather.color;
- g = false;
- }
- else if (Mafioso.mafioso != null && p == Mafioso.mafioso) {
- r = "Mafioso";
- c = Mafioso.color;
- g = false;
- }
- else if (Janitor.janitor != null && p == Janitor.janitor) {
- r = "Janitor";
- c = Janitor.color;
- g = false;
- }
- else if (Morphling.morphling != null && p == Morphling.morphling) {
- r = "Morphling";
- c = Morphling.color;
- g = false;
- }
- else if (Camouflager.camouflager != null && p == Camouflager.camouflager) {
- r = "Camouflager";
- c = Camouflager.color;
- g = false;
- }
- else if (Vampire.vampire != null && p == Vampire.vampire) {
- r = "Vampire";
- c = Vampire.color;
- g = false;
- }
- else if (Detective.detective != null && p == Detective.detective) {
- r = "Detective";
- c = Detective.color;
- }
- else if (TimeMaster.timeMaster != null && p == TimeMaster.timeMaster) {
- r = "Time Master";
- c = TimeMaster.color;
- }
- else if (Medic.medic != null && p == Medic.medic) {
- r = "Medic";
- c = Medic.color;
- }
- else if (Shifter.shifter != null && p == Shifter.shifter) {
- r = "Shifter";
- c = Shifter.color;
- g = false;
- }
- else if (Swapper.swapper != null && p == Swapper.swapper) {
- r = "Swapper";
- c = Swapper.color;
- }
- else if (Lovers.lover1 != null && p == Lovers.lover1) {
- r = Lovers.lover1.Data.IsImpostor ? "ImpLover" : "Lover";
- c = Lovers.lover1.Data.IsImpostor ? Palette.ImpostorRed : Lovers.color;
- g = !Lovers.lover1.Data.IsImpostor;
- }
- else if (Lovers.lover2 != null && p == Lovers.lover2) {
- r = Lovers.lover2.Data.IsImpostor ? "ImpLover" : "Lover";
- c = Lovers.lover2.Data.IsImpostor ? Palette.ImpostorRed : Lovers.color;
- g = !Lovers.lover2.Data.IsImpostor;
- }
- else if (Seer.seer != null && p == Seer.seer) {
- r = "Seer";
- c = Seer.color;
- }
- else if (Spy.spy != null && p == Spy.spy) {
- r = "Spy";
- c = Spy.color;
- }
- else if (Child.child != null && p == Child.child) {
- r = "Child";
- c = Child.color;
- }
- else if (BountyHunter.bountyHunter != null && p == BountyHunter.bountyHunter) {
- r = "Bounty Hunter";
- c = BountyHunter.color;
- g = false;
- }
- else if (Tracker.tracker != null && p == Tracker.tracker) {
- r = "Tracker";
- c = Tracker.color;
- }
- else if (Snitch.snitch != null && p == Snitch.snitch) {
- r = "Snitch";
- c = Snitch.color;
- }
- else if (p.Data.IsImpostor) { // Just Impostor
- r = "Impostor";
- c = Palette.ImpostorRed;
- g = false;
- }
- else if (!p.Data.IsImpostor) { // Just Crewmate
- r = "Crewmate";
- c = Color.white;
- }
-
- SeerInfo result = new SeerInfo();
- result.roleName = r;
- result.color = c;
- result.isGood = g;
-
- return result;
- }
}
}
\ No newline at end of file
var shifterTeam = new Il2CppSystem.Collections.Generic.List<PlayerControl>();
shifterTeam.Add(PlayerControl.LocalPlayer);
__instance.yourTeam = shifterTeam;
+ } else if (PlayerControl.LocalPlayer == Jackal.jackal) {
+ var jackalTeam = new Il2CppSystem.Collections.Generic.List<PlayerControl>();
+ jackalTeam.Add(PlayerControl.LocalPlayer);
+ __instance.yourTeam = jackalTeam;
}
}
// Intro display role
static void Postfix(IntroCutscene.CoBegin__d __instance)
{
- if (PlayerControl.LocalPlayer == Jester.jester)
- {
- __instance.__this.Title.Text = "Jester";
- __instance.__this.Title.Color = Jester.color;
- __instance.__this.ImpostorText.Text = "Get voted off of the ship to win";
- __instance.__this.BackgroundBar.material.color = Jester.color;
- }
- else if (PlayerControl.LocalPlayer == Mayor.mayor)
- {
- __instance.__this.Title.Text = "Mayor";
- __instance.__this.Title.Color = Mayor.color;
- __instance.__this.ImpostorText.Text = "Your vote counts twice";
- __instance.__this.BackgroundBar.material.color = Mayor.color;
- }
- else if (PlayerControl.LocalPlayer == Engineer.engineer)
- {
- __instance.__this.Title.Text = "Engineer";
- __instance.__this.Title.Color = Engineer.color;
- __instance.__this.ImpostorText.Text = "Maintain important systems on the ship";
- __instance.__this.BackgroundBar.material.color = Engineer.color;
- }
- else if (PlayerControl.LocalPlayer == Godfather.godfather)
- {
- __instance.__this.ImpostorText.gameObject.SetActive(true);
- __instance.__this.Title.Text = "Godfather";
- __instance.__this.Title.Color = Godfather.color;
- __instance.__this.ImpostorText.Text = "Kill all crewmates";
- __instance.__this.BackgroundBar.material.color = Godfather.color;
- }
- else if (PlayerControl.LocalPlayer == Mafioso.mafioso)
- {
- __instance.__this.ImpostorText.gameObject.SetActive(true);
- __instance.__this.Title.Text = "Mafioso";
- __instance.__this.Title.Color = Mafioso.color;
- __instance.__this.ImpostorText.Text = "Work with the [FF1919FF]Mafia[FFFFFFFF] to kill the crewmates";
- __instance.__this.BackgroundBar.material.color = Mafioso.color;
- }
- else if (PlayerControl.LocalPlayer == Janitor.janitor)
- {
- __instance.__this.ImpostorText.gameObject.SetActive(true);
- __instance.__this.Title.Text = "Janitor";
- __instance.__this.Title.Color = Janitor.color;
- __instance.__this.ImpostorText.Text = "Work with the [FF1919FF]Mafia[FFFFFFFF] by hiding dead bodies";
- __instance.__this.BackgroundBar.material.color = Janitor.color;
- }
- else if (PlayerControl.LocalPlayer == Morphling.morphling)
- {
- __instance.__this.ImpostorText.gameObject.SetActive(true);
- __instance.__this.Title.Text = "Morphling";
- __instance.__this.Title.Color = Morphling.color;
- __instance.__this.ImpostorText.Text = "Change your look to not get caught";
- __instance.__this.BackgroundBar.material.color = Morphling.color;
- }
- else if (PlayerControl.LocalPlayer == Camouflager.camouflager)
- {
- __instance.__this.ImpostorText.gameObject.SetActive(true);
- __instance.__this.Title.Text = "Camouflager";
- __instance.__this.Title.Color = Camouflager.color;
- __instance.__this.ImpostorText.Text = "Camouflage and kill the crewmates";
- __instance.__this.BackgroundBar.material.color = Camouflager.color;
- }
- else if (PlayerControl.LocalPlayer == Vampire.vampire)
- {
- __instance.__this.ImpostorText.gameObject.SetActive(true);
- __instance.__this.Title.Text = "Vampire";
- __instance.__this.Title.Color = Vampire.color;
- __instance.__this.ImpostorText.Text = "Kill the crewmates with your bites";
- __instance.__this.BackgroundBar.material.color = Vampire.color;
- }
- else if (PlayerControl.LocalPlayer == Sheriff.sheriff)
- {
- __instance.__this.Title.Text = "Sheriff";
- __instance.__this.Title.Color = Sheriff.color;
- __instance.__this.ImpostorText.Text = "Shoot the [FF1919FF]Impostors";
- __instance.__this.BackgroundBar.material.color = Sheriff.color;
- }
- else if (PlayerControl.LocalPlayer == Lighter.lighter)
- {
- __instance.__this.Title.Text = "Lighter";
- __instance.__this.Title.Color = Lighter.color;
- __instance.__this.ImpostorText.Text = "Your light never goes out";
- __instance.__this.BackgroundBar.material.color = Lighter.color;
- }
- else if (PlayerControl.LocalPlayer == Detective.detective)
- {
- __instance.__this.Title.Text = "Detective";
- __instance.__this.Title.Color = Detective.color;
- __instance.__this.ImpostorText.Text = "Find the [FF1919FF]Impostors[FFFFFFFF] by examining footprints";
- __instance.__this.BackgroundBar.material.color = Detective.color;
- }
- else if (PlayerControl.LocalPlayer == TimeMaster.timeMaster)
- {
- __instance.__this.Title.Text = "Time Master";
- __instance.__this.Title.Color = TimeMaster.color;
- __instance.__this.ImpostorText.Text = "Rewind time to find the [FF1919FF]Impostors";
- __instance.__this.BackgroundBar.material.color = TimeMaster.color;
- }
- else if (PlayerControl.LocalPlayer == Medic.medic)
- {
- __instance.__this.Title.Text = "Medic";
- __instance.__this.Title.Color = Medic.color;
- __instance.__this.ImpostorText.Text = "Create a shield to protect a person";
- __instance.__this.BackgroundBar.material.color = Medic.color;
- }
- else if (PlayerControl.LocalPlayer == Shifter.shifter)
- {
- __instance.__this.Title.Text = "Shifter";
- __instance.__this.Title.Color = Shifter.color;
- __instance.__this.ImpostorText.Text = "Shift your role before the game ends";
- __instance.__this.BackgroundBar.material.color = Shifter.color;
- }
- else if (PlayerControl.LocalPlayer == Swapper.swapper)
- {
- __instance.__this.Title.Text = "Swapper";
- __instance.__this.Title.Color = Swapper.color;
- __instance.__this.ImpostorText.Text = "Swap votes to exile the [FF1919FF]Impostors";
- __instance.__this.BackgroundBar.material.color = Swapper.color;
- }
- else if (PlayerControl.LocalPlayer == Lovers.lover1 || PlayerControl.LocalPlayer == Lovers.lover2)
+ RoleInfo roleInfo = RoleInfo.getRoleInfoForPlayer(PlayerControl.LocalPlayer);
+
+ if (PlayerControl.LocalPlayer == Lovers.lover1 || PlayerControl.LocalPlayer == Lovers.lover2)
{
PlayerControl otherLover = PlayerControl.LocalPlayer == Lovers.lover1 ? Lovers.lover2 : Lovers.lover1;
__instance.__this.Title.Text = PlayerControl.LocalPlayer.Data.IsImpostor ? "[FF1919FF]Imp[FC03BEFF]Lover" : "Lover";
__instance.__this.ImpostorText.gameObject.SetActive(true);
__instance.__this.BackgroundBar.material.color = Lovers.color;
}
- else if (PlayerControl.LocalPlayer == Seer.seer)
- {
- __instance.__this.Title.Text = "Seer";
- __instance.__this.Title.Color = Seer.color;
- __instance.__this.ImpostorText.Text = "Reveal the intentions of everyone on the ship";
- __instance.__this.BackgroundBar.material.color = Seer.color;
- }
- else if (PlayerControl.LocalPlayer == Spy.spy)
- {
- __instance.__this.Title.Text = "Spy";
- __instance.__this.Title.Color = Spy.color;
- __instance.__this.ImpostorText.Text = "Spy on everyone to find the [FF1919FF]Impostors";
- __instance.__this.BackgroundBar.material.color = Spy.color;
- }
- else if (PlayerControl.LocalPlayer == Child.child)
- {
- __instance.__this.Title.Text = "Child";
- __instance.__this.Title.Color = Child.color;
- __instance.__this.ImpostorText.Text = "No one will harm you until you grow up";
- __instance.__this.BackgroundBar.material.color = Child.color;
- }
else if (PlayerControl.LocalPlayer == BountyHunter.bountyHunter)
{
__instance.__this.Title.Text = "Bounty Hunter";
__instance.__this.ImpostorText.Text = "Hunt [ED653BFF]" + BountyHunter.target?.Data?.PlayerName + "[FFFFFFFF] down";
__instance.__this.BackgroundBar.material.color = BountyHunter.color;
}
- else if (PlayerControl.LocalPlayer == Tracker.tracker)
- {
- __instance.__this.Title.Text = "Tracker";
- __instance.__this.Title.Color = Tracker.color;
- __instance.__this.ImpostorText.Text = "Track the [FF1919FF]Impostors[FFFFFFFF] down";
- __instance.__this.BackgroundBar.material.color = Tracker.color;
- }
- else if (PlayerControl.LocalPlayer == Snitch.snitch)
- {
- __instance.__this.Title.Text = "Snitch";
- __instance.__this.Title.Color = Snitch.color;
- __instance.__this.ImpostorText.Text = "Finish your tasks to find the [FF1919FF]Impostors[FFFFFFFF]";
- __instance.__this.BackgroundBar.material.color = Snitch.color;
+ else if (roleInfo.name == "Crewmate" || roleInfo.name == "Impostor") {}
+ else {
+ __instance.__this.Title.Text = roleInfo.name;
+ __instance.__this.Title.Color = roleInfo.color;
+ __instance.__this.ImpostorText.gameObject.SetActive(true);
+ __instance.__this.ImpostorText.Text = roleInfo.introDescription;
+ __instance.__this.BackgroundBar.material.color = roleInfo.color;
}
}
}
{
public const string Id = "me.eisbison.theotherroles";
public Harmony Harmony { get; } = new Harmony(Id);
+ public static TheOtherRolesPlugin Instance { get { return PluginSingleton<TheOtherRolesPlugin>.Instance; } }
+ internal static BepInEx.Logging.ManualLogSource Logger { get { return Instance.Log; } }
// Roles
public static int num = 0;
public static string[] rates = new string[]{"0%", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%"};
+ public static string[] crewmateRoleCaps = new string[]{"0", "0-1", "1", "1-2", "2", "2-3", "3", "3-4", "4", "4-5", "5", "5-6", "6", "6-7", "7", "7-8", "8", "8-9", "9", "9-10", "10"};
+ public static string[] impostorRoleCaps = new string[]{"0", "0-1", "1", "1-2", "2", "2-3", "3"};
- public static CustomNumberOption crewmateRolesCount = CustomOption.AddNumber(num++.ToString(), "[CCCC00FF]Number Of Crewmate/Neutral Roles", 10, 0, 10, 0.5f);
- public static CustomNumberOption impostorRolesCount = CustomOption.AddNumber(num++.ToString(), "[CCCC00FF]Number Of Impostor Roles", 3, 0, 3, 0.5f);
+ public static CustomStringOption crewmateRolesCount = CustomOption.AddString(num++.ToString(), "[CCCC00FF]Number Of Crewmate/Neutral Roles", crewmateRoleCaps);
+ public static CustomStringOption impostorRolesCount = CustomOption.AddString(num++.ToString(), "[CCCC00FF]Number Of Impostor Roles", impostorRoleCaps);
public static CustomStringOption mafiaSpawnRate = CustomOption.AddString(num++.ToString(), cs(Janitor.color) + "Mafia", rates);
public static CustomStringOption morphlingSpawnRate = CustomOption.AddString(num++.ToString(), cs(Morphling.color) + "Morphling", rates);
// public static CustomStringOption bountyHunterSpawnRate = CustomOption.AddString(num++.ToString(), cs(BountyHunter.color) + "Bounty Hunter", rates);
public static CustomStringOption trackerSpawnRate = CustomOption.AddString(num++.ToString(), cs(Tracker.color) + "Tracker", rates);
public static CustomStringOption snitchSpawnRate = CustomOption.AddString(num++.ToString(), cs(Snitch.color) + "Snitch", rates);
+ public static CustomStringOption jackalSpawnRate = CustomOption.AddString(num++.ToString(), cs(Jackal.color) + "Jackal", rates);
// Role settings
public static CustomNumberOption janitorCooldown = CustomOption.AddNumber(num++.ToString(), "Janitor Cooldown", 30f, 10f, 60f, 2.5f);
public static CustomNumberOption morphlingCooldown = CustomOption.AddNumber(num++.ToString(), "Morphling Cooldown", 30f, 10f, 60f, 2.5f);
public static CustomNumberOption camouflagerCooldown = CustomOption.AddNumber(num++.ToString(), "Camouflager Cooldown", 30f, 10f, 60f, 2.5f);
- public static CustomNumberOption vampireKillDelay = CustomOption.AddNumber(num++.ToString(), "Vampire Kill Delay", 10f, 2.5f, 30f, 2.5f);
+ public static CustomNumberOption vampireKillDelay = CustomOption.AddNumber(num++.ToString(), "Vampire Kill Delay", 10f, 1f, 20f, 1f);
+ public static CustomNumberOption vampireCooldown = CustomOption.AddNumber(num++.ToString(), "Vampire Cooldown", 30f, 10f, 60f, 2.5f);
+ public static CustomToggleOption vampireCanKillNearGarlics = CustomOption.AddToggle(num++.ToString(), "Vampire Can Kill Near Garlics", true);
+ public static CustomNumberOption loversImpLoverRate = CustomOption.AddNumber(num++.ToString(), "Chance That One Lover Is Impostor", 30f, 0f, 100f, 10f);
public static CustomToggleOption loversBothDie = CustomOption.AddToggle(num++.ToString(), "Both Lovers Die", true);
public static CustomNumberOption shifterCooldown = CustomOption.AddNumber(num++.ToString(), "Shifter Cooldown", 30f, 10f, 60f, 2.5f);
public static CustomNumberOption trackerUpdateIntervall = CustomOption.AddNumber(num++.ToString(), "Tracker Update Intervall", 5f, 2.5f, 30f, 2.5f);
public static CustomNumberOption snitchLeftTasksForImpostors = CustomOption.AddNumber(num++.ToString(), "Task Count Where Impostors See Snitch", 1f, 0f, 5f, 1f);
+ public static CustomNumberOption jackalKillCooldown = CustomOption.AddNumber(num++.ToString(), "Jackal/Sidekick Kill Cooldown", 30f, 10f, 60f, 2.5f);
+ public static CustomNumberOption jackalCreateSidekickCooldown = CustomOption.AddNumber(num++.ToString(), "Jackal Create Sidekick Cooldown", 30f, 10f, 60f, 2.5f);
+ public static CustomToggleOption jackalCanUseVents = CustomOption.AddToggle(num++.ToString(), "Jackal can use vents", true);
+ public static CustomToggleOption jackalCanCreateSidekick = CustomOption.AddToggle(num++.ToString(), "Jackal can create a sidekick", false);
+ public static CustomToggleOption sidekickPromotesToJackal = CustomOption.AddToggle(num++.ToString(), "Sidekick gets promoted to Jackal on Jackal death", false);
+ public static CustomToggleOption sidekickCanKill = CustomOption.AddToggle(num++.ToString(), "Sidekick can kill", false);
+ public static CustomToggleOption sidekickCanUseVents = CustomOption.AddToggle(num++.ToString(), "Sidekick can use vents", true);
+ public static CustomToggleOption jackalPromotedFromSidekickCanCreateSidekick = CustomOption.AddToggle(num++.ToString(), "Jackals promoted from Sidekick can create a Sidekick", true);
+ public static CustomToggleOption jackalCanCreateSidekickFromImpostor = CustomOption.AddToggle(num++.ToString(), "Jackals can make an Impostor to his Sidekick", true);
+
+
public static List<CustomOption> options = new List<CustomOption>();
public static int optionsPage = 1;
// bountyHunterSpawnRate,
trackerSpawnRate,
snitchSpawnRate,
+ jackalSpawnRate,
janitorCooldown,
morphlingCooldown,
camouflagerCooldown,
vampireKillDelay,
+ vampireCooldown,
+ vampireCanKillNearGarlics,
+ loversImpLoverRate,
loversBothDie,
shifterCooldown,
childGrowingUpDuration,
// bountyHunterNotifyBounty,
trackerUpdateIntervall,
- snitchLeftTasksForImpostors
+ snitchLeftTasksForImpostors,
+
+ jackalKillCooldown,
+ jackalCreateSidekickCooldown,
+ jackalCanUseVents,
+ jackalCanCreateSidekick,
+ sidekickPromotesToJackal,
+ sidekickCanKill,
+ sidekickCanUseVents,
+ jackalPromotedFromSidekickCanCreateSidekick,
+ jackalCanCreateSidekickFromImpostor
};
}
}
Swapper.playerId2 = Byte.MaxValue;
// Lovers save next to be exiled, because RPC of ending game comes before RPC of exiled
- Lovers.notAckedExiledIsLover = ((Lovers.lover1 != null && Lovers.lover1.PlayerId == IHDMFDEEDEL.PlayerId) || (Lovers.lover2 != null && Lovers.lover2.PlayerId == IHDMFDEEDEL.PlayerId));
+ Lovers.notAckedExiledIsLover = false;
+ if (IHDMFDEEDEL != null)
+ Lovers.notAckedExiledIsLover = ((Lovers.lover1 != null && Lovers.lover1.PlayerId == IHDMFDEEDEL.PlayerId) || (Lovers.lover2 != null && Lovers.lover2.PlayerId == IHDMFDEEDEL.PlayerId));
}
}
static void onClick(int i, MeetingHud __instance)
{
+ if (Swapper.swapper == null || PlayerControl.LocalPlayer != Swapper.swapper || Swapper.swapper.Data.IsDead) return;
if (__instance.state == MeetingHud.VoteStates.Results) return;
if (__instance.playerStates[i].isDead) return;
[HarmonyPatch(typeof(ExileController), "Begin")]
class ExileBeginPatch {
+
public static void Prefix(ref GameData.PlayerInfo IHDMFDEEDEL, bool DCHFIBODGIL) {
// Prevent growing Child exile
if (Child.child != null && IHDMFDEEDEL != null && IHDMFDEEDEL.PlayerId == Child.child.PlayerId && !Child.isGrownUp()) {
__result = ExileController.Instance.exiled.PlayerName + " was The Tracker.";
else if(Snitch.snitch != null && ExileController.Instance.exiled.Object.PlayerId == Snitch.snitch.PlayerId)
__result = ExileController.Instance.exiled.PlayerName + " was The Snitch.";
+ else if(Jackal.jackal != null && ExileController.Instance.exiled.Object.PlayerId == Jackal.jackal.PlayerId)
+ __result = ExileController.Instance.exiled.PlayerName + " was The Jackal.";
+ else if(Sidekick.sidekick != null && ExileController.Instance.exiled.Object.PlayerId == Sidekick.sidekick.PlayerId)
+ __result = ExileController.Instance.exiled.PlayerName + " was The Sidekick.";
else
__result = ExileController.Instance.exiled.PlayerName + " was not The Impostor.";
}
else if(Lovers.lover2 != null && ExileController.Instance.exiled.Object.PlayerId == Lovers.lover2.PlayerId)
__result = ExileController.Instance.exiled.PlayerName + " was The ImpLover.";
else if(Vampire.vampire != null && ExileController.Instance.exiled.Object.PlayerId == Vampire.vampire.PlayerId)
- __result = ExileController.Instance.exiled.PlayerName + " was The Vamipre.";
+ __result = ExileController.Instance.exiled.PlayerName + " was The Vampire.";
}
// Hide number of remaining impostors on Jester win
}
}
- static PlayerControl setTarget(bool onlyCrewmates = false, bool targetPlayersInVents = false) {
+ static PlayerControl setTarget(bool onlyCrewmates = false, bool targetPlayersInVents = false, List<PlayerControl> untargetablePlayers = null) {
PlayerControl result = null;
float num = GameOptionsData.KillDistances[Mathf.Clamp(PlayerControl.GameOptions.KillDistance, 0, 2)];
if (!ShipStatus.Instance) return result;
if (!playerInfo.Disconnected && playerInfo.PlayerId != PlayerControl.LocalPlayer.PlayerId && !playerInfo.IsDead && (!onlyCrewmates || !playerInfo.IsImpostor))
{
PlayerControl @object = playerInfo.Object;
+ if(untargetablePlayers != null && untargetablePlayers.Any(x => x == @object)) {
+ // if that player is not targetable: skip check
+ continue;
+ }
+
if (@object && (!@object.inVent || targetPlayersInVents))
{
Vector2 vector = @object.GetTruePosition() - truePosition;
Vampire.currentTarget = target;
}
+ static void jackalSetTarget() {
+ if (Jackal.jackal == null || Jackal.jackal != PlayerControl.LocalPlayer) return;
+ var untargetablePlayers = new List<PlayerControl>();
+ if(Jackal.canCreateSidekickFromImpostor) {
+ // 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
+ Jackal.currentTarget = setTarget(untargetablePlayers : untargetablePlayers);
+ }
+
+ static void sidekickSetTarget() {
+ if (Sidekick.sidekick == null || Sidekick.sidekick != PlayerControl.LocalPlayer) return;
+ var untargetablePlayers = new List<PlayerControl>();
+ 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
+ Sidekick.currentTarget = setTarget(untargetablePlayers : untargetablePlayers);
+ }
+
static void engineerUpdate() {
if (PlayerControl.LocalPlayer.Data.IsImpostor && Engineer.engineer != null) {
foreach (Vent vent in ShipStatus.Instance.AllVents) {
engineerUpdate();
// Tracker
trackerUpdate();
+ // Jackal
+ jackalSetTarget();
+ // Sidekick
+ sidekickSetTarget();
}
}
}
public static class MurderPlayerPatch
{
public static bool resetToCrewmate = false;
+ public static bool resetToDead = false;
public static void Prefix(PlayerControl __instance, PlayerControl PAIBDFDMIGK)
{
// Allow everyone to murder players
resetToCrewmate = !__instance.Data.IsImpostor;
+ resetToDead = __instance.Data.IsDead;
__instance.Data.IsImpostor = true;
+ __instance.Data.IsDead = false;
}
public static void Postfix(PlayerControl __instance, PlayerControl PAIBDFDMIGK)
// Reset killer to crewmate if resetToCrewmate
if (resetToCrewmate) __instance.Data.IsImpostor = false;
+ if (resetToDead) __instance.Data.IsDead = true;
// Lover suicide trigger on murder
if ((Lovers.lover1 != null && PAIBDFDMIGK == Lovers.lover1) || (Lovers.lover2 != null && PAIBDFDMIGK == Lovers.lover2)) {
RPCProcedure.loverSuicide(otherLover.PlayerId);
}
}
+
+ // Sidekick promotion trigger on murder
+ if (Sidekick.promotesToJackal && Sidekick.sidekick != null && !Sidekick.sidekick.Data.IsDead && PAIBDFDMIGK == Jackal.jackal) {
+ MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SidekickPromotes, Hazel.SendOption.None, -1);
+ AmongUsClient.Instance.FinishRpcImmediately(writer);
+ RPCProcedure.sidekickPromotes();
+ }
}
}
-
+ [HarmonyPatch(typeof(KillAnimation),nameof(KillAnimation.CoPerformKill))]
+ class Test {
+ public static void Prefix(KillAnimation __instance, ref PlayerControl CPKODPCJPOO, ref PlayerControl PAIBDFDMIGK) {
+ if (Vampire.vampire != null && Vampire.vampire == CPKODPCJPOO && Vampire.bitten != null && Vampire.bitten == PAIBDFDMIGK)
+ CPKODPCJPOO = PAIBDFDMIGK;
+ }
+ }
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.Exiled))]
public static class ExilePlayerPatch
// Lover suicide trigger on exile
if ((Lovers.lover1 != null && __instance == Lovers.lover1) || (Lovers.lover2 != null && __instance == Lovers.lover2)) {
PlayerControl otherLover = __instance == Lovers.lover1 ? Lovers.lover2 : Lovers.lover1;
- if (PlayerControl.LocalPlayer == __instance && otherLover != null && !otherLover.Data.IsDead && Lovers.bothDie) { // Only the dead lover sends the rpc
- MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.LoverSuicide, Hazel.SendOption.None, -1);
- writer.Write(otherLover.PlayerId);
- AmongUsClient.Instance.FinishRpcImmediately(writer);
- RPCProcedure.loverSuicide(otherLover.PlayerId);
+ if (otherLover != null && !otherLover.Data.IsDead && Lovers.bothDie)
+ otherLover.Exiled();
+ }
+
+ // Sidekick promotion trigger on exile
+ if (Sidekick.promotesToJackal && Sidekick.sidekick != null && !Sidekick.sidekick.Data.IsDead && __instance == Jackal.jackal) {
+ MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SidekickPromotes, Hazel.SendOption.None, -1);
+ AmongUsClient.Instance.FinishRpcImmediately(writer);
+ RPCProcedure.sidekickPromotes();
+ }
+ }
+ }
+
+ [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.SetTasks))]
+ public static class Role
+ {
+ public static void Postfix(PlayerControl __instance)
+ {
+ if (PlayerControl.LocalPlayer == null) return;
+
+ // Remove default ImportantTextTasks
+ var toRemove = new List<PlayerTask>();
+ foreach (PlayerTask t in __instance.myTasks) {
+ if (t.gameObject.GetComponent<ImportantTextTask>() != null) {
+ toRemove.Add(t);
}
+ }
+ foreach (PlayerTask t in toRemove)
+ __instance.RemoveTask(t);
+
+ // Add description
+ RoleInfo roleInfo = RoleInfo.getRoleInfoForPlayer(__instance);
+ var task = new GameObject("RoleTask").AddComponent<ImportantTextTask>();
+ task.transform.SetParent(__instance.transform, false);
+
+ if (__instance == Jackal.jackal) {
+ var getSidekickText = Jackal.canCreateSidekick ? " and recruit a Sidekick" : "";
+ task.Text = $"{roleInfo.colorHexString()}{roleInfo.name}: Kill everyone{getSidekickText}";
+ } else {
+ task.Text = $"{roleInfo.colorHexString()}{roleInfo.name}: {roleInfo.shortDescription}";
}
+
+ __instance.myTasks.Insert(0, task);
}
}
}
BountyHunter,
Tracker,
Vampire,
- Snitch
+ Snitch,
+ Jackal,
+ Sidekick
}
enum CustomRPC
VampireBiteNotification = 97,
VampireTryKill = 98,
PlaceGarlic = 99,
+ JackalKill = 100,
+ SidekickKill = 101,
+ JackalCreatesSidekick = 102,
+ SidekickPromotes = 103
}
public static class RPCProcedure {
if (!player.Data.IsImpostor)
{
player.RemoveInfected();
- player.Die(DeathReason.Exile);
+ player.MurderPlayer(player);
player.Data.IsDead = true;
}
}
case RoleId.Snitch:
Snitch.snitch = player;
break;
+ case RoleId.Jackal:
+ Jackal.jackal = player;
+ break;
+ case RoleId.Sidekick:
+ Sidekick.sidekick = player;
+ break;
}
}
}
if (player.PlayerId == targetId && Shifter.shifter != null)
{
// Suicide when impostor or impostor variants
- if (player.Data.IsImpostor) {
+ if (player.Data.IsImpostor || player == Jackal.jackal || player == Sidekick.sidekick) {
Shifter.shifter.MurderPlayer(Shifter.shifter);
return;
}
Seer.revealedPlayers.Add(target, targetOrMistake);
if (PlayerControl.LocalPlayer == target && HudManager.Instance?.FullScreen != null) {
- SeerInfo si = SeerInfo.getSeerInfoForPlayer(target); // Use SeerInfo for target here, because we need the isGood of the targets role
+ RoleInfo si = RoleInfo.getRoleInfoForPlayer(target); // Use RoleInfo of target here, because we need the isGood of the targets role
bool showNotification = false;
if (Seer.playersWithNotification == 0 ) showNotification = true;
else if (Seer.playersWithNotification == 1 && si.isGood) showNotification = true;
if (Vampire.vampire == null || Vampire.bitten == null) return;
if (!Vampire.bitten.Data.IsDead && Helpers.handleMurderAttempt(Vampire.bitten, false)) {
- Vampire.bitten.MurderPlayer(Vampire.bitten); // Suicide because of the kill animation teleport
+ Vampire.vampire.MurderPlayer(Vampire.bitten);
}
Vampire.bitten = null;
}
if (player.PlayerId == targetId)
Tracker.tracked = player;
}
+
+ public static void jackalKill(byte targetId) {
+ foreach (PlayerControl player in PlayerControl.AllPlayerControls)
+ {
+ if (player.PlayerId == targetId)
+ {
+ Jackal.jackal.MurderPlayer(player);
+ return;
+ }
+ }
+ }
+
+ public static void sidekickKill(byte targetId) {
+ foreach (PlayerControl player in PlayerControl.AllPlayerControls)
+ {
+ if (player.PlayerId == targetId)
+ {
+ Sidekick.sidekick.MurderPlayer(player);
+ return;
+ }
+ }
+ }
+
+ public static void jackalCreatesSidekick(byte targetId) {
+ foreach (PlayerControl player in PlayerControl.AllPlayerControls)
+ {
+ if (player.PlayerId == targetId)
+ {
+ if(!Jackal.canCreateSidekickFromImpostor && player.Data.IsImpostor) {
+ Jackal.fakeSidekick = player;
+ return;
+ }
+ Sidekick.sidekick = player;
+ player.RemoveInfected();
+
+ // Crewmate roles
+ if(player == Jester.jester) Jester.clearAndReload();
+ if(player == Mayor.mayor) Mayor.clearAndReload();
+ if(player == Engineer.engineer) Engineer.clearAndReload();
+ if(player == Sheriff.sheriff) Sheriff.clearAndReload();
+ if(player == Lighter.lighter) Lighter.clearAndReload();
+ if(player == Detective.detective) Detective.clearAndReload();
+ if(player == TimeMaster.timeMaster) TimeMaster.clearAndReload();
+ if(player == Medic.medic) {
+ if (Medic.shielded != null) Medic.shielded.myRend.material.SetFloat("_Outline", 0f);
+ Medic.clearAndReload();
+ }
+ if(player == Shifter.shifter) Shifter.clearAndReload();
+ if(player == Seer.seer) Seer.clearAndReload();
+ if(player == Spy.spy) Spy.clearAndReload();
+ if(player == Child.child) Child.clearAndReload();
+ if(player == Tracker.tracker) Tracker.clearAndReload();
+ if(player == BountyHunter.bountyHunter) BountyHunter.clearAndReload();
+ if(player == Snitch.snitch) Snitch.clearAndReload();
+ if(player == Swapper.swapper) Swapper.clearAndReload();
+
+ // Impostor roles
+ if(player == Morphling.morphling) Morphling.clearAndReload();
+ if(player == Camouflager.camouflager) Camouflager.clearAndReload();
+ if(player == Godfather.godfather) Godfather.clearAndReload();
+ if(player == Mafioso.mafioso) Mafioso.clearAndReload();
+ if(player == Janitor.janitor) Janitor.clearAndReload();
+ if(player == Vampire.vampire) Vampire.clearAndReload();
+
+ // The Sidekick stays a part of the lover couple!
+
+ if (PlayerControl.LocalPlayer == null) return;
+ if (PlayerControl.LocalPlayer == player) {
+ //Only the Lover keeps his ImportantTextTask
+ Helpers.removeTasksFromPlayer(player, player != Lovers.lover1 && player != Lovers.lover2);
+
+ var task = new GameObject("RoleTask").AddComponent<ImportantTextTask>();
+ task.transform.SetParent(player.transform, false);
+ task.Text = "[00B4EBFF]Sidekick: Help your Jackal to kill everyone";
+ player.myTasks.Insert(0, task);
+ }
+
+ return;
+ }
+ }
+ }
+
+ public static void sidekickPromotes() {
+ var player = Sidekick.sidekick;
+ Jackal.removeCurrentJackal();
+ Jackal.jackal = player;
+ if (Jackal.jackalPromotedFromSidekickCanCreateSidekick == false) {
+ Jackal.canCreateSidekick = false;
+ }
+ Sidekick.clearAndReload();
+ if (PlayerControl.LocalPlayer == null) return;
+ if (PlayerControl.LocalPlayer == player) {
+ Helpers.removeTasksFromPlayer(player, true);
+ var textTask = new GameObject("RoleTask").AddComponent<ImportantTextTask>();
+ textTask.transform.SetParent(player.transform, false);
+ var getSidekickText = Jackal.canCreateSidekick ? " and recruit a Sidekick" : "";
+ textTask.Text = $"[00B4EBFF]Jackal: Kill everyone{getSidekickText}";
+ player.myTasks.Insert(0, textTask);
+ }
+ return;
+ }
}
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.HandleRpc))]
case (byte)CustomRPC.TrackerUsedTracker:
RPCProcedure.trackerUsedTracker(HFPCBBHJIPJ.ReadByte());
break;
+ case (byte)CustomRPC.JackalKill:
+ RPCProcedure.jackalKill(HFPCBBHJIPJ.ReadByte());
+ break;
+ case (byte)CustomRPC.SidekickKill:
+ RPCProcedure.sidekickKill(HFPCBBHJIPJ.ReadByte());
+ break;
+ case (byte)CustomRPC.JackalCreatesSidekick:
+ RPCProcedure.jackalCreatesSidekick(HFPCBBHJIPJ.ReadByte());
+ break;
+ case (byte)CustomRPC.SidekickPromotes:
+ RPCProcedure.sidekickPromotes();
+ break;
}
}
}
List<PlayerControl> impostors = PlayerControl.AllPlayerControls.ToArray().ToList().OrderBy(x => Guid.NewGuid()).ToList();
impostors.RemoveAll(x => !x.Data.IsImpostor);
- float crewCountSettings = TheOtherRolesPlugin.crewmateRolesCount.GetValue();
- float impCountSettings = TheOtherRolesPlugin.impostorRolesCount.GetValue();
+ float crewCountSettings = (float)TheOtherRolesPlugin.crewmateRolesCount.GetValue() / 2;
+ float impCountSettings = (float)TheOtherRolesPlugin.impostorRolesCount.GetValue() / 2;
if (crewCountSettings % 1 == 0.5f) crewCountSettings += 0.5f * (rnd.Next(2) * 2 - 1);
if (impCountSettings % 1 == 0.5f) impCountSettings += 0.5f * (rnd.Next(2) * 2 - 1);
crewSettings.Add((byte)RoleId.Child, TheOtherRolesPlugin.childSpawnRate.GetValue());
crewSettings.Add((byte)RoleId.Tracker, TheOtherRolesPlugin.trackerSpawnRate.GetValue());
crewSettings.Add((byte)RoleId.Snitch, TheOtherRolesPlugin.snitchSpawnRate.GetValue());
+ crewSettings.Add((byte)RoleId.Jackal, TheOtherRolesPlugin.jackalSpawnRate.GetValue());
// crewSettings.Add((byte)RoleId.BountyHunter, TheOtherRolesPlugin.bountyHunterSpawnRate.GetValue()); BOUNTY HUNTER
// Set multiple player roles
}
if (rnd.Next(1, 101) <= TheOtherRolesPlugin.loversSpawnRate.GetValue() * 10) {
- if (impostors.Count > 0 && crewmates.Count > 0 && maxCrewmateRoles > 0 && maxImpostorRoles > 0 && rnd.Next(1, 101) <= 33) {
+ if (impostors.Count > 0 && crewmates.Count > 0 && maxCrewmateRoles > 0 && maxImpostorRoles > 0 && rnd.Next(1, 101) <= TheOtherRolesPlugin.loversImpLoverRate.GetValue()) {
setRoleToRandomPlayer((byte)RoleId.Lover1, impostors);
setRoleToRandomPlayer((byte)RoleId.Lover2, crewmates);
maxCrewmateRoles--;
--- /dev/null
+using HarmonyLib;
+using System;
+using static TheOtherRoles.TheOtherRoles;
+using UnityEngine;
+
+namespace TheOtherRoles
+{
+ public class RoleInfo {
+ public Color color;
+ public string name;
+ public string introDescription;
+ public string shortDescription;
+ public bool isGood;
+
+ RoleInfo(Color color, string name, string introDescription, string shortDescription, bool isGood) {
+ this.color = color;
+ this.name = name;
+ this.introDescription = introDescription;
+ this.shortDescription = shortDescription;
+ this.isGood = isGood;
+ }
+
+ public string colorHexString() {
+ return string.Format("[{0:X2}{1:X2}{2:X2}{3:X2}]", ToByte(color.r), ToByte(color.g), ToByte(color.b), ToByte(color.a));
+ }
+
+ private static byte ToByte(float f) {
+ f = Mathf.Clamp01(f);
+ return (byte)(f * 255);
+ }
+
+
+ public static RoleInfo getRoleInfoForPlayer(PlayerControl p) {
+ string name = "";
+ string introDescription = "";
+ string shortDescription = "";
+ bool isGood = true;
+ Color color = Color.white;
+
+ if (Jester.jester != null && p == Jester.jester) {
+ name = "Jester";
+ color = Jester.color;
+ introDescription = "Get voted out";
+ shortDescription = introDescription;
+ isGood = false;
+ }
+ else if (Mayor.mayor != null && p == Mayor.mayor) {
+ name = "Mayor";
+ color = Mayor.color;
+ introDescription = "Your vote counts twice";
+ shortDescription = introDescription;
+ }
+ else if (Engineer.engineer != null && p == Engineer.engineer) {
+ name = "Engineer";
+ color = Engineer.color;
+ introDescription = "Maintain important systems on the ship";
+ shortDescription = "Repair the ship";
+ }
+ else if (Sheriff.sheriff != null && p == Sheriff.sheriff) {
+ name = "Sheriff";
+ color = Sheriff.color;
+ introDescription = "Shoot the [FF1919FF]Impostors";
+ shortDescription = "Shoot the Impostors";
+ }
+ else if (Lighter.lighter != null && p == Lighter.lighter) {
+ name = "Lighter";
+ color = Lighter.color;
+ introDescription = "Your light never goes out";
+ shortDescription = introDescription;
+ }
+ else if (Godfather.godfather != null && p == Godfather.godfather) {
+ name = "Godfather";
+ color = Godfather.color;
+ introDescription = "Kill all Crewmates";
+ shortDescription = introDescription;
+ isGood = false;
+ }
+ else if (Mafioso.mafioso != null && p == Mafioso.mafioso) {
+ name = "Mafioso";
+ color = Mafioso.color;
+ introDescription = "Work with the [FF1919FF]Mafia[] to kill the Crewmates";
+ shortDescription = "Kill all Crewmates";
+ isGood = false;
+ }
+ else if (Janitor.janitor != null && p == Janitor.janitor) {
+ name = "Janitor";
+ color = Janitor.color;
+ introDescription = "Work with the [FF1919FF]Mafia[] by hiding dead bodies";
+ shortDescription = "Hide dead bodies";
+ isGood = false;
+ }
+ else if (Morphling.morphling != null && p == Morphling.morphling) {
+ name = "Morphling";
+ color = Morphling.color;
+ introDescription = "Change your look to not get caught";
+ shortDescription = "Change your look";
+ isGood = false;
+ }
+ else if (Camouflager.camouflager != null && p == Camouflager.camouflager) {
+ name = "Camouflager";
+ color = Camouflager.color;
+ introDescription = "Camouflage and kill the Crewmates";
+ shortDescription = "Hide among others";
+ isGood = false;
+ }
+ else if (Vampire.vampire != null && p == Vampire.vampire) {
+ name = "Vampire";
+ color = Vampire.color;
+ introDescription = "Kill the Crewmates with your bites";
+ shortDescription = "Bite your enemies";
+ isGood = false;
+ }
+ else if (Detective.detective != null && p == Detective.detective) {
+ name = "Detective";
+ color = Detective.color;
+ introDescription = "Find the [FF1919FF]Impostors[] by examining footprints";
+ shortDescription = "Examine footprints";
+ }
+ else if (TimeMaster.timeMaster != null && p == TimeMaster.timeMaster) {
+ name = "Time Master";
+ color = TimeMaster.color;
+ introDescription = "Rewind time to find the [FF1919FF]Impostors";
+ shortDescription = "Rewind time";
+ }
+ else if (Medic.medic != null && p == Medic.medic) {
+ name = "Medic";
+ color = Medic.color;
+ introDescription = "Protect someone with your shield";
+ shortDescription = "Protect other players";
+ }
+ else if (Shifter.shifter != null && p == Shifter.shifter) {
+ name = "Shifter";
+ color = Shifter.color;
+ introDescription = "Shift your role before the game ends";
+ shortDescription = "Shift your role";
+ isGood = false;
+ }
+ else if (Swapper.swapper != null && p == Swapper.swapper) {
+ name = "Swapper";
+ color = Swapper.color;
+ introDescription = "Swap votes to exile the [FF1919FF]Impostors";
+ shortDescription = "Swap votes";
+ }
+ else if (Seer.seer != null && p == Seer.seer) {
+ name = "Seer";
+ color = Seer.color;
+ introDescription = "Reveal the intentions of everyone on the ship";
+ shortDescription = "Reveal all roles";
+ }
+ else if (Spy.spy != null && p == Spy.spy) {
+ name = "Spy";
+ color = Spy.color;
+ introDescription = "Spy on everyone to find the [FF1919FF]Impostors";
+ shortDescription = "Spy on everyone";
+ }
+ else if (Child.child != null && p == Child.child) {
+ name = "Child";
+ color = Child.color;
+ introDescription = "No one will harm you until you grow up";
+ shortDescription = "No one will harm you";
+ }
+ else if (BountyHunter.bountyHunter != null && p == BountyHunter.bountyHunter) {
+ name = "Bounty Hunter";
+ color = BountyHunter.color;
+ introDescription = "Hunt your bounty down";
+ shortDescription = "Hunt your bounty";
+ isGood = false;
+ }
+ else if (Tracker.tracker != null && p == Tracker.tracker) {
+ name = "Tracker";
+ color = Tracker.color;
+ introDescription = "Track the [FF1919FF]Impostors[] down";
+ shortDescription = "Track the Impostors down";
+ }
+ else if (Snitch.snitch != null && p == Snitch.snitch) {
+ name = "Snitch";
+ color = Snitch.color;
+ introDescription = "Finish your tasks to find the [FF1919FF]Impostors[]";
+ shortDescription = "Finish your tasks";
+ }
+ else if (Jackal.jackal != null && p == Jackal.jackal) {
+ name = "Jackal";
+ color = Jackal.color;
+ introDescription = "Kill all Crewmates and [FF1919FF]Impostors[FFFFFFFF] to win";
+ shortDescription = "";
+ isGood = false;
+ }
+ else if (Sidekick.sidekick != null && p == Sidekick.sidekick) {
+ name = "Sidekick";
+ color = Sidekick.color;
+ introDescription = "";
+ shortDescription = "Help your Jackal to kill everyone";
+ isGood = false;
+ }
+ else if ((Lovers.lover1 != null && p == Lovers.lover1) || (Lovers.lover2 != null && p == Lovers.lover2)) {
+ name = p.Data.IsImpostor ? "ImpLover" : "Lover";
+ color = p.Data.IsImpostor ? Palette.ImpostorRed : Lovers.color;
+ introDescription = "You are in [FC03BEFF]Love[]";
+ shortDescription = "You are in love";
+ isGood = !p.Data.IsImpostor;
+ }
+ else if (p.Data.IsImpostor) { // Just Impostor
+ name = "Impostor";
+ color = Palette.ImpostorRed;
+ introDescription = "";
+ shortDescription = "Sabotage and kill everyone";
+ isGood = false;
+ }
+ else { // Just Crewmate
+ name = "Crewmate";
+ color = Color.white;
+ introDescription = "";
+ shortDescription = "Find the Impostors";
+ }
+
+ return new RoleInfo(
+ color,
+ name,
+ introDescription,
+ shortDescription,
+ isGood
+ );
+ }
+ }
+}
\ No newline at end of file
namespace TheOtherRoles {
- [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.CalculateLightRadius))]
+ [HarmonyPatch(typeof(ShipStatus))]
public class ShipStatusPatch {
- public static void Postfix(ref float __result, ShipStatus __instance, [HarmonyArgument(0)] GameData.PlayerInfo PlayerData) {
+
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.CalculateLightRadius))]
+ public static void Postfix1(ref float __result, ShipStatus __instance, [HarmonyArgument(0)] GameData.PlayerInfo PlayerData) {
if (Lighter.lighter != null && Lighter.lighter.PlayerId == PlayerData.PlayerId) {
__result = __instance.MaxLightRadius * Lighter.lighterVision;
}
}
+
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.IsGameOverDueToDeath))]
+ public static void Postfix2(ShipStatus __instance, ref bool __result)
+ {
+ __result = false;
+ }
}
+
}
\ No newline at end of file
Tracker.clearAndReload();
Vampire.clearAndReload();
Snitch.clearAndReload();
+ Jackal.clearAndReload();
+ Sidekick.clearAndReload();
}
public static class Jester {
public static bool notAckedExiledIsLover = false;
public static bool existingAndAlive() {
- return lover1 != null && lover2 != null && !lover1.Data.IsDead && !lover2.Data.IsDead && !notAckedExiledIsLover; // ADD NOT ACKED IS LOVER
+ return lover1 != null && lover2 != null && !lover1.Data.IsDead && !lover2.Data.IsDead && !lover1.Data.Disconnected && !lover2.Data.Disconnected && !notAckedExiledIsLover; // ADD NOT ACKED IS LOVER
}
public static bool existingWithImpLover() {
- return lover1 != null && lover2 != null && (lover1.Data.IsImpostor || lover2.Data.IsImpostor);
+ return lover1 != null && lover2 != null && !lover1.Data.Disconnected && !lover2.Data.Disconnected && (lover1.Data.IsImpostor || lover2.Data.IsImpostor);
}
public static void clearAndReload() {
public static Color color = Palette.ImpostorRed;
public static float delay = 10f;
+ public static float cooldown = float.MaxValue;
+ public static bool canKillNearGarlics = true;
public static bool localPlacedGarlic = false;
public static bool garlicsActive = true;
currentTarget = null;
garlicsActive = TheOtherRolesPlugin.vampireSpawnRate.GetValue() > 0;
delay = TheOtherRolesPlugin.vampireKillDelay.GetValue();
+ cooldown = TheOtherRolesPlugin.vampireCooldown.GetValue();
+ canKillNearGarlics = TheOtherRolesPlugin.vampireCanKillNearGarlics.GetValue();
}
}
snitch = null;
}
}
+
+ public static class Jackal {
+ public static PlayerControl jackal;
+ public static Color color = new Color(0f / 255f, 180f / 255f, 235f / 255f, 1);
+ public static PlayerControl fakeSidekick;
+
+ public static PlayerControl currentTarget;
+ public static List<PlayerControl> formerJackals = new List<PlayerControl>();
+
+ public static float cooldown = float.MaxValue;
+ public static float createSidekickCooldown = float.MaxValue;
+ public static bool canUseVents = true;
+ public static bool canCreateSidekick = true;
+ public static Sprite buttonSprite;
+ public static bool jackalPromotedFromSidekickCanCreateSidekick = true;
+ public static bool canCreateSidekickFromImpostor = true;
+
+ public static Sprite getSidekickButtonSprite() {
+ if (buttonSprite) return buttonSprite;
+ buttonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.SidekickButton.png", 100f);
+ return buttonSprite;
+ }
+
+ public static void removeCurrentJackal() {
+ formerJackals.Add(jackal);
+ jackal = null;
+ currentTarget = null;
+ fakeSidekick = null;
+ cooldown = TheOtherRolesPlugin.jackalKillCooldown.GetValue();
+ createSidekickCooldown = TheOtherRolesPlugin.jackalCreateSidekickCooldown.GetValue();
+ }
+
+ public static void clearAndReload() {
+ jackal = null;
+ currentTarget = null;
+ fakeSidekick = null;
+ cooldown = TheOtherRolesPlugin.jackalKillCooldown.GetValue();
+ createSidekickCooldown = TheOtherRolesPlugin.jackalCreateSidekickCooldown.GetValue();
+ canUseVents = TheOtherRolesPlugin.jackalCanUseVents.GetValue();
+ canCreateSidekick = TheOtherRolesPlugin.jackalCanCreateSidekick.GetValue();
+ jackalPromotedFromSidekickCanCreateSidekick = TheOtherRolesPlugin.jackalPromotedFromSidekickCanCreateSidekick.GetValue();
+ canCreateSidekickFromImpostor = TheOtherRolesPlugin.jackalCanCreateSidekickFromImpostor.GetValue();
+ formerJackals.Clear();
+ }
+
+ }
+
+ public static class Sidekick {
+ public static PlayerControl sidekick;
+ public static Color color = new Color(0f / 255f, 180f / 255f, 235f / 255f, 1);
+
+ public static PlayerControl currentTarget;
+
+ public static float cooldown = float.MaxValue;
+ public static bool canUseVents = true;
+ public static bool canKill = true;
+ public static bool promotesToJackal = true;
+
+ public static void clearAndReload() {
+ sidekick = null;
+ currentTarget = null;
+ cooldown = TheOtherRolesPlugin.jackalKillCooldown.GetValue();
+ canUseVents = TheOtherRolesPlugin.sidekickCanUseVents.GetValue();
+ canKill = TheOtherRolesPlugin.sidekickCanKill.GetValue();
+ promotesToJackal = TheOtherRolesPlugin.sidekickPromotesToJackal.GetValue();
+ }
+
+ }
+
}
\ No newline at end of file
<EmbeddedResource Include="Resources\Footprint.png"/>
<EmbeddedResource Include="Resources\AdminTableIcon.png"/>
<EmbeddedResource Include="Resources\SwapperCheck.png"/>
+ <EmbeddedResource Include="Resources\SidekickButton.png"/>
</ItemGroup>
<Target Name="Copy" AfterTargets="Reobfuscate">
+ <Message Text="Deobfuscated: $(Deobfuscated)" Importance="high"/>
<Copy SourceFiles="$(OutputPath)reobfuscated/$(AssemblyName)-$(GameVersion).dll" DestinationFolder="$(AmongUs)/BepInEx/plugins/" Condition="'$(Configuration)' == 'Debug'" />
</Target>
</Project>
\ No newline at end of file
player.nameText.Color = Color.white;
}
}
- if (MeetingHud.Instance != null)
+ if (MeetingHud.Instance != null) {
foreach (PlayerVoteArea player in MeetingHud.Instance.playerStates) {
PlayerControl playerControl = playersById.ContainsKey((byte)player.TargetPlayerId) ? playersById[(byte)player.TargetPlayerId] : null;
if (playerControl != null) {
}
}
}
-
+ }
if (PlayerControl.LocalPlayer.Data.IsImpostor) {
List<PlayerControl> impostors = PlayerControl.AllPlayerControls.ToArray().ToList();
impostors.RemoveAll(x => !x.Data.IsImpostor);
player.NameText.Color = Palette.ImpostorRed;
}
}
-
+
}
static void setPlayerNameColor(PlayerControl p, Color color) {
setPlayerNameColor(Tracker.tracker, Tracker.color);
else if (Snitch.snitch != null && Snitch.snitch == PlayerControl.LocalPlayer)
setPlayerNameColor(Snitch.snitch, Snitch.color);
+ else if (Jackal.jackal != null && Jackal.jackal == PlayerControl.LocalPlayer) {
+ // Jackal can see his sidekick
+ setPlayerNameColor(Jackal.jackal, Jackal.color);
+ if (Sidekick.sidekick != null) {
+ setPlayerNameColor(Sidekick.sidekick, Jackal.color);
+ }
+ if (Jackal.fakeSidekick != null) {
+ setPlayerNameColor(Jackal.fakeSidekick, Jackal.color);
+ }
+ }
+
+ // No else if here, as a Lover of team Jackal needs the colors
+ if (Sidekick.sidekick != null && Sidekick.sidekick == PlayerControl.LocalPlayer) {
+ // Sidekick can see the jackal
+ setPlayerNameColor(Sidekick.sidekick, Sidekick.color);
+ if (Jackal.jackal != null) {
+ setPlayerNameColor(Jackal.jackal, Jackal.color);
+ }
+ }
// Crewmate roles with no changes: Child
// Impostor roles with no changes: Morphling, Camouflager, Vampire, Godfather, Janitor and Mafioso
}
}
- static void jesterClearTasks() {
- if (Jester.jester == null) return;
-
- var toRemove = new List<PlayerTask>();
- foreach (PlayerTask task in Jester.jester.myTasks)
- if (task.TaskType != TaskTypes.FixComms && task.TaskType != TaskTypes.FixLights && task.TaskType != TaskTypes.ResetReactor && task.TaskType != TaskTypes.ResetSeismic && task.TaskType != TaskTypes.RestoreOxy)
- toRemove.Add(task);
- foreach (PlayerTask task in toRemove)
- Jester.jester.RemoveTask(task);
- }
-
static void mafiosoDeactivateKillButtonIfNecessary(HudManager __instance) {
if (Mafioso.mafioso == null || Mafioso.mafioso != PlayerControl.LocalPlayer) return;
}
}
- static void shifterClearTasks() {
- if (Shifter.shifter == null) return;
-
- var toRemove = new List<PlayerTask>();
- foreach (PlayerTask task in Shifter.shifter.myTasks)
- if (task.TaskType != TaskTypes.FixComms && task.TaskType != TaskTypes.FixLights && task.TaskType != TaskTypes.ResetReactor && task.TaskType != TaskTypes.ResetSeismic && task.TaskType != TaskTypes.RestoreOxy)
- toRemove.Add(task);
- foreach (PlayerTask task in toRemove)
- Shifter.shifter.RemoveTask(task);
- }
-
static void seerUpdate() {
if (Seer.seer == null || Seer.seer != PlayerControl.LocalPlayer) return;
// Update color and name regarding settings and given info
string result = target.Data.PlayerName;
- SeerInfo si = SeerInfo.getSeerInfoForPlayer(targetOrMistake);
+ RoleInfo si = RoleInfo.getRoleInfoForPlayer(targetOrMistake);
if (Seer.kindOfInfo == 0)
- result = target.Data.PlayerName + " (" + si.roleName + ")";
+ result = target.Data.PlayerName + " (" + si.name + ")";
else if (Seer.kindOfInfo == 1) {
si.color = si.isGood ? new Color(250f / 255f, 217f / 255f, 52f / 255f, 1) : new Color (51f / 255f, 61f / 255f, 54f / 255f, 1);
}
if (Snitch.snitch.Data.IsDead) return;
int numberOfTasks = 0;
- foreach (PlayerTask t in Snitch.snitch.myTasks) {
- if (t.TaskType != TaskTypes.FixComms && t.TaskType != TaskTypes.FixLights && t.TaskType != TaskTypes.ResetReactor && t.TaskType != TaskTypes.ResetSeismic && t.TaskType != TaskTypes.RestoreOxy)
- if (!t.IsComplete) numberOfTasks++;
- }
+ GameData.PlayerInfo playerInfo = Snitch.snitch.Data;
+ if (!playerInfo.Disconnected && playerInfo.Tasks != null) {
+ for (int i = 0; i < playerInfo.Tasks.Count; i++) {
+ if (!playerInfo.Tasks[i].Complete)
+ numberOfTasks++;
+ }
+ }
if (PlayerControl.LocalPlayer.Data.IsImpostor && numberOfTasks <= Snitch.taskCountForImpostors) {
if (Snitch.localArrows.Count == 0) Snitch.localArrows.Add(new Arrow(Color.blue));
// Mafia
setMafiaNameTags();
// Jester
- jesterClearTasks();
+ Helpers.removeTasksFromPlayer(Jester.jester);
// Mafioso
mafiosoDeactivateKillButtonIfNecessary(__instance);
// Janitor
janitorDeactivateKillButton(__instance);
// Shifter
- shifterClearTasks();
+ Helpers.removeTasksFromPlayer(Shifter.shifter);
// Seer update
seerUpdate();
// Spy update();
vampireDeactivateKillButton(__instance);
// Snitch
snitchUpdate();
+ // Jackal
+ Helpers.removeTasksFromPlayer(Jackal.jackal);
+ // Sidekick
+ Helpers.removeTasksFromPlayer(Sidekick.sidekick);
}
}
}
\ No newline at end of file
bool roleCouldUse = false;
if (Engineer.engineer != null && Engineer.engineer == @object)
roleCouldUse = true;
+ else if (Jackal.canUseVents && Jackal.jackal != null && Jackal.jackal == @object)
+ roleCouldUse = true;
+ else if (Sidekick.canUseVents && Sidekick.sidekick != null && Sidekick.sidekick == @object)
+ roleCouldUse = true;
else if (pc.IsImpostor) {
if (Janitor.janitor != null && Janitor.janitor == PlayerControl.LocalPlayer)
roleCouldUse = false;