mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-20 01:46:25 +00:00
237 lines
7.8 KiB
C#
237 lines
7.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using AmongUs.GameOptions;
|
|
using EHR.Modules;
|
|
using Hazel;
|
|
|
|
namespace EHR.Roles;
|
|
|
|
public class Spider : RoleBase
|
|
{
|
|
public static bool On;
|
|
private static List<Spider> Instances = [];
|
|
|
|
public static OptionItem AbilityCooldown;
|
|
private static OptionItem WebTrapRange;
|
|
private static OptionItem LowerVisionBy;
|
|
private static OptionItem TrappedDuration;
|
|
private static OptionItem KillCooldown;
|
|
private static OptionItem CanVent;
|
|
private static OptionItem ImpostorVision;
|
|
|
|
public override bool IsEnable => On;
|
|
|
|
private Dictionary<Vector2, Dictionary<byte, long>> Webs = [];
|
|
private readonly List<Vector2> ToRemove = [];
|
|
private bool NameDirty;
|
|
private byte SpiderId;
|
|
|
|
public override void SetupCustomOption()
|
|
{
|
|
StartSetup(657500)
|
|
.AutoSetupOption(ref AbilityCooldown, 30f, new FloatValueRule(0f, 180f, 0.5f), OptionFormat.Seconds)
|
|
.AutoSetupOption(ref WebTrapRange, 2f, new FloatValueRule(0.05f, 10f, 0.05f), OptionFormat.Multiplier)
|
|
.AutoSetupOption(ref LowerVisionBy, 0.5f, new FloatValueRule(0f, 3f, 0.05f), OptionFormat.Multiplier)
|
|
.AutoSetupOption(ref TrappedDuration, 20f, new FloatValueRule(0f, 180f, 0.5f), OptionFormat.Seconds)
|
|
.AutoSetupOption(ref KillCooldown, 10f, new FloatValueRule(0f, 180f, 0.5f), OptionFormat.Seconds)
|
|
.AutoSetupOption(ref CanVent, true)
|
|
.AutoSetupOption(ref ImpostorVision, true);
|
|
}
|
|
|
|
public override void Init()
|
|
{
|
|
On = false;
|
|
Instances = [];
|
|
}
|
|
|
|
public override void Add(byte playerId)
|
|
{
|
|
On = true;
|
|
Webs = [];
|
|
NameDirty = false;
|
|
SpiderId = playerId;
|
|
Instances.Add(this);
|
|
}
|
|
|
|
public override void Remove(byte playerId)
|
|
{
|
|
Instances.Remove(this);
|
|
}
|
|
|
|
public override void SetKillCooldown(byte id)
|
|
{
|
|
Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
|
|
}
|
|
|
|
public override void ApplyGameOptions(IGameOptions opt, byte id)
|
|
{
|
|
opt.SetVision(ImpostorVision.GetBool());
|
|
|
|
if (Options.UsePhantomBasis.GetBool() && Options.UsePhantomBasisForNKs.GetBool())
|
|
{
|
|
AURoleOptions.PhantomCooldown = AbilityCooldown.GetFloat();
|
|
AURoleOptions.PhantomDuration = 1f;
|
|
}
|
|
else if (!Options.UsePets.GetBool())
|
|
{
|
|
AURoleOptions.ShapeshifterCooldown = AbilityCooldown.GetFloat();
|
|
AURoleOptions.ShapeshifterDuration = 1f;
|
|
}
|
|
}
|
|
|
|
public override bool CanUseImpostorVentButton(PlayerControl pc)
|
|
{
|
|
return CanVent.GetBool();
|
|
}
|
|
|
|
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
|
|
{
|
|
if (!shapeshifting) return true;
|
|
UseAbility(shapeshifter);
|
|
return false;
|
|
}
|
|
|
|
public override bool OnVanish(PlayerControl pc)
|
|
{
|
|
UseAbility(pc);
|
|
return false;
|
|
}
|
|
|
|
public override void OnPet(PlayerControl pc)
|
|
{
|
|
UseAbility(pc);
|
|
}
|
|
|
|
void UseAbility(PlayerControl player)
|
|
{
|
|
Vector2 pos = player.Pos();
|
|
if (Webs.Keys.Any(x => FastVector2.DistanceWithinRange(x, pos, WebTrapRange.GetFloat() * 2f))) return;
|
|
Webs[pos] = [];
|
|
player.RPCPlayCustomSound("Line");
|
|
LocateArrow.Add(player.PlayerId, pos);
|
|
player.Notify(Translator.GetString("MarkDone"));
|
|
}
|
|
|
|
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
|
{
|
|
return Webs.Any(x => x.Value.ContainsKey(target.PlayerId));
|
|
}
|
|
|
|
public override void OnMurder(PlayerControl killer, PlayerControl target)
|
|
{
|
|
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Eaten;
|
|
Webs.DoIf(x => x.Value.Remove(target.PlayerId), x => Utils.SendRPC(CustomRPC.SyncRoleData, SpiderId, 3, x.Key, target.PlayerId));
|
|
}
|
|
|
|
public override void OnCheckPlayerPosition(PlayerControl pc)
|
|
{
|
|
if (pc.PlayerId == SpiderId) return;
|
|
|
|
Vector2 pos = pc.Pos();
|
|
float range = WebTrapRange.GetFloat();
|
|
int trappedDuration = TrappedDuration.GetInt();
|
|
long expireTime = Utils.TimeStamp + trappedDuration;
|
|
|
|
foreach ((Vector2 webPos, Dictionary<byte, long> trappedPlayers) in Webs)
|
|
{
|
|
if (!FastVector2.DistanceWithinRange(webPos, pos, range)) continue;
|
|
if (!trappedPlayers.TryAdd(pc.PlayerId, expireTime)) continue;
|
|
|
|
RPC.PlaySoundRPC(SpiderId, Sounds.TaskUpdateSound);
|
|
pc.RPCPlayCustomSound("FlashBang");
|
|
pc.MarkDirtySettings();
|
|
|
|
Utils.SendRPC(CustomRPC.SyncRoleData, SpiderId, 1, webPos, pc.PlayerId, expireTime);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override void OnFixedUpdate(PlayerControl pc)
|
|
{
|
|
if (Webs.Count <= 0) return;
|
|
|
|
long now = Utils.TimeStamp;
|
|
ToRemove.Clear();
|
|
|
|
foreach ((Vector2 pos, Dictionary<byte, long> trapped) in Webs)
|
|
{
|
|
if (trapped.Count > 0) NameDirty = true;
|
|
if (trapped.Values.Min() <= now)
|
|
{
|
|
ToRemove.Add(pos);
|
|
LateTask.New(() =>
|
|
{
|
|
trapped.Keys.ToValidPlayers().Do(x =>
|
|
{
|
|
RPC.PlaySoundRPC(x.PlayerId, Sounds.TaskComplete);
|
|
Main.AllPlayerSpeed[x.PlayerId] = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
|
|
x.MarkDirtySettings();
|
|
});
|
|
}, 0.2f, log: false);
|
|
}
|
|
}
|
|
|
|
if (ToRemove.Count > 0)
|
|
ToRemove.ForEach(x =>
|
|
{
|
|
Webs.Remove(x);
|
|
LocateArrow.Remove(pc.PlayerId, x);
|
|
Utils.SendRPC(CustomRPC.SyncRoleData, SpiderId, 2, x);
|
|
});
|
|
|
|
if (NameDirty && PerSecondUpdateScheduler.ShouldRunUpdate(pc.PlayerId))
|
|
{
|
|
NameDirty = false;
|
|
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
|
|
}
|
|
}
|
|
|
|
public override void OnReportDeadBody()
|
|
{
|
|
Webs.SetAllValues([]);
|
|
}
|
|
|
|
public static void OnAnyoneApplyGameOptions(IGameOptions opt, byte id)
|
|
{
|
|
foreach (Spider instance in Instances)
|
|
{
|
|
if (instance.Webs.Any(x => x.Value.ContainsKey(id)))
|
|
{
|
|
float vision = Main.DefaultCrewmateVision - LowerVisionBy.GetFloat();
|
|
opt.SetFloat(FloatOptionNames.CrewLightMod, vision);
|
|
opt.SetFloat(FloatOptionNames.ImpostorLightMod, vision);
|
|
Main.AllPlayerSpeed[id] = Main.MinSpeed;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ReceiveRPC(MessageReader reader)
|
|
{
|
|
switch (reader.ReadPackedInt32())
|
|
{
|
|
case 1:
|
|
Vector2 pos = reader.ReadVector2();
|
|
byte trappedId = reader.ReadByte();
|
|
long releaseTS = long.Parse(reader.ReadString());
|
|
Webs.TryAdd(pos, []);
|
|
Webs[pos][trappedId] = releaseTS;
|
|
break;
|
|
case 2:
|
|
Webs.Remove(reader.ReadVector2());
|
|
break;
|
|
case 3:
|
|
Vector2 webPos = reader.ReadVector2();
|
|
byte deadId = reader.ReadByte();
|
|
Webs.GetValueOrDefault(webPos, []).Remove(deadId);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
|
{
|
|
if (seer.PlayerId != SpiderId || seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || meeting) return string.Empty;
|
|
return Webs.Where(x => x.Value.Count > 0).Join('\n', x => $"{LocateArrow.GetArrow(seer, x.Key)} {x.Value.Count} ({Math.Max(0, x.Value.Values.Min() - Utils.TimeStamp)}s)");
|
|
}
|
|
}
|