mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-20 01:46:25 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
72c2acc2a8
25 files changed
+561
-142
No files matched your search
@@ -378,6 +378,7 @@ public enum CustomRoles
|
||||
Slenderman,
|
||||
SoulCollector,
|
||||
SoulHunter,
|
||||
Spider,
|
||||
Spirit,
|
||||
Spiritcaller,
|
||||
Sprayer,
|
||||
@@ -416,6 +417,7 @@ public enum CustomRoles
|
||||
Enchanter,
|
||||
Goddess,
|
||||
Illusionist,
|
||||
MoonDancer,
|
||||
Poache,
|
||||
PotionMaster,
|
||||
Reaper,
|
||||
|
||||
@@ -557,6 +557,7 @@ public class Main : BasePlugin
|
||||
{ CustomRoles.Thanos, "#F9D401" },
|
||||
{ CustomRoles.Berserker, "#50538F" },
|
||||
{ CustomRoles.SerialKiller, "#233fcc" },
|
||||
{ CustomRoles.Spider, "#f54251" },
|
||||
{ CustomRoles.SoulCollector, "#6021A0" },
|
||||
{ CustomRoles.Sharpshooter, "#5901D4" },
|
||||
{ CustomRoles.Explosivist, "#ff5900" },
|
||||
@@ -1062,6 +1063,7 @@ public enum CustomWinner
|
||||
Necromancer = CustomRoles.Necromancer,
|
||||
Wraith = CustomRoles.Wraith,
|
||||
SerialKiller = CustomRoles.SerialKiller,
|
||||
Spider = CustomRoles.Spider,
|
||||
SoulCollector = CustomRoles.SoulCollector,
|
||||
Berserker = CustomRoles.Berserker,
|
||||
Sharpshooter = CustomRoles.Sharpshooter,
|
||||
|
||||
@@ -518,6 +518,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Berserker => RoleTypes.Impostor,
|
||||
CustomRoles.SerialKiller => RoleTypes.Impostor,
|
||||
CustomRoles.SoulCollector => RoleTypes.Impostor,
|
||||
CustomRoles.Spider => UsePets ? RoleTypes.Impostor : RoleTypes.Shapeshifter,
|
||||
CustomRoles.Explosivist => UsePets ? RoleTypes.Impostor : RoleTypes.Shapeshifter,
|
||||
CustomRoles.Sharpshooter => UsePets ? RoleTypes.Impostor : RoleTypes.Shapeshifter,
|
||||
CustomRoles.Thanos => UsePets ? RoleTypes.Impostor : RoleTypes.Shapeshifter,
|
||||
@@ -605,6 +606,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Enchanter => RoleTypes.Impostor,
|
||||
CustomRoles.Siren => RoleTypes.Phantom,
|
||||
CustomRoles.Wyrd => RoleTypes.Shapeshifter,
|
||||
CustomRoles.MoonDancer => RoleTypes.Phantom,
|
||||
|
||||
_ => RoleTypes.GuardianAngel
|
||||
};
|
||||
@@ -652,6 +654,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Parasite or
|
||||
CustomRoles.Berserker or
|
||||
CustomRoles.SerialKiller or
|
||||
CustomRoles.Spider or
|
||||
CustomRoles.SoulCollector or
|
||||
CustomRoles.Sharpshooter or
|
||||
CustomRoles.Explosivist or
|
||||
@@ -1093,6 +1096,7 @@ internal static class CustomRolesHelper
|
||||
or CustomRoles.RouleteGrandeur
|
||||
or CustomRoles.Enderman
|
||||
or CustomRoles.Explosivist
|
||||
or CustomRoles.Spider
|
||||
or CustomRoles.Hookshot
|
||||
or CustomRoles.Mycologist
|
||||
or CustomRoles.Magician
|
||||
@@ -1116,7 +1120,7 @@ internal static class CustomRolesHelper
|
||||
|
||||
public static bool CheckAddonConflict(CustomRoles role, PlayerControl pc)
|
||||
{
|
||||
return role.IsAdditionRole() && (!Main.NeverSpawnTogetherCombos.TryGetValue(OptionItem.CurrentPreset, out Dictionary<CustomRoles, List<CustomRoles>> neverList) || !neverList.TryGetValue(pc.GetCustomRole(), out List<CustomRoles> bannedAddonList) || !bannedAddonList.Contains(role)) && pc.GetCustomRole() is not CustomRoles.GuardianAngelEHR and not CustomRoles.God && !pc.Is(CustomRoles.Madmate) && !pc.Is(CustomRoles.GM) && role is not CustomRoles.Lovers && !pc.Is(CustomRoles.LazyGuy) && (!pc.HasSubRole() || pc.GetCustomSubRoles().Count < Options.NoLimitAddonsNumMax.GetInt()) && (!Options.AddonCanBeSettings.TryGetValue(role, out (OptionItem Imp, OptionItem Neutral, OptionItem Crew, OptionItem Coven) o) || ((o.Imp.GetBool() || !pc.GetCustomRole().IsImpostor()) && (o.Neutral.GetBool() || !pc.GetCustomRole().IsNeutral()) && (o.Crew.GetBool() || !pc.IsCrewmate()) && (o.Coven.GetBool() || !pc.Is(Team.Coven)))) && (!role.IsImpOnlyAddon() || (pc.IsImpostor() && !pc.Is(CustomRoles.DoubleAgent)) || (pc.Is(CustomRoles.Traitor) && Traitor.CanGetImpostorOnlyAddons.GetBool())) && role switch
|
||||
return role.IsAdditionRole() && !(role.IsGhostRole() && pc.IsAlive()) && (!Main.NeverSpawnTogetherCombos.TryGetValue(OptionItem.CurrentPreset, out Dictionary<CustomRoles, List<CustomRoles>> neverList) || !neverList.TryGetValue(pc.GetCustomRole(), out List<CustomRoles> bannedAddonList) || !bannedAddonList.Contains(role)) && pc.GetCustomRole() is not CustomRoles.GuardianAngelEHR and not CustomRoles.God && !pc.Is(CustomRoles.Madmate) && !pc.Is(CustomRoles.GM) && role is not CustomRoles.Lovers && !pc.Is(CustomRoles.LazyGuy) && (!pc.HasSubRole() || pc.GetCustomSubRoles().Count < Options.NoLimitAddonsNumMax.GetInt()) && (!Options.AddonCanBeSettings.TryGetValue(role, out (OptionItem Imp, OptionItem Neutral, OptionItem Crew, OptionItem Coven) o) || ((o.Imp.GetBool() || !pc.GetCustomRole().IsImpostor()) && (o.Neutral.GetBool() || !pc.GetCustomRole().IsNeutral()) && (o.Crew.GetBool() || !pc.IsCrewmate()) && (o.Coven.GetBool() || !pc.Is(Team.Coven)))) && (!role.IsImpOnlyAddon() || (pc.IsImpostor() && !pc.Is(CustomRoles.DoubleAgent)) || (pc.Is(CustomRoles.Traitor) && Traitor.CanGetImpostorOnlyAddons.GetBool())) && role switch
|
||||
{
|
||||
CustomRoles.Blind when pc.Is(CustomRoles.Sensor) => false,
|
||||
CustomRoles.Composter when float.IsNaN(pc.GetAbilityUseLimit()) => false,
|
||||
@@ -1285,7 +1289,8 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Timelord or
|
||||
CustomRoles.Enchanter or
|
||||
CustomRoles.Siren or
|
||||
CustomRoles.Wyrd;
|
||||
CustomRoles.Wyrd or
|
||||
CustomRoles.MoonDancer;
|
||||
}
|
||||
|
||||
public static bool IncompatibleWithVenom(this CustomRoles role)
|
||||
@@ -1965,6 +1970,7 @@ public enum CountTypes
|
||||
HexMaster,
|
||||
Wraith,
|
||||
SerialKiller,
|
||||
Spider,
|
||||
SoulCollector,
|
||||
Berserker,
|
||||
Sharpshooter,
|
||||
|
||||
@@ -891,10 +891,13 @@ internal static class ExtendedPlayerControl
|
||||
|
||||
PlayerState state = Main.PlayerStates[pc.PlayerId];
|
||||
|
||||
if (realKiller != null && state.Role is SchrodingersCat cat)
|
||||
switch (state.Role)
|
||||
{
|
||||
cat.OnCheckMurderAsTarget(realKiller, pc);
|
||||
return;
|
||||
case SchrodingersCat cat when realKiller != null:
|
||||
cat.OnCheckMurderAsTarget(realKiller, pc);
|
||||
return;
|
||||
case Veteran when Veteran.VeteranInProtect.ContainsKey(pc.PlayerId):
|
||||
return;
|
||||
}
|
||||
|
||||
state.deathReason = deathReason;
|
||||
|
||||
@@ -478,6 +478,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
|
||||
Curser.OnAnyoneApplyGameOptions(opt, player.PlayerId);
|
||||
Auditor.OnAnyoneApplyGameOptions(opt, player.PlayerId);
|
||||
Clerk.OnAnyoneApplyGameOptions(opt, player.PlayerId);
|
||||
Spider.OnAnyoneApplyGameOptions(opt, player.PlayerId);
|
||||
|
||||
if (Sprayer.LowerVisionList.Contains(player.PlayerId))
|
||||
{
|
||||
|
||||
@@ -74,6 +74,7 @@ public class PlayerState(byte playerId)
|
||||
Deafened,
|
||||
Patrolled,
|
||||
Misguess,
|
||||
LossOfBlood,
|
||||
|
||||
// Natural Disasters
|
||||
Meteor,
|
||||
@@ -409,9 +410,9 @@ public class PlayerState(byte playerId)
|
||||
return IsDead && RealKiller.TimeStamp != DateTime.MinValue ? RealKiller.ID : byte.MaxValue;
|
||||
}
|
||||
|
||||
public int GetKillCount(bool excludeSelfKill = false)
|
||||
public int GetKillCount()
|
||||
{
|
||||
return Main.PlayerStates.Values.Count(state => !(excludeSelfKill && state.PlayerId == PlayerId) && state.GetRealKiller() == PlayerId);
|
||||
return Main.PlayerStates.Values.Count(state => state.PlayerId != PlayerId && state.GetRealKiller() == PlayerId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,8 @@ internal static class ExitGamePatch
|
||||
{
|
||||
public static void Prefix()
|
||||
{
|
||||
Logger.Msg("Exiting game", "ExitGamePatch.Prefix");
|
||||
|
||||
Main.RealOptionsData?.Restore(GameOptionsManager.Instance.CurrentGameOptions);
|
||||
|
||||
if (SetUpRoleTextPatch.IsInIntro)
|
||||
|
||||
@@ -349,9 +349,6 @@ public static class Options
|
||||
public static OptionItem ArsonistMinPlayersToIgnite;
|
||||
public static OptionItem ArsonistMaxPlayersToIgnite;
|
||||
public static OptionItem LegacyNemesis;
|
||||
public static OptionItem VeteranSkillCooldown;
|
||||
public static OptionItem VeteranSkillDuration;
|
||||
public static OptionItem VeteranSkillMaxOfUsage;
|
||||
public static OptionItem BodyguardProtectRadius;
|
||||
public static OptionItem BodyguardKillsKiller;
|
||||
public static OptionItem WitnessCD;
|
||||
@@ -442,6 +439,8 @@ public static class Options
|
||||
public static OptionItem AutoPlayAgain;
|
||||
public static OptionItem AutoPlayAgainCountdown;
|
||||
public static OptionItem AutoStartTimer;
|
||||
|
||||
public static OptionItem EnterKeyToStartGame;
|
||||
|
||||
public static OptionItem SaboteurCD;
|
||||
public static OptionItem SaboteurCDAfterMeetings;
|
||||
@@ -1696,6 +1695,8 @@ public static class Options
|
||||
AutoDraftStartCommandCooldown = new IntegerOptionItem(19427, "AutoDraftStartCommandCooldown", new(10, 600, 5), 150, TabGroup.SystemSettings)
|
||||
.SetParent(AutoDraftStartCommandAfterJoin)
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
|
||||
EnterKeyToStartGame = new BooleanOptionItem(19432, "EnterKeyToStartGame", false, TabGroup.SystemSettings);
|
||||
|
||||
LowLoadMode = new BooleanOptionItem(19317, "LowLoadMode", true, TabGroup.SystemSettings)
|
||||
.SetHeader(true)
|
||||
@@ -3472,7 +3473,6 @@ public static class Options
|
||||
// ReSharper disable NotAccessedField.Global
|
||||
|
||||
// Ability Use Gain With Each Task Completed
|
||||
public static OptionItem VeteranAbilityUseGainWithEachTaskCompleted;
|
||||
public static OptionItem GrenadierAbilityUseGainWithEachTaskCompleted;
|
||||
public static OptionItem LighterAbilityUseGainWithEachTaskCompleted;
|
||||
public static OptionItem SecurityGuardAbilityUseGainWithEachTaskCompleted;
|
||||
@@ -3483,7 +3483,6 @@ public static class Options
|
||||
public static OptionItem LighterAbilityChargesWhenFinishedTasks;
|
||||
public static OptionItem SecurityGuardAbilityChargesWhenFinishedTasks;
|
||||
public static OptionItem PacifistAbilityChargesWhenFinishedTasks;
|
||||
public static OptionItem VeteranAbilityChargesWhenFinishedTasks;
|
||||
|
||||
// ReSharper restore NotAccessedField.Global
|
||||
}
|
||||
+86
-64
@@ -845,6 +845,7 @@ public static class Utils
|
||||
case CustomRoles.Eclipse:
|
||||
case CustomRoles.Pyromaniac:
|
||||
case CustomRoles.SerialKiller:
|
||||
case CustomRoles.Spider:
|
||||
case CustomRoles.SoulCollector:
|
||||
case CustomRoles.Berserker:
|
||||
case CustomRoles.Sharpshooter:
|
||||
@@ -3417,7 +3418,7 @@ public static class Utils
|
||||
CustomRoles.Grenadier => Options.GrenadierSkillCooldown.GetInt() + (includeDuration ? Options.GrenadierSkillDuration.GetInt() : 0),
|
||||
CustomRoles.Lighter => Options.LighterSkillCooldown.GetInt() + (includeDuration ? Options.LighterSkillDuration.GetInt() : 0),
|
||||
CustomRoles.SecurityGuard => Options.SecurityGuardSkillCooldown.GetInt() + (includeDuration ? Options.SecurityGuardSkillDuration.GetInt() : 0),
|
||||
CustomRoles.Veteran => Options.VeteranSkillCooldown.GetInt() + (includeDuration ? Options.VeteranSkillDuration.GetInt() : 0),
|
||||
CustomRoles.Veteran => Veteran.VeteranSkillCooldown.GetInt() + (includeDuration ? Veteran.VeteranSkillDuration.GetInt() : 0),
|
||||
CustomRoles.Rhapsode => Rhapsode.AbilityCooldown.GetInt() + (includeDuration ? Rhapsode.AbilityDuration.GetInt() : 0),
|
||||
CustomRoles.Whisperer => Whisperer.Cooldown.GetInt() + (includeDuration ? Whisperer.Duration.GetInt() : 0),
|
||||
CustomRoles.Astral => Astral.AbilityCooldown.GetInt() + (includeDuration ? Astral.AbilityDuration.GetInt() : 0),
|
||||
@@ -3453,6 +3454,7 @@ public static class Utils
|
||||
CustomRoles.Abyssbringer => Abyssbringer.BlackHolePlaceCooldown.GetInt(),
|
||||
CustomRoles.Venerer => Venerer.AbilityCooldown.GetInt(),
|
||||
CustomRoles.Wiper => Wiper.AbilityCooldown.GetInt(),
|
||||
CustomRoles.Spider => Spider.AbilityCooldown.GetInt(),
|
||||
CustomRoles.Warlock => Warlock.IsCursed ? -1 : Warlock.ShapeshiftCooldown.GetInt(),
|
||||
CustomRoles.Stealth => Stealth.AbilityCooldown.GetInt() + (includeDuration ? Stealth.OptionDarkenDuration.GetInt() : 0),
|
||||
CustomRoles.Stasis => Stasis.AbilityCooldown.GetInt() + (includeDuration ? Stasis.AbilityDuration.GetInt() : 0),
|
||||
@@ -3495,79 +3497,95 @@ public static class Utils
|
||||
public static void AfterMeetingTasks()
|
||||
{
|
||||
LateTask.New(() => GameEndChecker.ShouldNotCheck = false, 0.1f, "Enable GameEndChecker");
|
||||
|
||||
if (Lovers.PrivateChat.GetBool() && Main.LoversPlayers.TrueForAll(x => x.IsAlive()))
|
||||
Main.LoversPlayers.ForEach(x => x.SetChatVisible(true));
|
||||
|
||||
AFKDetector.NumAFK = 0;
|
||||
AFKDetector.PlayerData.Clear();
|
||||
try
|
||||
{
|
||||
if (Lovers.PrivateChat.GetBool() && Main.LoversPlayers.TrueForAll(x => x.IsAlive()))
|
||||
Main.LoversPlayers.ForEach(x => x.SetChatVisible(true));
|
||||
}
|
||||
catch (System.Exception e) { ThrowException(e); }
|
||||
|
||||
Camouflage.CheckCamouflage();
|
||||
try
|
||||
{
|
||||
AFKDetector.NumAFK = 0;
|
||||
AFKDetector.PlayerData.Clear();
|
||||
|
||||
CopyCat.ResetRoles();
|
||||
Imitator.SetRoles();
|
||||
Camouflage.CheckCamouflage();
|
||||
|
||||
CopyCat.ResetRoles();
|
||||
Imitator.SetRoles();
|
||||
}
|
||||
catch (System.Exception e) { ThrowException(e); }
|
||||
|
||||
foreach (PlayerControl pc in Main.AllPlayerControls)
|
||||
{
|
||||
if (pc.IsAlive())
|
||||
try
|
||||
{
|
||||
if (pc.Is(CustomRoles.Bloodlust))
|
||||
if (pc.IsAlive())
|
||||
{
|
||||
pc.RpcSetRoleDesync(RoleTypes.Impostor, pc.OwnerId);
|
||||
LateTask.New(() => pc.SetKillCooldown(), 0.2f, log: false);
|
||||
}
|
||||
|
||||
if (pc.Is(CustomRoles.Truant))
|
||||
{
|
||||
float beforeSpeed = Main.AllPlayerSpeed[pc.PlayerId];
|
||||
Main.AllPlayerSpeed[pc.PlayerId] = Main.MinSpeed;
|
||||
pc.MarkDirtySettings();
|
||||
|
||||
LateTask.New(() =>
|
||||
try
|
||||
{
|
||||
Main.AllPlayerSpeed[pc.PlayerId] = beforeSpeed;
|
||||
pc.MarkDirtySettings();
|
||||
}, Options.TruantWaitingTime.GetFloat(), $"Truant Waiting: {pc.GetNameWithRole()}");
|
||||
}
|
||||
if (pc.Is(CustomRoles.Bloodlust))
|
||||
{
|
||||
pc.RpcSetRoleDesync(RoleTypes.Impostor, pc.OwnerId);
|
||||
LateTask.New(() => pc.SetKillCooldown(), 0.2f, log: false);
|
||||
}
|
||||
|
||||
if (Options.UsePets.GetBool())
|
||||
{
|
||||
pc.AddAbilityCD(false);
|
||||
if (pc.Is(CustomRoles.Truant))
|
||||
{
|
||||
float beforeSpeed = Main.AllPlayerSpeed[pc.PlayerId];
|
||||
Main.AllPlayerSpeed[pc.PlayerId] = Main.MinSpeed;
|
||||
pc.MarkDirtySettings();
|
||||
|
||||
LateTask.New(() =>
|
||||
LateTask.New(() =>
|
||||
{
|
||||
Main.AllPlayerSpeed[pc.PlayerId] = beforeSpeed;
|
||||
pc.MarkDirtySettings();
|
||||
}, Options.TruantWaitingTime.GetFloat(), $"Truant Waiting: {pc.GetNameWithRole()}");
|
||||
}
|
||||
}
|
||||
catch (Exception e) { ThrowException(e); }
|
||||
|
||||
if (Options.UsePets.GetBool())
|
||||
{
|
||||
if (GameStates.IsEnded) return;
|
||||
string petId = PetsHelper.GetPetId();
|
||||
PetsHelper.SetPet(pc, petId);
|
||||
pc.Data.DefaultOutfit.PetSequenceId += 10;
|
||||
pc.RpcSetPet(petId);
|
||||
}, 3f, "No Pet Reassign");
|
||||
pc.AddAbilityCD(false);
|
||||
|
||||
LateTask.New(() =>
|
||||
{
|
||||
if (GameStates.IsEnded) return;
|
||||
string petId = PetsHelper.GetPetId();
|
||||
PetsHelper.SetPet(pc, petId);
|
||||
pc.Data.DefaultOutfit.PetSequenceId += 10;
|
||||
pc.RpcSetPet(petId);
|
||||
}, 3f, "No Pet Reassign");
|
||||
}
|
||||
|
||||
AFKDetector.RecordPosition(pc);
|
||||
|
||||
if (Camouflage.IsCamouflage)
|
||||
Camouflage.RpcSetSkin(pc);
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskState taskState = pc.GetTaskState();
|
||||
|
||||
if (pc.IsCrewmate() && !taskState.IsTaskFinished && taskState.HasTasks)
|
||||
pc.Notify(GetString("DoYourTasksPlease"), 8f);
|
||||
|
||||
GhostRolesManager.NotifyAboutGhostRole(pc);
|
||||
}
|
||||
|
||||
AFKDetector.RecordPosition(pc);
|
||||
Main.PlayerStates[pc.PlayerId].Role.AfterMeetingTasks();
|
||||
|
||||
if (Camouflage.IsCamouflage)
|
||||
Camouflage.RpcSetSkin(pc);
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskState taskState = pc.GetTaskState();
|
||||
|
||||
if (pc.IsCrewmate() && !taskState.IsTaskFinished && taskState.HasTasks)
|
||||
pc.Notify(GetString("DoYourTasksPlease"), 8f);
|
||||
|
||||
GhostRolesManager.NotifyAboutGhostRole(pc);
|
||||
}
|
||||
|
||||
Main.PlayerStates[pc.PlayerId].Role.AfterMeetingTasks();
|
||||
|
||||
if (pc.Is(CustomRoles.Phantasm) || pc.Is(CustomRoles.Haunter))
|
||||
pc.RpcResetAbilityCooldown();
|
||||
if (pc.Is(CustomRoles.Phantasm) || pc.Is(CustomRoles.Haunter))
|
||||
pc.RpcResetAbilityCooldown();
|
||||
|
||||
if (pc.Is(CustomRoles.TaskMaster))
|
||||
TaskMaster.AfterMeetingTasks(pc);
|
||||
if (pc.Is(CustomRoles.TaskMaster))
|
||||
TaskMaster.AfterMeetingTasks(pc);
|
||||
|
||||
Main.CheckShapeshift[pc.PlayerId] = false;
|
||||
Main.CheckShapeshift[pc.PlayerId] = false;
|
||||
}
|
||||
catch (System.Exception e) { ThrowException(e); }
|
||||
}
|
||||
|
||||
LateTask.New(() => Main.ProcessShapeshifts = true, 1f, log: false);
|
||||
@@ -3575,22 +3593,26 @@ public static class Utils
|
||||
if (Options.DiseasedCDReset.GetBool())
|
||||
{
|
||||
Main.KilledDiseased.SetAllValues(0);
|
||||
Main.KilledDiseased.Keys.ToValidPlayers().Do(x => x?.ResetKillCooldown());
|
||||
Main.KilledDiseased.Keys.ToValidPlayers().Do(x => x.ResetKillCooldown());
|
||||
Main.KilledDiseased.Clear();
|
||||
}
|
||||
|
||||
if (Options.AntidoteCDReset.GetBool())
|
||||
{
|
||||
Main.KilledAntidote.SetAllValues(0);
|
||||
Main.KilledAntidote.Keys.ToValidPlayers().Do(x => x?.ResetKillCooldown());
|
||||
Main.KilledAntidote.Keys.ToValidPlayers().Do(x => x.ResetKillCooldown());
|
||||
Main.KilledAntidote.Clear();
|
||||
}
|
||||
|
||||
Damocles.AfterMeetingTasks();
|
||||
Stressed.AfterMeetingTasks();
|
||||
Circumvent.AfterMeetingTasks();
|
||||
Deadlined.AfterMeetingTasks();
|
||||
Tired.Reset();
|
||||
try
|
||||
{
|
||||
Damocles.AfterMeetingTasks();
|
||||
Stressed.AfterMeetingTasks();
|
||||
Circumvent.AfterMeetingTasks();
|
||||
Deadlined.AfterMeetingTasks();
|
||||
Tired.Reset();
|
||||
}
|
||||
catch (System.Exception e) { ThrowException(e); }
|
||||
|
||||
if (Options.AirshipVariableElectrical.GetBool())
|
||||
AirshipElectricalDoors.Initialize();
|
||||
|
||||
@@ -5,28 +5,26 @@ namespace EHR;
|
||||
|
||||
public static class AirshipElectricalDoors
|
||||
{
|
||||
private static ElectricalDoors Instance
|
||||
=> ShipStatus.Instance.Systems[SystemTypes.Decontamination].CastFast<ElectricalDoors>();
|
||||
private static ElectricalDoors Instance => ShipStatus.Instance.Systems[SystemTypes.Decontamination].CastFast<ElectricalDoors>();
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (Main.NormalOptions.MapId != 4) return;
|
||||
|
||||
Instance.Initialize();
|
||||
}
|
||||
|
||||
public static byte[] GetClosedDoors()
|
||||
public static IEnumerable<byte> GetClosedDoors()
|
||||
{
|
||||
List<byte> DoorsArray = [];
|
||||
if (Instance.Doors == null || Instance.Doors.Length == 0) return [.. DoorsArray];
|
||||
List<byte> doorsArray = [];
|
||||
if (Instance.Doors == null || Instance.Doors.Length == 0) return doorsArray;
|
||||
|
||||
for (byte i = 0; i < Instance.Doors.Count; i++)
|
||||
{
|
||||
StaticDoor door = Instance.Doors[i];
|
||||
if (door != null && !door.IsOpen) DoorsArray.Add(i);
|
||||
if (door != null && !door.IsOpen) doorsArray.Add(i);
|
||||
}
|
||||
|
||||
return DoorsArray?.ToArray();
|
||||
return doorsArray;
|
||||
}
|
||||
// 0: BottomRightHort
|
||||
// 1: BottomHort
|
||||
@@ -48,22 +46,6 @@ internal static class ElectricalDoorsInitializePatch
|
||||
public static void Postfix( /*ElectricalDoors __instance*/)
|
||||
{
|
||||
if (!GameStates.IsInGame) return;
|
||||
|
||||
var closedoors = string.Empty;
|
||||
var isFirst = true;
|
||||
byte[] array = AirshipElectricalDoors.GetClosedDoors();
|
||||
|
||||
foreach (byte num in array)
|
||||
{
|
||||
if (isFirst)
|
||||
{
|
||||
isFirst = false;
|
||||
closedoors += num.ToString();
|
||||
}
|
||||
else
|
||||
closedoors += $", {num}";
|
||||
}
|
||||
|
||||
Logger.Info($"Closed Doors: {closedoors}", "ElectricalDoors Initialize");
|
||||
Logger.Info($"Closed Doors: {string.Join(", ", AirshipElectricalDoors.GetClosedDoors())}", "ElectricalDoors Initialize");
|
||||
}
|
||||
}
|
||||
+22
-15
@@ -148,7 +148,7 @@ internal static class ChatCommands
|
||||
new(["gamestate", "gstate", "gs", "kcount", "kc", "кубийц", "гс", "статигры", "对局状态", "estadojogo", "status"], "", GetString("CommandDescription.KCount"), Command.UsageLevels.Everyone, Command.UsageTimes.InGame, KCountCommand, true, false),
|
||||
new(["addmod", "добмодера", "指定协管", "moderador-add"], "{id}", GetString("CommandDescription.AddMod"), Command.UsageLevels.Host, Command.UsageTimes.Always, AddModCommand, true, false, [GetString("CommandArgs.AddMod.Id")]),
|
||||
new(["deletemod", "убрмодера", "удмодера", "убратьмодера", "удалитьмодера", "移除协管", "moderador-remover"], "{id}", GetString("CommandDescription.DeleteMod"), Command.UsageLevels.Host, Command.UsageTimes.Always, DeleteModCommand, true, false, [GetString("CommandArgs.DeleteMod.Id")]),
|
||||
new(["combo", "комбо", "设置不会同时出现的职业", "combinação", "combinar"], "{mode} {role} {addon} [all]", GetString("CommandDescription.Combo"), Command.UsageLevels.Host, Command.UsageTimes.Always, ComboCommand, true, false, [GetString("CommandArgs.Combo.Mode"), GetString("CommandArgs.Combo.Role"), GetString("CommandArgs.Combo.Addon"), GetString("CommandArgs.Combo.All")]),
|
||||
new(["combo", "комбо", "设置不会同时出现的职业", "combinação", "combinar"], "{mode} {role} {addon} [all]", GetString("CommandDescription.Combo"), Command.UsageLevels.Everyone, Command.UsageTimes.Always, ComboCommand, true, false, [GetString("CommandArgs.Combo.Mode"), GetString("CommandArgs.Combo.Role"), GetString("CommandArgs.Combo.Addon"), GetString("CommandArgs.Combo.All")]),
|
||||
new(["eff", "effect", "эффект", "效果", "efeito"], "{effect}", GetString("CommandDescription.Effect"), Command.UsageLevels.Host, Command.UsageTimes.InGame, EffectCommand, true, false, [GetString("CommandArgs.Effect.Effect")]),
|
||||
new(["afkexempt", "освафк", "афкосв", "挂机检测器不会检测", "afk-isentar"], "{id}", GetString("CommandDescription.AFKExempt"), Command.UsageLevels.HostOrAdmin, Command.UsageTimes.Always, AFKExemptCommand, true, false, [GetString("CommandArgs.AFKExempt.Id")]),
|
||||
new(["m", "myrole", "м", "мояроль", "我的职业", "minhafunção"], "", GetString("CommandDescription.MyRole"), Command.UsageLevels.Everyone, Command.UsageTimes.InGame, MyRoleCommand, true, false),
|
||||
@@ -156,7 +156,7 @@ internal static class ChatCommands
|
||||
new(["tpin", "тпин", "传送进"], "", GetString("CommandDescription.TPIn"), Command.UsageLevels.Everyone, Command.UsageTimes.InLobby, TPInCommand, true, false),
|
||||
new(["t", "template", "т", "темплейт", "模板"], "{tag}", GetString("CommandDescription.Template"), Command.UsageLevels.Everyone, Command.UsageTimes.Always, TemplateCommand, true, false, [GetString("CommandArgs.Template.Tag")]),
|
||||
new(["mw", "messagewait", "мв", "медленныйрежим", "消息冷却", "espera-mensagens"], "{duration}", GetString("CommandDescription.MessageWait"), Command.UsageLevels.Host, Command.UsageTimes.Always, MessageWaitCommand, true, false, [GetString("CommandArgs.MessageWait.Duration")]),
|
||||
new(["death", "d", "д", "смерть", "死亡原因", "abate"], "", GetString("CommandDescription.Death"), Command.UsageLevels.Everyone, Command.UsageTimes.AfterDeath, DeathCommand, true, false),
|
||||
new(["death", "d", "д", "смерть", "死亡原因", "abate"], "[id]", GetString("CommandDescription.Death"), Command.UsageLevels.Everyone, Command.UsageTimes.AfterDeath, DeathCommand, true, false, [GetString("CommandArgs.Death.Id")]),
|
||||
new(["say", "s", "сказать", "с", "说", "falar", "dizer"], "{message}", GetString("CommandDescription.Say"), Command.UsageLevels.HostOrModerator, Command.UsageTimes.Always, SayCommand, true, false, [GetString("CommandArgs.Say.Message")]),
|
||||
new(["vote", "голос", "投票给", "votar"], "{id}", GetString("CommandDescription.Vote"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, VoteCommand, true, true, [GetString("CommandArgs.Vote.Id")]),
|
||||
new(["ask", "спр", "спросить", "数学家提问", "perguntar"], "{number1} {number2}", GetString("CommandDescription.Ask"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, AskCommand, true, true, [GetString("CommandArgs.Ask.Number1"), GetString("CommandArgs.Ask.Number2")]),
|
||||
@@ -212,7 +212,7 @@ internal static class ChatCommands
|
||||
new(["deletetag", "удалитьтег", "убратьтег", "удтег", "убртег", "删除标签"], "{id}", GetString("CommandDescription.DeleteTag"), Command.UsageLevels.Host, Command.UsageTimes.InLobby, DeleteTagCommand, true, false, [GetString("CommandArgs.DeleteTag.Id")]),
|
||||
new(["daybreak", "db", "дейбрейк", "破晓"], "", GetString("CommandDescription.DayBreak"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, DayBreakCommand, true, true),
|
||||
new(["fix", "blackscreenfix", "fixblackscreen", "фикс", "исправить", "修复"], "{id}", GetString("CommandDescription.Fix"), Command.UsageLevels.HostOrModerator, Command.UsageTimes.InGame, FixCommand, true, false, [GetString("CommandArgs.Fix.Id")]),
|
||||
new(["xor", "异或命令"], "{role} {role}", GetString("CommandDescription.XOR"), Command.UsageLevels.Host, Command.UsageTimes.InLobby, XORCommand, true, false, [GetString("CommandArgs.XOR.Role"), GetString("CommandArgs.XOR.Role")]),
|
||||
new(["xor", "异或命令"], "{role} {role}", GetString("CommandDescription.XOR"), Command.UsageLevels.Everyone, Command.UsageTimes.Always, XORCommand, true, false, [GetString("CommandArgs.XOR.Role"), GetString("CommandArgs.XOR.Role")]),
|
||||
new(["ci", "chemistinfo", "химик", "化学家"], "", GetString("CommandDescription.ChemistInfo"), Command.UsageLevels.Everyone, Command.UsageTimes.Always, ChemistInfoCommand, true, false),
|
||||
new(["forge"], "{id} {role}", GetString("CommandDescription.Forge"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, ForgeCommand, true, true, [GetString("CommandArgs.Forge.Id"), GetString("CommandArgs.Forge.Role")]),
|
||||
new(["choose", "pick", "выбрать", "选择", "escolher"], "{role}", GetString("CommandDescription.Choose"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, ChooseCommand, true, true, [GetString("CommandArgs.Choose.Role")]),
|
||||
@@ -852,7 +852,17 @@ internal static class ChatCommands
|
||||
|
||||
private static void XORCommand(PlayerControl player, string text, string[] args)
|
||||
{
|
||||
if (args.Length < 3 || !GetRoleByName(args[1], out CustomRoles role1) || !GetRoleByName(args[2], out CustomRoles role2)) return;
|
||||
if (!AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
RequestCommandProcessingFromHost(nameof(XORCommand), text);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player.IsHost() || args.Length < 3 || !GetRoleByName(args[1], out CustomRoles role1) || !GetRoleByName(args[2], out CustomRoles role2))
|
||||
{
|
||||
Utils.SendMessage(string.Join('\n', Main.XORRoles.ConvertAll(x => $"{x.Item1.ToColoredString()} ⊕ {x.Item2.ToColoredString()}")), player.PlayerId, GetString("XORListTitle"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Main.XORRoles.Remove((role1, role2)) || Main.XORRoles.Remove((role2, role1)))
|
||||
{
|
||||
@@ -2381,11 +2391,14 @@ internal static class ChatCommands
|
||||
if (!GameStates.IsInGame) return;
|
||||
if (Main.DiedThisRound.Contains(player.PlayerId) && Utils.IsRevivingRoleAlive()) return;
|
||||
|
||||
PlayerControl killer = player.GetRealKiller();
|
||||
PlayerControl target = args.Length < 2 || !byte.TryParse(args[1], out byte targetId) ? player : targetId.GetPlayer();
|
||||
if (target == null) return;
|
||||
|
||||
PlayerControl killer = target.GetRealKiller();
|
||||
|
||||
if (killer == null)
|
||||
{
|
||||
Utils.SendMessage("\n", player.PlayerId, string.Format(GetString("DeathCommandFail"), GetString($"DeathReason.{Main.PlayerStates[player.PlayerId].deathReason}")), sendOption: SendOption.None);
|
||||
Utils.SendMessage("\n", player.PlayerId, string.Format(GetString("DeathCommandFail"), GetString($"DeathReason.{Main.PlayerStates[target.PlayerId].deathReason}")), sendOption: SendOption.None);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2547,7 +2560,7 @@ internal static class ChatCommands
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Length < 4)
|
||||
if (!player.IsHost() || args.Length < 4)
|
||||
{
|
||||
if (Main.AlwaysSpawnTogetherCombos.Count == 0 && Main.NeverSpawnTogetherCombos.Count == 0) return;
|
||||
|
||||
@@ -2934,10 +2947,10 @@ internal static class ChatCommands
|
||||
|
||||
if (args.Length < 2) return;
|
||||
|
||||
string name = string.Join(' ', args[1..]);
|
||||
string name = Regex.Replace(string.Join(' ', args[1..]), "<size=[^>]*>", string.Empty).Trim();
|
||||
|
||||
if (name.RemoveHtmlTags().Length is > 15 or < 1)
|
||||
Utils.SendMessage(GetString("Message.AllowNameLength"), player.PlayerId);
|
||||
Utils.SendMessage(GetString("Message.AllowNameLength"), player.PlayerId, sendOption: SendOption.None);
|
||||
else
|
||||
{
|
||||
if (player.AmOwner)
|
||||
@@ -2956,12 +2969,6 @@ internal static class ChatCommands
|
||||
return;
|
||||
}
|
||||
|
||||
if (name.Length is > 50 or < 1)
|
||||
{
|
||||
Utils.SendMessage(GetString("Message.AllowNameLength"), player.PlayerId, sendOption: SendOption.None);
|
||||
return;
|
||||
}
|
||||
|
||||
Main.AllPlayerNames[player.PlayerId] = name;
|
||||
player.RpcSetName(name);
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ internal static class ControllerManagerUpdatePatch
|
||||
Logger.SendInGame(GetString("CancelStartCountDown"));
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Return) && GameStates.IsLobby && GameStartManager.InstanceExists && GameStartManager.Instance.startState == GameStartManager.StartingStates.NotStarting && !HudManager.Instance.Chat.IsOpenOrOpening && !OnGameJoinedPatch.JoiningGame && GameSettingMenu.Instance == null)
|
||||
if (Input.GetKeyDown(KeyCode.Return) && GameStates.IsLobby && GameStartManager.InstanceExists && Options.EnterKeyToStartGame.GetBool() && GameStartManager.Instance.startState == GameStartManager.StartingStates.NotStarting && !HudManager.Instance.Chat.IsOpenOrOpening && !OnGameJoinedPatch.JoiningGame && GameSettingMenu.Instance == null)
|
||||
{
|
||||
Logger.Info("ENTER pressed: Starting game by host", "KeyCommand");
|
||||
GameStartManager.Instance.BeginGame();
|
||||
|
||||
@@ -22,7 +22,7 @@ internal static class CreateOptionsPickerPatch
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_SelectMap([HarmonyArgument(0)] ref int mapId)
|
||||
{
|
||||
if (mapId > 5) mapId = 0;
|
||||
if (mapId is > 5 or 3) mapId = 0;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GameOptionsMapPicker), nameof(GameOptionsMapPicker.SetupMapButtons))]
|
||||
|
||||
@@ -295,6 +295,9 @@ internal static class CheckMurderPatch
|
||||
|
||||
if (Pursuer.OnClientMurder(killer)) return false;
|
||||
|
||||
if (Main.PlayerStates[target.PlayerId].Role is Veteran veteran && Veteran.VeteranAlertActivatesOnNonKillingInteractions.GetBool() && !veteran.OnCheckMurderAsTarget(killer, target))
|
||||
return false;
|
||||
|
||||
Seamstress.OnAnyoneCheckMurder(killer, target);
|
||||
|
||||
if (killer.PlayerId != target.PlayerId)
|
||||
@@ -878,18 +881,15 @@ internal static class ShapeshiftPatch
|
||||
PlagueBearer.CheckAndSpreadInfection(target, shapeshifter);
|
||||
}
|
||||
|
||||
var isSSneeded = true;
|
||||
if (Main.PlayerStates[target.PlayerId].Role is Veteran veteran && Veteran.VeteranAlertActivatesOnNonKillingInteractions.GetBool())
|
||||
veteran.OnCheckMurderAsTarget(shapeshifter, target);
|
||||
|
||||
if (!Pelican.IsEaten(shapeshifter.PlayerId) && !GameStates.IsVoting)
|
||||
var isSSneeded = shapeshifter.IsAlive();
|
||||
|
||||
if (!Pelican.IsEaten(shapeshifter.PlayerId) && !GameStates.IsVoting && isSSneeded)
|
||||
isSSneeded = Main.PlayerStates[shapeshifter.PlayerId].Role.OnShapeshift(shapeshifter, target, shapeshifting);
|
||||
|
||||
if (shapeshifter.Is(CustomRoles.Hangman) && shapeshifter.GetAbilityUseLimit() < 1 && shapeshifting)
|
||||
{
|
||||
shapeshifter.SetKillCooldown(Hangman.ShapeshiftDuration.GetFloat() + 1f);
|
||||
isSSneeded = false;
|
||||
}
|
||||
|
||||
bool forceCancel = role.ForceCancelShapeshift();
|
||||
bool forceCancel = role.ForceCancelShapeshift() || !shapeshifter.IsAlive();
|
||||
|
||||
if (Changeling.ChangedRole.TryGetValue(shapeshifter.PlayerId, out bool changed) && changed && shapeshifter.GetRoleTypes() != RoleTypes.Shapeshifter)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## Hackers and cheaters
|
||||
chancygrid#7957,1466867f1
|
||||
flowerfit#4735,422cfefe8
|
||||
wideink#3618,677db6a5c, Fastpathos // Hacking streamer's lobby
|
||||
wiggreeny#9194,91f48d4e0
|
||||
@@ -92,6 +93,7 @@ woveelixir#4088,42044d211,DMD 3CD //hacker and probably BoomplayYT's alt
|
||||
tenpalace#1924,6a259fcd9,Goaty
|
||||
cat#2761,f70d4ddb1,ULC
|
||||
pinbrittle#1538,4955c179f,Moralmaser // Cheating and disrupting lobbies
|
||||
dollfly#4791,5ba449454,Mari // Spamming RPCs to crash lobbies
|
||||
|
||||
## Griefers, toxic, harrasing, morons
|
||||
setmolar#4374,藏族顶针
|
||||
|
||||
@@ -1093,6 +1093,7 @@
|
||||
"AutoMPollCommandCooldown": "Delay Before Auto Executing /mpoll (After Joining The Lobby)",
|
||||
"AutoDraftStartCommandAfterJoin": "Auto Execute /draftstart After Joining The Lobby",
|
||||
"AutoDraftStartCommandCooldown": "Delay Before Auto Executing /draftstart (After Joining The Lobby)",
|
||||
"EnterKeyToStartGame": "Begin the starting countdown by pressing the Enter key",
|
||||
"RemovePetsAtDeadPlayers": "Remove pets from dead players",
|
||||
"SeeEjectedRolesInMeeting": "See ejected roles in meetings",
|
||||
"EnableKillerLeftCommand": "Enable use of /gs (formerly /kcount) command",
|
||||
@@ -1767,6 +1768,7 @@
|
||||
"JudgeCanTrialMadmate": "Can trial <color=#ff1919>Madmates</color>",
|
||||
"JudgeCanTrialConverted": "Can trial <#ffab1b>Converted</color> players",
|
||||
"VeteranSkillMaxOfUsage": "(Initial) Max number of Alerts",
|
||||
"VeteranAlertActivatesOnNonKillingInteractions": "Kill Killer on Non-Killing Interactions (e.g., douse, shield, etc.)",
|
||||
"SwooperCooldown": "Swoop Cooldown",
|
||||
"SwooperDuration": "Swoop Duration",
|
||||
"SwooperVentNormallyOnCooldown": "<color=#ff1919>Swooper</color> vents normally when swooping is on cooldown",
|
||||
@@ -1803,10 +1805,6 @@
|
||||
"TargetKnowsLawyer": "Target knows their <color=#008080>Lawyer</color>",
|
||||
"YourLawyerIsNotify": "Your <color=#008080>Lawyer</color> is {0}. They win if you win.",
|
||||
"ShowLongInfo": "Show Long Role Descriptions instead of Short ones",
|
||||
"InfectiousBiteCooldownIncrese": "Infect Cooldown Increase Per Use",
|
||||
"InfectiousBiteMax": "Maximum Infections",
|
||||
"InfectiousKnowTargetRole": "Know <color=#7B8968>infected</color> player's role",
|
||||
"InfectiousTargetKnowOtherTarget": "<color=#7B8968>Infected</color> players know each other",
|
||||
"VirusKillCooldown": "Kill Cooldown",
|
||||
"VirusCanVent": "Can Vent",
|
||||
"VirusInfectMax": "Maximum Number Of <color=#2E8B57>Spreads</color>",
|
||||
@@ -1978,6 +1976,7 @@
|
||||
"DeathReason.Deafened": "Deafened",
|
||||
"DeathReason.Patrolled": "Patrolled",
|
||||
"DeathReason.Misguess": "Misguess",
|
||||
"DeathReason.LossOfBlood": "Loss Of Blood",
|
||||
"ND_DRLaughMessage.Meteor": "Meteor.... I hardly knew her!",
|
||||
"ND_DRLaughMessage.Lava": "Lava is not the best place to swim, huh?",
|
||||
"ND_DRLaughMessage.Tornado": "Out for a spin? I hope this tornado didn't make you dizzy!",
|
||||
@@ -2433,7 +2432,7 @@
|
||||
"CommandDescription.TPIn": "Teleport into the lobby",
|
||||
"CommandDescription.Template": "Send a message template (from template.txt)",
|
||||
"CommandDescription.MessageWait": "Set the buffer time for system messages",
|
||||
"CommandDescription.Death": "View who killed you and their role",
|
||||
"CommandDescription.Death": "View who killed you (or another player) and their role",
|
||||
"CommandDescription.Say": "Send a message as the host/moderator",
|
||||
"CommandDescription.Vote": "Vote for a player",
|
||||
"CommandDescription.Ask": "Ask a question as the Mathematician",
|
||||
@@ -2530,6 +2529,7 @@
|
||||
"CommandArgs.AFKExempt.Id": "The player's ID to exempt from AFK detection",
|
||||
"CommandArgs.Template.Tag": "The tag of the template to send",
|
||||
"CommandArgs.MessageWait.Duration": "The duration in seconds",
|
||||
"CommandArgs.Death.Id": "The player's ID to view the killer of (leave blank for yourself)",
|
||||
"CommandArgs.Say.Message": "The message to send",
|
||||
"CommandArgs.Vote.Id": "The player's ID to vote for",
|
||||
"CommandArgs.Ask.Number1": "The first number in the question",
|
||||
@@ -2676,6 +2676,7 @@
|
||||
"XORSameRole": "<#ff0000>X</color> XOR combinations must have 2 different roles.",
|
||||
"XORAdditionRole": "<#ff0000>X</color> XOR combinations don't support add-ons. Use <#ffff00>/combo</color> for that.",
|
||||
"XORAdded": "<#00ffa5>✓</color> XOR combination added successfully.",
|
||||
"XORListTitle": "<#00a5ff>➡ XOR Combinations ⬅</color>",
|
||||
"ForgeSuccess": "<#00ffa5>✓</color> Successfully forged {1} as {2}.\\n<size=80%>You have {0} ability uses left.</size>",
|
||||
"ThankYouForUsingEHR": "Thank you for using EHR!",
|
||||
"GMRoles": "<color=#ff5b70>【 ★ Roles ★ 】</color>",
|
||||
@@ -9461,6 +9462,7 @@
|
||||
"Bloodmoon.Suffix": "<{1}>You will die in <{2}>{0}s</color></color>",
|
||||
"Bloodmoon.Speed": "<#ff1919>Bloodmoon</color> Speed",
|
||||
"Bloodmoon.DieOnMeetingCall": "Targets Die on Meeting Call",
|
||||
"Bloodmoon.CDCIncreasePerUse": "Cooldown Increase Per Use",
|
||||
"BloodmoonNearYou": "<#ffff00>The {0} is near you! Stay away to avoid death!</color>",
|
||||
"Prefix.Bloodmoon": "Bloodmoon",
|
||||
"GA": "Guardian Angel",
|
||||
@@ -9658,6 +9660,12 @@
|
||||
"DazzlerResetDazzledVisionOnDeath": "Reset vision of dazzled players on death/eject",
|
||||
"DazzleCooldown": "Dazzle Cooldown",
|
||||
"DazzleButtonText": "Dazzle",
|
||||
"Spider": "Spider",
|
||||
"SpiderInfo": "Trap players in your webs then eat them",
|
||||
"SpiderInfoLong": "(Neutrals):\\nAs the Spider, vanish/pet/shapeshift to place a web. If someone walks into it, they'll be frozen and have lower vision, and you'll get a notification about someone being trapped in your web. If someone is in your web for too long, the web breaks and they get freed. You can only kill the players that are trapped in a web!\\nKill everyone to win!",
|
||||
"Spider.WebTrapRange": "Web Trap Range",
|
||||
"Spider.LowerVisionBy": "Lower Vision By",
|
||||
"Spider.TrappedDuration": "Trapped Duration (after which the web breaks)",
|
||||
"Gardener": "Gardener",
|
||||
"GardenerInfo": "Plant Plants to Block Kills",
|
||||
"GardenerInfoLong": "(Crewmates):\\nAs the Gardener, pet to place a plant. After the next meeting, it will be grown. No one can directly kill near grown plants. However, once a plant has blocked a kill attempt, it despawns.",
|
||||
@@ -10083,6 +10091,14 @@
|
||||
"Wyrd.Suffix.SuicideWarningOnAction.Report": "Reporting a dead body",
|
||||
"Wyrd.Suffix.SuicideWarningOnAction.Button": "Calling an emergency meeting",
|
||||
"Wyrd.Suffix.SuicideWarnings": "<#ffff00><size=80%>⚠ The following actions will cause you to suicide: ⚠\\n{0}</size></color>",
|
||||
"MoonDancer": "Moon Dancer",
|
||||
"MoonDancerInfo": "Give and Steal Add-ons",
|
||||
"MoonDancerInfoLong": "(Coven):\\nAs the Moon Dancer, you can vanish to assign yourself a random add-on. You can also use your kill button on someone,\\n- once to give them an add-on, that is: helpful if they're a coven member, otherwise harmful (assigned after the next meeting)\\n- twice to steal all add-ons from them<sup>*</sup>\\n\\nWith the Necronomicon, double clicks kill the target, while also stealing all add-ons from them<sup>*</sup>.\\n\\n<sup>*</sup>If settings allow",
|
||||
"MoonDancer.SelfAssignAddonCooldown": "Self-Assign Add-on Cooldown",
|
||||
"MoonDancer.OthersAssignAddonCooldown": "Cooldown for Assigning Add-ons to Others",
|
||||
"MoonDancer.StealAddonCooldown": "Add-on Stealing Cooldown",
|
||||
"MoonDancer.CanStealAddonsWithoutNecronomicon": "Can Steal Add-ons Without Necronomicon",
|
||||
"MoonDancer.StealAllAddonsOnKill": "Steal All Add-ons On Kill",
|
||||
"DCNotify.Hacking": "You were <color=#ff1919>banned</color> for hacking.\\n\\nPlease stop.",
|
||||
"DCNotify.Banned": "You were <color=#ff1919>banned</color> from this lobby.\\n\\nContact the host if this was a mistake.",
|
||||
"DCNotify.Kicked": "You were <color=#ffff00>kicked</color> from this lobby.\\n\\nYou may still rejoin.",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.AddOns.GhostRoles;
|
||||
|
||||
@@ -8,18 +10,22 @@ internal class Bloodmoon : IGhostRole
|
||||
private static OptionItem Duration;
|
||||
private static OptionItem Speed;
|
||||
private static OptionItem DieOnMeetingCall;
|
||||
private static OptionItem CDIncreasePerUse;
|
||||
|
||||
private static readonly Dictionary<byte, long> ScheduledDeaths = [];
|
||||
|
||||
private long LastUpdate;
|
||||
private byte BloodmoonID;
|
||||
|
||||
public Team Team => Team.Impostor | Team.Neutral;
|
||||
public int Cooldown => Duration.GetInt() + CD.GetInt();
|
||||
public int Cooldown => CD.GetInt() + Main.PlayerStates.Values.Count(x => x.GetRealKiller() == BloodmoonID && x.deathReason == PlayerState.DeathReason.LossOfBlood) * CDIncreasePerUse.GetInt();
|
||||
|
||||
public void OnAssign(PlayerControl pc)
|
||||
{
|
||||
Main.AllPlayerSpeed[pc.PlayerId] = Speed.GetFloat();
|
||||
pc.MarkDirtySettings();
|
||||
LastUpdate = Utils.TimeStamp;
|
||||
BloodmoonID = pc.PlayerId;
|
||||
}
|
||||
|
||||
public void OnProtect(PlayerControl pc, PlayerControl target)
|
||||
@@ -46,6 +52,10 @@ internal class Bloodmoon : IGhostRole
|
||||
|
||||
DieOnMeetingCall = new BooleanOptionItem(649405, "Bloodmoon.DieOnMeetingCall", true, TabGroup.OtherRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Bloodmoon]);
|
||||
|
||||
CDIncreasePerUse = new IntegerOptionItem(649406, "Bloodmoon.CDCIncreasePerUse", new(0, 60, 1), 15, TabGroup.OtherRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Bloodmoon])
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
}
|
||||
|
||||
public static void Update(PlayerControl pc, Bloodmoon instance)
|
||||
@@ -67,10 +77,10 @@ internal class Bloodmoon : IGhostRole
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pc.RpcCheckAndMurder(player, true)) player.Suicide(realKiller: pc);
|
||||
if (pc.RpcCheckAndMurder(player, true)) player.Suicide(PlayerState.DeathReason.LossOfBlood, pc);
|
||||
}
|
||||
|
||||
Utils.GetPlayersInRadius(5f, pc.Pos()).DoIf(x => !x.Is(Team.Impostor), x => x.Notify(string.Format(Translator.GetString("BloodmoonNearYou"), CustomRoles.Bloodmoon.ToColoredString())));
|
||||
Utils.GetPlayersInRadius(7f, pc.Pos()).DoIf(x => !x.Is(Team.Impostor), x => x.Notify(string.Format(Translator.GetString("BloodmoonNearYou"), CustomRoles.Bloodmoon.ToColoredString()), sendOption: SendOption.None));
|
||||
}
|
||||
|
||||
public static void OnMeetingStart()
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class MoonDancer : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
private static OptionItem SelfAssignAddonCooldown;
|
||||
private static OptionItem OthersAssignAddonCooldown;
|
||||
private static OptionItem StealAddonCooldown;
|
||||
private static OptionItem KillCooldown;
|
||||
private static OptionItem CanStealAddonsWithoutNecronomicon;
|
||||
private static OptionItem StealAllAddonsOnKill;
|
||||
private static OptionItem CanVentBeforeNecronomicon;
|
||||
private static OptionItem CanVentAfterNecronomicon;
|
||||
|
||||
protected override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Random;
|
||||
|
||||
private Dictionary<byte, List<CustomRoles>> ToAssign = [];
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(657300)
|
||||
.AutoSetupOption(ref SelfAssignAddonCooldown, 15f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref OthersAssignAddonCooldown, 15f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref StealAddonCooldown, 15f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref KillCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref CanStealAddonsWithoutNecronomicon, true)
|
||||
.AutoSetupOption(ref StealAllAddonsOnKill, true)
|
||||
.AutoSetupOption(ref CanVentBeforeNecronomicon, false)
|
||||
.AutoSetupOption(ref CanVentAfterNecronomicon, true);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
ToAssign = [];
|
||||
}
|
||||
|
||||
public override bool CanUseImpostorVentButton(PlayerControl pc)
|
||||
{
|
||||
return HasNecronomicon ? CanVentAfterNecronomicon.GetBool() : CanVentBeforeNecronomicon.GetBool();
|
||||
}
|
||||
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
|
||||
{
|
||||
AURoleOptions.PhantomCooldown = SelfAssignAddonCooldown.GetFloat();
|
||||
AURoleOptions.PhantomDuration = 0.1f;
|
||||
}
|
||||
|
||||
public override void SetKillCooldown(byte id)
|
||||
{
|
||||
Main.AllPlayerKillCooldown[id] = HasNecronomicon ? KillCooldown.GetFloat() : OthersAssignAddonCooldown.GetFloat();
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (!HasNecronomicon && !CanStealAddonsWithoutNecronomicon.GetBool())
|
||||
{
|
||||
GiveAddon();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (killer.CheckDoubleTrigger(target, GiveAddon))
|
||||
{
|
||||
if (HasNecronomicon && !StealAllAddonsOnKill.GetBool()) return true;
|
||||
PlayerState state = Main.PlayerStates[target.PlayerId];
|
||||
state.SubRoles.FindAll(x => !killer.Is(x) && !x.IsNotAssignableMidGame() && CustomRolesHelper.CheckAddonConflict(x, killer)).ForEach(x =>
|
||||
{
|
||||
killer.RpcSetCustomRole(x);
|
||||
state.RemoveSubRole(x);
|
||||
});
|
||||
if (!HasNecronomicon) killer.SetKillCooldown(StealAddonCooldown.GetFloat());
|
||||
return HasNecronomicon;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
void GiveAddon()
|
||||
{
|
||||
CustomRoles addon = Options.GroupedAddons[target.Is(Team.Coven) ? AddonTypes.Helpful : AddonTypes.Harmful].Where(x => !target.Is(x) && !x.IsNotAssignableMidGame() && CustomRolesHelper.CheckAddonConflict(x, target)).RandomElement();
|
||||
if (addon == default(CustomRoles))
|
||||
return;
|
||||
|
||||
if (ToAssign.TryGetValue(target.PlayerId, out var list))
|
||||
list.Add(addon);
|
||||
else
|
||||
ToAssign[target.PlayerId] = [addon];
|
||||
|
||||
killer.SetKillCooldown(OthersAssignAddonCooldown.GetFloat());
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnVanish(PlayerControl pc)
|
||||
{
|
||||
CustomRoles addon = Enum.GetValues<CustomRoles>().Where(x => x.IsAdditionRole() && !x.IsGhostRole() && !pc.Is(x) && !x.IsNotAssignableMidGame() && CustomRolesHelper.CheckAddonConflict(x, pc)).RandomElement();
|
||||
if (addon == default(CustomRoles)) return false;
|
||||
pc.RpcSetCustomRole(addon);
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void AfterMeetingTasks()
|
||||
{
|
||||
foreach ((byte id, List<CustomRoles> addons) in ToAssign)
|
||||
{
|
||||
var pc = id.GetPlayer();
|
||||
if (pc == null || !pc.IsAlive()) continue;
|
||||
|
||||
addons.ForEach(x => pc.RpcSetCustomRole(x));
|
||||
}
|
||||
|
||||
ToAssign = [];
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,13 @@ internal class Veteran : RoleBase
|
||||
|
||||
public static bool On;
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public static OptionItem VeteranSkillCooldown;
|
||||
public static OptionItem VeteranSkillDuration;
|
||||
public static OptionItem VeteranSkillMaxOfUsage;
|
||||
public static OptionItem VeteranAbilityUseGainWithEachTaskCompleted;
|
||||
public static OptionItem VeteranAbilityChargesWhenFinishedTasks;
|
||||
public static OptionItem VeteranAlertActivatesOnNonKillingInteractions;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
@@ -36,6 +43,9 @@ internal class Veteran : RoleBase
|
||||
VeteranAbilityChargesWhenFinishedTasks = new FloatOptionItem(id + 6, "AbilityChargesWhenFinishedTasks", new(0f, 5f, 0.05f), 0.2f, TabGroup.CrewmateRoles)
|
||||
.SetParent(CustomRoleSpawnChances[CustomRoles.Veteran])
|
||||
.SetValueFormat(OptionFormat.Times);
|
||||
|
||||
VeteranAlertActivatesOnNonKillingInteractions = new BooleanOptionItem(id + 7, "VeteranAlertActivatesOnNonKillingInteractions", false, TabGroup.CrewmateRoles)
|
||||
.SetParent(CustomRoleSpawnChances[CustomRoles.Veteran]);
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
@@ -106,9 +116,7 @@ internal class Veteran : RoleBase
|
||||
{
|
||||
if (!killer.IsAlive()) return false;
|
||||
|
||||
if (VeteranInProtect.ContainsKey(target.PlayerId)
|
||||
&& killer.PlayerId != target.PlayerId
|
||||
&& VeteranInProtect[target.PlayerId] + VeteranSkillDuration.GetInt() >= Utils.TimeStamp)
|
||||
if (VeteranInProtect.ContainsKey(target.PlayerId) && killer.PlayerId != target.PlayerId)
|
||||
{
|
||||
if (!killer.Is(CustomRoles.Pestilence))
|
||||
{
|
||||
|
||||
@@ -52,7 +52,7 @@ public class ClockBlocker : RoleBase
|
||||
|
||||
private int GetAddedTime()
|
||||
{
|
||||
return !Main.PlayerStates.TryGetValue(ClockBlockerId, out PlayerState state) || (state.IsDead && !CountAddedTimeAfterDeath.GetBool()) ? 0 : EmergencyCooldownIncreasePerKill.GetInt() * state.GetKillCount(true);
|
||||
return !Main.PlayerStates.TryGetValue(ClockBlockerId, out PlayerState state) || (state.IsDead && !CountAddedTimeAfterDeath.GetBool()) ? 0 : EmergencyCooldownIncreasePerKill.GetInt() * state.GetKillCount();
|
||||
}
|
||||
|
||||
public static int GetTotalTime(int originalTime)
|
||||
|
||||
@@ -96,6 +96,17 @@ public class Hangman : RoleBase
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
|
||||
{
|
||||
if (shapeshifter.GetAbilityUseLimit() < 1 && shapeshifting)
|
||||
{
|
||||
shapeshifter.SetKillCooldown(ShapeshiftDuration.GetFloat() + 1f);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SetButtonTexts(HudManager hud, byte id)
|
||||
{
|
||||
if (id.IsPlayerShifted()) hud.KillButton?.OverrideText(Translator.GetString("HangmanKillButtonTextDuringSS"));
|
||||
|
||||
@@ -63,7 +63,7 @@ public class Mercenary : RoleBase
|
||||
|
||||
private static bool HasKilled(PlayerControl pc)
|
||||
{
|
||||
return pc != null && pc.Is(CustomRoles.Mercenary) && pc.IsAlive() && (Main.PlayerStates[pc.PlayerId].GetKillCount(true) > 0 || !WaitFor1Kill.GetBool());
|
||||
return pc != null && pc.Is(CustomRoles.Mercenary) && pc.IsAlive() && (Main.PlayerStates[pc.PlayerId].GetKillCount() > 0 || !WaitFor1Kill.GetBool());
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
|
||||
@@ -57,7 +57,7 @@ public class TimeThief : RoleBase
|
||||
private static int StolenTime(byte id)
|
||||
{
|
||||
return PlayerIdList.Contains(id) && (Utils.GetPlayerById(id).IsAlive() || !ReturnStolenTimeUponDeath.GetBool())
|
||||
? DecreaseMeetingTime.GetInt() * Main.PlayerStates[id].GetKillCount(true)
|
||||
? DecreaseMeetingTime.GetInt() * Main.PlayerStates[id].GetKillCount()
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ public class Curser : RoleBase
|
||||
target.RpcRemoveAbilityUse();
|
||||
break;
|
||||
case 1:
|
||||
CustomRoles addon = Options.GroupedAddons[AddonTypes.Harmful].Where(x => x.IsAdditionRole() && !target.Is(x) && !x.IsNotAssignableMidGame() && CustomRolesHelper.CheckAddonConflict(x, target)).RandomElement();
|
||||
CustomRoles addon = Options.GroupedAddons[AddonTypes.Harmful].Where(x => !target.Is(x) && !x.IsNotAssignableMidGame() && CustomRolesHelper.CheckAddonConflict(x, target)).RandomElement();
|
||||
if (addon == default(CustomRoles)) goto Pick;
|
||||
target.RpcSetCustomRole(addon);
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.Neutral;
|
||||
|
||||
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 long LastNotifyTS;
|
||||
private bool NameDirty;
|
||||
private byte SpiderId;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(12800)
|
||||
.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 = [];
|
||||
LastNotifyTS = 0;
|
||||
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 => Vector2.Distance(x, pos) <= WebTrapRange.GetFloat() * 2f)) return;
|
||||
Webs[pos] = [];
|
||||
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();
|
||||
|
||||
if (Webs.FindFirst(x => Vector2.Distance(x.Key, pos) <= WebTrapRange.GetFloat() && x.Value.TryAdd(pc.PlayerId, Utils.TimeStamp + TrappedDuration.GetInt()), out KeyValuePair<Vector2, Dictionary<byte, long>> kvp))
|
||||
{
|
||||
pc.MarkDirtySettings();
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, SpiderId, 1, kvp.Key, pc.PlayerId, kvp.Value.Last().Value);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnFixedUpdate(PlayerControl pc)
|
||||
{
|
||||
long now = Utils.TimeStamp;
|
||||
List<Vector2> toRemove = [];
|
||||
|
||||
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 =>
|
||||
{
|
||||
Main.AllPlayerSpeed[x.PlayerId] = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
|
||||
x.MarkDirtySettings();
|
||||
});
|
||||
}, 0.2f, log: false);
|
||||
}
|
||||
}
|
||||
|
||||
toRemove.ForEach(x =>
|
||||
{
|
||||
Webs.Remove(x);
|
||||
LocateArrow.Remove(pc.PlayerId, x);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, SpiderId, 2, x);
|
||||
});
|
||||
|
||||
if (NameDirty && now != LastNotifyTS)
|
||||
{
|
||||
NameDirty = false;
|
||||
LastNotifyTS = now;
|
||||
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
|
||||
}
|
||||
}
|
||||
|
||||
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 string.Join('\n', Webs.Where(x => x.Value.Count > 0).Select(x => $"{LocateArrow.GetArrow(seer, x.Key)} {x.Value.Count} ({Math.Max(0, x.Value.Values.Min() - Utils.TimeStamp)}s)"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user