mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-20 01:46:25 +00:00
Add Centralizer, Vacuum, Shade, Task Master
This commit is contained in:
1 parent
fbdaae733d
commit
3f4a9cce47
25 files changed
+379
-34
No files matched your search
@@ -34,6 +34,7 @@ public enum CustomRoles
|
||||
Camouflager,
|
||||
Capitalism,
|
||||
Cantankerous,
|
||||
Centralizer,
|
||||
Changeling,
|
||||
Chronomancer,
|
||||
Cleaner,
|
||||
@@ -272,6 +273,7 @@ public enum CustomRoles
|
||||
Tracefinder,
|
||||
Tree,
|
||||
Tunneler,
|
||||
Vacuum,
|
||||
Ventguard,
|
||||
Veteran,
|
||||
SwordsMan, // Vigilante
|
||||
@@ -560,11 +562,13 @@ public enum CustomRoles
|
||||
Trainee,
|
||||
DualPersonality, // Schizophrenic
|
||||
Seer,
|
||||
Shade, // Ghost role
|
||||
Shy,
|
||||
Sleuth,
|
||||
Sonar,
|
||||
Stained,
|
||||
Taskcounter,
|
||||
TaskMaster,
|
||||
TicketsStealer, // Stealer
|
||||
Stressed,
|
||||
Swift,
|
||||
|
||||
@@ -422,6 +422,7 @@ public class Main : BasePlugin
|
||||
{ CustomRoles.Astral, "#b329d6" },
|
||||
{ CustomRoles.Gardener, "#00ff00" },
|
||||
{ CustomRoles.Farmer, "#FFDE59" },
|
||||
{ CustomRoles.Vacuum, "#E44CD6" },
|
||||
{ CustomRoles.Transmitter, "#c9a11e" },
|
||||
{ CustomRoles.Captain, "#53B3EF" },
|
||||
{ CustomRoles.Tree, "#00ff00" },
|
||||
@@ -637,6 +638,7 @@ public class Main : BasePlugin
|
||||
{ CustomRoles.Bloodmoon, "#ff1313" },
|
||||
{ CustomRoles.GA, "#8cffff" },
|
||||
{ CustomRoles.Facilitator, CovenColor },
|
||||
{ CustomRoles.Shade, "#060270" },
|
||||
// GM
|
||||
{ CustomRoles.GM, "#ff5b70" },
|
||||
// Add-ons
|
||||
@@ -653,6 +655,7 @@ public class Main : BasePlugin
|
||||
{ CustomRoles.Listener, "#060270" },
|
||||
{ CustomRoles.Unbound, "#DFC57B" },
|
||||
{ CustomRoles.AntiTP, "#fcba03" },
|
||||
{ CustomRoles.TaskMaster, "#00ffa5" },
|
||||
{ CustomRoles.Compelled, "#D2E44C" },
|
||||
{ CustomRoles.Commited, "#f5c542" },
|
||||
{ CustomRoles.BananaMan, "#ffe135" },
|
||||
@@ -1115,6 +1118,7 @@ public enum AdditionalWinners
|
||||
|
||||
// -------------
|
||||
Specter = CustomRoles.Specter,
|
||||
Shade = CustomRoles.Shade,
|
||||
Lovers = CustomRoles.Lovers,
|
||||
Executioner = CustomRoles.Executioner,
|
||||
Opportunist = CustomRoles.Opportunist,
|
||||
|
||||
@@ -156,6 +156,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Psychopath => CustomRoles.Impostor,
|
||||
CustomRoles.Loner => CustomRoles.Impostor,
|
||||
CustomRoles.Postponer => CustomRoles.Impostor,
|
||||
CustomRoles.Centralizer => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
|
||||
CustomRoles.Venerer => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
|
||||
CustomRoles.Kidnapper => CustomRoles.Shapeshifter,
|
||||
CustomRoles.Ambusher => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
|
||||
@@ -245,6 +246,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Gardener => CustomRoles.Crewmate,
|
||||
CustomRoles.Imitator => CustomRoles.Crewmate,
|
||||
CustomRoles.PortalMaker => CustomRoles.Crewmate,
|
||||
CustomRoles.Vacuum => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer,
|
||||
CustomRoles.Astral => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer,
|
||||
CustomRoles.Helper => CustomRoles.Crewmate,
|
||||
CustomRoles.Ankylosaurus => CustomRoles.Crewmate,
|
||||
@@ -360,7 +362,13 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Lurker => CustomRoles.Impostor,
|
||||
CustomRoles.Doomsayer => CustomRoles.Crewmate,
|
||||
CustomRoles.Godfather => CustomRoles.Impostor,
|
||||
CustomRoles.Silencer => Silencer.SilenceMode.GetValue() == 1 ? CustomRoles.Shapeshifter : CustomRoles.Impostor,
|
||||
CustomRoles.Silencer => Silencer.SilenceMode.GetValue() switch
|
||||
{
|
||||
0 => CustomRoles.Impostor,
|
||||
1 => CustomRoles.Shapeshifter,
|
||||
2 => CustomRoles.Phantom,
|
||||
_ => CustomRoles.Impostor
|
||||
},
|
||||
CustomRoles.NoteKiller => CustomRoles.Crewmate,
|
||||
CustomRoles.RoomRusher => RoomRusher.CanVent ? CustomRoles.Engineer : CustomRoles.Crewmate,
|
||||
CustomRoles.Clerk => CustomRoles.Crewmate,
|
||||
@@ -720,6 +728,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Psychopath or
|
||||
CustomRoles.Ambusher or
|
||||
CustomRoles.Kidnapper or
|
||||
CustomRoles.Centralizer or
|
||||
CustomRoles.Postponer or
|
||||
CustomRoles.Venerer or
|
||||
CustomRoles.ClockBlocker or
|
||||
@@ -1043,6 +1052,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Bomber or
|
||||
CustomRoles.Nuker or
|
||||
CustomRoles.Camouflager or
|
||||
CustomRoles.Centralizer or
|
||||
CustomRoles.Disperser or
|
||||
CustomRoles.Escapee or
|
||||
CustomRoles.FireWorks or
|
||||
@@ -1084,6 +1094,7 @@ internal static class CustomRolesHelper
|
||||
{
|
||||
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.Needy) && (!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.TaskMaster when !pc.IsCrewmate() || !Utils.HasTasks(pc.Data, forRecompute: false) => false,
|
||||
CustomRoles.Commited when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => false,
|
||||
CustomRoles.Underdog when pc.Is(CustomRoles.Mare) => false,
|
||||
CustomRoles.Shy when Options.DisableWhisperCommand.GetBool() => false,
|
||||
@@ -1633,6 +1644,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Blackmailer => RoleOptionType.Impostor_Support,
|
||||
CustomRoles.Camouflager => RoleOptionType.Impostor_Support,
|
||||
CustomRoles.Capitalism => RoleOptionType.Impostor_Support,
|
||||
CustomRoles.Centralizer => RoleOptionType.Impostor_Support,
|
||||
CustomRoles.Cleaner => RoleOptionType.Impostor_Support,
|
||||
CustomRoles.ClockBlocker => RoleOptionType.Impostor_Support,
|
||||
CustomRoles.Commander => RoleOptionType.Impostor_Support,
|
||||
@@ -1741,6 +1753,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Transmitter => RoleOptionType.Crewmate_Miscellaneous,
|
||||
CustomRoles.Tracefinder => RoleOptionType.Crewmate_Miscellaneous,
|
||||
CustomRoles.Tunneler => RoleOptionType.Crewmate_Miscellaneous,
|
||||
CustomRoles.Vacuum => RoleOptionType.Crewmate_Miscellaneous,
|
||||
CustomRoles.Analyst => RoleOptionType.Crewmate_Investigate,
|
||||
CustomRoles.Captain => RoleOptionType.Crewmate_Investigate,
|
||||
CustomRoles.Catcher => RoleOptionType.Crewmate_Investigate,
|
||||
|
||||
@@ -1950,6 +1950,8 @@ internal static class ExtendedPlayerControl
|
||||
|
||||
void DoKill()
|
||||
{
|
||||
Vacuum.BeforeMurderCheck(target);
|
||||
|
||||
killer.RpcMurderPlayer(target, true);
|
||||
|
||||
if (Main.PlayerStates.TryGetValue(target.PlayerId, out var state) && !state.IsDead)
|
||||
|
||||
@@ -90,6 +90,7 @@ internal static class GhostRolesManager
|
||||
{
|
||||
try
|
||||
{
|
||||
if (GameStates.IsEnded) return false;
|
||||
if (Options.CurrentGameMode != CustomGameMode.Standard) return false;
|
||||
if (AssignedGhostRoles.Count >= GhostRoles.Count) return false;
|
||||
|
||||
@@ -100,6 +101,7 @@ internal static class GhostRolesManager
|
||||
case CustomRoles.GM:
|
||||
case CustomRoles.Curser:
|
||||
case CustomRoles.Backstabber:
|
||||
case CustomRoles.Innocent:
|
||||
case CustomRoles.Workaholic when !Workaholic.WorkaholicCannotWinAtDeath.GetBool():
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1114,7 +1114,7 @@ public static class GuessManager
|
||||
CreateGuesserButton(__instance);
|
||||
}
|
||||
|
||||
private static bool CanGuess(PlayerControl lp, bool restrictions)
|
||||
public static bool CanGuess(PlayerControl lp, bool restrictions)
|
||||
{
|
||||
if (Banshee.Instances.Exists(x => x.ScreechedPlayers.Contains(lp.PlayerId))) return false; // Vanilla clients can't guess with their chat hidden, so don't let modded clients guess for fairness
|
||||
return lp.Is(CustomRoles.Guesser) || lp.GetCustomRole() switch
|
||||
|
||||
@@ -100,6 +100,7 @@ public static class Options
|
||||
public static Dictionary<CustomGameMode, OptionItem> GMPollGameModesSettings;
|
||||
public static Dictionary<MapNames, OptionItem> MPollMapsSettings;
|
||||
public static Dictionary<CustomRoles, OptionItem> CrewAdvancedGameEndCheckingSettings;
|
||||
public static OptionItem GuessersKeepTheGameGoing;
|
||||
|
||||
public static readonly Dictionary<Team, (OptionItem MinSetting, OptionItem MaxSetting)> FactionMinMaxSettings = [];
|
||||
public static readonly Dictionary<RoleOptionType, OptionItem[]> RoleSubCategoryLimits = [];
|
||||
@@ -3034,6 +3035,9 @@ public static class Options
|
||||
yield return null;
|
||||
|
||||
id = 69900;
|
||||
|
||||
new TextOptionItem(110020, "MenuTitle.GMPoll", TabGroup.SystemSettings)
|
||||
.SetHeader(true);
|
||||
|
||||
GMPollGameModesSettings = Enum.GetValues<CustomGameMode>()[..^1].ToDictionary(x => x, x => new BooleanOptionItem(id++, "GMPoll.Allow", true, TabGroup.SystemSettings)
|
||||
.SetColor(Main.GameModeColors[x])
|
||||
@@ -3042,6 +3046,9 @@ public static class Options
|
||||
|
||||
id = 69920;
|
||||
|
||||
new TextOptionItem(110030, "MenuTitle.MPoll", TabGroup.SystemSettings)
|
||||
.SetHeader(true);
|
||||
|
||||
MPollMapsSettings = Enum.GetValues<MapNames>().ToDictionary(x => x, x => new BooleanOptionItem(id++, "MPoll.Allow", true, TabGroup.SystemSettings)
|
||||
.SetHeader(x == MapNames.Skeld)
|
||||
.AddReplacement(("{m}", Translator.GetString($"{x}"))));
|
||||
@@ -3051,6 +3058,9 @@ public static class Options
|
||||
CrewAdvancedGameEndCheckingSettings = [];
|
||||
bool first = true;
|
||||
|
||||
new TextOptionItem(110000, "MenuTitle.CrewAdvancedGameEndChecking", TabGroup.SystemSettings)
|
||||
.SetHeader(true);
|
||||
|
||||
foreach (RoleBase roleBase in Main.AllRoleClasses)
|
||||
{
|
||||
Type type = roleBase.GetType();
|
||||
@@ -3063,9 +3073,16 @@ public static class Options
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
GuessersKeepTheGameGoing = new BooleanOptionItem(id, "GuessersKeepTheGameGoing", false, TabGroup.SystemSettings)
|
||||
.SetHeader(true)
|
||||
.SetColor(Color.yellow);
|
||||
|
||||
id = 69000;
|
||||
|
||||
new TextOptionItem(110010, "MenuTitle.AutoGMRotation", TabGroup.SystemSettings)
|
||||
.SetHeader(true);
|
||||
|
||||
EnableAutoGMRotation = new BooleanOptionItem(id++, "EnableAutoGMRotation", false, TabGroup.SystemSettings)
|
||||
.SetColor(Color.magenta)
|
||||
.SetHeader(true);
|
||||
|
||||
@@ -3316,6 +3316,7 @@ public static class Utils
|
||||
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),
|
||||
CustomRoles.Vacuum => Vacuum.AbilityCooldown.GetInt() + (includeDuration ? Vacuum.AbilityDuration.GetInt() : 0),
|
||||
CustomRoles.TimeMaster => TimeMaster.TimeMasterSkillCooldown.GetInt(),
|
||||
CustomRoles.Perceiver => Perceiver.CD.GetInt(),
|
||||
CustomRoles.Convener => Convener.CD.GetInt(),
|
||||
@@ -3342,6 +3343,7 @@ public static class Utils
|
||||
CustomRoles.QuickShooter => QuickShooter.ShapeshiftCooldown.GetInt(),
|
||||
CustomRoles.Disperser => Disperser.DisperserShapeshiftCooldown.GetInt(),
|
||||
CustomRoles.Twister => Twister.ShapeshiftCooldown.GetInt(),
|
||||
CustomRoles.Centralizer => Centralizer.AbilityCooldown.GetInt(),
|
||||
CustomRoles.Abyssbringer => Abyssbringer.BlackHolePlaceCooldown.GetInt(),
|
||||
CustomRoles.Venerer => Venerer.AbilityCooldown.GetInt(),
|
||||
CustomRoles.Wiper => Wiper.AbilityCooldown.GetInt(),
|
||||
@@ -3453,6 +3455,9 @@ public static class Utils
|
||||
|
||||
if (pc.Is(CustomRoles.Specter) || pc.Is(CustomRoles.Haunter))
|
||||
pc.RpcResetAbilityCooldown();
|
||||
|
||||
if (pc.Is(CustomRoles.TaskMaster))
|
||||
TaskMaster.AfterMeetingTasks(pc);
|
||||
|
||||
Main.CheckShapeshift[pc.PlayerId] = false;
|
||||
}
|
||||
|
||||
@@ -190,6 +190,12 @@ internal static class GameEndChecker
|
||||
CustomRoles role = pc.GetCustomRole();
|
||||
RoleBase roleBase = Main.PlayerStates[pc.PlayerId].Role;
|
||||
|
||||
if (GhostRolesManager.AssignedGhostRoles.TryGetValue(pc.PlayerId, out var ghostRole) && ghostRole.Instance is Shade shade && shade.Protected.IsSupersetOf(Main.AllAlivePlayerControls.Select(x => x.PlayerId)))
|
||||
{
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Shade);
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
}
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case CustomRoles.Stalker when pc.IsAlive() && ((WinnerTeam == CustomWinner.Impostor && !reason.Equals(GameOverReason.ImpostorsBySabotage)) || WinnerTeam == CustomWinner.Stalker || (WinnerTeam == CustomWinner.Crewmate && !reason.Equals(GameOverReason.CrewmatesByTask) && roleBase is Stalker { IsWinKill: true } && Stalker.SnatchesWin.GetBool())):
|
||||
@@ -600,6 +606,12 @@ internal static class GameEndChecker
|
||||
int coven = AlivePlayersCount(CountTypes.Coven);
|
||||
|
||||
var crewKeepsGameGoing = false;
|
||||
|
||||
if (Options.GuessersKeepTheGameGoing.GetBool())
|
||||
{
|
||||
bool restrictions = Options.GuesserNumRestrictions.GetBool();
|
||||
crewKeepsGameGoing |= statesCoutingAsCrew.Any(x => !x.IsDead && x.Player != null && GuessManager.StartMeetingPatch.CanGuess(x.Player, restrictions));
|
||||
}
|
||||
|
||||
foreach (PlayerState playerState in statesCoutingAsCrew)
|
||||
{
|
||||
|
||||
@@ -4500,6 +4500,7 @@
|
||||
"LibrarianNormalMode": "Normal",
|
||||
"CanKillWhileShifted": "Can Kill While Shapeshifting",
|
||||
"Shapeshift": "Shapeshift",
|
||||
"Vanish": "Vanish",
|
||||
"UsesPetInsteadOfKillNotice": "<#ffff00>⚠ As this role, you need to use your pet button as your kill button!</color>",
|
||||
"UsesMeetingShapeshiftNotice": "<#ffff00>⚠ As this role, instead of commands and votes, you need to use the shapeshift menu in meetings to select your target!</color>",
|
||||
"UseMeetingShapeshift": "Use Shapeshift Menu in Meetings to Select Targets Instead of Commands/Votes",
|
||||
@@ -9125,6 +9126,16 @@
|
||||
"NonplusInfoLong": "(Neutrals):\\nAs the Nonplus, you can blind others by venting or petting. Blinded players can't see anything for a set amount of time.\\nKill everyone to win.",
|
||||
"Nonplus.BlindDuration": "Blind Duration",
|
||||
"Nonplus.BlindCooldown": "Blind Cooldown",
|
||||
"TaskMaster": "Task Master",
|
||||
"TaskMasterInfo": "Auto-Completes 1 Task After Every Meeting",
|
||||
"TaskMasterInfoLong": "(Add-ons):\\nAs the Task Master, you automatically complete 1 task after every meeting.",
|
||||
"Prefix.TaskMaster": "Task Master",
|
||||
"Centralizer": "Centralizer",
|
||||
"CentralizerInfo": "Centralize Players' Locations",
|
||||
"CentralizerInfoLong": "(Impostors):\\nAs the Centralizer, mark a position. Then use your ability again to teleport a certain number of players to that position. You may also be included.",
|
||||
"Vacuum": "Vacuum",
|
||||
"VacuumInfo": "Suck In Dead Bodies",
|
||||
"VacuumInfoLong": "(Crewmates):\\nAs the Vacuum, while using your ability, all kills' bodies will teleport to you.",
|
||||
"Rookie": "Rookie",
|
||||
"RookieInfo": "You Can't Kill on the First Round",
|
||||
"RookieInfoLong": "(Add-ons):\\nYou can't kill on the first round.",
|
||||
@@ -9297,6 +9308,10 @@
|
||||
"FacilitatorInfo": "Give Necronomicon",
|
||||
"FacilitatorInfoLong": "(Coven Ghost):\\nAs the Facilitator, you can protect a Coven member to give them the Necronomicon. (Protecting a non-Coven member or one who already has the Necronomicon does nothing.)",
|
||||
"Prefix.Facilitator": "Facilitator",
|
||||
"Shade": "Shade",
|
||||
"ShadeInfo": "Protect Everyone to Win",
|
||||
"ShadeInfoLong": "(Neutral Ghost):\\nAs the Shade, you must protect everyone to win! You can protect someone by using your protect button on them. If you succeed to protect everyone before the end of the game, you win with the winning team at the end. This is only an additional win condition, it does not override your original win condition.",
|
||||
"Prefix.Shade": "Shade",
|
||||
"Haunter": "Haunter",
|
||||
"HaunterInfo": "You're the Impostors' Worst Nightmare",
|
||||
"HaunterInfoLong": "(Crewmate/Neutral Ghost):\\nAs the Haunter, when you complete all of your tasks, a random living impostor's name will show in red to everyone in meetings. When you have 1 task left, all impostors will get an arrow pointing towards you, and if any of them come close to you, you lose this role.",
|
||||
@@ -9980,6 +9995,7 @@
|
||||
"AdditionalWinnerRoleText.Postman": "The Postman delivered mail to everyone",
|
||||
"AdditionalWinnerRoleText.SoulHunter": "Soul Hunter wins along",
|
||||
"AdditionalWinnerRoleText.Specter": "Phantasm wins along",
|
||||
"AdditionalWinnerRoleText.Shade": "Shade wins along",
|
||||
"AdditionalWinnerRoleText.Default": "{0} wins along",
|
||||
"ErrorEndText": "An error occurred",
|
||||
"ErrorEndTextDescription": "To avoid crashing, the game was forcibly ended.",
|
||||
@@ -9998,7 +10014,12 @@
|
||||
"SettingsForRoleText": "Settings for",
|
||||
"GMPoll.Allow": "Show {gm} in /gmpoll choices",
|
||||
"MPoll.Allow": "Show {m} in /mpoll choices",
|
||||
"MenuTitle.GMPoll": "Game Mode Poll",
|
||||
"MenuTitle.MPoll": "Map Poll",
|
||||
"CrewAdvancedGameEndChecking": "Enable Advanced Game End Checking For {role}",
|
||||
"GuessersKeepTheGameGoing": "Crewmate Guessers Keep The Game Going",
|
||||
"MenuTitle.CrewAdvancedGameEndChecking": "Advanced Game End Checking",
|
||||
"MenuTitle.AutoGMRotation": "Automatic Game Mode Rotation",
|
||||
"EnableAutoGMRotation": "Automatically Rotate Game Modes",
|
||||
"AutoGMRotationSlot": "Game Mode for Game {index} (Slot {index})",
|
||||
"AGMR.Explicit": "Specified Explicitly",
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using EHR.Modules;
|
||||
using Il2CppSystem;
|
||||
using Exception = System.Exception;
|
||||
|
||||
namespace EHR.AddOns.Crewmate;
|
||||
|
||||
public class TaskMaster : IAddon
|
||||
{
|
||||
public AddonTypes Type => AddonTypes.Helpful;
|
||||
|
||||
public void SetupCustomOption()
|
||||
{
|
||||
Options.SetupAdtRoleOptions(655500, CustomRoles.TaskMaster, canSetNum: true);
|
||||
}
|
||||
|
||||
public static void AfterMeetingTasks(PlayerControl pc)
|
||||
{
|
||||
try
|
||||
{
|
||||
TaskState ts = pc.GetTaskState();
|
||||
if (!ts.HasTasks || ts.IsTaskFinished || !Utils.HasTasks(pc.Data, forRecompute: false)) return;
|
||||
var incompleteTasks = pc.myTasks.FindAll((Predicate<PlayerTask>)(x => !x.IsComplete));
|
||||
RPC.PlaySoundRPC(pc.PlayerId, Sounds.TaskUpdateSound);
|
||||
pc.RpcCompleteTask(incompleteTasks[IRandom.Instance.Next(0, incompleteTasks.Count)].Id);
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EHR.AddOns.GhostRoles;
|
||||
|
||||
public class Shade : IGhostRole
|
||||
{
|
||||
private static OptionItem CD;
|
||||
public HashSet<byte> Protected = [];
|
||||
public Team Team => Team.Neutral;
|
||||
public int Cooldown => CD.GetInt();
|
||||
public void OnProtect(PlayerControl pc, PlayerControl target)
|
||||
{
|
||||
Protected.Add(target.PlayerId);
|
||||
}
|
||||
public void OnAssign(PlayerControl pc)
|
||||
{
|
||||
Protected = [];
|
||||
}
|
||||
public void SetupCustomOption()
|
||||
{
|
||||
Options.SetupRoleOptions(649900, TabGroup.OtherRoles, CustomRoles.Bloodmoon);
|
||||
|
||||
CD = new IntegerOptionItem(649902, "AbilityCooldown", new(0, 120, 1), 10, TabGroup.OtherRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Bloodmoon])
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
}
|
||||
}
|
||||
@@ -123,12 +123,7 @@ public class Coroner : RoleBase
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (target != null && seer.PlayerId != target.PlayerId) return string.Empty;
|
||||
|
||||
if (GameStates.IsMeeting || seer.PlayerId != CoronerId || hud) return string.Empty;
|
||||
|
||||
if (Main.PlayerStates[seer.PlayerId].Role is not Coroner bh) return string.Empty;
|
||||
|
||||
return bh.CoronerTargets.Count > 0 ? bh.CoronerTargets.Select(targetId => TargetArrow.GetArrows(seer, targetId)).Aggregate(string.Empty, (current, arrow) => current + Utils.ColorString(seer.GetRoleColor(), arrow)) : Utils.ColorString(Color.white, LocateArrow.GetArrows(seer));
|
||||
if (target != null && seer.PlayerId != target.PlayerId || GameStates.IsMeeting || seer.PlayerId != CoronerId || hud || Main.PlayerStates[seer.PlayerId].Role is not Coroner cn) return string.Empty;
|
||||
return cn.CoronerTargets.Count > 0 ? cn.CoronerTargets.Select(targetId => TargetArrow.GetArrows(seer, targetId)).Aggregate(string.Empty, (current, arrow) => current + Utils.ColorString(seer.GetRoleColor(), arrow)) : Utils.ColorString(Color.white, LocateArrow.GetArrows(seer));
|
||||
}
|
||||
}
|
||||
@@ -82,11 +82,8 @@ public class Mortician : RoleBase
|
||||
{
|
||||
if (ShowArrows.GetBool())
|
||||
{
|
||||
if (target != null && seer.PlayerId != target.PlayerId) return string.Empty;
|
||||
|
||||
if (seer.PlayerId != MorticianId) return string.Empty;
|
||||
|
||||
return GameStates.IsMeeting ? string.Empty : Utils.ColorString(Color.white, LocateArrow.GetArrows(seer));
|
||||
if (target != null && seer.PlayerId != target.PlayerId || seer.PlayerId != MorticianId || hud) return string.Empty;
|
||||
return meeting ? string.Empty : Utils.ColorString(Color.white, LocateArrow.GetArrows(seer));
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
|
||||
@@ -56,7 +56,7 @@ internal class Rabbit : RoleBase
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (seer == null || seer.PlayerId != target.PlayerId || !RabbitStates.TryGetValue(seer.PlayerId, out RabbitState state)) return string.Empty;
|
||||
if (seer == null || seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || !RabbitStates.TryGetValue(seer.PlayerId, out RabbitState state)) return string.Empty;
|
||||
|
||||
string suffix = state.Suffix;
|
||||
return hud ? $"<size=200%>{suffix}</size>" : suffix;
|
||||
|
||||
@@ -125,7 +125,7 @@ public class Scout : RoleBase
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (seer == null || seer.PlayerId != TrackerId || target != null && seer.PlayerId != target.PlayerId || !TrackerTarget.ContainsKey(seer.PlayerId) || GameStates.IsMeeting) return string.Empty;
|
||||
if (seer == null || seer.PlayerId != TrackerId || target != null && seer.PlayerId != target.PlayerId || !TrackerTarget.ContainsKey(seer.PlayerId) || meeting || hud) return string.Empty;
|
||||
return TrackerTarget[seer.PlayerId].Aggregate(string.Empty, (current, trackTarget) => current + Utils.ColorString(CanGetColoredArrow.GetBool() ? Main.PlayerColors[trackTarget] : Color.white, TargetArrow.GetArrows(seer, trackTarget))) + LocateArrow.GetArrows(seer);
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ public class Snitch : RoleBase
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (seer.Is(CustomRoles.Madmate) || !EnableTargetArrow || GameStates.IsMeeting || seer.PlayerId != SnitchId || (target != null && seer.PlayerId != target.PlayerId)) return string.Empty;
|
||||
if (seer.Is(CustomRoles.Madmate) || !EnableTargetArrow || GameStates.IsMeeting || seer.PlayerId != SnitchId || (target != null && seer.PlayerId != target.PlayerId) || hud) return string.Empty;
|
||||
|
||||
var arrows = string.Empty;
|
||||
|
||||
|
||||
@@ -126,12 +126,7 @@ internal class Spiritualist : RoleBase
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (!seer.IsAlive() || seer.PlayerId != SpiritualistId) return string.Empty;
|
||||
|
||||
if (target != null && seer.PlayerId != target.PlayerId) return string.Empty;
|
||||
|
||||
if (GameStates.IsMeeting) return string.Empty;
|
||||
|
||||
if (!seer.IsAlive() || seer.PlayerId != SpiritualistId || target != null && seer.PlayerId != target.PlayerId || meeting || hud) return string.Empty;
|
||||
return SpiritualistTarget != byte.MaxValue && ShowArrow ? Utils.ColorString(seer.GetRoleColor(), TargetArrow.GetArrows(seer, SpiritualistTarget)) : string.Empty;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AmongUs.GameOptions;
|
||||
|
||||
namespace EHR.Crewmate;
|
||||
|
||||
public class Vacuum : RoleBase
|
||||
{
|
||||
public static bool On;
|
||||
private static List<Vacuum> Instances = [];
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public static OptionItem AbilityCooldown;
|
||||
public static OptionItem AbilityDuration;
|
||||
private static OptionItem AbilityUseLimit;
|
||||
private static OptionItem AbilityUseGainWithEachTaskCompleted;
|
||||
private static OptionItem AbilityChargesWhenFinishedTasks;
|
||||
|
||||
private byte VacuumId;
|
||||
private long AbilityEndTS;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(655600)
|
||||
.AutoSetupOption(ref AbilityCooldown, 30, new IntegerValueRule(0, 120, 1), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref AbilityDuration, 15, new IntegerValueRule(0, 60, 1), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref AbilityUseLimit, 1f, new FloatValueRule(0, 20, 0.05f), OptionFormat.Times)
|
||||
.AutoSetupOption(ref AbilityUseGainWithEachTaskCompleted, 1f, new FloatValueRule(0f, 5f, 0.05f), OptionFormat.Times)
|
||||
.AutoSetupOption(ref AbilityChargesWhenFinishedTasks, 0.2f, new FloatValueRule(0f, 5f, 0.05f), OptionFormat.Times);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
Instances = [];
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
AbilityEndTS = 0;
|
||||
VacuumId = playerId;
|
||||
playerId.SetAbilityUseLimit(AbilityUseLimit.GetFloat());
|
||||
Instances.Add(this);
|
||||
}
|
||||
|
||||
public override string GetProgressText(byte playerId, bool comms)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append(Utils.GetAbilityUseLimitDisplay(playerId, AbilityEndTS != 0));
|
||||
sb.Append(Utils.GetTaskCount(playerId, comms));
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
|
||||
{
|
||||
if (Options.UsePets.GetBool()) return;
|
||||
|
||||
AURoleOptions.EngineerCooldown = AbilityCooldown.GetFloat();
|
||||
AURoleOptions.EngineerInVentMaxTime = 1f;
|
||||
|
||||
try { AURoleOptions.GuardianAngelCooldown = 900f; }
|
||||
catch { }
|
||||
}
|
||||
|
||||
public override void OnEnterVent(PlayerControl pc, Vent vent)
|
||||
{
|
||||
if (Options.UsePets.GetBool()) return;
|
||||
AbilityEndTS = Utils.TimeStamp + AbilityDuration.GetInt();
|
||||
}
|
||||
|
||||
public override void OnPet(PlayerControl pc)
|
||||
{
|
||||
AbilityEndTS = Utils.TimeStamp + AbilityDuration.GetInt();
|
||||
}
|
||||
|
||||
public override void OnFixedUpdate(PlayerControl pc)
|
||||
{
|
||||
if (AbilityEndTS == 0) return;
|
||||
if (AbilityEndTS > Utils.TimeStamp) return;
|
||||
AbilityEndTS = 0;
|
||||
}
|
||||
|
||||
public static void BeforeMurderCheck(PlayerControl target)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (Vacuum instance in Instances)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (instance.AbilityEndTS == 0) continue;
|
||||
PlayerControl vacuum = instance.VacuumId.GetPlayer();
|
||||
if (vacuum == null || !vacuum.IsAlive()) continue;
|
||||
target.TP(vacuum);
|
||||
return;
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
}
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
}
|
||||
}
|
||||
@@ -223,6 +223,7 @@ public class BountyHunter : RoleBase
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (seer.IsModdedClient() && !hud) return string.Empty;
|
||||
return GetTargetText(seer, target, hud) + GetTargetArrow(seer, target);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
|
||||
namespace EHR.Impostor;
|
||||
|
||||
public class Centralizer : RoleBase
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
private static OptionItem NumPlayersTeleported;
|
||||
public static OptionItem AbilityCooldown;
|
||||
private static OptionItem AbilityUseLimit;
|
||||
private static OptionItem AbilityUseGainWithEachKill;
|
||||
|
||||
private Vector2? MarkedPosition;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(655700)
|
||||
.AutoSetupOption(ref NumPlayersTeleported, 4, new IntegerValueRule(1, 15, 1), OptionFormat.Players)
|
||||
.AutoSetupOption(ref AbilityCooldown, 30, new IntegerValueRule(0, 120, 1), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref AbilityUseLimit, 1f, new FloatValueRule(0, 20, 0.1f), OptionFormat.Times)
|
||||
.AutoSetupOption(ref AbilityUseGainWithEachKill, 0.6f, new FloatValueRule(0f, 5f, 0.1f), OptionFormat.Times);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
MarkedPosition = null;
|
||||
playerId.SetAbilityUseLimit(AbilityUseLimit.GetFloat());
|
||||
}
|
||||
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte id)
|
||||
{
|
||||
if (Options.UsePhantomBasis.GetBool())
|
||||
AURoleOptions.PhantomCooldown = AbilityCooldown.GetFloat();
|
||||
else
|
||||
{
|
||||
if (Options.UsePets.GetBool()) return;
|
||||
|
||||
AURoleOptions.ShapeshifterCooldown = AbilityCooldown.GetFloat();
|
||||
AURoleOptions.ShapeshifterDuration = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
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 pc)
|
||||
{
|
||||
if (MarkedPosition.HasValue)
|
||||
{
|
||||
if (pc.GetAbilityUseLimit() < 1) return;
|
||||
Main.AllAlivePlayerControls.Shuffle().Take(NumPlayersTeleported.GetInt()).MassTP(MarkedPosition.Value);
|
||||
MarkedPosition = null;
|
||||
pc.RpcRemoveAbilityUse();
|
||||
}
|
||||
else
|
||||
{
|
||||
MarkedPosition = pc.Pos();
|
||||
pc.Notify(Translator.GetString("MarkDone"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,6 +258,7 @@ internal class Commander : RoleBase
|
||||
|
||||
if (seer.PlayerId == target.PlayerId)
|
||||
{
|
||||
if (hud) return string.Empty;
|
||||
if (arrowToCommander.Length > 0) return $"{Translator.GetString("Commander")} {arrowToCommander}";
|
||||
}
|
||||
else if (isTargetTarget)
|
||||
|
||||
@@ -218,13 +218,7 @@ public class Deathpact : RoleBase
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (GameStates.IsMeeting) return string.Empty;
|
||||
|
||||
if (!ShowArrowsToOtherPlayersInPact.GetBool()) return string.Empty;
|
||||
|
||||
if (target != null && seer.PlayerId != target.PlayerId) return string.Empty;
|
||||
|
||||
if (!IsInActiveDeathpact(seer)) return string.Empty;
|
||||
if (meeting || hud || !ShowArrowsToOtherPlayersInPact.GetBool() || target != null && seer.PlayerId != target.PlayerId || !IsInActiveDeathpact(seer)) return string.Empty;
|
||||
|
||||
var arrows = string.Empty;
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ public class EvilTracker : RoleBase
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (!GameStates.IsInTask) return string.Empty;
|
||||
if (meeting || hud) return string.Empty;
|
||||
|
||||
byte trackerId = target.PlayerId;
|
||||
if (seer.PlayerId != trackerId || seer.PlayerId != EvilTrackerId) return string.Empty;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Crewmate;
|
||||
using EHR.Modules;
|
||||
using UnityEngine;
|
||||
using static EHR.Options;
|
||||
|
||||
namespace EHR.Impostor;
|
||||
@@ -21,7 +23,8 @@ public class Silencer : RoleBase
|
||||
private static readonly string[] SilenceModes =
|
||||
[
|
||||
"EKill",
|
||||
"Shapeshift"
|
||||
"Shapeshift",
|
||||
"Vanish"
|
||||
];
|
||||
|
||||
public override bool IsEnable => PlayerIdList.Count > 0;
|
||||
@@ -58,8 +61,17 @@ public class Silencer : RoleBase
|
||||
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte id)
|
||||
{
|
||||
AURoleOptions.ShapeshifterCooldown = SkillCooldown.GetFloat();
|
||||
AURoleOptions.ShapeshifterDuration = 1f;
|
||||
switch (SilenceMode.GetValue())
|
||||
{
|
||||
case 1:
|
||||
AURoleOptions.ShapeshifterCooldown = SkillCooldown.GetFloat();
|
||||
AURoleOptions.ShapeshifterDuration = 1f;
|
||||
break;
|
||||
case 2:
|
||||
AURoleOptions.PhantomCooldown = SkillCooldown.GetFloat();
|
||||
AURoleOptions.PhantomDuration = 1f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
@@ -101,6 +113,31 @@ public class Silencer : RoleBase
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool OnVanish(PlayerControl pc)
|
||||
{
|
||||
if (SilenceMode.GetValue() == 2 && ForSilencer.Count == 0)
|
||||
{
|
||||
var pos = pc.Pos();
|
||||
var killRange = NormalGameOptionsV10.KillDistances[Mathf.Clamp(Main.NormalOptions.KillDistance, 0, 2)];
|
||||
var nearPlayers = Main.AllAlivePlayerControls.Without(pc).Where(x => !x.IsImpostor()).Select(x => (pc: x, distance: Vector2.Distance(x.Pos(), pos))).Where(x => x.distance <= killRange).ToArray();
|
||||
PlayerControl target = nearPlayers.Length == 0 ? null : nearPlayers.MinBy(x => x.distance).pc;
|
||||
if (target == null) return false;
|
||||
|
||||
ForSilencer.Add(target.PlayerId);
|
||||
|
||||
if (pc.IsLocalPlayer())
|
||||
{
|
||||
LocalPlayerTotalSilences++;
|
||||
if (LocalPlayerTotalSilences >= 5) Achievements.Type.Censorship.Complete();
|
||||
|
||||
if (target.Is(CustomRoles.Snitch) && Snitch.IsExposed.TryGetValue(target.PlayerId, out bool exposed) && exposed)
|
||||
Achievements.Type.YouWontTellAnyone.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void AfterMeetingTasks()
|
||||
{
|
||||
ForSilencer.Clear();
|
||||
|
||||
Reference in new issue
Block a user