<|start_filename|>CultistPlugin/ShipStatusPatch.cs<|end_filename|>
﻿using HarmonyLib;
using static CultistPlugin.CultistMod;
using static CultistPlugin.CultistSettings;

namespace CultistPlugin
{
    [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.CalculateLightRadius))]
    public class ShipStatusPatch
    {
        public static void Postfix(ref float __result, GameData.PlayerInfo IIEKJBMPELC)
        {
            if ( IsCultistUsed && IsCultist(IIEKJBMPELC.PlayerId))
            {
                __result = __result * CultistVisionModifier;
            }
        }
    }

    [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.CheckEndCriteria))]
    public class CheckEndCriteriaPatch
    {
        public static bool Prefix()
        {
            if (!IsCultistUsed)
            {
                return true;
            }

            return !DisableGameEnd;
        }
    }
}

<|start_filename|>CultistPlugin/CultistMod.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.IO;
using HarmonyLib;
using Hazel;
using Reactor.Extensions;
using Reactor.Unstrip;
using UnityEngine;

namespace CultistPlugin
{
    [HarmonyPatch]
    public static class CultistMod
    {
        public static AssetBundle bundle =
            AssetBundle.LoadFromFile(Directory.GetCurrentDirectory() + "\\Assets\\cultistbundle");

        public static Sprite convertIcon = bundle.LoadAsset<Sprite>("CO").DontUnload();
        public static KillButtonManager KillButton;
        public static double DistLocalClosest;
        public static PlayerControl CurrentTarget = null;

        public static List<byte> CultistList = new List<byte>();

        //necessary, because for win condition checks the Ids are unknown
        public static List<String> CultistNameList = new List<String>();

        public static bool DidCultistsWin = false;
        
        //TODO swap dummy count for bool "hasDummy"
        public static int ImpostorDummyCount = 0;
        public static bool DisableGameEnd = false;
        public static int ConversionsLeft { get; set; }
        public static DateTime? LastConversion { get; set; }

        public static bool IsCultist(byte playerId)
        {
            return CultistList.IndexOf(playerId) != -1;
        }

        public static bool IsCultist(String playerName)
        {
            return CultistNameList.IndexOf(playerName) != -1;
        }


        public static Color CultistColor = new Color(100f / 255f, 20f / 255f, 200f / 255f, 1);

        public static void AddCultistToLists(PlayerControl playerControl)
        {
            if (!IsCultist(playerControl.PlayerId))
            {
                CultistList.Add(playerControl.PlayerId);
                CultistNameList.Add(playerControl.Data.PlayerName);
            }
        }

        public static void AddCultistToLists(byte playerId)
        {
            if (!IsCultist(playerId))
            {
                CultistList.Add(playerId);
                foreach (var player in PlayerControl.AllPlayerControls)
                {
                    if (player.PlayerId == playerId)
                    {
                        CultistNameList.Add(player.Data.PlayerName);
                    }
                }
            }
        }

        public static void ClearCultistLists()
        {
            CultistList.Clear();
            CultistNameList.Clear();
        }


        public static bool CheckCultistWin()
        {
            
            CLog.Info("CHECK CULTIST WIN:");
            
            int alive = 0;
            int cultists = 0;
            foreach (PlayerControl player in PlayerControl.AllPlayerControls)
            {
                if (!player.Data.IsDead)
                {
                    alive++;
                    if (IsCultist(player.PlayerId))
                    {
                        cultists++;
                        CLog.Info("FOUND ALIVE CULTIST: " + player.name);
                    }
                }
            }

            if (ImpostorDummyCount > 0)
            {
                alive--;
            }

            if (alive / 2 < cultists)
            {
                CLog.Info("-----CULTIST WIN-----");
                return true;
            }
            else{CLog.Info("Cultists did not win.");}

            return false;
        }

        public static void ExecuteCultistWin()
        {
            CLog.Info("Executing WIN");
            DidCultistsWin = true;

            DisableGameEnd = true;

            if (ImpostorDummyCount > 0)
            {
                KillDummy();
            }

            foreach (PlayerControl player in PlayerControl.AllPlayerControls)
            {
                // for each player check if cultist, if not kill
                if (!IsCultist(player.PlayerId))
                {
                    if (!player.Data.IsDead)
                    {
                        //player.MurderPlayer(player);
                        player.Data.IsDead = true;
                    }

                    player.RemoveInfected();
                    player.Data.IsImpostor = false;
                }
            }

            DisableGameEnd = false;
        }

        public static void KillDummy()
        {
            CLog.Info("Trying to Kill a Dummy.");
            PlayerControl playerToRemove = null;
            foreach (var player in PlayerControl.AllPlayerControls)
            {
                if (player.name == "IMPOSTOR_DUMMY")
                {
                    playerToRemove = player;
                }
            }

            if (playerToRemove != null)
            {
                CLog.Info("Killing dummy after receiving RPC command.");
                GameData.Instance.RemovePlayer(playerToRemove.PlayerId);
                PlayerControl.AllPlayerControls.Remove(playerToRemove);

                if (AmongUsClient.Instance.AmHost)
                {
                    MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(
                        PlayerControl.LocalPlayer.NetId, (byte) CustomRPC.KillDummy, Hazel.SendOption.None, -1);
                    AmongUsClient.Instance.FinishRpcImmediately(writer);
                }
            }
        }
    }
}

<|start_filename|>CultistPlugin/TaskPatch.cs<|end_filename|>
﻿using System.Collections.Generic;
using HarmonyLib;
using Il2CppSystem.Text;
using UnityEngine;
using static CultistPlugin.CultistSettings;

namespace CultistPlugin
{
    //FIXME for the host (only for the host), a NRE gets thrown once (only in the second time the coroutine is executed)
    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.SetTasks))]
    public class TaskPatch
    {
        public static void Postfix(PlayerControl __instance)
        {
            if (IsCultistUsed && InitialCultist != null)
            {
                foreach (var player in PlayerControl.AllPlayerControls)
                {
                    //TODO create proper reusable methods for this
                    //TODO enable fake tasks for cultis
                    //TODO Or something interesting like ritual 
                    if (player.PlayerId == InitialCultist.PlayerId)
                    {
                        CLog.Info("Removing tasks for cultist:");
                        var tasksToRemove = new List<PlayerTask>();
                        foreach (var task in player.myTasks)
                        {
                            tasksToRemove.Add(task);
                        }

                        foreach (var taskToRemove in tasksToRemove)
                        {
                            player.RemoveTask(taskToRemove);
                        }

                        player.myTasks.Clear();
                        CLog.Info("Done with removing tasks");

                        ImportantTextTask convertedTask =
                            new GameObject("CultistLeaderTask").AddComponent<ImportantTextTask>();
                        convertedTask.transform.SetParent(player.transform, false);

                        convertedTask.Text =
                            "You are the cult leader.\nConvert crewmates to your cult.\nConversions left: " +
                            CultistMod.ConversionsLeft + "/" + MaxCultistConversions;
                        player.myTasks.Insert(0, convertedTask);
                    }
                }
            }
        }
    }


    [HarmonyPatch(typeof(ImportantTextTask), nameof(ImportantTextTask.AppendTaskText))]
    public static class ImportantTextTaskPatch
    {
        public static bool Prefix(ImportantTextTask __instance, StringBuilder DOJIEDCICAJ)
        {
            if (IsCultistUsed && CultistMod.IsCultist(PlayerControl.LocalPlayer.PlayerId))
            {
                DOJIEDCICAJ.AppendLine("[6414C8FF]" + __instance.Text + "[]");
                return false;
            }

            return true;
        }
    }
}

<|start_filename|>CultistPlugin/RPC.cs<|end_filename|>
﻿using HarmonyLib;
using Hazel;
using UnityEngine;
using static CultistPlugin.CultistMod;
using static CultistPlugin.CultistSettings;

namespace CultistPlugin
{
    enum RPC
    {
        PlayAnimation = 0,
        CompleteTask = 1,
        SyncSettings = 2,
        SetInfected = 3,
        Exiled = 4,
        CheckName = 5,
        SetName = 6,
        CheckColor = 7,
        SetColor = 8,
        SetHat = 9,
        SetSkin = 10,
        ReportDeadBody = 11,
        MurderPlayer = 12,
        SendChat = 13,
        StartMeeting = 14,
        SetScanner = 15,
        SendChatNote = 16,
        SetPet = 17,
        SetStartCounter = 18,
        EnterVent = 19,
        ExitVent = 20,
        SnapTo = 21,
        Close = 22,
        VotingComplete = 23,
        CastVote = 24,
        ClearVote = 25,
        AddVote = 26,
        CloseDoorsOfType = 27,
        RepairSystem = 28,
        SetTasks = 29,
        UpdateGameData = 30,
    }

    enum CustomRPC
    {
        SetCultist = 58,
        ConvertAction = 59,
        KillDummy = 60,
        ConversionsLeft = 61
    }


    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.HandleRpc))]
    public static class HandleRpcPatch
    {
        static void Prefix(byte HKHMBLJFLMC, MessageReader ALMCIJKELCP)
        {
            if (IsCultistUsed)
            {
                byte byteParam = HKHMBLJFLMC;
                MessageReader reader = ALMCIJKELCP;

                CLog.Info("Got RPC call: " + byteParam + " Msg Length: " + reader.Length);

                if (HKHMBLJFLMC == (byte) RPC.MurderPlayer)
                {
                    DisableGameEnd = true;
                }
            }
        }

        static void Postfix(byte HKHMBLJFLMC, MessageReader ALMCIJKELCP)
        {
            byte packetId = HKHMBLJFLMC;
            MessageReader reader = ALMCIJKELCP;
            switch (packetId)
            {
                case ((byte) CustomRPC.SetCultist):
                    CLog.Info("Cultist Set Through RPC!");
                    ClearCultistLists();
                    DidCultistsWin = false;
                    byte CultistId = ALMCIJKELCP.ReadByte();
                    foreach (PlayerControl player in PlayerControl.AllPlayerControls)
                    {
                        if (player.PlayerId == CultistId)
                        {
                            InitialCultist = player;
                            AddCultistToLists(player);
                            CLog.Info("SET PLAYER TO CULTIST " + player.name);
                        }
                    }

                    CLog.Info("Setting Cultist Settings");
                    SetCultistSettings();

                    CLog.Info("Cultist list after cultist set through RPC:");

                    break;
                case ((byte) CustomRPC.ConvertAction):
                    CLog.Info("Conversion from RPC!");
                    byte CultistThatCalledId = ALMCIJKELCP.ReadByte();
                    byte TargetId = ALMCIJKELCP.ReadByte();

                    foreach (PlayerControl player in PlayerControl.AllPlayerControls)
                    {
                        if (player.PlayerId == TargetId && !IsCultist(player.PlayerId))
                        {
                            CLog.Info(player.Data.PlayerName + "is now a cultist.");
                            AddCultistToLists(player);

                            for (int i = 0; i < player.myTasks.Count; i++)
                            {
                                PlayerTask playerTask = player.myTasks[i];
                                player.RemoveTask(playerTask);
                            }

                            player.myTasks.Clear();

                            ImportantTextTask convertedTask =
                                new GameObject("CultistTask").AddComponent<ImportantTextTask>();
                            convertedTask.transform.SetParent(player.transform, false);

                            convertedTask.Text =
                                PlayerTools.getPlayerById(CultistThatCalledId).name +
                                " converted you to the cult.\nHelp your cult leader convert other crewmates.";

                            player.myTasks.Insert(0, convertedTask);
                        }
                    }


                    if (CheckCultistWin())
                    {
                        CLog.Info("Cultists won by conversion.");
                        ExecuteCultistWin();
                    }

                    break;
                case ((byte) CustomRPC.KillDummy):
                    PlayerControl playerToRemove = null;
                    foreach (var player in PlayerControl.AllPlayerControls)
                    {
                        if (player.name == "IMPOSTOR_DUMMY")
                        {
                            playerToRemove = player;
                        }
                    }

                    if (playerToRemove != null)
                    {
                        CLog.Info("Killing dummy after receiving RPC command.");
                        GameData.Instance.RemovePlayer(playerToRemove.PlayerId);
                        PlayerControl.AllPlayerControls.Remove(playerToRemove);

                        if (CheckCultistWin())
                        {
                            ExecuteCultistWin();
                        }
                    }

                    break;
                case (byte) RPC.MurderPlayer:
                    DisableGameEnd = false;
                    break;
                case (byte) CustomRPC.ConversionsLeft:
                    byte conversionsLeft = ALMCIJKELCP.ReadByte();
                    ConversionsLeft = conversionsLeft;
                    break;
            }
        }
    }
}

<|start_filename|>CultistPlugin/MurderAndDiePatch.cs<|end_filename|>
﻿using System;
using HarmonyLib;
using Hazel;
using UnityEngine;
using static CultistPlugin.CultistMod;
using static CultistPlugin.CultistSettings;

namespace CultistPlugin

{
    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.Die))]
    public class DiePatch
    {
        public static void Prefix()
        {
            if (IsCultistUsed)
            {
                DisableGameEnd = true;
            }
        }

        public static void Postfix(PlayerControl __instance, DeathReason OECOPGMHMKC)
        {
            if (IsCultistUsed)
            {
                if (CheckCultistWin())
                {
                    CLog.Info("CULTIST WIN THROUGH DEATH!");

                    ExecuteCultistWin();
                }

                if (__instance.PlayerId == InitialCultist.PlayerId && CultistLeadIsPassedOnDeath)
                {
                    CLog.Info("CULTIST LEADER DIED, ASSIGNING NEW LEADER");
                    foreach (var player in PlayerControl.AllPlayerControls
                    ) //TODO use another way that guarantees the same order for all playersd
                    {
                        if (IsCultist(player.PlayerId) && player.PlayerId != InitialCultist.PlayerId && !player.Data.IsDead)
                        {
                            CLog.Info("NEW CULT LEADER:" + player.name);
                            InitialCultist = player;
                            LastConversion = DateTime.UtcNow;

                            player.myTasks.Clear();

                            ImportantTextTask convertedTask =
                                new GameObject("CultistLeaderTask").AddComponent<ImportantTextTask>();
                            convertedTask.transform.SetParent(player.transform, false);

                            convertedTask.Text =
                                "The cult leader died.\nYou are the new cult leader.\nConvert crewmates to your cult.\nConversions left:" +
                                ConversionsLeft + "/" + MaxCultistConversions;
                            player.myTasks.Insert(0, convertedTask);
                            break;
                        }
                    }
                }
            }

            DisableGameEnd = false;
        }
    }

    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.RpcMurderPlayer))]
    public class RpcMurderPatch
    {
        public static bool Prefix(PlayerControl CAKODNGLPDF)
        {
            if (!IsCultistUsed)
            {
                return true;
            }

            // swaps the execution of the original method, this is necessary, because otherwise the rightful winners are only shown for host when host is impostor
            MessageWriter messageWriter =
                AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, 12, SendOption.Reliable,
                    -1);
            messageWriter.WritePacked(CAKODNGLPDF.NetId);
            AmongUsClient.Instance.FinishRpcImmediately(messageWriter);
            if (AmongUsClient.Instance.AmClient)
            {
                PlayerControl.LocalPlayer.MurderPlayer(CAKODNGLPDF);
            }

            return false;
        }
    }
}

<|start_filename|>CultistPlugin/HudUpdatePatch.cs<|end_filename|>
﻿using HarmonyLib;
using UnityEngine;
using static CultistPlugin.CultistMod;
using static CultistPlugin.CultistSettings;


namespace CultistPlugin
{
    [HarmonyPatch(typeof(HudManager), nameof(HudManager.Update))]
    public class HudUpdatePatch
    {
        static bool lastQ = false;

        //TODO there is some null ref exception thrown in this Postfix everytime the game starts (only after the first game)
        static void Postfix(HudManager __instance)
        {
            if (IsCultistUsed && __instance != null)
            {
                if (AmongUsClient.Instance.GameState == InnerNetClient.GameStates.Started &&
                    __instance.KillButton != null)
                {
                    if (!PlayerControl.LocalPlayer.Data.IsImpostor && Input.GetKeyDown(KeyCode.Q) && !lastQ)
                    {
                        KillButtonPatch.Prefix();
                    }

                    lastQ = Input.GetKeyUp(KeyCode.Q);
                    KillButton = __instance.KillButton;
                    PlayerTools.closestPlayer = PlayerTools.getClosestPlayer(PlayerControl.LocalPlayer);
                    DistLocalClosest =
                        PlayerTools.getDistBetweenPlayers(PlayerControl.LocalPlayer, PlayerTools.closestPlayer);

                    if (InitialCultist.PlayerId == PlayerControl.LocalPlayer.PlayerId && ConversionsLeft <= 0)
                    {
                        KillButton.gameObject.SetActive(false);
                        KillButton.isActive = false;
                    }

                    else if (InitialCultist.PlayerId == PlayerControl.LocalPlayer.PlayerId &&
                             __instance.UseButton.isActiveAndEnabled)
                    {
                        KillButton.gameObject.SetActive(true);
                        KillButton.isActive = true;
                        KillButton.SetCoolDown(PlayerTools.GetConversionCooldown(),
                            PlayerControl.GameOptions.KillCooldown + 15.0f);
                        KillButton.renderer.sprite = convertIcon;
                        KillButton.renderer.color = Palette.EnabledColor;
                        KillButton.renderer.material.SetFloat("_Desat", 0f);

                        if (DistLocalClosest < GameOptionsData.KillDistances[PlayerControl.GameOptions.KillDistance])
                        {
                            KillButton.SetTarget(PlayerTools.closestPlayer);
                            CurrentTarget = PlayerTools.closestPlayer;
                        }
                        else
                        {
                            KillButton.SetTarget(null);
                            CurrentTarget = null;
                        }
                    }


                    // adds purple color during the game and meetings depending on settings
                    if (IsCultist(PlayerControl.LocalPlayer.PlayerId))
                    {
                        PlayerControl.LocalPlayer.nameText.Color = CultistColor;
                        var isLocalCultLeader = PlayerControl.LocalPlayer.PlayerId == InitialCultist.PlayerId;

                        foreach (var player in PlayerControl.AllPlayerControls)
                        {
                            if (((CultistsKnowEachOther || isLocalCultLeader) && IsCultist(player.PlayerId)) ||
                                player.PlayerId == InitialCultist.PlayerId)
                            {
                                player.nameText.Color = CultistColor;
                            }
                        }

                        if (MeetingHud.Instance != null)
                        {
                            foreach (PlayerVoteArea player in MeetingHud.Instance.playerStates)
                            {
                                if ((player.TargetPlayerId == PlayerControl.LocalPlayer.PlayerId) ||
                                    (player.NameText != null && InitialCultist.PlayerId == player.TargetPlayerId) ||
                                    ((CultistsKnowEachOther || isLocalCultLeader) &&
                                     IsCultist((byte) player.TargetPlayerId)))
                                {
                                    player.NameText.Color = CultistColor;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    [HarmonyPatch(typeof(GameOptionsData), nameof(GameOptionsData.Method_24))]
    class GameOptionsScalePatch
    {
        static void Postfix(ref string __result)
        {
            DestroyableSingleton<HudManager>.Instance.GameSettings.scale = 0.66f;
        }
    }
}

<|start_filename|>CultistPlugin/IntroCutscenePatch.cs<|end_filename|>
﻿using System;
using HarmonyLib;
using static CultistPlugin.CultistSettings;

namespace CultistPlugin
{
    [HarmonyPatch(typeof(IntroCutscene.CoBegin__d), nameof(IntroCutscene.CoBegin__d.MoveNext))]
    public class IntroCutscenePatch
    {
        private static String originalText = null;

        static bool Prefix(IntroCutscene.CoBegin__d __instance)
        {
            if (PlayerControl.LocalPlayer == InitialCultist)
            {
                var cultistTeam = new Il2CppSystem.Collections.Generic.List<PlayerControl>();
                cultistTeam.Add(PlayerControl.LocalPlayer);
                __instance.yourTeam = cultistTeam;
            }

            return true;
        }

        static void Postfix(IntroCutscene.CoBegin__d __instance)
        {
            if (IsCultistUsed)
            {
                CultistMod.LastConversion = DateTime.UtcNow; //.AddSeconds(__instance.timer);

                if (PlayerControl.LocalPlayer == InitialCultist)
                {
                    __instance.__this.Title.Text = "Cultist";
                    __instance.__this.Title.Color = CultistMod.CultistColor;
                    __instance.__this.ImpostorText.Text = "Convert Crewmates to your Cult";
                    __instance.__this.BackgroundBar.material.color = CultistMod.CultistColor;
                }
                else if (!PlayerControl.LocalPlayer.Data.IsImpostor)
                {
                    if (originalText == null)
                    {
                        originalText = __instance.__this.ImpostorText.Text;
                    }

                    __instance.__this.ImpostorText.Text =
                        originalText + "\nThere is one " + "[6414C8FF]" + "Cultist" + "[]" + ".";
                }
            }
        }
    }
}

<|start_filename|>CultistPlugin/EndGameManagerPatch.cs<|end_filename|>
﻿using HarmonyLib;
using System.Collections.Generic;
using static CultistPlugin.CultistMod;
using static CultistPlugin.CultistSettings;

namespace CultistPlugin
{
    [HarmonyPatch(typeof(EndGameManager), "SetEverythingUp")]
    public class EndGameManagerPatch
    {
        public static void Prefix(EndGameManager __instance)
        {
            {
                if (DidCultistsWin)
                {
                    Il2CppSystem.Collections.Generic.List<WinningPlayerData> newWinners =
                        new Il2CppSystem.Collections.Generic.List<WinningPlayerData>();
                    for (int i = 0; i < GameData.Instance.PlayerCount; i++)
                    {
                        GameData.PlayerInfo playerInfo = GameData.Instance.AllPlayers[i];
                        if (IsCultist(playerInfo.PlayerId))
                        {
                            newWinners.Add(new WinningPlayerData(playerInfo));
                        }
                    }

                    TempData.winners = newWinners;
                }

                if (TempData.DidHumansWin(TempData.EndReason) && !DidCultistsWin)
                {
                    var toRemove = new List<WinningPlayerData>();

                    foreach (var winner in TempData.winners)
                    {
                        if (IsCultist(winner.Name))
                        {
                            toRemove.Add(winner);
                        }
                    }

                    foreach (var winnerToRemove in toRemove)
                    {
                        TempData.winners.Remove(winnerToRemove);
                    }
                }
            }
        }

        public static void Postfix(EndGameManager __instance)
        {
            if (DidCultistsWin)
            {
                __instance.WinText.Color = CultistColor;
                __instance.BackgroundBar.material.color = CultistColor;

                //TODO make text color purple!


                if (IsCultist(PlayerControl.LocalPlayer.PlayerId))
                {
                    __instance.WinText.Text = "Victory: The Cult";
                }
                else
                {
                    __instance.WinText.Text = "Defeat: The Cult recruited to many members.";
                }
            }
            else if (TempData.DidHumansWin(TempData.EndReason))
            {
                if (IsCultist(PlayerControl.LocalPlayer.PlayerId))
                {
                    __instance.WinText.Color = Palette.ImpostorRed;
                    __instance.BackgroundBar.material.color = CultistColor;
                    __instance.WinText.Text = "Defeat: You did not convert enough members.";
                }
            }

            InitialCultist = null;
        }
    }
}

<|start_filename|>CultistPlugin/MeetingPatch.cs<|end_filename|>
﻿using System;
using HarmonyLib;
using Hazel;
using Reactor;
using Reactor.Extensions;
using UnityEngine;
using static CultistPlugin.CultistSettings;
using static CultistPlugin.CultistMod;

namespace CultistPlugin
{
    [HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.Awake))]
    public class MeetingAwakePatch
    {
        static void Prefix()
        {
            if (IsCultistUsed)
            {
                if (ImpostorDummyCount > 0)
                {
                    DisableGameEnd = true;
                    KillDummy();
                }
            }
        }
    }

    [HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.HandleProceed))]
    public class MeetingPatch
    {
        static void Postfix(MeetingHud __instance)
        {
            if (IsCultistUsed)
            {
                if (DoCrewmatesWinWhenImpostorsAreDead)
                {
                    return;
                }

                CLog.Info("In Postfix Meeting");
                if (AmongUsClient.Instance.AmHost)
                {
                    bool shouldCreateDummy = false;

                    if (__instance.exiledPlayer != null)
                    {
                        int amntAliveImpostors = 0;
                        int amntAliveCultists = 0;
                        int amntAliveCrewmates = 0;
                        foreach (var player in PlayerControl.AllPlayerControls)
                        {
                            if (!player.Data.IsDead)
                            {
                                if (player.Data.IsImpostor)
                                {
                                    amntAliveImpostors++;
                                }
                                else if (IsCultist(player.PlayerId))
                                {
                                    amntAliveCultists++;
                                }
                                else
                                {
                                    amntAliveCrewmates++;
                                }
                            }
                        }

                        CLog.Info("Alive Impostors: " + amntAliveImpostors + " amnt alive cultists: " +
                                  amntAliveCultists);
                        if (!IsCultist(__instance.exiledPlayer.PlayerId) &&
                            (amntAliveCultists > (amntAliveCrewmates + amntAliveImpostors - 1)))
                        {
                            CLog.Info("CULTISTS WIN BY VOTING OFF, not creating dummy.!");
                            return;
                        }

                        if (IsCultist(__instance.exiledPlayer.PlayerId) && amntAliveCrewmates > 0 &&
                            amntAliveCultists <= 1 && amntAliveImpostors == 0)
                        {
                            CLog.Info("Crewmates WIN BY VOTING last Cultists OFF, not creating dummy.!");
                            return;
                        }

                        //if last impostor just got kicked, might need to create dummy if cultists left in the game
                        if (amntAliveImpostors == 1 && amntAliveCultists > 0 && __instance.exiledPlayer.IsImpostor)
                        {
                            shouldCreateDummy = true;
                        }
                    }
                    // if there was an dummy already we need a new one, if the voting ended in a skip
                    if (ImpostorDummyCount > 0)
                    {
                        shouldCreateDummy = true;
                    }

                    if (shouldCreateDummy)
                    {
                        
                        CLog.Info("Creating Dummy Impostor");
                        DisableGameEnd = true;
                        ImpostorDummyCount++;
                        var gameObject = new GameObject(nameof(ImpostorDummy)).DontDestroy();
                        var dummy = gameObject.AddComponent<ImpostorDummy>();
                        
                        //this means DisableEndGame stays true until after the Death which is Important
                        return;
                    }
                }

                CLog.Info("End of postfix!");
                DisableGameEnd = false;
            }
        }
    }

    [HarmonyPatch(typeof(ExileController), nameof(ExileController.Begin))]
    class ExileControllerPatch
    {
        static void Postfix(ExileController __instance)
        {
            if (IsCultistUsed)
            {
                LastConversion = DateTime.UtcNow.AddMilliseconds(__instance.Duration);
                if (CheckCultistWin())
                {
                    ExecuteCultistWin();
                }
            }
        }
    }


    [RegisterInIl2Cpp]
    public class ImpostorDummy : MonoBehaviour
    {
        public ImpostorDummy(IntPtr ptr) : base(ptr)
        {
            var playerControl = Instantiate(AmongUsClient.Instance.PlayerPrefab);
            var i = playerControl.PlayerId = (byte) GameData.Instance.GetAvailableId();
            GameData.Instance.AddPlayer(playerControl);
            AmongUsClient.Instance.Spawn(playerControl, -2, SpawnFlags.None);
            playerControl.transform.position = PlayerControl.LocalPlayer.transform.position; //TODO maybe spawn outside?
            playerControl.GetComponent<DummyBehaviour>().enabled = true;
            playerControl.NetTransform.enabled = false;
            playerControl.SetName("IMPOSTOR_DUMMY");
            playerControl.SetColor((byte) (i % Palette.PlayerColors.Length));
            playerControl.Data.IsImpostor = true;
            playerControl.GetComponent<DummyBehaviour>().transform.localScale = new Vector3(0, 0, 0);
        }
    }
}

<|start_filename|>CultistPlugin/CultistSettings.cs<|end_filename|>
﻿namespace CultistPlugin
{
    public static class CultistSettings
    {
        public static PlayerControl InitialCultist { get; set; }
        public static bool IsCultistUsed;
        public static float CultistVisionModifier;
        public static float CultistConversionCooldown;
        public static int MaxCultistConversions;
        public static bool ImpostorConversionAttemptUsesConversion;
        public static bool DoCrewmatesWinWhenImpostorsAreDead;
        public static bool CultistsKnowEachOther;
        public static bool CultistLeadIsPassedOnDeath;

        public static void SetCultistSettings()
        {
            IsCultistUsed = CultistPlugin.UseCultist.GetValue();
            CultistVisionModifier = CultistPlugin.CultistVisionModifier.GetValue();
            CultistConversionCooldown = CultistPlugin.CultistConversionCooldown.GetValue();
            MaxCultistConversions = (int) CultistPlugin.CultistConversions.GetValue();
            ImpostorConversionAttemptUsesConversion = CultistPlugin.ImpostorConversionAttemptUsesConversion.GetValue();
            DoCrewmatesWinWhenImpostorsAreDead = CultistPlugin.CrewWinsWhenImpDead.GetValue();
            CultistsKnowEachOther = CultistPlugin.CultistsKnowEachOther.GetValue();
            CultistLeadIsPassedOnDeath = CultistPlugin.CultistLeadIsPassedOnDeath.GetValue();

            CultistMod.ConversionsLeft = MaxCultistConversions;
            CultistMod.LastConversion = null;
            CultistMod.ImpostorDummyCount = 0;
        }
    }
}

<|start_filename|>CultistPlugin/KillButtonPatch.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using HarmonyLib;
using Hazel;
using UnityEngine;
using static CultistPlugin.CultistMod;
using static CultistPlugin.CultistSettings;

namespace CultistPlugin
{
    [HarmonyPatch(typeof(KillButtonManager), nameof(KillButtonManager.PerformKill))]
    public class KillButtonPatch
    {
        public static void Postfix()
        {
            if (IsCultistUsed)
            {
                if (CheckCultistWin())
                {
                    ExecuteCultistWin();
                }
            }
        }

        public static bool Prefix()
        {
            if (PlayerControl.LocalPlayer.Data.IsDead)
            {
                return false;
            }

            if (PlayerControl.LocalPlayer.Data.IsImpostor)
            {
                return true;
            }

            if (IsCultistUsed && CurrentTarget != null && !PlayerControl.LocalPlayer.Data.IsImpostor)
            {
                PlayerControl target = CurrentTarget;

                if (PlayerControl.LocalPlayer == InitialCultist && PlayerTools.GetConversionCooldown() == 0)
                {
                    bool createNewTask = false;
                    var player = PlayerControl.LocalPlayer;
                    CLog.Info("CULTIST TRYING TO CONVERT!");
                    CLog.Info("Target is Impostor:" + target.Data.IsImpostor);
                    CLog.Info("Target is Cultist:" + IsCultist(target.PlayerId));

                    if (!target.Data.IsImpostor && !IsCultist(target.PlayerId))
                    {
                        if (ConversionsLeft > 0)
                        {
                            MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(
                                PlayerControl.LocalPlayer.NetId, (byte) CustomRPC.ConvertAction, Hazel.SendOption.None,
                                -1);
                            writer.Write(PlayerControl.LocalPlayer.PlayerId);
                            writer.Write(target.PlayerId);
                            AmongUsClient.Instance.FinishRpcImmediately(writer);
                            AddCultistToLists(target);
                            ConversionsLeft--;
                            LastConversion = DateTime.UtcNow;

                            if (CheckCultistWin())
                            {
                                ExecuteCultistWin();
                            }

                            var tasksToRemove = new List<PlayerTask>();
                            foreach (var task in target.myTasks)
                            {
                                if (task.TaskType != TaskTypes.FixComms && task.TaskType != TaskTypes.FixLights &&
                                    task.TaskType != TaskTypes.ResetReactor &&
                                    task.TaskType != TaskTypes.ResetSeismic && task.TaskType != TaskTypes.RestoreOxy)
                                    tasksToRemove.Add(task);
                            }

                            foreach (var taskToRemove in tasksToRemove)
                            {
                                player.RemoveTask(taskToRemove);
                            }

                            target.myTasks.Clear();
                            CLog.Info("Done with removing tasks for target!");

                            createNewTask = true;
                        }
                    }

                    if (target.Data.IsImpostor)
                    {
                        LastConversion = DateTime.UtcNow;
                        if (ImpostorConversionAttemptUsesConversion)
                        {
                            if (ConversionsLeft > 0)
                            {
                                ConversionsLeft--;
                                createNewTask = true;
                            }
                        }
                    }

                    if (createNewTask)
                    {
                        
                        MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(
                            PlayerControl.LocalPlayer.NetId, (byte) CustomRPC.ConversionsLeft, Hazel.SendOption.None,
                            -1);
                        writer.Write(ConversionsLeft);
                        AmongUsClient.Instance.FinishRpcImmediately(writer);
                        
                        
                        ImportantTextTask cultLeaderTask =
                            new GameObject("CultistLeaderTask").AddComponent<ImportantTextTask>();
                        cultLeaderTask.transform.SetParent(player.transform, false);

                        cultLeaderTask.Text =
                            "You are the cult leader.\nConvert crewmates to your cult.\nConversions left: " +
                            ConversionsLeft + "/" + MaxCultistConversions;
                        player.myTasks.Clear();
                        player.myTasks.Insert(0, cultLeaderTask);
                    }

                    return false;
                }
            }

            return true;
        }
    }
}

<|start_filename|>CultistPlugin/CultistPlugin.cs<|end_filename|>
﻿using BepInEx;
using BepInEx.IL2CPP;
using HarmonyLib;
using Reactor;
using Essentials.CustomOptions;


namespace CultistPlugin
{
    [BepInPlugin(Id)]
    [BepInProcess("Among Us.exe")]
    [BepInDependency(ReactorPlugin.Id)]
    public class CultistPlugin : BasePlugin
    {
        public const string Id = "gg.reactor.cultistmod";
        public const string ModVersion = "1.0.3";
        public Harmony Harmony { get; } = new Harmony(Id);

        //I am using Reactor Essentials by DorCoMaNdO for these settings
        // https://github.com/DorCoMaNdO/Reactor-Essentials
        // I'm using a Essentials version without the watermark
        // but I am giving credit here and in the Credits Tab in the readme
        // If there is any Problem with this, hit me up on Discord @ Aeolic#5560

        public static CustomToggleOption UseCultist = CustomOption.AddToggle("Enable Cultist", true);

        public static CustomToggleOption CrewWinsWhenImpDead =
            CustomOption.AddToggle("Crew Wins VS Cult", true);

        public static CustomToggleOption ImpostorConversionAttemptUsesConversion =
            CustomOption.AddToggle("Fail Conversion Uses Attempt", true);

        public static CustomToggleOption CultistsKnowEachOther =
            CustomOption.AddToggle("Cult Knows Who Is Cult", false);

        public static CustomToggleOption CultistLeadIsPassedOnDeath =
            CustomOption.AddToggle("Pass Cult Lead On Death", true);

        public static CustomNumberOption
            CultistConversions = CustomOption.AddNumber("Cultist Conversions", 3, 1, 10, 1);

        public static CustomNumberOption CultistConversionCooldown =
            CustomOption.AddNumber("Conversion Cooldown", 60f, 10f, 180f, 5f);

        public static CustomNumberOption CultistVisionModifier =
            CustomOption.AddNumber("Cultist Vision Modifier", 0.7f, 0.3f, 1.5f, 0.1f);

        public override void Load()
        {
            RegisterInIl2CppAttribute.Register();
            Harmony.PatchAll();
        }
    }
}

<|start_filename|>CultistPlugin/CLog.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace CultistPlugin
{
    class CLog
    {
        public static void Info(string message)
        {
            System.Console.ForegroundColor = ConsoleColor.Yellow;
            System.Console.WriteLine("[---INFO---] " + message);
            System.Console.ForegroundColor = ConsoleColor.White;
        }

        public static void Error(string message)
        {
            System.Console.ForegroundColor = ConsoleColor.Red;
            System.Console.WriteLine("[---ERROR---] " + message);
            System.Console.ForegroundColor = ConsoleColor.White;
        }
    }
}

<|start_filename|>CultistPlugin/PlayerTools.cs<|end_filename|>
﻿using HarmonyLib;
using System;

//this class is copied from https://github.com/NotHunter101/ExtraRolesAmongUs and modified/shortened
namespace CultistPlugin
{
    public static class PlayerTools
    {
        public static PlayerControl closestPlayer = null;

        public static PlayerControl getPlayerById(byte id)
        {
            foreach (PlayerControl player in PlayerControl.AllPlayerControls)
            {
                if (player.PlayerId == id)
                {
                    return player;
                }
            }

            return null;
        }

        public static float GetConversionCooldown()
        {
            if (CultistMod.LastConversion == null)
            {
                return CultistSettings.CultistConversionCooldown;
            }

            DateTime now = DateTime.UtcNow;
            TimeSpan diff = (TimeSpan) (now - CultistMod.LastConversion);

            var KillCoolDown = CultistSettings.CultistConversionCooldown * 1000.0f;
            if (KillCoolDown - (float) diff.TotalMilliseconds < 0) return 0;
            else
            {
                return (KillCoolDown - (float) diff.TotalMilliseconds) / 1000.0f;
            }
        }

        public static PlayerControl getClosestPlayer(PlayerControl refplayer)
        {
            double mindist = double.MaxValue;
            PlayerControl closestplayer = null;
            foreach (PlayerControl player in PlayerControl.AllPlayerControls)
            {
                if (player.Data.IsDead) continue;
                if (player != refplayer)
                {
                    double dist = getDistBetweenPlayers(player, refplayer);
                    if (dist < mindist)
                    {
                        mindist = dist;
                        closestplayer = player;
                    }
                }
            }

            return closestplayer;
        }

        public static double getDistBetweenPlayers(PlayerControl player, PlayerControl refplayer)
        {
            var refpos = refplayer.GetTruePosition();
            var playerpos = player.GetTruePosition();

            return Math.Sqrt((refpos[0] - playerpos[0]) * (refpos[0] - playerpos[0]) +
                             (refpos[1] - playerpos[1]) * (refpos[1] - playerpos[1]));
        }
    }
}

<|start_filename|>CultistPlugin/SetInfectedPatch.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using Hazel;
using UnhollowerBaseLib;
using UnityEngine;
using static CultistPlugin.CultistMod;
using static CultistPlugin.CultistSettings;

namespace CultistPlugin
{
    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.RpcSetInfected))]
    public class SetInfectedPatch
    {
        public static void Postfix(Il2CppReferenceArray<GameData.PlayerInfo> JPGEIBIBJPJ)
        {
            List<PlayerControl> crewmates = PlayerControl.AllPlayerControls.ToArray().ToList();
            crewmates.RemoveAll(x => x.Data.IsImpostor);

            if (CultistPlugin.UseCultist.GetValue())
            {
                ClearCultistLists();
                DidCultistsWin = false;
                CLog.Info("Cultist is on for this game.");
                int cultistRandomId = new System.Random().Next(0, crewmates.Count);
                InitialCultist = crewmates[cultistRandomId];
                crewmates.RemoveAt(cultistRandomId);
                byte CultistId = InitialCultist.PlayerId;
                CLog.Info("Randomly chose cultist on host side: " + InitialCultist.name);

                MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId,
                    (byte) CustomRPC.SetCultist, Hazel.SendOption.None, -1);
                writer.Write(CultistId);
                AmongUsClient.Instance.FinishRpcImmediately(writer);

                AddCultistToLists(CultistId);
                SetCultistSettings();
               

                if (PlayerControl.LocalPlayer.PlayerId == cultistRandomId)
                {
                    ImportantTextTask cultistLeaderTask =
                        new GameObject("CultistLeaderTask").AddComponent<ImportantTextTask>();
                    cultistLeaderTask.transform.SetParent(PlayerControl.LocalPlayer.transform, false);

                    cultistLeaderTask.Text =
                        "You are the cult leader.\nConvert crewmates to join your cult.\nConversions left: " +
                        ConversionsLeft + "/" + MaxCultistConversions;

                    PlayerControl.LocalPlayer.myTasks.Insert(0, cultistLeaderTask);
                }
            }
        }
    }
}

<|start_filename|>CultistPlugin/OtherPatches.cs<|end_filename|>
﻿using HarmonyLib;

namespace CultistPlugin
{
    public class OtherPatches
    {
        [HarmonyPatch(typeof(VersionShower), nameof(VersionShower.Start))]
        public static class VersionStartPatch
        {
            static void Postfix(VersionShower __instance)
            {
                __instance.text.Text = __instance.text.Text + "\n\n\n\n\n\n\n\nCultist Mod " + CultistPlugin.ModVersion +
                                       " by Aeolic\nhttps://github.com/Aeolic/CultistMod";
            }
        }

        [HarmonyPatch(typeof(StatsManager), nameof(StatsManager.AmBanned), MethodType.Getter)]
        public static class AmBannedPatch
        {
            public static void Postfix(out bool __result)
            {
                __result = false;
            }
        }

        [HarmonyPatch(typeof(PingTracker), nameof(PingTracker.Update))]
        public static class PingPatch
        {
            public static void Postfix(PingTracker __instance)
            {
                __instance.text.Text += "\nCultist Mod " + CultistPlugin.ModVersion;
            }
        }
    }
}