Add Clerk (Tax Collector)

This commit is contained in:
Gurge44 committed 2025-09-12 10:54:51 +02:00
1 parent c9e52ac4b0
commit 5136fd7f87
16 files changed
+177 -18

No files matched your search

+1
View File
@@ -289,6 +289,7 @@ public enum CustomRoles
Bubble,
Chemist,
Cherokious,
Clerk,
Collector,
Curser,
Deathknight,
+1 -1
View File
@@ -48,7 +48,7 @@ internal static class HotPotato
.SetValueFormat(OptionFormat.Multiplier)
.SetColor(new Color32(232, 205, 70, byte.MaxValue));
HolderCanPassViaKillButton = new BooleanOptionItem(69_213_003, "HotPotato_HolderCanPassViaKillButton", false, TabGroup.GameSettings)
HolderCanPassViaKillButton = new BooleanOptionItem(69_213_003, "HotPotato_HolderCanPassViaKillButton", true, TabGroup.GameSettings)
.SetGameMode(CustomGameMode.HotPotato)
.SetColor(new Color32(232, 205, 70, byte.MaxValue))
.SetHeader(true);
+2
View File
@@ -573,6 +573,7 @@ public class Main : BasePlugin
{ CustomRoles.Seamstress, "#BFE44C" },
{ CustomRoles.Spirit, "#26B652" },
{ CustomRoles.Starspawn, "#4CE4B4" },
{ CustomRoles.Clerk, "#26B6A9" },
{ CustomRoles.RoomRusher, "#ffab1b" },
{ CustomRoles.SchrodingersCat, "#616161" },
{ CustomRoles.Shifter, "#777777" },
@@ -1117,6 +1118,7 @@ public enum AdditionalWinners
Seamstress = CustomRoles.Seamstress,
Spirit = CustomRoles.Spirit,
Starspawn = CustomRoles.Starspawn,
Clerk = CustomRoles.Clerk,
RoomRusher = CustomRoles.RoomRusher,
Impartial = CustomRoles.Impartial,
Gaslighter = CustomRoles.Gaslighter,
+3
View File
@@ -516,6 +516,7 @@ internal static class CustomRolesHelper
CustomRoles.Postman => RoleTypes.Impostor,
CustomRoles.Dealer => RoleTypes.Impostor,
CustomRoles.Auditor => RoleTypes.Impostor,
CustomRoles.Clerk => RoleTypes.Impostor,
CustomRoles.Seamstress => RoleTypes.Shapeshifter,
CustomRoles.Spirit => RoleTypes.Shapeshifter,
CustomRoles.Starspawn => RoleTypes.Impostor,
@@ -889,6 +890,7 @@ internal static class CustomRolesHelper
CustomRoles.Godfather when Options.GodfatherCancelVote.GetBool() => true,
CustomRoles.Socialite when Socialite.CancelVote.GetBool() => true,
CustomRoles.Negotiator when Negotiator.CancelVote.GetBool() => true,
CustomRoles.Clerk when Clerk.CancelVote.GetBool() => true,
CustomRoles.President => true,
@@ -1525,6 +1527,7 @@ internal static class CustomRolesHelper
CustomRoles.Innocent => RoleOptionType.Neutral_Evil,
CustomRoles.Curser => RoleOptionType.Neutral_Pariah,
CustomRoles.Auditor => RoleOptionType.Neutral_Pariah,
CustomRoles.Clerk => RoleOptionType.Neutral_Pariah,
CustomRoles.Magistrate => RoleOptionType.Neutral_Pariah,
CustomRoles.Seamstress => RoleOptionType.Neutral_Pariah,
CustomRoles.Spirit => RoleOptionType.Neutral_Pariah,
+7
View File
@@ -15,6 +15,7 @@ public static class FixedUpdateCaller
private static int NonLowLoadPlayerIndex;
private static long LastFileLoadTS;
private static long LastAutoMessageSendTS;
// ReSharper disable once UnusedMember.Global
public static void Postfix()
@@ -48,6 +49,12 @@ public static class FixedUpdateCaller
LastFileLoadTS = now;
Options.LoadUserData();
}
if (Options.EnableAutoMessage.GetBool() && now - LastAutoMessageSendTS > Options.AutoMessageSendInterval.GetInt())
{
LastAutoMessageSendTS = now;
TemplateManager.SendTemplate("Notification", sendOption: Hazel.SendOption.None);
}
}
HudManager hudManager = FastDestroyableSingleton<HudManager>.Instance;
@@ -320,6 +320,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
Wizard.OnAnyoneApplyGameOptions(opt, player.PlayerId);
Curser.OnAnyoneApplyGameOptions(opt, player.PlayerId);
Auditor.OnAnyoneApplyGameOptions(opt, player.PlayerId);
Clerk.OnAnyoneApplyGameOptions(opt, player.PlayerId);
if (Sprayer.LowerVisionList.Contains(player.PlayerId))
{
+3
View File
@@ -68,6 +68,7 @@ public class PlayerState(byte playerId)
WipedOut,
OutOfOxygen,
Retribution,
Taxes,
// Natural Disasters
Meteor,
@@ -456,6 +457,8 @@ public class TaskState
if (AmongUsClient.Instance.AmHost)
{
Clerk.DidTaskThisRound.Add(player.PlayerId);
bool alive = player.IsAlive();
if (alive && Options.CurrentGameMode == CustomGameMode.Speedrun)
+9
View File
@@ -729,6 +729,8 @@ public static class Options
public static OptionItem AllCrewRolesHaveVanillaColor;
public static OptionItem MessageRpcSizeLimit;
public static OptionItem KickSlowJoiningPlayers;
public static OptionItem EnableAutoMessage;
public static OptionItem AutoMessageSendInterval;
public static OptionItem DraftMaxRolesPerPlayer;
public static OptionItem LargerRoleTextSize;
public static OptionItem ShowTaskCountWhenAlive;
@@ -1738,6 +1740,13 @@ public static class Options
KickSlowJoiningPlayers = new BooleanOptionItem(19428, "KickSlowJoiningPlayers", false, TabGroup.SystemSettings)
.SetHeader(true);
EnableAutoMessage = new BooleanOptionItem(19429, "EnableAutoMessage", false, TabGroup.SystemSettings)
.SetHeader(true);
AutoMessageSendInterval = new IntegerOptionItem(19430, "AutoMessageSendInterval", new(10, 300, 5), 60, TabGroup.SystemSettings)
.SetParent(EnableAutoMessage)
.SetValueFormat(OptionFormat.Seconds);
RoleAssigningAlgorithm = new StringOptionItem(19409, "RoleAssigningAlgorithm", RoleAssigningAlgorithms, 4, TabGroup.SystemSettings, true)
.SetHeader(true)
+1
View File
@@ -871,6 +871,7 @@ public static class Utils
case CustomRoles.Postman:
case CustomRoles.Dealer:
case CustomRoles.Auditor:
case CustomRoles.Clerk:
case CustomRoles.Magistrate:
case CustomRoles.Seamstress:
case CustomRoles.Spirit:
+27 -7
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
@@ -164,7 +165,7 @@ internal static class ChatCommands
new(["target", "цель", "腹语者标记", "alvo"], "{id}", GetString("CommandDescription.Target"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, TargetCommand, true, true, [GetString("CommandArgs.Target.Id")]),
new(["chat", "сообщение", "腹语者发送消息"], "{message}", GetString("CommandDescription.Chat"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, ChatCommand, true, true, [GetString("CommandArgs.Chat.Message")]),
new(["check", "проверить", "检查", "veificar"], "{id} {role}", GetString("CommandDescription.Check"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, CheckCommand, true, true, [GetString("CommandArgs.Check.Id"), GetString("CommandArgs.Check.Role")]),
new(["ban", "kick", "бан", "кик", "забанить", "кикнуть", "封禁", "踢出", "banir", "expulsar"], "{id}", GetString("CommandDescription.Ban"), Command.UsageLevels.HostOrModerator, Command.UsageTimes.Always, BanKickCommand, true, false, [GetString("CommandArgs.Ban.Id")]),
new(["ban", "kick", "бан", "кик", "забанить", "кикнуть", "封禁", "踢出", "banir", "expulsar"], "{id} [reason]", GetString("CommandDescription.Ban"), Command.UsageLevels.HostOrModerator, Command.UsageTimes.Always, BanKickCommand, true, false, [GetString("CommandArgs.Ban.Id"), GetString("CommandArgs.Ban.Reason")]),
new(["exe", "выкинуть", "驱逐", "executar"], "{id}", GetString("CommandDescription.Exe"), Command.UsageLevels.HostOrAdmin, Command.UsageTimes.Always, ExeCommand, true, false, [GetString("CommandArgs.Exe.Id")]),
new(["kill", "убить", "击杀", "matar"], "{id}", GetString("CommandDescription.Kill"), Command.UsageLevels.Host, Command.UsageTimes.Always, KillCommand, true, false, [GetString("CommandArgs.Kill.Id")]),
new(["colour", "color", "цвет", "更改颜色", "cor"], "{color}", GetString("CommandDescription.Colour"), Command.UsageLevels.Everyone, Command.UsageTimes.InLobby, ColorCommand, true, false, [GetString("CommandArgs.Colour.Color")]),
@@ -1304,6 +1305,8 @@ internal static class ChatCommands
return;
}
DraftResult = [];
byte[] allPlayerIds = Main.AllPlayerControls.Select(x => x.PlayerId).ToArray();
List<CustomRoles> allRoles = Enum.GetValues<CustomRoles>().Where(x => x < CustomRoles.NotAssigned && x.IsEnable() && !x.IsForOtherGameMode() && !CustomHnS.AllHnSRoles.Contains(x) && !x.IsVanilla() && x is not CustomRoles.GM and not CustomRoles.Konan).ToList();
@@ -2088,13 +2091,30 @@ internal static class ChatCommands
return;
}
// Kick the specified Player
AmongUsClient.Instance.KickPlayer(kickedPlayer.OwnerId, args[0] == "/ban");
string kickedPlayerName = kickedPlayer.GetRealName();
var textToSend = $"{kickedPlayerName} {GetString("KickCommandKicked")}";
if (GameStates.IsInGame) textToSend += $"{GetString("KickCommandKickedRole")} {kickedPlayer.GetCustomRole().ToColoredString()}";
try
{
string kickedPlayerName = kickedPlayer.GetRealName();
var textToSend = $"{kickedPlayerName} {GetString("KickCommandKicked")}";
if (GameStates.IsInGame) textToSend += $"{GetString("KickCommandKickedRole")} {kickedPlayer.GetCustomRole().ToColoredString()}";
if (args.Length >= 3) textToSend += $"\n{GetString("KickCommandKickedReason")} {string.Join(' ', args[2..])}";
Utils.SendMessage(textToSend, sendOption: GameStates.IsInGame ? SendOption.Reliable : SendOption.None);
Utils.SendMessage(textToSend, sendOption: GameStates.IsInGame ? SendOption.Reliable : SendOption.None);
string modLogFilePath = $"{Main.DataPath}/EHR_DATA/ModLogs/{DateTime.Now:yyyy-MM-dd}.txt";
if (!File.Exists(modLogFilePath))
{
string directoryName = Path.GetDirectoryName(modLogFilePath);
if (!string.IsNullOrEmpty(directoryName)) Directory.CreateDirectory(directoryName);
File.WriteAllText(modLogFilePath, "=== Moderation Log ===\n");
}
string logEntry = $"[{DateTime.Now:HH:mm:ss}] {player.GetRealName()} {(args[0] == "/ban" ? "banned" : "kicked")} {kickedPlayerName} [{kickedPlayer.FriendCode}|{kickedPlayer.GetClient().GetHashedPuid()}] for {(args.Length >= 3 ? string.Join(' ', args[2..]) : "[no reason provided]")}\n";
File.AppendAllText(modLogFilePath, logEntry);
}
catch (Exception e) { Utils.ThrowException(e); }
AmongUsClient.Instance.KickPlayer(kickedPlayer.OwnerId, args[0] == "/ban");
}
private static void CheckCommand(PlayerControl player, string text, string[] args)
-3
View File
@@ -207,9 +207,6 @@ public static class ChatManager
case CustomGameMode.FFA when GameStates.InGame && !message.StartsWith('/'):
FreeForAll.UpdateLastChatMessage(player.GetRealName(), message);
break;
case CustomGameMode.Standard when GameStates.InGame && operate != 1 && Banshee.On:
Banshee.OnReceiveChat();
break;
}
}
+2
View File
@@ -236,6 +236,7 @@ internal static class GameEndChecker
case CustomRoles.NoteKiller when !NoteKiller.CountsAsNeutralKiller && NoteKiller.Kills >= NoteKiller.NumKillsNeededToWin:
case CustomRoles.NecroGuesser when (roleBase as NecroGuesser).GuessedPlayers >= NecroGuesser.NumGuessesToWin.GetInt():
case CustomRoles.RoomRusher when (roleBase as RoomRusher).Won:
case CustomRoles.Clerk when WinnerTeam != CustomWinner.Crewmate || reason == GameOverReason.CrewmatesByTask:
case CustomRoles.Auditor when WinnerTeam != CustomWinner.Crewmate:
case CustomRoles.Magistrate when WinnerTeam != CustomWinner.Crewmate:
case CustomRoles.Seamstress when WinnerTeam != CustomWinner.Crewmate:
@@ -461,6 +462,7 @@ internal static class GameEndChecker
case CustomRoles.NecroGuesser when ((NecroGuesser)state.Role).GuessedPlayers >= NecroGuesser.NumGuessesToWin.GetInt():
case CustomRoles.RoomRusher when ((RoomRusher)state.Role).Won:
case CustomRoles.Auditor:
case CustomRoles.Clerk:
case CustomRoles.Magistrate:
case CustomRoles.Seamstress:
case CustomRoles.Spirit:
+1 -1
View File
@@ -149,7 +149,7 @@ internal static class OnGameJoinedPatch
float timer;
if (nextGM != CustomGameMode.All) timer = 0f;
else if (Options.AutoGMPollCommandAfterJoin.GetBool()) timer = Options.AutoGMPollCommandCooldown.GetInt();
else if (Options.AutoGMPollCommandAfterJoin.GetBool()) timer = Options.AutoGMPollCommandCooldown.GetInt() - 10;
else if (Main.AutoStart.Value) timer = (Options.MinWaitAutoStart.GetFloat() * 60) - 65;
else timer = 30f;
+12 -1
View File
@@ -1884,6 +1884,7 @@
"KickCommandKickMod": "You are not permitted to kick other moderators.",
"KickCommandKicked": "was kicked from the game.",
"KickCommandKickedRole": "Their role was",
"KickCommandKickedReason": "Reason:",
"ModeratorTag": "<#8000ff>Moderator</color>",
"VIPTag": "<#ffff00>VIP</color>",
"AdminTag": "<#ff0000>Admin</color>",
@@ -1958,6 +1959,7 @@
"DeathReason.Mauled": "Mauled",
"DeathReason.WipedOut": "Wiped Out",
"DeathReason.Retribution": "Retribution",
"DeathReason.Taxes": "Taxes",
"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!",
@@ -2514,6 +2516,7 @@
"CommandArgs.Check.Id": "The player's ID to check",
"CommandArgs.Check.Role": "The role or add-on to check for",
"CommandArgs.Ban.Id": "The player's ID to ban or kick",
"CommandArgs.Ban.Reason": "The reason for the punishment",
"CommandArgs.Exe.Id": "The player's ID to execute",
"CommandArgs.Kill.Id": "The player's ID to kill",
"CommandArgs.Colour.Color": "The color to set (ex. red, blue, yellow)",
@@ -4470,12 +4473,14 @@
"GuessBackstabber": "You cannot guess a revealed Backstabber.",
"HideResults": "Hide Results",
"ShowResults": "Show Results",
"EnableAutoMessage": "Enable Auto-Message (sends the template with the 'Notification' tag on a given interval in lobby)",
"AutoMessageSendInterval": "The Auto-Message Is Sent Every",
"Wiper": "Wiper",
"WiperInfo": "Wipe Out Rooms",
"WiperInfoLong": "(Impostors):\\nAs the Wiper, you can wipe out a room by using pet/vanish. This will kill everyone in that room. (Even other impostors, depending on the settings.)\\nYou cannot kill normally.\\nYou cannot use your ability during sabotages.",
"Wiper.KillOtherImpostors": "Wiping Out a Room Kills Other <#ff1919>Impostors</color>",
"Wiper.CannotUseAbilityDuringSabotage": "You cannot use your ability during sabotages",
"Wiper.MustBeInRoomToUseAbility": "You must be in a generic room to use your ability",
"Wiper.MustBeInRoomToUseAbility": "You must be in a room to use your ability\\n<size=80%>Not in the spawn area</size>",
"Wiper.CurrentRoom": "<#00ffa5>Current Room:</color> <b>{0}</b>",
"Curser": "Curser",
"CurserInfo": "Make Others' Life Worse",
@@ -9251,6 +9256,12 @@
"SleepInfo": "You Sleep During Lights Out",
"SleepInfoLong": "(Add-ons):\\nYou sleep during lights out. You can't see anything, and you cannot move. You can only wake up when the lights are back on. If someone with the Glow add-on comes near you, you fully wake up and lose this add-on.",
"Prefix.Sleep": "Sleeping",
"Clerk": "Clerk",
"ClerkInfo": "Check Players' Taxes",
"ClerkInfoLong": "(Neutrals):\\nAs the Clerk, you may vote for someone in a meeting to check their taxes. If they've completed a task in the last round (or finished them all, or they have none), they get a tax refund (speed & vision boost for the next round). Otherwise, they die.",
"Clerk.CanUseAbilityOnFirstMeeting": "Can Use Ability on First Meeting",
"Clerk.DidTax": "{0} did their taxes. They got a refund!",
"Clerk.Killed": "{0} was cought not doing their taxes (tasks). They were killed!",
"Decryptor": "Decryptor",
"DecryptorInfo": "Get Letters From Everyone's Roles",
"DecryptorInfoLong": "(Crewmates):\\nAs the Decryptor, each X tasks you complete (X is set by the host), you get to know one more letter from everyone's roles. You can see these characters below players' names. The letters are not in order and each letter only appears once for every occurance in the role name. Settings determine whether you can guess.",
+2 -5
View File
@@ -111,11 +111,8 @@ public class Banshee : Coven
}
}
public static void OnReceiveChat()
public override void OnReportDeadBody()
{
PlayerControl[] screechedPlayers = Instances.SelectMany(x => x.ScreechedPlayers).Distinct().ToValidPlayers().ToArray();
if (screechedPlayers.Length == 0) return;
LateTask.New(() => ChatManager.ClearChat(screechedPlayers), Utils.CalculatePingDelay());
LateTask.New(() => Instances.SelectMany(x => x.ScreechedPlayers).Distinct().ToValidPlayers().Do(x => x.SetChatVisible(false)), 4f, "Set Chat Hidden For Banshee Victims");
}
}
+105
View File
@@ -0,0 +1,105 @@
using System.Collections.Generic;
using AmongUs.GameOptions;
using EHR.Crewmate;
using Hazel;
namespace EHR.Neutral;
public class Clerk : RoleBase
{
public static bool On;
public override bool IsEnable => On;
private static OptionItem AbilityUseLimit;
private static OptionItem CanUseAbilityOnFirstMeeting;
private static OptionItem IncreasedSpeed;
private static OptionItem IncreasedVision;
public static OptionItem CancelVote;
public static HashSet<byte> DidTaskThisRound = [];
public static HashSet<byte> HasBoost = [];
public override void SetupCustomOption()
{
StartSetup(654800)
.AutoSetupOption(ref AbilityUseLimit, 5, new IntegerValueRule(1, 30, 1), OptionFormat.Times)
.AutoSetupOption(ref CanUseAbilityOnFirstMeeting, false)
.AutoSetupOption(ref IncreasedSpeed, 1.8f, new FloatValueRule(0.1f, 3f, 0.1f), OptionFormat.Multiplier)
.AutoSetupOption(ref IncreasedVision, 1f, new FloatValueRule(0.1f, 1.5f, 0.1f), OptionFormat.Multiplier)
.CreateVoteCancellingUseSetting(ref CancelVote);
}
public override void Init()
{
On = false;
DidTaskThisRound = [];
HasBoost = [];
}
public override void Add(byte playerId)
{
On = true;
playerId.SetAbilityUseLimit(AbilityUseLimit.GetInt());
}
public override bool OnVote(PlayerControl voter, PlayerControl target)
{
if (Starspawn.IsDayBreak) return false;
if (Main.DontCancelVoteList.Contains(voter.PlayerId)) return false;
if (MeetingStates.FirstMeeting && !CanUseAbilityOnFirstMeeting.GetBool()) return false;
if (voter.GetAbilityUseLimit() < 1) return false;
if (target.PlayerId == voter.PlayerId) return false;
voter.RpcRemoveAbilityUse();
TaskState ts = target.GetTaskState();
if (DidTaskThisRound.Contains(target.PlayerId) || !ts.HasTasks || ts.IsTaskFinished)
{
HasBoost.Add(target.PlayerId);
Utils.SendMessage(string.Format(Translator.GetString("Clerk.DidTax"), target.PlayerId.ColoredPlayerName()), voter.PlayerId, CustomRoles.Clerk.ToColoredString());
}
else if (!target.Is(CustomRoles.Pestilence))
{
target.SetRealKiller(voter);
PlayerState state = Main.PlayerStates[target.PlayerId];
state.deathReason = PlayerState.DeathReason.Taxes;
state.SetDead();
Medic.IsDead(target);
target.RpcExileV2();
Utils.AfterPlayerDeathTasks(target, true);
Utils.SendMessage(string.Format(Translator.GetString("Clerk.Killed"), target.PlayerId.ColoredPlayerName()), title: CustomRoles.Clerk.ToColoredString());
}
Main.DontCancelVoteList.Add(voter.PlayerId);
return true;
}
public override void OnMeetingShapeshift(PlayerControl shapeshifter, PlayerControl target)
{
OnVote(shapeshifter, target);
}
public override void AfterMeetingTasks()
{
DidTaskThisRound = [];
}
public override void OnReportDeadBody()
{
HasBoost.Do(x => Main.AllPlayerSpeed[x] = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod));
HasBoost = [];
}
public static void OnAnyoneApplyGameOptions(IGameOptions opt, byte id)
{
if (HasBoost.Contains(id))
{
Main.AllPlayerSpeed[id] = IncreasedSpeed.GetFloat();
float increasedVision = IncreasedVision.GetFloat();
opt.SetFloat(FloatOptionNames.CrewLightMod, increasedVision);
opt.SetFloat(FloatOptionNames.ImpostorLightMod, increasedVision);
}
}
}