Files
EndlessHostRoles/Roles/Standard/Neutral/Killing/Thanos.cs
T
2026-05-19 10:25:43 +02:00

306 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
using EHR.Modules;
using Hazel;
namespace EHR.Roles;
public class Thanos : RoleBase
{
public static bool On;
private static List<Thanos> Instances = [];
private static readonly Stone[] StoneEnum = Enum.GetValues<Stone>();
private static readonly StringBuilder Suffix = new();
private static OptionItem KillCooldown;
private static OptionItem CanVent;
private static OptionItem ImpostorVision;
private static OptionItem FirstKillAlwaysSoulStone;
private static OptionItem PowerStoneReducesKillCooldownBy;
private static OptionItem PowerStoneKillsIgnoreShields;
private static OptionItem TimeStoneReducesSpeedBy;
private static OptionItem CanWinAfterCollectingAllStones;
public override bool IsEnable => On;
private List<Stone> StonesWaitingForUse;
private HashSet<Stone> CollectedStones;
private Stone? ActiveStone;
private bool MindStoneUsed;
private List<byte> PlayersWithStones;
private byte ThanosId;
public override void SetupCustomOption()
{
StartSetup(654200)
.AutoSetupOption(ref KillCooldown, 22.5f, new FloatValueRule(0f, 180f, 0.5f), OptionFormat.Seconds)
.AutoSetupOption(ref CanVent, true)
.AutoSetupOption(ref ImpostorVision, true)
.AutoSetupOption(ref FirstKillAlwaysSoulStone, false)
.AutoSetupOption(ref PowerStoneReducesKillCooldownBy, 10f, new FloatValueRule(0f, 60f, 0.5f), OptionFormat.Seconds)
.AutoSetupOption(ref PowerStoneKillsIgnoreShields, true)
.AutoSetupOption(ref TimeStoneReducesSpeedBy, 0.5f, new FloatValueRule(0.1f, 3f, 0.1f), OptionFormat.Multiplier)
.AutoSetupOption(ref CanWinAfterCollectingAllStones, true);
}
public override void Init()
{
On = false;
Instances = [];
}
public override void Add(byte playerId)
{
On = true;
StonesWaitingForUse = [];
CollectedStones = [];
ActiveStone = null;
MindStoneUsed = false;
PlayersWithStones = Main.EnumerateAlivePlayerControls().Select(x => x.PlayerId).TakeRandomToList(StoneEnum.Length);
ThanosId = playerId;
Instances.Add(this);
}
public override void Remove(byte playerId)
{
Instances.Remove(this);
}
public override void SetKillCooldown(byte id)
{
Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
if (ActiveStone is Stone.Power) Main.AllPlayerKillCooldown[id] -= PowerStoneReducesKillCooldownBy.GetFloat();
}
public override void ApplyGameOptions(IGameOptions opt, byte id)
{
opt.SetVision(ImpostorVision.GetBool());
if (Options.UsePhantomBasis.GetBool())
{
AURoleOptions.PhantomCooldown = 5f;
AURoleOptions.PhantomDuration = 1f;
}
else
{
if (Options.UsePets.GetBool()) return;
AURoleOptions.ShapeshifterCooldown = 5f;
AURoleOptions.ShapeshifterDuration = 1f;
}
}
public override bool CanUseImpostorVentButton(PlayerControl pc)
{
return CanVent.GetBool();
}
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
if (target.Is(CustomRoles.Pestilence)) return ActiveStone is Stone.Power or Stone.Space;
if (ActiveStone is Stone.Space)
{
RPC.PlaySoundRPC(killer.PlayerId, Sounds.KillSound);
ActiveStone = null;
Utils.SendRPC(CustomRPC.SyncRoleData, ThanosId, 2);
target.SetRealKiller(killer);
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Scavenged;
target.RpcExileV2();
target.Data.IsDead = true;
Main.PlayerStates[target.PlayerId].SetDead();
Utils.AfterPlayerDeathTasks(target);
target.SetRealKiller(killer);
killer.SetKillCooldown(KillCooldown.GetFloat());
OnMurder(killer, target);
return false;
}
return true;
}
public override bool OnCheckMurderAsTarget(PlayerControl killer, PlayerControl target)
{
return ActiveStone is not Stone.Reality;
}
public override void OnMurder(PlayerControl killer, PlayerControl target)
{
if (PlayersWithStones.Contains(target.PlayerId))
{
Stone stone = CollectedStones.Count == 0 && FirstKillAlwaysSoulStone.GetBool() ? Stone.Soul : StoneEnum.Except(CollectedStones).RandomElement();
CollectedStones.Add(stone);
StonesWaitingForUse.Add(stone);
Utils.SendRPC(CustomRPC.SyncRoleData, ThanosId, 1, (int)stone);
if (killer.AmOwner && CollectedStones.Count == StoneEnum.Length)
Achievements.Type.MasterOfTheStones.Complete();
}
}
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
{
UseStone(shapeshifter);
return false;
}
public override bool OnVanish(PlayerControl pc)
{
UseStone(pc);
return false;
}
public override void OnPet(PlayerControl pc)
{
UseStone(pc);
}
void UseStone(PlayerControl pc)
{
if (CollectedStones.Count == StoneEnum.Length && CanWinAfterCollectingAllStones.GetBool())
{
CustomWinnerHolder.ShiftWinnerAndSetWinner(CustomWinner.Thanos);
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
return;
}
if (StonesWaitingForUse.Count == 0) return;
if (ActiveStone.HasValue)
{
switch (ActiveStone)
{
case Stone.Time:
Main.EnumerateAlivePlayerControls().DoIf(x => x.PlayerId != pc.PlayerId, x =>
{
Main.AllPlayerSpeed[x.PlayerId] = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
if (GameStates.IsInTask && !ExileController.Instance && !AntiBlackout.SkipTasks) x.MarkDirtySettings();
});
break;
case Stone.Soul:
TargetArrow.RemoveAllTarget(pc.PlayerId);
break;
}
}
Stone stone = StonesWaitingForUse[0];
StonesWaitingForUse.RemoveAt(0);
ActiveStone = stone;
switch (stone)
{
case Stone.Space:
break;
case Stone.Mind:
MindStoneUsed = true;
ActiveStone = null;
break;
case Stone.Reality:
break;
case Stone.Power:
pc.ResetKillCooldown();
break;
case Stone.Time:
Main.EnumerateAlivePlayerControls().DoIf(x => x.PlayerId != pc.PlayerId, x =>
{
Main.AllPlayerSpeed[x.PlayerId] -= TimeStoneReducesSpeedBy.GetFloat();
x.MarkDirtySettings();
});
break;
case Stone.Soul:
TargetArrow.Add(pc.PlayerId, PlayersWithStones.RandomElement());
break;
}
Utils.SendRPC(CustomRPC.SyncRoleData, ThanosId, 3);
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
}
public static void OnDeath(PlayerControl killer, PlayerControl target, bool noKiller)
{
if (killer == null || killer.PlayerId == target.PlayerId) noKiller = true;
foreach (Thanos instance in Instances)
{
if (!instance.PlayersWithStones.Remove(target.PlayerId)) continue;
instance.PlayersWithStones.Add(noKiller ? Main.EnumerateAlivePlayerControls().RandomElement().PlayerId : killer.PlayerId);
}
}
public static bool IsImmune(PlayerControl pc) => On && pc != null && Instances.Exists(x => x.ThanosId == pc.PlayerId && x.MindStoneUsed);
enum Stone
{
Space,
Mind,
Reality,
Power,
Time,
Soul
}
public void ReceiveRPC(MessageReader reader)
{
switch (reader.ReadPackedInt32())
{
case 1:
{
Stone stone = (Stone)reader.ReadPackedInt32();
CollectedStones.Add(stone);
StonesWaitingForUse.Add(stone);
break;
}
case 2:
{
ActiveStone = null;
break;
}
case 3:
{
Stone stone = StonesWaitingForUse[0];
StonesWaitingForUse.RemoveAt(0);
ActiveStone = stone;
if (stone == Stone.Mind)
{
MindStoneUsed = true;
ActiveStone = null;
}
break;
}
}
}
public override void GetProgressText(byte playerId, bool comms, StringBuilder resultText)
{
base.GetProgressText(playerId, comms, resultText);
resultText.Append(' ')
.Append(CollectedStones.Count)
.Append('/')
.Append(StoneEnum.Length);
}
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
{
if (seer.PlayerId != ThanosId || seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || meeting) return string.Empty;
Suffix.Clear();
if (ActiveStone.HasValue) Suffix.AppendFormat(Translator.GetString("Thanos.ActiveStone"), Translator.GetString($"Thanos.Stone.{ActiveStone.Value}")).AppendLine();
if (MindStoneUsed) Suffix.AppendLine(Translator.GetString("Thanos.MindStoneUsed"));
if (StonesWaitingForUse.Count > 0)
{
string action = Translator.GetString(Options.UsePhantomBasis.GetBool() && Options.UsePhantomBasisForNKs.GetBool() ? "AbilityButtonText.Phantom" : Options.UsePets.GetBool() ? "PetButtonText" : "Shapeshift");
Suffix.AppendFormat(Translator.GetString("Thanos.StoneWaitingForUse"), action, Translator.GetString($"Thanos.Stone.{StonesWaitingForUse[0]}")).AppendLine();
}
Suffix.AppendLine(TargetArrow.GetAllArrows(seer.PlayerId));
return Suffix.ToString().Trim();
}
}