mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-19 15:51:58 +00:00
365 lines
13 KiB
C#
365 lines
13 KiB
C#
using System;
|
|
using System.Collections;
|
|
using AmongUs.GameOptions;
|
|
using EHR.Crewmate;
|
|
using EHR.Impostor;
|
|
using HarmonyLib;
|
|
using Hazel;
|
|
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
|
using Il2CppSystem.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace EHR.Patches;
|
|
// By TommyXL & NikoCat233
|
|
|
|
[HarmonyPatch(typeof(PlayerControl))]
|
|
public static class PhantomRolePatch
|
|
{
|
|
/*private static readonly List<PlayerControl> InvisibilityList = new();
|
|
private static readonly System.Collections.Generic.Dictionary<byte, string> PetsList = [];*/
|
|
|
|
[HarmonyPatch(nameof(PlayerControl.CmdCheckVanish))]
|
|
[HarmonyPrefix]
|
|
private static bool CmdCheckVanish_Prefix(PlayerControl __instance, float maxDuration)
|
|
{
|
|
if (AmongUsClient.Instance.AmHost)
|
|
{
|
|
__instance.CheckVanish();
|
|
return false;
|
|
}
|
|
|
|
__instance.SetRoleInvisibility(true);
|
|
MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(__instance.NetId, (byte)RpcCalls.CheckVanish, SendOption.Reliable, AmongUsClient.Instance.HostId);
|
|
messageWriter.Write(maxDuration);
|
|
AmongUsClient.Instance.FinishRpcImmediately(messageWriter);
|
|
return false;
|
|
}
|
|
|
|
[HarmonyPatch(nameof(PlayerControl.CmdCheckAppear))]
|
|
[HarmonyPrefix]
|
|
private static bool CmdCheckAppear_Prefix(PlayerControl __instance, bool shouldAnimate)
|
|
{
|
|
if (AmongUsClient.Instance.AmHost)
|
|
{
|
|
__instance.CheckAppear(shouldAnimate);
|
|
return false;
|
|
}
|
|
|
|
MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(__instance.NetId, (byte)RpcCalls.CheckAppear, SendOption.Reliable, AmongUsClient.Instance.HostId);
|
|
messageWriter.Write(shouldAnimate);
|
|
AmongUsClient.Instance.FinishRpcImmediately(messageWriter);
|
|
return false;
|
|
}
|
|
|
|
[HarmonyPatch(nameof(PlayerControl.CheckVanish))] // This doesn't always get called for non-hosts, so we invoke CheckTrigger directly when the CheckVanish RPC is received
|
|
[HarmonyPrefix]
|
|
private static bool CheckVanish_Prefix(PlayerControl __instance)
|
|
{
|
|
if (!AmongUsClient.Instance.AmHost) return true;
|
|
Logger.Info($" {__instance.GetNameWithRole()}", "CheckVanish");
|
|
return __instance.AmOwner && CheckTrigger(__instance); // This is assuming that all non-host vanish requests are for ability triggers and should be cancelled
|
|
}
|
|
|
|
// Called when Phantom press vanish button when visible
|
|
/*[HarmonyPatch(nameof(PlayerControl.CheckVanish))]
|
|
[HarmonyPrefix]
|
|
private static bool CheckVanish_Prefix(PlayerControl __instance)
|
|
{
|
|
if (!AmongUsClient.Instance.AmHost) return true;
|
|
|
|
PlayerControl phantom = __instance;
|
|
Logger.Info($"Player: {phantom.GetNameWithRole()}", "CheckVanish");
|
|
|
|
if (!CheckTrigger(phantom))
|
|
return false;
|
|
|
|
foreach (PlayerControl target in Main.AllPlayerControls)
|
|
{
|
|
if (!target.IsAlive() || phantom.PlayerId == target.PlayerId || target.AmOwner || !target.HasDesyncRole()) continue;
|
|
|
|
int clientId = target.OwnerId;
|
|
|
|
if (AmongUsClient.Instance.ClientId == clientId)
|
|
{
|
|
phantom.SetRole(RoleTypes.Phantom);
|
|
phantom.CheckVanish();
|
|
}
|
|
else
|
|
{
|
|
var writer = CustomRpcSender.Create("PhantomRolePatch.CheckVanish_Prefix", SendOption.Reliable);
|
|
|
|
writer.RpcSetRole(phantom, RoleTypes.Phantom, clientId);
|
|
|
|
writer.AutoStartRpc(phantom.NetId, RpcCalls.CheckVanish, clientId);
|
|
writer.Write(0); // not used, lol
|
|
writer.EndRpc();
|
|
|
|
writer.SendMessage();
|
|
}
|
|
}
|
|
|
|
LateTask.New(() =>
|
|
{
|
|
foreach (PlayerControl target in Main.AllPlayerControls)
|
|
{
|
|
if (GameStates.IsMeeting || phantom == null || target.PlayerId == phantom.PlayerId) return;
|
|
|
|
int clientId = target.OwnerId;
|
|
string petId = phantom.Data.DefaultOutfit.PetId;
|
|
|
|
var sender = CustomRpcSender.Create("PhantomRolePatch.CheckVanish_Prefix - LateTask", SendOption.Reliable);
|
|
var hasData = false;
|
|
|
|
if (petId != "")
|
|
{
|
|
PetsList[phantom.PlayerId] = petId;
|
|
|
|
if (clientId != -1)
|
|
{
|
|
if (AmongUsClient.Instance.ClientId == clientId)
|
|
phantom.SetPet("");
|
|
else
|
|
{
|
|
phantom.Data.DefaultOutfit.PetSequenceId += 10;
|
|
|
|
sender.AutoStartRpc(phantom.NetId, RpcCalls.SetPetStr, clientId);
|
|
sender.Write("");
|
|
sender.Write(phantom.GetNextRpcSequenceId(RpcCalls.SetPetStr));
|
|
sender.EndRpc();
|
|
|
|
hasData = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (AmongUsClient.Instance.ClientId == clientId)
|
|
phantom.Exiled();
|
|
else
|
|
{
|
|
sender.AutoStartRpc(phantom.NetId, RpcCalls.Exiled, clientId);
|
|
sender.EndRpc();
|
|
|
|
hasData = true;
|
|
}
|
|
|
|
sender.SendMessage(!hasData);
|
|
}
|
|
}, 1.2f, "Set Phantom Invisible");
|
|
|
|
InvisibilityList.Add(phantom);
|
|
return true;
|
|
}*/
|
|
|
|
public static bool CheckTrigger(PlayerControl phantom)
|
|
{
|
|
if (!HudManager.InstanceExists) return true;
|
|
|
|
RoleBase roleBase = Main.PlayerStates[phantom.PlayerId].Role;
|
|
|
|
if ((phantom.Is(CustomRoles.Trainee) && MeetingStates.FirstMeeting) || !Rhapsode.CheckAbilityUse(phantom) || Stasis.IsTimeFrozen || TimeMaster.Rewinding || !roleBase.OnVanish(phantom))
|
|
{
|
|
if (phantom.AmOwner)
|
|
{
|
|
HudManager.Instance.AbilityButton.SetFromSettings(phantom.Data.Role.Ability);
|
|
if (Utils.ShouldNotApplyAbilityCooldown(roleBase)) return false;
|
|
phantom.Data.Role.SetCooldown();
|
|
return false;
|
|
}
|
|
|
|
var sender = CustomRpcSender.Create($"Cancel vanish for {phantom.GetRealName()}", SendOption.Reliable);
|
|
sender.StartMessage(phantom.OwnerId);
|
|
|
|
sender.StartRpc(phantom.NetId, RpcCalls.SetRole)
|
|
.Write((ushort)RoleTypes.Phantom)
|
|
.Write(true)
|
|
.EndRpc();
|
|
|
|
if (Utils.ShouldNotApplyAbilityCooldown(roleBase))
|
|
{
|
|
sender.EndMessage();
|
|
sender.SendMessage();
|
|
return false;
|
|
}
|
|
|
|
sender.StartRpc(phantom.NetId, RpcCalls.ProtectPlayer)
|
|
.WriteNetObject(phantom)
|
|
.Write(0)
|
|
.EndRpc();
|
|
|
|
sender.EndMessage();
|
|
sender.SendMessage();
|
|
|
|
LateTask.New(() => phantom.SetKillCooldown(Math.Max(Main.KillTimers[phantom.PlayerId], 0.001f)), 0.2f);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*[HarmonyPatch(nameof(PlayerControl.CheckAppear))]
|
|
[HarmonyPrefix]
|
|
private static void CheckAppear_Prefix(PlayerControl __instance, bool shouldAnimate)
|
|
{
|
|
if (!AmongUsClient.Instance.AmHost) return;
|
|
|
|
PlayerControl phantom = __instance;
|
|
Logger.Info($"Player: {phantom.GetRealName()} => shouldAnimate {shouldAnimate}", "CheckAppear");
|
|
|
|
if (phantom.inVent)
|
|
{
|
|
int ventId = Main.LastEnteredVent[phantom.PlayerId].Id;
|
|
phantom.MyPhysics.RpcBootFromVent(ventId);
|
|
}
|
|
|
|
foreach (PlayerControl target in Main.AllPlayerControls)
|
|
{
|
|
if (!target.IsAlive() || phantom.PlayerId == target.PlayerId || target.AmOwner || !target.HasDesyncRole()) continue;
|
|
phantom.RpcSetRoleDesync(RoleTypes.Phantom, target.OwnerId);
|
|
}
|
|
|
|
LateTask.New(() =>
|
|
{
|
|
foreach (PlayerControl target in Main.AllPlayerControls)
|
|
{
|
|
if (target == null || target.PlayerId == phantom.PlayerId) continue;
|
|
|
|
int clientId = target.OwnerId;
|
|
|
|
if (AmongUsClient.Instance.ClientId == clientId)
|
|
phantom.CheckAppear(shouldAnimate);
|
|
else
|
|
{
|
|
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(phantom.NetId, 64, SendOption.Reliable, clientId);
|
|
writer.Write(shouldAnimate);
|
|
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
|
}
|
|
}
|
|
}, 0.5f, "Check Appear when vanish is over");
|
|
|
|
LateTask.New(() =>
|
|
{
|
|
foreach (PlayerControl target in Main.AllPlayerControls)
|
|
{
|
|
if (GameStates.IsMeeting || phantom == null || target.PlayerId == phantom.PlayerId) return;
|
|
|
|
int clientId = target.OwnerId;
|
|
|
|
InvisibilityList.Remove(phantom);
|
|
|
|
var sender = CustomRpcSender.Create("PhantomRolePatch.CheckAppear_Prefix - LateTask 2", SendOption.Reliable);
|
|
sender.RpcSetRole(phantom, RoleTypes.Scientist, clientId);
|
|
|
|
if (PetsList.TryGetValue(phantom.PlayerId, out string petId))
|
|
{
|
|
if (clientId != -1)
|
|
{
|
|
if (AmongUsClient.Instance.ClientId == clientId)
|
|
phantom.SetPet(petId);
|
|
else
|
|
{
|
|
phantom.Data.DefaultOutfit.PetSequenceId += 10;
|
|
|
|
sender.AutoStartRpc(phantom.NetId, RpcCalls.SetPetStr, clientId);
|
|
sender.Write(petId);
|
|
sender.Write(phantom.GetNextRpcSequenceId(RpcCalls.SetPetStr));
|
|
sender.EndRpc();
|
|
}
|
|
}
|
|
}
|
|
|
|
sender.SendMessage();
|
|
}
|
|
}, 1.8f, "Set Scientist when vanish is over");
|
|
}
|
|
|
|
public static void OnReportDeadBody(PlayerControl seer)
|
|
{
|
|
try
|
|
{
|
|
if (InvisibilityList.Count == 0 || !seer.IsAlive() || seer.Data.Role.Role is RoleTypes.Phantom || seer.AmOwner || !seer.HasDesyncRole()) return;
|
|
|
|
foreach (PlayerControl phantom in InvisibilityList)
|
|
{
|
|
if (!phantom.IsAlive())
|
|
{
|
|
InvisibilityList.Remove(phantom);
|
|
continue;
|
|
}
|
|
|
|
Main.Instance.StartCoroutine(CoRevertInvisible(phantom, seer));
|
|
}
|
|
}
|
|
catch (Exception e) { Utils.ThrowException(e); }
|
|
}
|
|
|
|
private static IEnumerator CoRevertInvisible(PlayerControl phantom, PlayerControl seer)
|
|
{
|
|
if (seer.OwnerId == -1 || phantom == null) yield break;
|
|
phantom.RpcSetRoleDesync(RoleTypes.Scientist, seer.OwnerId);
|
|
|
|
// Return Phantom in meeting
|
|
yield return new WaitForSeconds(1f);
|
|
|
|
{
|
|
if (seer.OwnerId == -1 || phantom == null) yield break;
|
|
phantom.RpcSetRoleDesync(RoleTypes.Phantom, seer.OwnerId);
|
|
}
|
|
|
|
// Revert invis for phantom
|
|
yield return new WaitForSeconds(1f);
|
|
|
|
{
|
|
if (seer.OwnerId == -1 || phantom == null) yield break;
|
|
phantom.RpcStartAppearDesync(false, seer);
|
|
}
|
|
|
|
// Set Scientist back
|
|
yield return new WaitForSeconds(4f);
|
|
|
|
{
|
|
if (seer.OwnerId == -1 || phantom == null) yield break;
|
|
phantom.RpcSetRoleDesync(RoleTypes.Scientist, seer.OwnerId);
|
|
|
|
if (PetsList.TryGetValue(phantom.PlayerId, out string petId))
|
|
phantom.RpcSetPetDesync(petId, seer);
|
|
}
|
|
}
|
|
|
|
public static void AfterMeeting()
|
|
{
|
|
InvisibilityList.Clear();
|
|
PetsList.Clear();
|
|
}*/
|
|
}
|
|
|
|
// Fixed vanilla bug for host (from TOH-Y)
|
|
[HarmonyPatch(typeof(PhantomRole), nameof(PhantomRole.UseAbility))]
|
|
public static class PhantomRoleUseAbilityPatch
|
|
{
|
|
public static bool Prefix(PhantomRole __instance)
|
|
{
|
|
if (!AmongUsClient.Instance.AmHost || !HudManager.InstanceExists) return true;
|
|
|
|
if (__instance.Player.AmOwner && !__instance.Player.Data.IsDead && __instance.Player.moveable && !Minigame.Instance && !__instance.IsCoolingDown && !__instance.fading)
|
|
{
|
|
bool RoleEffectAnimation(RoleEffectAnimation x) => x.effectType == global::RoleEffectAnimation.EffectType.Vanish_Charge;
|
|
|
|
if (!__instance.Player.currentRoleAnimations.Find((Func<RoleEffectAnimation, bool>)RoleEffectAnimation) && !__instance.Player.walkingToVent && !__instance.Player.inMovingPlat)
|
|
{
|
|
if (__instance.isInvisible)
|
|
{
|
|
__instance.MakePlayerVisible();
|
|
return false;
|
|
}
|
|
|
|
HudManager.Instance.AbilityButton.SetSecondImage(__instance.Ability);
|
|
HudManager.Instance.AbilityButton.OverrideText(TranslationController.Instance.GetString(StringNames.PhantomAbilityUndo, new Il2CppReferenceArray<Il2CppSystem.Object>(0)));
|
|
__instance.Player.CmdCheckVanish(GameManager.Instance.LogicOptions.GetRoleFloat(FloatOptionNames.PhantomDuration));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
} |