Files
2026-09-17 18:40:21 +02:00

278 lines
9.9 KiB
C#

using System;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using AmongUs.Data;
using EHR.Modules;
using EHR.Roles;
using HarmonyLib;
using Hazel;
using UnityEngine;
namespace EHR.Patches;
internal static class ExileControllerWrapUpPatch
{
public static NetworkedPlayerInfo LastExiled;
public static Stopwatch Stopwatch;
public static void WrapUpPostfix(NetworkedPlayerInfo exiled)
{
if (!AmongUsClient.Instance.AmHost) return;
var decidedWinner = false;
if (!Collector.CollectorWin(false) && exiled != null)
{
//exiled.IsDead = true;
Main.PlayerStates[exiled.PlayerId].deathReason = PlayerState.DeathReason.Vote;
CustomRoles role = exiled.GetCustomRole();
if (Main.EnumeratePlayerControls().Any(x => x.Is(CustomRoles.Innocent) && !x.IsAlive() && x.GetRealKiller()?.PlayerId == exiled.PlayerId))
{
if (!Options.InnocentCanWinByImp.GetBool() && role.IsImpostor())
Logger.Info("The exiled player is an impostor, but the Innocent cannot win due to the settings", "Exeiled Winner Check");
else
{
CustomWinnerHolder.SetWinnerOrAdditonalWinner(CustomWinner.Innocent);
Main.EnumeratePlayerControls()
.Where(x => x.Is(CustomRoles.Innocent) && !x.IsAlive() && x.GetRealKiller()?.PlayerId == exiled.PlayerId)
.Do(x => CustomWinnerHolder.WinnerIds.Add(x.PlayerId));
decidedWinner = true;
}
}
if (role.Is(Team.Impostor) || role.Is(Team.Neutral))
Stressed.OnNonCrewmateEjected();
else
Stressed.OnCrewmateEjected();
if (role.Is(Team.Impostor))
Damocles.OnImpostorEjected();
else
{
Cantankerous.OnCrewmateEjected();
Mafioso.OnCrewmateEjected();
Damocles.OnCrewmateEjected();
}
if (role == CustomRoles.Jester)
{
if (decidedWinner) CustomWinnerHolder.ShiftWinnerAndSetWinner(CustomWinner.Jester);
else CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Jester);
CustomWinnerHolder.WinnerIds.Add(exiled.PlayerId);
decidedWinner = true;
}
if (Executioner.CheckExileTarget(exiled)) decidedWinner = true;
if (CustomWinnerHolder.WinnerTeam != CustomWinner.Terrorist)
Main.PlayerStates[exiled.PlayerId].SetDead();
}
Witch.RemoveSpelledPlayer();
Swapper.OnExileFinish();
foreach (PlayerControl pc in Main.CachedAllPlayerControls())
{
if (pc.Is(CustomRoles.Warlock))
{
Warlock.CursedPlayers[pc.PlayerId] = null;
Warlock.IsCurseAndKill[pc.PlayerId] = false;
}
pc.ResetKillCooldown(false);
if (!Utils.ShouldNotApplyAbilityCooldownAfterMeeting(pc)) pc.RpcResetAbilityCooldown();
PetsHelper.RpcRemovePet(pc);
}
if (Options.RandomSpawn.GetBool() && Main.CurrentMap != MapNames.Airship && !Main.LIMap)
{
var map = RandomSpawn.SpawnMap.GetSpawnMap();
Main.EnumerateAlivePlayerControls().Do(map.RandomTeleport);
}
try
{
if (Lazy.BeforeMeetingPositions != null)
{
foreach ((byte id, Vector2 pos) in Lazy.BeforeMeetingPositions)
{
PlayerControl pc = id.GetPlayer();
if (!pc || !pc.IsAlive()) continue;
pc.TP(pos);
}
}
}
catch (Exception e) { Utils.ThrowException(e); }
Lazy.BeforeMeetingPositions = null;
FallFromLadder.Reset();
Utils.CountAlivePlayers(true);
if (decidedWinner)
{
GameEndChecker.ShouldNotCheck = false;
GameEndChecker.CheckCustomEndCriteria();
}
if (!exiled) return;
PlayerControl exiledPlayer = exiled.Object;
LateTask.New(() =>
{
if (!GameStates.IsEnded && exiledPlayer)
{
exiledPlayer.RpcExileV2();
Utils.AfterPlayerDeathTasks(exiledPlayer, true);
if (exiledPlayer.IsAlive()) Main.PlayerStates[exiledPlayer.PlayerId].SetDead();
}
}, 3.5f, "AfterPlayerDeathTasks For Exiled Player");
}
public static void WrapUpFinalizer()
{
// Even if an exception occurs in WrapUpPostfix, this part will be executed reliably.
if (AmongUsClient.Instance.AmHost)
{
Stopwatch = Stopwatch.StartNew();
LateTask.New(() =>
{
if (GameStates.IsEnded) return;
AntiBlackout.RevertToActualRoleTypes();
}, 2f, "Revert AntiBlackout Measures");
if (Options.EnableGameTimeLimit.GetBool() && !Options.GameTimeLimitRunsDuringMeetings.GetBool())
Main.GameTimer.Start();
}
GameStates.AlreadyDied |= !Utils.IsAllAlive;
RemoveDisableDevicesPatch.UpdateDisableDevices();
SoundManager.Instance.ChangeAmbienceVolume(DataManager.Settings.Audio.AmbienceVolume);
Logger.Info("Start task phase", "Phase");
if (!AmongUsClient.Instance.AmHost || GameStates.IsEnded) return;
bool showRemainingKillers = Options.EnableGameStateCommand.GetBool() && Options.ShowImpRemainOnEject.GetBool();
bool ejectionNotify = CheckForEndVotingPatch.EjectionText != string.Empty;
Logger.Msg($"Ejection Text: {CheckForEndVotingPatch.EjectionText}", "ExilePatch");
if ((showRemainingKillers || ejectionNotify) && Options.CurrentGameMode == CustomGameMode.Standard)
{
string text = showRemainingKillers ? Utils.GetRemainingKillers(true) : string.Empty;
string finalText = ejectionNotify ? "<#ffffff>" + CheckForEndVotingPatch.EjectionText.Trim() : text;
if (Options.EnableGameTimeLimit.GetBool()) finalText += $"\n<#888888>{Options.GameTimeLimit.GetInt() - Main.GameTimer.Elapsed.TotalSeconds:N0}s {Translator.GetString("RemainingText.Suffix")}";
if (!string.IsNullOrWhiteSpace(finalText))
Main.EnumerateAlivePlayerControls().NotifyPlayers(finalText, 13f, setName: false, sendOption: ejectionNotify ? SendOption.None : SendOption.Reliable);
}
LateTask.New(() =>
{
if (ChatCommands.HasMessageDuringEjectionScreen)
ChatManager.ClearChat();
}, 3f, log: false);
}
public static void AfterMeetingTasks()
{
if (CustomWinnerHolder.WinnerTeam != CustomWinner.Default || GameStates.IsEnded)
{
Stopwatch.Reset();
return;
}
bool hasValue = false;
CustomRpcSender sender = CustomRpcSender.Create("Exile AfterMeetingDeathPlayers", SendOption.Reliable);
Main.AfterMeetingDeathPlayers.Keys.ToValidPlayers().Do(x => hasValue |= sender.RpcExileV2(x));
sender.SendMessage(dispose: !hasValue);
foreach ((byte id, PlayerState.DeathReason deathReason) in Main.AfterMeetingDeathPlayers)
{
var player = id.GetPlayer();
var state = Main.PlayerStates[id];
Logger.Info($"{Main.AllPlayerNames[id]} ({state.MainRole}) died with {deathReason}", "AfterMeetingDeath");
state.deathReason = deathReason;
state.SetDead();
if (!player) continue;
if (deathReason == PlayerState.DeathReason.Suicide)
player.SetRealKiller(player, true);
Utils.AfterPlayerDeathTasks(player);
}
Main.AfterMeetingDeathPlayers.Clear();
Utils.AfterMeetingTasks();
Utils.MarkEveryoneDirtySettings();
Utils.CheckAndSetVentInteractions();
Main.Instance.StartCoroutine(Coroutine());
Stopwatch.Reset();
return;
IEnumerator Coroutine()
{
yield return new WaitForSecondsRealtime(0.2f);
if (GameStates.IsEnded) yield break;
yield return Utils.NotifyEveryoneAsync();
}
}
[HarmonyPatch(typeof(ExileController), nameof(ExileController.WrapUp))]
private static class BaseExileControllerPatch
{
public static void Postfix(ExileController __instance)
{
if (Main.LIMap) return;
try { WrapUpPostfix(__instance.initData.networkedPlayer); }
finally { WrapUpFinalizer(); }
}
}
[HarmonyPatch]
static class AirshipExileControllerPatch
{
public static MethodBase TargetMethod()
{
return Utils.GetStateMachineMoveNext<AirshipExileController>(nameof(AirshipExileController.WrapUpAndSpawn));
}
public static void Postfix(object __instance, ref bool __result)
{
var wrapper = new StateMachineWrapper<AirshipExileController>(__instance);
if (wrapper.State != 1 || !__result || Main.LIMap) return;
try { WrapUpPostfix(wrapper.Instance.initData.networkedPlayer); }
finally { WrapUpFinalizer(); }
}
}
}
[HarmonyPatch(typeof(PbExileController), nameof(PbExileController.PlayerSpin))]
internal static class PolusExileHatFixPatch
{
public static void Prefix(PbExileController __instance)
{
__instance.Player.cosmetics.hat.transform.localPosition = new(-0.2f, 0.6f, 1.1f);
}
}