mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-19 15:51:58 +00:00
246 lines
10 KiB
C#
246 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using AmongUs.GameOptions;
|
|
using EHR.Modules;
|
|
using Hazel;
|
|
using UnityEngine;
|
|
|
|
namespace EHR.Gamemodes;
|
|
|
|
internal static class HotPotato
|
|
{
|
|
private static OptionItem Time;
|
|
private static OptionItem HolderSpeed;
|
|
private static OptionItem Range;
|
|
private static OptionItem HolderCanPassViaKillButton;
|
|
private static OptionItem ExtraTimeOnAirship;
|
|
private static OptionItem ExtraTimeOnFungle;
|
|
private static OptionItem HolderHasArrowToNearestPlayerIfPlayersLessThan;
|
|
|
|
private static (byte HolderID, byte LastHolderID, int TimeLeft, int RoundNum) HotPotatoState;
|
|
private static Dictionary<byte, int> SurvivalTimes;
|
|
private static float DefaultSpeed;
|
|
private static long LastPassTS;
|
|
|
|
public static bool CanPassViaKillButton => HolderCanPassViaKillButton.GetBool();
|
|
|
|
public static (byte HolderID, byte LastHolderID) GetState()
|
|
{
|
|
return (HotPotatoState.HolderID, HotPotatoState.LastHolderID);
|
|
}
|
|
|
|
public static void SetupCustomOption()
|
|
{
|
|
Time = new IntegerOptionItem(69_213_001, "HotPotato_Time", new(1, 90, 1), 20, TabGroup.GameSettings)
|
|
.SetHeader(true)
|
|
.SetGameMode(CustomGameMode.HotPotato)
|
|
.SetValueFormat(OptionFormat.Seconds)
|
|
.SetColor(new Color32(232, 205, 70, byte.MaxValue));
|
|
|
|
HolderSpeed = new FloatOptionItem(69_213_002, "HotPotato_HolderSpeed", new(0.1f, 5f, 0.1f), 2f, TabGroup.GameSettings)
|
|
.SetGameMode(CustomGameMode.HotPotato)
|
|
.SetValueFormat(OptionFormat.Multiplier)
|
|
.SetColor(new Color32(232, 205, 70, byte.MaxValue));
|
|
|
|
Range = new FloatOptionItem(69_213_004, "HotPotato_Range", new(0.25f, 5f, 0.25f), 1f, TabGroup.GameSettings)
|
|
.SetGameMode(CustomGameMode.HotPotato)
|
|
.SetValueFormat(OptionFormat.Multiplier)
|
|
.SetColor(new Color32(232, 205, 70, byte.MaxValue));
|
|
|
|
HolderCanPassViaKillButton = new BooleanOptionItem(69_213_003, "HotPotato_HolderCanPassViaKillButton", true, TabGroup.GameSettings)
|
|
.SetGameMode(CustomGameMode.HotPotato)
|
|
.SetColor(new Color32(232, 205, 70, byte.MaxValue))
|
|
.SetHeader(true);
|
|
|
|
ExtraTimeOnAirship = new IntegerOptionItem(69_213_005, "HotPotato_ExtraTimeOnAirship", new(0, 60, 1), 15, TabGroup.GameSettings)
|
|
.SetGameMode(CustomGameMode.HotPotato)
|
|
.SetValueFormat(OptionFormat.Seconds)
|
|
.SetColor(new Color32(232, 205, 70, byte.MaxValue));
|
|
|
|
ExtraTimeOnFungle = new IntegerOptionItem(69_213_006, "HotPotato_ExtraTimeOnFungle", new(0, 60, 1), 10, TabGroup.GameSettings)
|
|
.SetGameMode(CustomGameMode.HotPotato)
|
|
.SetValueFormat(OptionFormat.Seconds)
|
|
.SetColor(new Color32(232, 205, 70, byte.MaxValue));
|
|
|
|
HolderHasArrowToNearestPlayerIfPlayersLessThan = new IntegerOptionItem(69_213_007, "HotPotato_HolderHasArrowToNearestPlayerIfPlayersLessThan", new(2, 15, 1), 5, TabGroup.GameSettings)
|
|
.SetGameMode(CustomGameMode.HotPotato)
|
|
.SetValueFormat(OptionFormat.Players)
|
|
.SetColor(new Color32(232, 205, 70, byte.MaxValue));
|
|
}
|
|
|
|
public static void Init()
|
|
{
|
|
HotPotatoState = (byte.MaxValue, byte.MaxValue, Time.GetInt() + 8, 1);
|
|
SurvivalTimes = [];
|
|
foreach (PlayerControl pc in Main.CachedAllPlayerControls()) SurvivalTimes[pc.PlayerId] = 0;
|
|
|
|
DefaultSpeed = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
|
|
}
|
|
|
|
public static void OnGameStart()
|
|
{
|
|
HotPotatoState = (byte.MaxValue, byte.MaxValue, GetKillInterval(), 1);
|
|
LastPassTS = Utils.TimeStamp;
|
|
}
|
|
|
|
public static int GetSurvivalTime(byte id)
|
|
{
|
|
return SurvivalTimes.GetValueOrDefault(id, 1);
|
|
}
|
|
|
|
public static string GetIndicator(byte id)
|
|
{
|
|
return HotPotatoState.HolderID == id ? " \u2668 <b>" : string.Empty;
|
|
}
|
|
|
|
public static string GetSuffixText(byte id, bool hud)
|
|
{
|
|
if (!Main.PlayerStates.TryGetValue(id, out PlayerState state) || id.IsPlayerModdedClient() && !hud || state.IsDead && !hud) return string.Empty;
|
|
string holding = HotPotatoState.HolderID == id ? $"{Translator.GetString("HotPotato_HoldingNotify")}\n" : string.Empty;
|
|
string arrows = TargetArrow.GetAllArrows(id);
|
|
arrows = arrows.Length > 0 ? $"\n{arrows}" : string.Empty;
|
|
return $"{holding}<{Utils.GetRoleColorCode(CustomRoles.Potato)}>{Translator.GetString("HotPotato_TimeLeftSuffix")}</color>{(hud ? "<b>" : string.Empty)}{HotPotatoState.TimeLeft}{(hud ? "</b>" : string.Empty)}s{arrows}";
|
|
}
|
|
|
|
public static void ReceiveRPC(MessageReader reader)
|
|
{
|
|
switch (reader.ReadPackedInt32())
|
|
{
|
|
case 1:
|
|
HotPotatoState.TimeLeft = reader.ReadPackedInt32();
|
|
break;
|
|
case 2:
|
|
HotPotatoState.HolderID = reader.ReadByte();
|
|
HotPotatoState.LastHolderID = reader.ReadByte();
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static void RecordDeath(byte id, int time)
|
|
{
|
|
SurvivalTimes[id] = time;
|
|
}
|
|
|
|
public static int GetKillInterval()
|
|
{
|
|
return Time.GetInt() + Main.CurrentMap switch
|
|
{
|
|
MapNames.Airship => ExtraTimeOnAirship.GetInt(),
|
|
MapNames.Fungle => ExtraTimeOnFungle.GetInt(),
|
|
_ => 0
|
|
};
|
|
}
|
|
|
|
//[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))]
|
|
public static class FixedUpdatePatch
|
|
{
|
|
private static long LastFixedUpdate;
|
|
|
|
public static void Postfix(PlayerControl __instance)
|
|
{
|
|
if (Options.CurrentGameMode != CustomGameMode.HotPotato || !Main.IntroDestroyed || !AmongUsClient.Instance.AmHost || !GameStates.IsInTask || ExileController.Instance || __instance.PlayerId >= 254 || IntroCutsceneDestroyPatch.IntroDestroyTS + 5 > Utils.TimeStamp) return;
|
|
|
|
PlayerControl holder = Utils.GetPlayerById(HotPotatoState.HolderID);
|
|
|
|
if (!holder || holder.Data.Disconnected || !holder.IsAlive())
|
|
{
|
|
PassHotPotato();
|
|
return;
|
|
}
|
|
|
|
long now = Utils.TimeStamp;
|
|
|
|
if (now > LastFixedUpdate)
|
|
{
|
|
HotPotatoState.TimeLeft--;
|
|
Utils.SendRPC(CustomRPC.HotPotatoSync, 1, HotPotatoState.TimeLeft);
|
|
LastFixedUpdate = now;
|
|
Utils.NotifyRoles(SendOption: SendOption.None);
|
|
}
|
|
|
|
if (HotPotatoState.TimeLeft <= 0)
|
|
{
|
|
holder.Suicide();
|
|
RecordDeath(HotPotatoState.HolderID, Options.IntegrateNaturalDisasters.GetBool() ? (int)(Utils.TimeStamp - IntroCutsceneDestroyPatch.IntroDestroyTS - NaturalDisasters.FixedUpdatePatch.WaitTime) : (HotPotatoState.RoundNum - 1) * GetKillInterval());
|
|
PassHotPotato();
|
|
|
|
if (holder.AmOwner)
|
|
Achievements.Type.OutOfTime.Complete();
|
|
|
|
return;
|
|
}
|
|
|
|
if (CanPassViaKillButton || LastPassTS == now) return;
|
|
if (HotPotatoState.HolderID != __instance.PlayerId) return;
|
|
|
|
Dictionary<byte, Vector2> alivePlayerPositions = Main.EnumerateAlivePlayerControls().ToDictionary(x => x.PlayerId, x => x.Pos());
|
|
bool allowLastHolder = alivePlayerPositions.Count == 2;
|
|
Vector2 holderPos = alivePlayerPositions[HotPotatoState.HolderID];
|
|
float range = Range.GetFloat();
|
|
|
|
if (!FastVector2.TryGetClosestInRange(holderPos, alivePlayerPositions, range, out byte passToId, x => x != HotPotatoState.HolderID)) return;
|
|
if (!allowLastHolder && passToId == HotPotatoState.LastHolderID) return;
|
|
PassHotPotato(passToId.GetPlayer(), false);
|
|
}
|
|
|
|
public static void PassHotPotato(PlayerControl target = null, bool resetTime = true)
|
|
{
|
|
var aapc = Main.CachedAlivePlayerControls();
|
|
var aapcCount = aapc.Count;
|
|
|
|
if (!Main.IntroDestroyed || aapcCount < 2) return;
|
|
|
|
if (resetTime)
|
|
{
|
|
HotPotatoState.TimeLeft = GetKillInterval();
|
|
HotPotatoState.RoundNum++;
|
|
}
|
|
|
|
try
|
|
{
|
|
target ??= aapc.RandomElement();
|
|
|
|
HotPotatoState.LastHolderID = HotPotatoState.HolderID;
|
|
HotPotatoState.HolderID = target.PlayerId;
|
|
|
|
Utils.SendRPC(CustomRPC.HotPotatoSync, 2, HotPotatoState.HolderID, HotPotatoState.LastHolderID);
|
|
|
|
if (CanPassViaKillButton)
|
|
{
|
|
target.RpcSetRoleDesync(RoleTypes.Impostor, target.OwnerId);
|
|
LateTask.New(() => target.SetKillCooldownNonSync(1f), 0.2f, log: false);
|
|
}
|
|
|
|
if (aapcCount < HolderHasArrowToNearestPlayerIfPlayersLessThan.GetInt())
|
|
{
|
|
Vector2 pos = target.Pos();
|
|
TargetArrow.Add(HotPotatoState.HolderID, aapc.Without(target).Where(x => x.PlayerId != HotPotatoState.LastHolderID).MinBy(x => Vector2.Distance(x.Pos(), pos)).PlayerId);
|
|
}
|
|
|
|
Main.AllPlayerSpeed[target.PlayerId] = HolderSpeed.GetFloat();
|
|
target.MarkDirtySettings();
|
|
|
|
PlayerControl lastHolder = Utils.GetPlayerById(HotPotatoState.LastHolderID);
|
|
|
|
if (lastHolder)
|
|
{
|
|
if (CanPassViaKillButton && lastHolder.IsAlive()) lastHolder.RpcSetRoleGlobal(RoleTypes.Crewmate);
|
|
|
|
TargetArrow.RemoveAllTarget(HotPotatoState.LastHolderID);
|
|
|
|
Main.AllPlayerSpeed[HotPotatoState.LastHolderID] = DefaultSpeed;
|
|
PlayerGameOptionsSender.SetDirty(HotPotatoState.LastHolderID);
|
|
Utils.NotifyRoles(SpecifyTarget: lastHolder);
|
|
|
|
Logger.Info($"Hot Potato Passed: {lastHolder.GetRealName()} => {target.GetRealName()}", "HotPotato");
|
|
}
|
|
}
|
|
catch (Exception ex) { Logger.Exception(ex, "HotPotatoManager.FixedUpdatePatch.PassHotPotato"); }
|
|
finally { Utils.NotifyRoles(SpecifyTarget: target); }
|
|
|
|
LastPassTS = Utils.TimeStamp;
|
|
}
|
|
}
|
|
}
|