diff --git a/Helpers/ColorHelper.cs b/Helpers/ColorHelper.cs index bbe650ee1..1961c80c4 100644 --- a/Helpers/ColorHelper.cs +++ b/Helpers/ColorHelper.cs @@ -12,6 +12,22 @@ public static Color ToMarkingColor(this Color color, bool bright = true) var markingColor = Color.HSVToRGB(h, MarkerSat, bright ? MarkerVal : v).SetAlpha(MarkerAlpha); return markingColor; } + /// 白背景での可読性を保てる色に変換する + public static Color ToReadableColor(this Color color) + { + Color.RGBToHSV(color, out var h, out var s, out var v); + // 適切な彩度でない場合は彩度を変更 + if (s < ReadableSat) + { + s = ReadableSat; + } + // 適切な明度でない場合は明度を変更 + if (v > ReadableVal) + { + v = ReadableVal; + } + return Color.HSVToRGB(h, s, v); + } /// マーカー色のS値 = 彩度 private const float MarkerSat = 1f; @@ -19,4 +35,8 @@ public static Color ToMarkingColor(this Color color, bool bright = true) private const float MarkerVal = 1f; /// マーカー色のアルファ = 不透明度 private const float MarkerAlpha = 0.2f; + /// 白背景テキスト色の最大S = 彩度 + private const float ReadableSat = 0.8f; + /// 白背景テキスト色の最大V = 明度 + private const float ReadableVal = 0.8f; } diff --git a/Helpers/CustomRolesHelper.cs b/Helpers/CustomRolesHelper.cs index 42a64f6e0..98d4d18f7 100644 --- a/Helpers/CustomRolesHelper.cs +++ b/Helpers/CustomRolesHelper.cs @@ -1,3 +1,4 @@ +using System.Linq; using AmongUs.GameOptions; using TownOfHost.Roles.Core; @@ -6,7 +7,14 @@ namespace TownOfHost { static class CustomRolesHelper { - public static readonly CustomRoles[] AllRoles = EnumHelper.GetAllValues(); + /// すべての役職(属性は含まない) + public static readonly CustomRoles[] AllRoles = EnumHelper.GetAllValues().Where(role => role < CustomRoles.NotAssigned).ToArray(); + /// すべての属性 + public static readonly CustomRoles[] AllAddOns = EnumHelper.GetAllValues().Where(role => role > CustomRoles.NotAssigned).ToArray(); + /// スタンダードモードで出現できるすべての役職 + public static readonly CustomRoles[] AllStandardRoles = AllRoles.Where(role => role is not (CustomRoles.HASFox or CustomRoles.HASTroll)).ToArray(); + /// HASモードで出現できるすべての役職 + public static readonly CustomRoles[] AllHASRoles = { CustomRoles.HASFox, CustomRoles.HASTroll }; public static readonly CustomRoleTypes[] AllRoleTypes = EnumHelper.GetAllValues(); public static bool IsImpostor(this CustomRoles role) diff --git a/Images/Airship_Random_Map_en.png b/Images/Airship_Random_Map_en.png new file mode 100644 index 000000000..2d81dbc0c Binary files /dev/null and b/Images/Airship_Random_Map_en.png differ diff --git a/Images/Airship_Random_Map_ja.png b/Images/Airship_Random_Map_ja.png new file mode 100644 index 000000000..df0229128 Binary files /dev/null and b/Images/Airship_Random_Map_ja.png differ diff --git a/Images/Fungle_Random_Map_en.png b/Images/Fungle_Random_Map_en.png new file mode 100644 index 000000000..dab2caa97 Binary files /dev/null and b/Images/Fungle_Random_Map_en.png differ diff --git a/Images/Fungle_Random_Map_ja.png b/Images/Fungle_Random_Map_ja.png new file mode 100644 index 000000000..47dd4a2f3 Binary files /dev/null and b/Images/Fungle_Random_Map_ja.png differ diff --git a/Images/MIRA_HQ_Random_Map.png b/Images/MIRA_HQ_Random_Map.png deleted file mode 100644 index 6de7fece1..000000000 Binary files a/Images/MIRA_HQ_Random_Map.png and /dev/null differ diff --git a/Images/Mira_HQ_Random_Map.png b/Images/Mira_HQ_Random_Map.png deleted file mode 100644 index 6de7fece1..000000000 Binary files a/Images/Mira_HQ_Random_Map.png and /dev/null differ diff --git a/Images/Mirahq_Random_Map_en.png b/Images/Mirahq_Random_Map_en.png new file mode 100644 index 000000000..4a17589ae Binary files /dev/null and b/Images/Mirahq_Random_Map_en.png differ diff --git a/Images/Mirahq_Random_Map_ja.png b/Images/Mirahq_Random_Map_ja.png new file mode 100644 index 000000000..0e83f33c0 Binary files /dev/null and b/Images/Mirahq_Random_Map_ja.png differ diff --git a/Images/Polus_Random_Map.png b/Images/Polus_Random_Map.png deleted file mode 100644 index 3db4e9902..000000000 Binary files a/Images/Polus_Random_Map.png and /dev/null differ diff --git a/Images/Polus_Random_Map_en.png b/Images/Polus_Random_Map_en.png new file mode 100644 index 000000000..5cb268e35 Binary files /dev/null and b/Images/Polus_Random_Map_en.png differ diff --git a/Images/Polus_Random_Map_ja.png b/Images/Polus_Random_Map_ja.png new file mode 100644 index 000000000..6db9b7a38 Binary files /dev/null and b/Images/Polus_Random_Map_ja.png differ diff --git a/Images/Skeld_Random_Map_en.png b/Images/Skeld_Random_Map_en.png new file mode 100644 index 000000000..2e303a562 Binary files /dev/null and b/Images/Skeld_Random_Map_en.png differ diff --git a/Images/Skeld_Random_Map_ja.png b/Images/Skeld_Random_Map_ja.png new file mode 100644 index 000000000..74cffb2ba Binary files /dev/null and b/Images/Skeld_Random_Map_ja.png differ diff --git a/Images/The_Airship_Random_Map.png b/Images/The_Airship_Random_Map.png deleted file mode 100644 index d189ef464..000000000 Binary files a/Images/The_Airship_Random_Map.png and /dev/null differ diff --git a/Images/The_Skeld_Random_Map.png b/Images/The_Skeld_Random_Map.png deleted file mode 100644 index 08aac1c14..000000000 Binary files a/Images/The_Skeld_Random_Map.png and /dev/null differ diff --git a/Modules/Camouflague.cs b/Modules/Camouflague.cs index 5d9be5851..a7c24dd5b 100644 --- a/Modules/Camouflague.cs +++ b/Modules/Camouflague.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using HarmonyLib; using TownOfHost.Attributes; namespace TownOfHost @@ -53,7 +52,16 @@ public static void CheckCamouflage() if (oldIsCamouflage != IsCamouflage) { - Main.AllPlayerControls.Do(pc => Camouflage.RpcSetSkin(pc)); + foreach (var pc in Main.AllPlayerControls) + { + RpcSetSkin(pc); + + // The code is intended to remove pets at dead players to combat a vanilla bug + if (!IsCamouflage && !pc.IsAlive()) + { + pc.RpcSetPet(""); + } + } Utils.NotifyRoles(NoCache: true); } } @@ -86,6 +94,10 @@ public static void RpcSetSkin(PlayerControl target, bool ForceRevert = false, bo newOutfit = PlayerSkins[id]; } + + + if (newOutfit.Compare(target.Data.DefaultOutfit)) return; + Logger.Info($"newOutfit={newOutfit.GetString()}", "RpcSetSkin"); var sender = CustomRpcSender.Create(name: $"Camouflage.RpcSetSkin({target.Data.PlayerName})"); @@ -118,4 +130,4 @@ public static void RpcSetSkin(PlayerControl target, bool ForceRevert = false, bo sender.SendMessage(); } } -} \ No newline at end of file +} diff --git a/Modules/CustomRpcSender.cs b/Modules/CustomRpcSender.cs index b3721c064..f96808134 100644 --- a/Modules/CustomRpcSender.cs +++ b/Modules/CustomRpcSender.cs @@ -263,6 +263,7 @@ public static void RpcMurderPlayer(this CustomRpcSender sender, PlayerControl pl { sender.AutoStartRpc(player.NetId, (byte)RpcCalls.MurderPlayer, targetClientId) .WriteNetObject(target) + .Write((int)ExtendedPlayerControl.SucceededFlags) .EndRpc(); } } diff --git a/Modules/CustomWinnerHolder.cs b/Modules/CustomWinnerHolder.cs index 8764bb49f..2858c14cd 100644 --- a/Modules/CustomWinnerHolder.cs +++ b/Modules/CustomWinnerHolder.cs @@ -12,9 +12,9 @@ public static class CustomWinnerHolder // リザルトの背景色の決定などに使用されます。 // 注: この変数を変更する時、WinnerRoles・WinnerIdsを同時に変更しないと予期せぬ勝者が現れる可能性があります。 public static CustomWinner WinnerTeam; - // 追加勝利するプレイヤーのチームが格納されます。 + // 追加勝利するプレイヤーの役職が格納されます。 // リザルトの表示に使用されます。 - public static HashSet AdditionalWinnerTeams; + public static HashSet AdditionalWinnerRoles; // 勝者の役職が格納され、この変数に格納されている役職のプレイヤーは全員勝利となります。 // チームとなるニュートラルの処理に最適です。 public static HashSet WinnerRoles; @@ -26,7 +26,7 @@ public static class CustomWinnerHolder public static void Reset() { WinnerTeam = CustomWinner.Default; - AdditionalWinnerTeams = new(); + AdditionalWinnerRoles = new(); WinnerRoles = new(); WinnerIds = new(); } @@ -35,17 +35,17 @@ public static void ClearWinners() WinnerRoles.Clear(); WinnerIds.Clear(); } - /// WinnerTeamに値を代入します。すでに代入されている場合、AdditionalWinnerTeamsに追加します。 + /// WinnerTeamに値を代入します。すでに代入されている場合、AdditionalWinnerRolesに追加します。 public static void SetWinnerOrAdditonalWinner(CustomWinner winner) { if (WinnerTeam == CustomWinner.Default) WinnerTeam = winner; - else AdditionalWinnerTeams.Add((AdditionalWinners)winner); + else AdditionalWinnerRoles.Add((CustomRoles)winner); } - /// WinnerTeamに値を代入します。すでに代入されている場合、既存の値をAdditionalWinnerTeamsに追加してから代入します。 + /// WinnerTeamに値を代入します。すでに代入されている場合、既存の値をAdditionalWinnerRolesに追加してから代入します。 public static void ShiftWinnerAndSetWinner(CustomWinner winner) { if (WinnerTeam != CustomWinner.Default) - AdditionalWinnerTeams.Add((AdditionalWinners)WinnerTeam); + AdditionalWinnerRoles.Add((CustomRoles)WinnerTeam); WinnerTeam = winner; } /// 既存の値をすべて削除してから、WinnerTeamに値を代入します。 @@ -57,17 +57,17 @@ public static void ResetAndSetWinner(CustomWinner winner) public static MessageWriter WriteTo(MessageWriter writer) { - writer.Write((int)WinnerTeam); + writer.WritePacked((int)WinnerTeam); - writer.Write(AdditionalWinnerTeams.Count); - foreach (var wt in AdditionalWinnerTeams) - writer.Write((int)wt); + writer.WritePacked(AdditionalWinnerRoles.Count); + foreach (var wr in AdditionalWinnerRoles) + writer.WritePacked((int)wr); - writer.Write(WinnerRoles.Count); + writer.WritePacked(WinnerRoles.Count); foreach (var wr in WinnerRoles) - writer.Write((int)wr); + writer.WritePacked((int)wr); - writer.Write(WinnerIds.Count); + writer.WritePacked(WinnerIds.Count); foreach (var id in WinnerIds) writer.Write(id); @@ -75,20 +75,20 @@ public static MessageWriter WriteTo(MessageWriter writer) } public static void ReadFrom(MessageReader reader) { - WinnerTeam = (CustomWinner)reader.ReadInt32(); + WinnerTeam = (CustomWinner)reader.ReadPackedInt32(); - AdditionalWinnerTeams = new(); - int AdditionalWinnerTeamsCount = reader.ReadInt32(); - for (int i = 0; i < AdditionalWinnerTeamsCount; i++) - AdditionalWinnerTeams.Add((AdditionalWinners)reader.ReadInt32()); + AdditionalWinnerRoles = new(); + int AdditionalWinnerRolesCount = reader.ReadPackedInt32(); + for (int i = 0; i < AdditionalWinnerRolesCount; i++) + AdditionalWinnerRoles.Add((CustomRoles)reader.ReadPackedInt32()); WinnerRoles = new(); - int WinnerRolesCount = reader.ReadInt32(); + int WinnerRolesCount = reader.ReadPackedInt32(); for (int i = 0; i < WinnerRolesCount; i++) - WinnerRoles.Add((CustomRoles)reader.ReadInt32()); + WinnerRoles.Add((CustomRoles)reader.ReadPackedInt32()); WinnerIds = new(); - int WinnerIdsCount = reader.ReadInt32(); + int WinnerIdsCount = reader.ReadPackedInt32(); for (int i = 0; i < WinnerIdsCount; i++) WinnerIds.Add(reader.ReadByte()); } diff --git a/Modules/DeviceTimer.cs b/Modules/DeviceTimer.cs index 1a439d7bb..25a79b20a 100644 --- a/Modules/DeviceTimer.cs +++ b/Modules/DeviceTimer.cs @@ -14,8 +14,10 @@ public static bool CamerasRanOut private set => camerasRanOut = value; } private static bool camerasRanOut; - private static int NumPlayersWatchingCamera => playersWatchingCamera.Count; - private static HashSet playersWatchingCamera; + private static int NumPlayersWatchingCamera => ShipStatus.Instance.Systems.TryGetValue(SystemTypes.Security, out var system) + ? system.TryCast(out var securitySystem) + ? securitySystem.PlayersUsing.Count : 0 + : 0; private static bool isEnabled; private static float camerasRemaining; private static string notifyText; @@ -24,7 +26,6 @@ public static bool CamerasRanOut public static void Init() { CamerasRanOut = false; - playersWatchingCamera = new(); isEnabled = Options.CamerasTimer.GetBool(); camerasRemaining = Options.CamerasMaxTimer.GetInt(); UpdateNotifyText(); @@ -49,24 +50,9 @@ public static void HandleRepairSystem(SystemTypes systemType, PlayerControl play } public static void BeginCamera(PlayerControl player) { - if (!AmongUsClient.Instance.AmHost || !isEnabled) - { - return; - } - playersWatchingCamera.Add(player.PlayerId); - Logger.Info($"Begin: {System.DateTime.Now:HH:mm:ss}", nameof(DeviceTimer)); } public static void CloseCamera(PlayerControl player) { - if (!AmongUsClient.Instance.AmHost || !isEnabled) - { - return; - } - if (playersWatchingCamera.Contains(player.PlayerId)) - { - playersWatchingCamera.Remove(player.PlayerId); - Logger.Info($"Close: {System.DateTime.Now:HH:mm:ss}", nameof(DeviceTimer)); - } } public static void ConsumeCamera() { diff --git a/Modules/DisableDevice.cs b/Modules/DisableDevice.cs index 20eaf9063..7eff0a068 100644 --- a/Modules/DisableDevice.cs +++ b/Modules/DisableDevice.cs @@ -28,7 +28,8 @@ class DisableDevice ["AirshipCockpitAdmin"] = new(-22.32f, 0.91f), ["AirshipRecordsAdmin"] = new(19.89f, 12.60f), ["AirshipCamera"] = new(8.10f, -9.63f), - ["AirshipVital"] = new(25.24f, -7.94f) + ["AirshipVital"] = new(25.24f, -7.94f), + ["FungleVital"] = new(-2.765f, -9.819f) }; public static float UsableDistance() { @@ -40,6 +41,7 @@ public static float UsableDistance() MapNames.Polus => 1.8f, //MapNames.Dleks => 1.5f, MapNames.Airship => 1.8f, + MapNames.Fungle => 1.8f, _ => 0.0f }; } @@ -100,6 +102,12 @@ public static void FixedUpdate() if (Options.DisableAirshipVital.GetBool()) doComms |= Vector2.Distance(PlayerPos, DevicePos["AirshipVital"]) <= UsableDistance(); break; + case 5: + if (Options.DisableFungleVital.GetBool()) + { + doComms |= Vector2.Distance(PlayerPos, DevicePos["FungleVital"]) <= UsableDistance(); + } + break; } } doComms &= !ignore; @@ -108,15 +116,15 @@ public static void FixedUpdate() if (!DesyncComms.Contains(pc.PlayerId)) DesyncComms.Add(pc.PlayerId); - pc.RpcDesyncRepairSystem(SystemTypes.Comms, 128); + pc.RpcDesyncUpdateSystem(SystemTypes.Comms, 128); } else if (!Utils.IsActive(SystemTypes.Comms) && DesyncComms.Contains(pc.PlayerId)) { DesyncComms.Remove(pc.PlayerId); - pc.RpcDesyncRepairSystem(SystemTypes.Comms, 16); + pc.RpcDesyncUpdateSystem(SystemTypes.Comms, 16); - if (Main.NormalOptions.MapId == 1) - pc.RpcDesyncRepairSystem(SystemTypes.Comms, 17); + if (Main.NormalOptions.MapId is 1 or 5) + pc.RpcDesyncUpdateSystem(SystemTypes.Comms, 17); } } catch (Exception ex) @@ -182,6 +190,12 @@ public static void UpdateDisableDevices() if (Options.DisableAirshipVital.GetBool()) consoles.DoIf(x => x.name == "panel_vitals", x => x.gameObject.GetComponent().enabled = ignore); break; + case 5: + if (Options.DisableFungleVital.GetBool()) + { + consoles.DoIf(x => x.name == "VitalsConsole", x => x.GetComponent().enabled = ignore); + } + break; } } } diff --git a/Modules/DoorsReset.cs b/Modules/DoorsReset.cs index c377f273e..97c74be82 100644 --- a/Modules/DoorsReset.cs +++ b/Modules/DoorsReset.cs @@ -12,8 +12,8 @@ public static class DoorsReset [GameModuleInitializer] public static void Initialize() { - // AirshipとPolus以外は非対応 - if ((MapNames)Main.NormalOptions.MapId is not (MapNames.Airship or MapNames.Polus)) + // AirshipとPolusとFungle以外は非対応 + if ((MapNames)Main.NormalOptions.MapId is not (MapNames.Airship or MapNames.Polus or MapNames.Fungle)) { isEnabled = false; return; @@ -72,7 +72,7 @@ private static void OpenOrCloseAllDoorsRandomly() /// ドアの開閉状況を設定する.サボタージュで閉められないドアに対しては何もしない /// 対象のドア /// 開けるならtrue,閉めるならfalse - private static void SetDoorOpenState(PlainDoor door, bool isOpen) + private static void SetDoorOpenState(OpenableDoor door, bool isOpen) { if (IsValidDoor(door)) { @@ -81,7 +81,7 @@ private static void SetDoorOpenState(PlainDoor door, bool isOpen) } /// リセット対象のドアかどうか判定する /// リセット対象ならtrue - private static bool IsValidDoor(PlainDoor door) + private static bool IsValidDoor(OpenableDoor door) { // エアシラウンジトイレとPolus除染室のドアは対象外 if (door.Room is SystemTypes.Lounge or SystemTypes.Decontamination) diff --git a/Modules/ErrorText.cs b/Modules/ErrorText.cs index 2332f6a95..63895075c 100644 --- a/Modules/ErrorText.cs +++ b/Modules/ErrorText.cs @@ -152,6 +152,7 @@ public enum ErrorCode // ========== // 001 Main Main_DictionaryError = 0010003, // 001-000-3 Main Dictionary Error + OptionIDDuplicate = 001_010_3, // 001-010-3 オプションIDが重複している(DEBUGビルド時のみ) // 002 サポート関連 UnsupportedVersion = 002_000_1, // 002-000-1 AmongUsのバージョンが古い // ========== diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index f1c0b13c9..3df0856d8 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -11,7 +11,6 @@ using TownOfHost.Roles.Core; using TownOfHost.Roles.Core.Interfaces; using TownOfHost.Roles.Impostor; -using TownOfHost.Roles.Neutral; using TownOfHost.Roles.AddOns.Impostor; using static TownOfHost.Translator; @@ -33,11 +32,14 @@ public static void RpcSetCustomRole(this PlayerControl player, CustomRoles role) } if (AmongUsClient.Instance.AmHost) { - var roleClass = player.GetRoleClass(); - if (roleClass != null) + if (role < CustomRoles.NotAssigned) { - roleClass.Dispose(); - CustomRoleManager.CreateInstance(role, player); + var roleClass = player.GetRoleClass(); + if (roleClass != null) + { + roleClass.Dispose(); + CustomRoleManager.CreateInstance(role, player); + } } MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetCustomRole, Hazel.SendOption.Reliable, -1); @@ -169,34 +171,6 @@ public static void RpcSetRoleDesync(this PlayerControl player, RoleTypes role, i AmongUsClient.Instance.FinishRpcImmediately(writer); } - public static void RpcGuardAndKill(this PlayerControl killer, PlayerControl target = null, int colorId = 0) - { - //killerが死んでいる場合は実行しない - if (!killer.IsAlive()) return; - - if (target == null) target = killer; - // Host - if (killer.AmOwner) - { - killer.ProtectPlayer(target, colorId); - killer.MurderPlayer(target); - } - // Other Clients - if (killer.PlayerId != 0) - { - var sender = CustomRpcSender.Create("GuardAndKill Sender", SendOption.None); - sender.StartMessage(killer.GetClientId()); - sender.StartRpc(killer.NetId, (byte)RpcCalls.ProtectPlayer) - .WriteNetObject((InnerNetObject)target) - .Write(colorId) - .EndRpc(); - sender.StartRpc(killer.NetId, (byte)RpcCalls.MurderPlayer) - .WriteNetObject((InnerNetObject)target) - .EndRpc(); - sender.EndMessage(); - sender.SendMessage(); - } - } public static void SetKillCooldown(this PlayerControl player, float time = -1f) { if (player == null) return; @@ -211,7 +185,7 @@ public static void SetKillCooldown(this PlayerControl player, float time = -1f) Main.AllPlayerKillCooldown[player.PlayerId] *= 2; } player.SyncSettings(); - player.RpcGuardAndKill(); + player.RpcProtectedMurderPlayer(); player.ResetKillCooldown(); } public static void RpcSpecificMurderPlayer(this PlayerControl killer, PlayerControl target = null) @@ -225,6 +199,7 @@ public static void RpcSpecificMurderPlayer(this PlayerControl killer, PlayerCont { MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(killer.NetId, (byte)RpcCalls.MurderPlayer, SendOption.Reliable, killer.GetClientId()); messageWriter.WriteNetObject(target); + messageWriter.Write((int)SucceededFlags); AmongUsClient.Instance.FinishRpcImmediately(messageWriter); } } @@ -267,9 +242,9 @@ public static void RpcResetAbilityCooldown(this PlayerControl target) ホストのクールダウンは直接リセットします。 */ } - public static void RpcDesyncRepairSystem(this PlayerControl target, SystemTypes systemType, int amount) + public static void RpcDesyncUpdateSystem(this PlayerControl target, SystemTypes systemType, int amount) { - MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(ShipStatus.Instance.NetId, (byte)RpcCalls.RepairSystem, SendOption.Reliable, target.GetClientId()); + MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(ShipStatus.Instance.NetId, (byte)RpcCalls.UpdateSystem, SendOption.Reliable, target.GetClientId()); messageWriter.Write((byte)systemType); messageWriter.WriteNetObject(target); messageWriter.Write((byte)amount); @@ -351,12 +326,10 @@ public static void ResetPlayerCam(this PlayerControl pc, float delay = 0f) { if (pc == null || !AmongUsClient.Instance.AmHost || pc.AmOwner) return; - var systemtypes = SystemTypes.Reactor; - if (Main.NormalOptions.MapId == 2) systemtypes = SystemTypes.Laboratory; - + var systemtypes = Utils.GetCriticalSabotageSystemType(); _ = new LateTask(() => { - pc.RpcDesyncRepairSystem(systemtypes, 128); + pc.RpcDesyncUpdateSystem(systemtypes, 128); }, 0f + delay, "Reactor Desync"); _ = new LateTask(() => @@ -366,9 +339,9 @@ public static void ResetPlayerCam(this PlayerControl pc, float delay = 0f) _ = new LateTask(() => { - pc.RpcDesyncRepairSystem(systemtypes, 16); + pc.RpcDesyncUpdateSystem(systemtypes, 16); if (Main.NormalOptions.MapId == 4) //Airship用 - pc.RpcDesyncRepairSystem(systemtypes, 17); + pc.RpcDesyncUpdateSystem(systemtypes, 17); }, 0.4f + delay, "Fix Desync Reactor"); } public static void ReactorFlash(this PlayerControl pc, float delay = 0f) @@ -376,18 +349,17 @@ public static void ReactorFlash(this PlayerControl pc, float delay = 0f) if (pc == null) return; int clientId = pc.GetClientId(); // Logger.Info($"{pc}", "ReactorFlash"); - var systemtypes = SystemTypes.Reactor; - if (Main.NormalOptions.MapId == 2) systemtypes = SystemTypes.Laboratory; + var systemtypes = Utils.GetCriticalSabotageSystemType(); float FlashDuration = Options.KillFlashDuration.GetFloat(); - pc.RpcDesyncRepairSystem(systemtypes, 128); + pc.RpcDesyncUpdateSystem(systemtypes, 128); _ = new LateTask(() => { - pc.RpcDesyncRepairSystem(systemtypes, 16); + pc.RpcDesyncUpdateSystem(systemtypes, 16); if (Main.NormalOptions.MapId == 4) //Airship用 - pc.RpcDesyncRepairSystem(systemtypes, 17); + pc.RpcDesyncUpdateSystem(systemtypes, 17); }, FlashDuration + delay, "Fix Desync Reactor"); } @@ -405,16 +377,17 @@ public static bool CanUseKillButton(this PlayerControl pc) } public static bool CanUseImpostorVentButton(this PlayerControl pc) { - if (!pc.IsAlive() || pc.Data.Role.Role == RoleTypes.GuardianAngel) return false; + if (!pc.IsAlive()) return false; - return pc.GetCustomRole() switch - { - CustomRoles.Sheriff => false, - CustomRoles.Egoist => true, - CustomRoles.Jackal => Jackal.CanVent, - CustomRoles.Arsonist => Arsonist.IsDouseDone(pc), - _ => pc.Is(CustomRoleTypes.Impostor), - }; + var roleCanUse = (pc.GetRoleClass() as IKiller)?.CanUseImpostorVentButton(); + + return roleCanUse ?? false; + } + public static bool CanUseSabotageButton(this PlayerControl pc) + { + var roleCanUse = (pc.GetRoleClass() as IKiller)?.CanUseSabotageButton(); + + return roleCanUse ?? false; } public static void ResetKillCooldown(this PlayerControl player) { @@ -443,6 +416,15 @@ public static void RpcExileV2(this PlayerControl player) MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)RpcCalls.Exiled, SendOption.None, -1); AmongUsClient.Instance.FinishRpcImmediately(writer); } + public static void MurderPlayer(this PlayerControl killer, PlayerControl target) + { + killer.MurderPlayer(target, SucceededFlags); + } + public const MurderResultFlags SucceededFlags = MurderResultFlags.Succeeded | MurderResultFlags.DecisionByHost; + public static void RpcMurderPlayer(this PlayerControl killer, PlayerControl target) + { + killer.RpcMurderPlayer(target, true); + } public static void RpcMurderPlayerV2(this PlayerControl killer, PlayerControl target) { if (target == null) target = killer; @@ -452,9 +434,30 @@ public static void RpcMurderPlayerV2(this PlayerControl killer, PlayerControl ta } MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(killer.NetId, (byte)RpcCalls.MurderPlayer, SendOption.None, -1); messageWriter.WriteNetObject(target); + messageWriter.Write((int)SucceededFlags); AmongUsClient.Instance.FinishRpcImmediately(messageWriter); Utils.NotifyRoles(); } + public static void RpcProtectedMurderPlayer(this PlayerControl killer, PlayerControl target = null) + { + //killerが死んでいる場合は実行しない + if (!killer.IsAlive()) return; + + if (target == null) target = killer; + // Host + if (killer.AmOwner) + { + killer.MurderPlayer(target, MurderResultFlags.FailedProtected); + } + // Other Clients + if (killer.PlayerId != 0) + { + var writer = AmongUsClient.Instance.StartRpcImmediately(killer.NetId, (byte)RpcCalls.MurderPlayer, SendOption.Reliable); + writer.WriteNetObject(target); + writer.Write((int)MurderResultFlags.FailedProtected); + AmongUsClient.Instance.FinishRpcImmediately(writer); + } + } public static void NoCheckStartMeeting(this PlayerControl reporter, GameData.PlayerInfo target) { /*サボタージュ中でも関係なしに会議を起こせるメソッド targetがnullの場合はボタンとなる*/ @@ -571,6 +574,20 @@ public static PlainShipRoom GetPlainShipRoom(this PlayerControl pc) } return null; } + public static void RpcSnapTo(this PlayerControl pc, Vector2 position) + { + pc.NetTransform.RpcSnapTo(position); + } + public static void RpcSnapToDesync(this PlayerControl pc, PlayerControl target, Vector2 position) + { + var net = pc.NetTransform; + var num = (ushort)(net.lastSequenceId + 2); + MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(net.NetId, (byte)RpcCalls.SnapTo, SendOption.None, target.GetClientId()); + NetHelpers.WriteVector2(position, messageWriter); + messageWriter.Write(num); + AmongUsClient.Instance.FinishRpcImmediately(messageWriter); + } + public static bool IsProtected(this PlayerControl self) => self.protectedByGuardianId > -1; //汎用 public static bool Is(this PlayerControl target, CustomRoles role) => diff --git a/Modules/ModUpdater.cs b/Modules/ModUpdater.cs index f96a12c4c..261f10074 100644 --- a/Modules/ModUpdater.cs +++ b/Modules/ModUpdater.cs @@ -34,8 +34,8 @@ public static void StartPostfix() { CheckRelease(Main.BetaBuildURL.Value != "").GetAwaiter().GetResult(); } - MainMenuManagerPatch.UpdateButton.gameObject.SetActive(hasUpdate); - MainMenuManagerPatch.UpdateButton.transform.Find("FontPlacer/Text_TMP").GetComponent().SetText($"{GetString("updateButton")}\n{latestTitle}"); + MainMenuManagerPatch.UpdateButton.Button.gameObject.SetActive(hasUpdate); + MainMenuManagerPatch.UpdateButton.Button.transform.Find("FontPlacer/Text_TMP").GetComponent().SetText($"{GetString("updateButton")}\n{latestTitle}"); } public static async Task CheckRelease(bool beta = false) { diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index aa8bc0a14..a29ec39b2 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -61,6 +61,7 @@ public static CustomGameMode CurrentGameMode public static bool IsActiveMiraHQ => AddedMiraHQ.GetBool() || Main.NormalOptions.MapId == 1; public static bool IsActivePolus => AddedPolus.GetBool() || Main.NormalOptions.MapId == 2; public static bool IsActiveAirship => AddedTheAirShip.GetBool() || Main.NormalOptions.MapId == 4; + public static bool IsActiveFungle => AddedTheFungle.GetBool() || Main.NormalOptions.MapId == 5; // 役職数・確率 public static Dictionary CustomRoleCounts; @@ -124,6 +125,8 @@ public static CustomGameMode CurrentGameMode public static OptionItem DisableAirshipRecordsAdmin; public static OptionItem DisableAirshipCamera; public static OptionItem DisableAirshipVital; + public static OptionItem DisableFungleDevices; + public static OptionItem DisableFungleVital; public static OptionItem DisableDevicesIgnoreConditions; public static OptionItem DisableDevicesIgnoreImpostors; public static OptionItem DisableDevicesIgnoreMadmates; @@ -137,11 +140,103 @@ public static CustomGameMode CurrentGameMode public static OptionItem AddedMiraHQ; public static OptionItem AddedPolus; public static OptionItem AddedTheAirShip; + public static OptionItem AddedTheFungle; // public static OptionItem AddedDleks; // ランダムスポーン - public static OptionItem RandomSpawn; - public static OptionItem AirshipAdditionalSpawn; + public static OptionItem EnableRandomSpawn; + //Skeld + public static OptionItem RandomSpawnSkeld; + public static OptionItem RandomSpawnSkeldCafeteria; + public static OptionItem RandomSpawnSkeldWeapons; + public static OptionItem RandomSpawnSkeldLifeSupp; + public static OptionItem RandomSpawnSkeldNav; + public static OptionItem RandomSpawnSkeldShields; + public static OptionItem RandomSpawnSkeldComms; + public static OptionItem RandomSpawnSkeldStorage; + public static OptionItem RandomSpawnSkeldAdmin; + public static OptionItem RandomSpawnSkeldElectrical; + public static OptionItem RandomSpawnSkeldLowerEngine; + public static OptionItem RandomSpawnSkeldUpperEngine; + public static OptionItem RandomSpawnSkeldSecurity; + public static OptionItem RandomSpawnSkeldReactor; + public static OptionItem RandomSpawnSkeldMedBay; + //Mira + public static OptionItem RandomSpawnMira; + public static OptionItem RandomSpawnMiraCafeteria; + public static OptionItem RandomSpawnMiraBalcony; + public static OptionItem RandomSpawnMiraStorage; + public static OptionItem RandomSpawnMiraJunction; + public static OptionItem RandomSpawnMiraComms; + public static OptionItem RandomSpawnMiraMedBay; + public static OptionItem RandomSpawnMiraLockerRoom; + public static OptionItem RandomSpawnMiraDecontamination; + public static OptionItem RandomSpawnMiraLaboratory; + public static OptionItem RandomSpawnMiraReactor; + public static OptionItem RandomSpawnMiraLaunchpad; + public static OptionItem RandomSpawnMiraAdmin; + public static OptionItem RandomSpawnMiraOffice; + public static OptionItem RandomSpawnMiraGreenhouse; + //Polus + public static OptionItem RandomSpawnPolus; + public static OptionItem RandomSpawnPolusOfficeLeft; + public static OptionItem RandomSpawnPolusOfficeRight; + public static OptionItem RandomSpawnPolusAdmin; + public static OptionItem RandomSpawnPolusComms; + public static OptionItem RandomSpawnPolusWeapons; + public static OptionItem RandomSpawnPolusBoilerRoom; + public static OptionItem RandomSpawnPolusLifeSupp; + public static OptionItem RandomSpawnPolusElectrical; + public static OptionItem RandomSpawnPolusSecurity; + public static OptionItem RandomSpawnPolusDropship; + public static OptionItem RandomSpawnPolusStorage; + public static OptionItem RandomSpawnPolusRocket; + public static OptionItem RandomSpawnPolusLaboratory; + public static OptionItem RandomSpawnPolusToilet; + public static OptionItem RandomSpawnPolusSpecimens; + //AIrShip + public static OptionItem RandomSpawnAirship; + public static OptionItem RandomSpawnAirshipBrig; + public static OptionItem RandomSpawnAirshipEngine; + public static OptionItem RandomSpawnAirshipKitchen; + public static OptionItem RandomSpawnAirshipCargoBay; + public static OptionItem RandomSpawnAirshipRecords; + public static OptionItem RandomSpawnAirshipMainHall; + public static OptionItem RandomSpawnAirshipNapRoom; + public static OptionItem RandomSpawnAirshipMeetingRoom; + public static OptionItem RandomSpawnAirshipGapRoom; + public static OptionItem RandomSpawnAirshipVaultRoom; + public static OptionItem RandomSpawnAirshipComms; + public static OptionItem RandomSpawnAirshipCockpit; + public static OptionItem RandomSpawnAirshipArmory; + public static OptionItem RandomSpawnAirshipViewingDeck; + public static OptionItem RandomSpawnAirshipSecurity; + public static OptionItem RandomSpawnAirshipElectrical; + public static OptionItem RandomSpawnAirshipMedical; + public static OptionItem RandomSpawnAirshipToilet; + public static OptionItem RandomSpawnAirshipShowers; + //Fungle + public static OptionItem RandomSpawnFungle; + public static OptionItem RandomSpawnFungleKitchen; + public static OptionItem RandomSpawnFungleBeach; + public static OptionItem RandomSpawnFungleCafeteria; + public static OptionItem RandomSpawnFungleRecRoom; + public static OptionItem RandomSpawnFungleBonfire; + public static OptionItem RandomSpawnFungleDropship; + public static OptionItem RandomSpawnFungleStorage; + public static OptionItem RandomSpawnFungleMeetingRoom; + public static OptionItem RandomSpawnFungleSleepingQuarters; + public static OptionItem RandomSpawnFungleLaboratory; + public static OptionItem RandomSpawnFungleGreenhouse; + public static OptionItem RandomSpawnFungleReactor; + public static OptionItem RandomSpawnFungleJungleTop; + public static OptionItem RandomSpawnFungleJungleBottom; + public static OptionItem RandomSpawnFungleLookout; + public static OptionItem RandomSpawnFungleMiningPit; + public static OptionItem RandomSpawnFungleHighlands; + public static OptionItem RandomSpawnFungleUpperEngine; + public static OptionItem RandomSpawnFunglePrecipice; + public static OptionItem RandomSpawnFungleComms; // 投票モード public static OptionItem VoteMode; @@ -189,6 +284,8 @@ public static CustomGameMode CurrentGameMode public static OptionItem SabotageTimeControl; public static OptionItem PolusReactorTimeLimit; public static OptionItem AirshipReactorTimeLimit; + public static OptionItem FungleReactorTimeLimit; + public static OptionItem FungleMushroomMixupDuration; // サボタージュのクールダウン変更 public static OptionItem ModifySabotageCooldown; @@ -208,6 +305,7 @@ public static CustomGameMode CurrentGameMode public static OptionItem DisableAirshipMovingPlatform; public static OptionItem ResetDoorsEveryTurns; public static OptionItem DoorsResetMode; + public static OptionItem DisableFungleSporeTrigger; // その他 public static OptionItem FixFirstKillCooldown; @@ -381,6 +479,12 @@ public static void Load() AirshipReactorTimeLimit = FloatOptionItem.Create(100802, "AirshipReactorTimeLimit", new(1f, 90f, 1f), 60f, TabGroup.MainSettings, false).SetParent(SabotageTimeControl) .SetValueFormat(OptionFormat.Seconds) .SetGameMode(CustomGameMode.Standard); + FungleReactorTimeLimit = FloatOptionItem.Create(100803, "FungleReactorTimeLimit", new(1f, 90f, 1f), 60f, TabGroup.MainSettings, false).SetParent(SabotageTimeControl) + .SetValueFormat(OptionFormat.Seconds) + .SetGameMode(CustomGameMode.Standard); + FungleMushroomMixupDuration = FloatOptionItem.Create(100804, "FungleMushroomMixupDuration", new(1f, 20f, 1f), 10f, TabGroup.MainSettings, false).SetParent(SabotageTimeControl) + .SetValueFormat(OptionFormat.Seconds) + .SetGameMode(CustomGameMode.Standard); // サボタージュのクールダウン変更 ModifySabotageCooldown = BooleanOptionItem.Create(100810, "ModifySabotageCooldown", false, TabGroup.MainSettings, false) @@ -412,6 +516,7 @@ public static void Load() DisableAirshipMovingPlatform = BooleanOptionItem.Create(101700, "DisableAirshipMovingPlatform", false, TabGroup.MainSettings, false).SetParent(MapModification); ResetDoorsEveryTurns = BooleanOptionItem.Create(101800, "ResetDoorsEveryTurns", false, TabGroup.MainSettings, false).SetParent(MapModification); DoorsResetMode = StringOptionItem.Create(101810, "DoorsResetMode", EnumHelper.GetAllNames(), 0, TabGroup.MainSettings, false).SetParent(ResetDoorsEveryTurns); + DisableFungleSporeTrigger = BooleanOptionItem.Create(101900, "DisableFungleSporeTrigger", false, TabGroup.MainSettings, false).SetParent(MapModification); // タスク無効化 DisableTasks = BooleanOptionItem.Create(100300, "DisableTasks", false, TabGroup.MainSettings, false) @@ -479,6 +584,10 @@ public static void Load() .SetGameMode(CustomGameMode.Standard); DisableAirshipVital = BooleanOptionItem.Create(101244, "DisableAirshipVital", false, TabGroup.MainSettings, false).SetParent(DisableAirshipDevices) .SetGameMode(CustomGameMode.Standard); + DisableFungleDevices = BooleanOptionItem.Create(101250, "DisableFungleDevices", false, TabGroup.MainSettings, false).SetParent(DisableDevices) + .SetGameMode(CustomGameMode.Standard); + DisableFungleVital = BooleanOptionItem.Create(101251, "DisableFungleVital", false, TabGroup.MainSettings, false).SetParent(DisableFungleDevices) + .SetGameMode(CustomGameMode.Standard); DisableDevicesIgnoreConditions = BooleanOptionItem.Create(101290, "IgnoreConditions", false, TabGroup.MainSettings, false).SetParent(DisableDevices) .SetGameMode(CustomGameMode.Standard); DisableDevicesIgnoreImpostors = BooleanOptionItem.Create(101291, "IgnoreImpostors", false, TabGroup.MainSettings, false).SetParent(DisableDevicesIgnoreConditions) @@ -506,18 +615,18 @@ public static void Load() .SetGameMode(CustomGameMode.All); // MapDleks = CustomOption.Create(100405, Color.white, "AddedDleks", false, RandomMapMode) // .SetGameMode(CustomGameMode.All); + AddedTheFungle = BooleanOptionItem.Create(100406, "AddedTheFungle", false, TabGroup.MainSettings, false).SetParent(RandomMapsMode); // ランダムスポーン - RandomSpawn = BooleanOptionItem.Create(101300, "RandomSpawn", true, TabGroup.MainSettings, false) + EnableRandomSpawn = BooleanOptionItem.Create(101300, "RandomSpawn", true, TabGroup.MainSettings, false) .SetHeader(true) .SetGameMode(CustomGameMode.All); - AirshipAdditionalSpawn = BooleanOptionItem.Create(101301, "AirshipAdditionalSpawn", false, TabGroup.MainSettings, false).SetParent(RandomSpawn) - .SetGameMode(CustomGameMode.All); + RandomSpawn.SetupCustomOption(); // ボタン回数同期 SyncButtonMode = BooleanOptionItem.Create(100200, "SyncButtonMode", false, TabGroup.MainSettings, false) - .SetHeader(true) - .SetGameMode(CustomGameMode.Standard); + .SetHeader(true) + .SetGameMode(CustomGameMode.Standard); SyncedButtonCount = IntegerOptionItem.Create(100201, "SyncedButtonCount", new(0, 100, 1), 10, TabGroup.MainSettings, false).SetParent(SyncButtonMode) .SetValueFormat(OptionFormat.Times) .SetGameMode(CustomGameMode.Standard); @@ -622,7 +731,7 @@ public static void Load() } public static void SetupRoleOptions(SimpleRoleInfo info) => - SetupRoleOptions(info.ConfigId, info.Tab, info.RoleName, info.AssignCountRule); + SetupRoleOptions(info.ConfigId, info.Tab, info.RoleName, info.AssignInfo.AssignCountRule); public static void SetupRoleOptions(int id, TabGroup tab, CustomRoles role, IntegerValueRule assignCountRule = null, CustomGameMode customGameMode = CustomGameMode.Standard) { if (role.IsVanilla()) return; diff --git a/Modules/OptionItem/OptionItem.cs b/Modules/OptionItem/OptionItem.cs index b5efeb5d7..2984af8d3 100644 --- a/Modules/OptionItem/OptionItem.cs +++ b/Modules/OptionItem/OptionItem.cs @@ -14,6 +14,9 @@ public abstract class OptionItem public static IReadOnlyDictionary FastOptions => _fastOptions; private static Dictionary _fastOptions = new(1024); public static int CurrentPreset { get; set; } +#if DEBUG + public static bool IdDuplicated { get; private set; } = false; +#endif #endregion // 必須情報 (コンストラクタで必ず設定させる必要がある値) @@ -106,6 +109,9 @@ public OptionItem(int id, string name, int defaultValue, TabGroup tab, bool isSi } else { +#if DEBUG + IdDuplicated = true; +#endif Logger.Error($"ID:{id}が重複しています", "OptionItem"); } } @@ -181,7 +187,7 @@ public virtual void Refresh() opt.oldValue = opt.Value = CurrentValue; } } - public void SetValue(int afterValue, bool doSave, bool doSync = true) + public virtual void SetValue(int afterValue, bool doSave, bool doSync = true) { int beforeValue = CurrentValue; if (IsSingleValue) diff --git a/Modules/OptionItem/PresetOptionItem.cs b/Modules/OptionItem/PresetOptionItem.cs index 9b62ff297..90b141e41 100644 --- a/Modules/OptionItem/PresetOptionItem.cs +++ b/Modules/OptionItem/PresetOptionItem.cs @@ -40,5 +40,10 @@ public override void SetValue(int value, bool doSync = true) base.SetValue(Rule.RepeatIndex(value), doSync); SwitchPreset(Rule.RepeatIndex(value)); } + public override void SetValue(int afterValue, bool doSave, bool doSync = true) + { + base.SetValue(Rule.RepeatIndex(afterValue), doSave, doSync); + SwitchPreset(Rule.RepeatIndex(afterValue)); + } } } \ No newline at end of file diff --git a/Modules/RPC.cs b/Modules/RPC.cs index 9ebe19b60..83f0f0c69 100644 --- a/Modules/RPC.cs +++ b/Modules/RPC.cs @@ -12,9 +12,9 @@ namespace TownOfHost { public enum CustomRPC { - VersionCheck = 60, - RequestRetryVersionCheck = 61, - SyncCustomSettings = 80, + VersionCheck = 80, + RequestRetryVersionCheck = 81, + SyncCustomSettings = 100, SetDeathReason, EndGame, PlaySound, @@ -123,7 +123,7 @@ public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)] byte c foreach (var co in OptionItem.AllOptions) { //すべてのカスタムオプションについてインデックス値で受信 - co.SetValue(reader.ReadInt32()); + co.SetValue(reader.ReadPackedInt32()); } break; case CustomRPC.SetDeathReason: @@ -174,11 +174,11 @@ static class RPC public static void SyncCustomSettingsRPC() { if (!AmongUsClient.Instance.AmHost) return; - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, 80, Hazel.SendOption.Reliable, -1); + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SyncCustomSettings, SendOption.Reliable, -1); foreach (var co in OptionItem.AllOptions) { //すべてのカスタムオプションについてインデックス値で送信 - writer.Write(co.GetValue()); + writer.WritePacked(co.GetValue()); } AmongUsClient.Instance.FinishRpcImmediately(writer); } diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 76fc083db..ed378467b 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -30,7 +30,11 @@ public static class Utils { public static bool IsActive(SystemTypes type) { - //Logger.Info($"SystemTypes:{type}", "IsActive"); + // ないものはfalse + if (!ShipStatus.Instance.Systems.ContainsKey(type)) + { + return false; + } int mapId = Main.NormalOptions.MapId; switch (type) { @@ -42,11 +46,6 @@ public static bool IsActive(SystemTypes type) case SystemTypes.Reactor: { if (mapId == 2) return false; - else if (mapId == 4) - { - var HeliSabotageSystem = ShipStatus.Instance.Systems[type].Cast(); - return HeliSabotageSystem != null && HeliSabotageSystem.IsActive; - } else { var ReactorSystemType = ShipStatus.Instance.Systems[type].Cast(); @@ -67,7 +66,7 @@ public static bool IsActive(SystemTypes type) } case SystemTypes.Comms: { - if (mapId == 1) + if (mapId is 1 or 5) { var HqHudSystemType = ShipStatus.Instance.Systems[type].Cast(); return HqHudSystemType != null && HqHudSystemType.IsActive; @@ -78,10 +77,26 @@ public static bool IsActive(SystemTypes type) return HudOverrideSystemType != null && HudOverrideSystemType.IsActive; } } + case SystemTypes.HeliSabotage: + { + var HeliSabotageSystem = ShipStatus.Instance.Systems[type].Cast(); + return HeliSabotageSystem != null && HeliSabotageSystem.IsActive; + } + case SystemTypes.MushroomMixupSabotage: + { + var mushroomMixupSabotageSystem = ShipStatus.Instance.Systems[type].TryCast(); + return mushroomMixupSabotageSystem != null && mushroomMixupSabotageSystem.IsActive; + } default: return false; } } + public static SystemTypes GetCriticalSabotageSystemType() => (MapNames)Main.NormalOptions.MapId switch + { + MapNames.Polus => SystemTypes.Laboratory, + MapNames.Airship => SystemTypes.HeliSabotage, + _ => SystemTypes.Reactor, + }; public static void SetVision(this IGameOptions opt, bool HasImpVision) { if (HasImpVision) @@ -146,9 +161,7 @@ public static bool KillFlashCheck(MurderInfo info, PlayerControl seer) public static void KillFlash(this PlayerControl player) { //キルフラッシュ(ブラックアウト+リアクターフラッシュ)の処理 - bool ReactorCheck = false; //リアクターフラッシュの確認 - if (Main.NormalOptions.MapId == 2) ReactorCheck = IsActive(SystemTypes.Laboratory); - else ReactorCheck = IsActive(SystemTypes.Reactor); + bool ReactorCheck = IsActive(GetCriticalSabotageSystemType()); var Duration = Options.KillFlashDuration.GetFloat(); if (ReactorCheck) Duration += 0.2f; //リアクター中はブラックアウトを長くする @@ -204,7 +217,7 @@ public static (bool enabled, string text) GetRoleNameAndProgressTextData(PlayerC /// 見る側 /// 見られる側 /// 構築されたRoleName - private static string GetDisplayRoleName(PlayerControl seer, PlayerControl seen = null) + public static string GetDisplayRoleName(PlayerControl seer, PlayerControl seen = null) { seen ??= seer; //デフォルト値 @@ -428,15 +441,7 @@ public static bool HasTasks(GameData.PlayerInfo p, bool ForRecompute = true) private static string GetProgressText(PlayerControl seer, PlayerControl seen = null) { seen ??= seer; - var comms = false; - foreach (PlayerTask task in PlayerControl.LocalPlayer.myTasks) - { - if (task.TaskType == TaskTypes.FixComms) - { - comms = true; - break; - } - } + var comms = IsActive(SystemTypes.Comms); bool enabled = seer == seen || (Main.VisibleTasksCount && !seer.IsAlive() && Options.GhostCanSeeOtherTasks.GetBool()); string text = GetProgressText(seen.PlayerId, comms); @@ -500,10 +505,20 @@ public static void ShowActiveSettingsHelp(byte PlayerId = byte.MaxValue) if (Options.RandomMapsMode.GetBool()) { SendMessage(GetString("RandomMapsModeInfo"), PlayerId); } if (Options.IsStandardHAS) { SendMessage(GetString("StandardHASInfo"), PlayerId); } if (Options.EnableGM.GetBool()) { SendMessage(GetRoleName(CustomRoles.GM) + GetString("GMInfoLong"), PlayerId); } - foreach (var role in CustomRolesHelper.AllRoles) + foreach (var role in CustomRolesHelper.AllStandardRoles) { - if (role is CustomRoles.HASFox or CustomRoles.HASTroll) continue; - if (role.IsEnable() && !role.IsVanilla()) SendMessage(GetRoleName(role) + GetString(Enum.GetName(typeof(CustomRoles), role) + "InfoLong"), PlayerId); + if (role.IsEnable()) + { + if (role.GetRoleInfo()?.Description is { } description) + { + SendMessage(description.FullFormatHelp, PlayerId, removeTags: false); + } + // RoleInfoがない役職は従来処理 + else + { + SendMessage(GetRoleName(role) + GetString(Enum.GetName(typeof(CustomRoles), role) + "InfoLong"), PlayerId); + } + } } } if (Options.NoGameEnd.GetBool()) { SendMessage(GetString("NoGameEndInfo"), PlayerId); } @@ -534,24 +549,68 @@ public static void ShowActiveSettings(byte PlayerId = byte.MaxValue) if (RoleAssignManager.OptionAssignMode.GetBool()) { ShowChildrenSettings(RoleAssignManager.OptionAssignMode, ref sb); + CheckPageChange(PlayerId, sb); } foreach (var role in Options.CustomRoleCounts) { if (!role.Key.IsEnable()) continue; + if (role.Key is CustomRoles.HASFox or CustomRoles.HASTroll) continue; + sb.Append($"\n【{GetRoleName(role.Key)}×{role.Key.GetCount()}】\n"); ShowChildrenSettings(Options.CustomRoleSpawnChances[role.Key], ref sb); + CheckPageChange(PlayerId, sb); } foreach (var opt in OptionItem.AllOptions.Where(x => x.GetBool() && x.Parent == null && x.Id >= 80000 && !x.IsHiddenOn(Options.CurrentGameMode))) { - if (opt.Name is "KillFlashDuration" or "RoleAssigningAlgorithm") - sb.Append($"\n【{opt.GetName(true)}: {opt.GetString()}】\n"); + if (opt.Name is "RandomSpawn") + { + foreach (var randomOpt in opt.Children) + { + if ((randomOpt.Id / 100) % 10 != mapId) continue; + //現在のマップのみ表示する + if (randomOpt.GetBool()) + { + //Onの時は頭に改ページを入れる + CheckPageChange(PlayerId, sb, true); + sb.Append($"\n【{opt.GetName(true)}】"); + sb.Append($"\n {randomOpt.GetName(true)}: {randomOpt.GetString()}\n"); + + ShowChildrenSettings(randomOpt, ref sb, 1); + } + else + { + //オフならそのままで大丈夫 + sb.Append($"\n【{opt.GetName(true)}】"); + sb.Append($"\n {randomOpt.GetName(true)}: {randomOpt.GetString()}\n"); + } + } + CheckPageChange(PlayerId, sb); + } else - sb.Append($"\n【{opt.GetName(true)}】\n"); - ShowChildrenSettings(opt, ref sb); + { + if (opt.Name is "KillFlashDuration" or "RoleAssigningAlgorithm") + sb.Append($"\n【{opt.GetName(true)}: {opt.GetString()}】\n"); + else + sb.Append($"\n【{opt.GetName(true)}】\n"); + ShowChildrenSettings(opt, ref sb); + CheckPageChange(PlayerId, sb); + } } } SendMessage(sb.ToString(), PlayerId, removeTags: false); } + + private static void CheckPageChange(byte PlayerId, StringBuilder sb, bool force = false) + { + //2Byte文字想定で1000byt越えるならページを変える + if (force || sb.Length > 500) + { + SendMessage(sb.ToString(), PlayerId, removeTags: false); + sb.Clear(); + sb.AppendFormat("", ActiveSettingsSize); + } + } + public static void CopyCurrentSettings() { var sb = new StringBuilder(); @@ -594,9 +653,8 @@ public static void ShowActiveRoles(byte PlayerId = byte.MaxValue) sb.AppendFormat("", ActiveSettingsSize); sb.Append("").Append(GetString("Roles")).Append('\n').Append(""); sb.AppendFormat("\n{0}:{1}", GetRoleName(CustomRoles.GM), Options.EnableGM.GetString()); - foreach (CustomRoles role in CustomRolesHelper.AllRoles) + foreach (CustomRoles role in CustomRolesHelper.AllStandardRoles) { - if (role is CustomRoles.HASFox or CustomRoles.HASTroll) continue; if (role.IsEnable()) sb.AppendFormat("\n{0}:{1}x{2}", GetRoleName(role), $"{role.GetChance()}%", role.GetCount()); } SendMessage(sb.ToString(), PlayerId, removeTags: false); @@ -773,6 +831,7 @@ public static void NotifyRoles(bool isForMeeting = false, PlayerControl SpecifyS seerList = new(); seerList.Add(SpecifySeer); } + var isMushroomMixupActive = IsActive(SystemTypes.MushroomMixupSabotage); //seer:ここで行われた変更を見ることができるプレイヤー //target:seerが見ることができる変更の対象となるプレイヤー foreach (var seer in seerList) @@ -781,59 +840,66 @@ public static void NotifyRoles(bool isForMeeting = false, PlayerControl SpecifyS if (seer == null || seer.Data.Disconnected) continue; if (seer.IsModClient()) continue; + var seerRole = seer.GetRoleClass(); string fontSize = isForMeeting ? "1.5" : Main.RoleTextSize.ToString(); if (isForMeeting && (seer.GetClient().PlatformData.Platform is Platforms.Playstation or Platforms.Switch)) fontSize = "70%"; logger.Info("NotifyRoles-Loop1-" + seer.GetNameWithRole() + ":START"); - var seerRole = seer.GetRoleClass(); - - //名前の後ろに付けるマーカー - SelfMark.Clear(); + // 会議じゃなくて,キノコカオス中で,seerが生きていてdesyncインポスターの場合に自身の名前を消す + if (!isForMeeting && isMushroomMixupActive && seer.IsAlive() && !seer.Is(CustomRoleTypes.Impostor) && seer.GetCustomRole().GetRoleInfo()?.IsDesyncImpostor == true) + { + seer.RpcSetNamePrivate("", true, force: NoCache); + } + else + { + //名前の後ろに付けるマーカー + SelfMark.Clear(); - //seer役職が対象のMark - SelfMark.Append(seerRole?.GetMark(seer, isForMeeting: isForMeeting)); - //seerに関わらず発動するMark - SelfMark.Append(CustomRoleManager.GetMarkOthers(seer, isForMeeting: isForMeeting)); + //seer役職が対象のMark + SelfMark.Append(seerRole?.GetMark(seer, isForMeeting: isForMeeting)); + //seerに関わらず発動するMark + SelfMark.Append(CustomRoleManager.GetMarkOthers(seer, isForMeeting: isForMeeting)); - //ハートマークを付ける(自分に) - if (seer.Is(CustomRoles.Lovers)) SelfMark.Append(ColorString(GetRoleColor(CustomRoles.Lovers), "♡")); + //ハートマークを付ける(自分に) + if (seer.Is(CustomRoles.Lovers)) SelfMark.Append(ColorString(GetRoleColor(CustomRoles.Lovers), "♡")); - //Markとは違い、改行してから追記されます。 - SelfSuffix.Clear(); + //Markとは違い、改行してから追記されます。 + SelfSuffix.Clear(); - //seer役職が対象のLowerText - SelfSuffix.Append(seerRole?.GetLowerText(seer, isForMeeting: isForMeeting)); - //seerに関わらず発動するLowerText - SelfSuffix.Append(CustomRoleManager.GetLowerTextOthers(seer, isForMeeting: isForMeeting)); + //seer役職が対象のLowerText + SelfSuffix.Append(seerRole?.GetLowerText(seer, isForMeeting: isForMeeting)); + //seerに関わらず発動するLowerText + SelfSuffix.Append(CustomRoleManager.GetLowerTextOthers(seer, isForMeeting: isForMeeting)); - //seer役職が対象のSuffix - SelfSuffix.Append(seerRole?.GetSuffix(seer, isForMeeting: isForMeeting)); - //seerに関わらず発動するSuffix - SelfSuffix.Append(CustomRoleManager.GetSuffixOthers(seer, isForMeeting: isForMeeting)); + //seer役職が対象のSuffix + SelfSuffix.Append(seerRole?.GetSuffix(seer, isForMeeting: isForMeeting)); + //seerに関わらず発動するSuffix + SelfSuffix.Append(CustomRoleManager.GetSuffixOthers(seer, isForMeeting: isForMeeting)); - var deviceUnusableNotify = DeviceTimer.GetNameNotifyText(seer); - if (deviceUnusableNotify != string.Empty) - { - SelfSuffix.Append(deviceUnusableNotify); - } + var deviceUnusableNotify = DeviceTimer.GetNameNotifyText(seer); + if (deviceUnusableNotify != string.Empty) + { + SelfSuffix.Append(deviceUnusableNotify); + } - //RealNameを取得 なければ現在の名前をRealNamesに書き込む - string SeerRealName = seer.GetRealName(isForMeeting); + //RealNameを取得 なければ現在の名前をRealNamesに書き込む + string SeerRealName = seer.GetRealName(isForMeeting); - if (!isForMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool()) - SeerRealName = seer.GetRoleInfo(); + if (!isForMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool()) + SeerRealName = seer.GetRoleInfo(); - //seerの役職名とSelfTaskTextとseerのプレイヤー名とSelfMarkを合成 - var (enabled, text) = GetRoleNameAndProgressTextData(seer); - string SelfRoleName = enabled ? $"{text}" : ""; - string SelfDeathReason = seer.KnowDeathReason(seer) ? $"({ColorString(GetRoleColor(CustomRoles.Doctor), GetVitalText(seer.PlayerId))})" : ""; - string SelfName = $"{ColorString(seer.GetRoleColor(), SeerRealName)}{SelfDeathReason}{SelfMark}"; - SelfName = SelfRoleName + "\r\n" + SelfName; - SelfName += SelfSuffix.ToString() == "" ? "" : "\r\n " + SelfSuffix.ToString(); - if (!isForMeeting) SelfName += "\r\n"; + //seerの役職名とSelfTaskTextとseerのプレイヤー名とSelfMarkを合成 + var (enabled, text) = GetRoleNameAndProgressTextData(seer); + string SelfRoleName = enabled ? $"{text}" : ""; + string SelfDeathReason = seer.KnowDeathReason(seer) ? $"({ColorString(GetRoleColor(CustomRoles.Doctor), GetVitalText(seer.PlayerId))})" : ""; + string SelfName = $"{ColorString(seer.GetRoleColor(), SeerRealName)}{SelfDeathReason}{SelfMark}"; + SelfName = SelfRoleName + "\r\n" + SelfName; + SelfName += SelfSuffix.ToString() == "" ? "" : "\r\n " + SelfSuffix.ToString(); + if (!isForMeeting) SelfName += "\r\n"; - //適用 - seer.RpcSetNamePrivate(SelfName, true, force: NoCache); + //適用 + seer.RpcSetNamePrivate(SelfName, true, force: NoCache); + } //seerが死んでいる場合など、必要なときのみ第二ループを実行する if (seer.Data.IsDead //seerが死んでいる @@ -848,6 +914,7 @@ public static void NotifyRoles(bool isForMeeting = false, PlayerControl SpecifyS || seer.IsNeutralKiller() //seerがキル出来るニュートラル || IsActive(SystemTypes.Electrical) || IsActive(SystemTypes.Comms) + || isMushroomMixupActive || NoCache || ForceLoop ) @@ -858,62 +925,70 @@ public static void NotifyRoles(bool isForMeeting = false, PlayerControl SpecifyS if (target == seer) continue; logger.Info("NotifyRoles-Loop2-" + target.GetNameWithRole() + ":START"); - //名前の後ろに付けるマーカー - TargetMark.Clear(); - - //seer役職が対象のMark - TargetMark.Append(seerRole?.GetMark(seer, target, isForMeeting)); - //seerに関わらず発動するMark - TargetMark.Append(CustomRoleManager.GetMarkOthers(seer, target, isForMeeting)); - - //ハートマークを付ける(相手に) - if (seer.Is(CustomRoles.Lovers) && target.Is(CustomRoles.Lovers)) + // 会議じゃなくて,キノコカオス中で,targetが生きていてseerがdesyncインポスターの場合にtargetの名前を消す + if (!isForMeeting && isMushroomMixupActive && target.IsAlive() && !seer.Is(CustomRoleTypes.Impostor) && seer.GetCustomRole().GetRoleInfo()?.IsDesyncImpostor == true) { - TargetMark.Append($"♡"); + target.RpcSetNamePrivate("", true, seer, force: NoCache); } - //霊界からラバーズ視認 - else if (seer.Data.IsDead && !seer.Is(CustomRoles.Lovers) && target.Is(CustomRoles.Lovers)) - { - TargetMark.Append($"♡"); - } - - //他人の役職とタスクは幽霊が他人の役職を見れるようになっていてかつ、seerが死んでいる場合のみ表示されます。それ以外の場合は空になります。 - var targetRoleData = GetRoleNameAndProgressTextData(seer, target); - var TargetRoleText = targetRoleData.enabled ? $"{targetRoleData.text}\r\n" : ""; - - TargetSuffix.Clear(); - //seerに関わらず発動するLowerText - TargetSuffix.Append(CustomRoleManager.GetLowerTextOthers(seer, target, isForMeeting: isForMeeting)); - - //seer役職が対象のSuffix - TargetSuffix.Append(seerRole?.GetSuffix(seer, target, isForMeeting: isForMeeting)); - //seerに関わらず発動するSuffix - TargetSuffix.Append(CustomRoleManager.GetSuffixOthers(seer, target, isForMeeting: isForMeeting)); - // 空でなければ先頭に改行を挿入 - if (TargetSuffix.Length > 0) + else { - TargetSuffix.Insert(0, "\r\n"); + //名前の後ろに付けるマーカー + TargetMark.Clear(); + + //seer役職が対象のMark + TargetMark.Append(seerRole?.GetMark(seer, target, isForMeeting)); + //seerに関わらず発動するMark + TargetMark.Append(CustomRoleManager.GetMarkOthers(seer, target, isForMeeting)); + + //ハートマークを付ける(相手に) + if (seer.Is(CustomRoles.Lovers) && target.Is(CustomRoles.Lovers)) + { + TargetMark.Append($"♡"); + } + //霊界からラバーズ視認 + else if (seer.Data.IsDead && !seer.Is(CustomRoles.Lovers) && target.Is(CustomRoles.Lovers)) + { + TargetMark.Append($"♡"); + } + + //他人の役職とタスクは幽霊が他人の役職を見れるようになっていてかつ、seerが死んでいる場合のみ表示されます。それ以外の場合は空になります。 + var targetRoleData = GetRoleNameAndProgressTextData(seer, target); + var TargetRoleText = targetRoleData.enabled ? $"{targetRoleData.text}\r\n" : ""; + + TargetSuffix.Clear(); + //seerに関わらず発動するLowerText + TargetSuffix.Append(CustomRoleManager.GetLowerTextOthers(seer, target, isForMeeting: isForMeeting)); + + //seer役職が対象のSuffix + TargetSuffix.Append(seerRole?.GetSuffix(seer, target, isForMeeting: isForMeeting)); + //seerに関わらず発動するSuffix + TargetSuffix.Append(CustomRoleManager.GetSuffixOthers(seer, target, isForMeeting: isForMeeting)); + // 空でなければ先頭に改行を挿入 + if (TargetSuffix.Length > 0) + { + TargetSuffix.Insert(0, "\r\n"); + } + + //RealNameを取得 なければ現在の名前をRealNamesに書き込む + string TargetPlayerName = target.GetRealName(isForMeeting); + + //ターゲットのプレイヤー名の色を書き換えます。 + TargetPlayerName = TargetPlayerName.ApplyNameColorData(seer, target, isForMeeting); + + string TargetDeathReason = ""; + if (seer.KnowDeathReason(target)) + TargetDeathReason = $"({ColorString(GetRoleColor(CustomRoles.Doctor), GetVitalText(target.PlayerId))})"; + + if (IsActive(SystemTypes.Comms) && Options.CommsCamouflage.GetBool() && !isForMeeting) + TargetPlayerName = $"{TargetPlayerName}"; + + //全てのテキストを合成します。 + string TargetName = $"{TargetRoleText}{TargetPlayerName}{TargetDeathReason}{TargetMark}{TargetSuffix}"; + + //適用 + target.RpcSetNamePrivate(TargetName, true, seer, force: NoCache); } - //RealNameを取得 なければ現在の名前をRealNamesに書き込む - string TargetPlayerName = target.GetRealName(isForMeeting); - - //ターゲットのプレイヤー名の色を書き換えます。 - TargetPlayerName = TargetPlayerName.ApplyNameColorData(seer, target, isForMeeting); - - string TargetDeathReason = ""; - if (seer.KnowDeathReason(target)) - TargetDeathReason = $"({ColorString(GetRoleColor(CustomRoles.Doctor), GetVitalText(target.PlayerId))})"; - - if (IsActive(SystemTypes.Comms) && Options.CommsCamouflage.GetBool() && !isForMeeting) - TargetPlayerName = $"{TargetPlayerName}"; - - //全てのテキストを合成します。 - string TargetName = $"{TargetRoleText}{TargetPlayerName}{TargetDeathReason}{TargetMark}{TargetSuffix}"; - - //適用 - target.RpcSetNamePrivate(TargetName, true, seer, force: NoCache); - logger.Info("NotifyRoles-Loop2-" + target.GetNameWithRole() + ":END"); } } @@ -1001,28 +1076,40 @@ public static void OpenDirectory(string path) } public static string SummaryTexts(byte id, bool isForChat) { - // 全プレイヤー中最長の名前の長さからプレイヤー名の後の水平位置を計算する - // 1em ≒ 半角2文字 - // 空白は0.5emとする - // SJISではアルファベットは1バイト,日本語は基本的に2バイト - var longestNameByteCount = Main.AllPlayerNames.Values.Select(name => name.GetByteCount()).OrderByDescending(byteCount => byteCount).FirstOrDefault(); - //最大11.5emとする(★+日本語10文字分+半角空白) - var pos = Math.Min(((float)longestNameByteCount / 2) + 1.5f /* ★+末尾の半角空白 */ , 11.5f); var builder = new StringBuilder(); - builder.Append(isForChat ? Main.AllPlayerNames[id] : ColorString(Main.PlayerColors[id], Main.AllPlayerNames[id])); - builder.AppendFormat("", pos).Append(isForChat ? GetProgressText(id).RemoveColorTags() : GetProgressText(id)).Append(""); - // "(00/00) " = 4em - pos += 4f; - builder.AppendFormat("", pos).Append(GetVitalText(id)).Append(""); - // "Lover's Suicide " = 8em - // "回線切断 " = 4.5em - pos += DestroyableSingleton.Instance.currentLanguage.languageID == SupportedLangs.English ? 8f : 4.5f; - builder.AppendFormat("", pos); - builder.Append(PlayerState.GetByPlayerId(id).GetPreviousRolesText()); - builder.Append(isForChat ? GetTrueRoleName(id, false).RemoveColorTags() : GetTrueRoleName(id, false)); - builder.Append(isForChat ? GetSubRolesText(id).RemoveColorTags() : GetSubRolesText(id)); - builder.Append(""); + // チャットならposタグを使わない(文字数削減) + if (isForChat) + { + builder.Append(Main.AllPlayerNames[id]); + builder.Append(": ").Append(GetProgressText(id).RemoveColorTags()); + builder.Append(' ').Append(GetVitalText(id)); + builder.Append(' ').Append(PlayerState.GetByPlayerId(id).GetPreviousRolesText().RemoveColorTags()).Append(GetTrueRoleName(id, false).RemoveColorTags()); + builder.Append(' ').Append(GetSubRolesText(id).RemoveColorTags()); + } + else + { + // 全プレイヤー中最長の名前の長さからプレイヤー名の後の水平位置を計算する + // 1em ≒ 半角2文字 + // 空白は0.5emとする + // SJISではアルファベットは1バイト,日本語は基本的に2バイト + var longestNameByteCount = Main.AllPlayerNames.Values.Select(name => name.GetByteCount()).OrderByDescending(byteCount => byteCount).FirstOrDefault(); + //最大11.5emとする(★+日本語10文字分+半角空白) + var pos = Math.Min(((float)longestNameByteCount / 2) + 1.5f /* ★+末尾の半角空白 */ , 11.5f); + builder.Append(ColorString(Main.PlayerColors[id], Main.AllPlayerNames[id])); + builder.AppendFormat("", pos).Append(GetProgressText(id)).Append(""); + // "(00/00) " = 4em + pos += 4f; + builder.AppendFormat("", pos).Append(GetVitalText(id)).Append(""); + // "Lover's Suicide " = 8em + // "回線切断 " = 4.5em + pos += DestroyableSingleton.Instance.currentLanguage.languageID == SupportedLangs.English ? 8f : 4.5f; + builder.AppendFormat("", pos); + builder.Append(PlayerState.GetByPlayerId(id).GetPreviousRolesText()); + builder.Append(GetTrueRoleName(id, false)); + builder.Append(GetSubRolesText(id)); + builder.Append(""); + } return builder.ToString(); } public static string RemoveHtmlTags(this string str) => Regex.Replace(str, "<[^>]*?>", ""); diff --git a/Patches/AirshipStatus.cs b/Patches/AirshipStatus.cs index a76b74591..fa7744024 100644 --- a/Patches/AirshipStatus.cs +++ b/Patches/AirshipStatus.cs @@ -10,7 +10,14 @@ public static class AirshipStatusPrespawnStepPatch { public static bool Prefix() { - return !PlayerControl.LocalPlayer.Is(CustomRoles.GM); // GMは湧き画面をスキップ + if (PlayerControl.LocalPlayer.Is(CustomRoles.GM)) + { + RandomSpawn.hostReady = true; + RandomSpawn.AirshipSpawn(PlayerControl.LocalPlayer); + // GMは湧き画面をスキップ + return false; + } + return true; } } } \ No newline at end of file diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index aaad9f9b4..f320701e4 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -131,7 +131,6 @@ public static bool Prefix(ChatController __instance) cancelVal = "/dis"; break; } - ShipStatus.Instance.RpcRepairSystem(SystemTypes.Admin, 0); break; case "/h": @@ -207,9 +206,15 @@ public static bool Prefix(ChatController __instance) case "/m": case "/myrole": canceled = true; - var role = PlayerControl.LocalPlayer.GetCustomRole(); if (GameStates.IsInGame) - HudManager.Instance.Chat.AddChat(PlayerControl.LocalPlayer, GetString(role.ToString()) + PlayerControl.LocalPlayer.GetRoleInfo(true)); + { + var role = PlayerControl.LocalPlayer.GetCustomRole(); + HudManager.Instance.Chat.AddChat( + PlayerControl.LocalPlayer, + role.GetRoleInfo()?.Description?.FullFormatHelp ?? + // roleInfoがない役職 + GetString(role.ToString()) + PlayerControl.LocalPlayer.GetRoleInfo(true)); + } break; case "/t": @@ -312,7 +317,16 @@ public static void GetRolesInfo(string role) if (String.Compare(role, roleName, true) == 0 || String.Compare(role, roleShort, true) == 0) { - Utils.SendMessage(GetString(roleName) + GetString($"{roleName}InfoLong")); + var roleInfo = r.Key.GetRoleInfo(); + if (roleInfo != null && roleInfo.Description != null) + { + Utils.SendMessage(roleInfo.Description.FullFormatHelp, removeTags: false); + } + // RoleInfoがない役職は従来の処理 + else + { + Utils.SendMessage(GetString(roleName) + GetString($"{roleName}InfoLong")); + } return; } @@ -392,9 +406,19 @@ public static void OnReceiveChat(PlayerControl player, string text) case "/m": case "/myrole": - var role = player.GetCustomRole(); if (GameStates.IsInGame) - Utils.SendMessage(GetString(role.ToString()) + player.GetRoleInfo(true), player.PlayerId); + { + var role = player.GetCustomRole(); + if (role.GetRoleInfo()?.Description is { } description) + { + Utils.SendMessage(description.FullFormatHelp, player.PlayerId, removeTags: false); + } + // roleInfoがない役職 + else + { + Utils.SendMessage(GetString(role.ToString()) + player.GetRoleInfo(true), player.PlayerId); + } + } break; case "/t": @@ -473,7 +497,7 @@ public static bool Prefix(PlayerControl __instance, string chatText, ref bool __ if (AmongUsClient.Instance.AmClient && DestroyableSingleton.Instance) DestroyableSingleton.Instance.Chat.AddChat(__instance, chatText); if (chatText.Contains("who", StringComparison.OrdinalIgnoreCase)) - DestroyableSingleton.Instance.SendWho(); + DestroyableSingleton.Instance.SendWho(); MessageWriter messageWriter = AmongUsClient.Instance.StartRpc(__instance.NetId, (byte)RpcCalls.SendChat, SendOption.None); messageWriter.Write(chatText); messageWriter.EndMessage(); diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index bf58d2e87..008870575 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -1,8 +1,11 @@ +using System.Collections; using System.Collections.Generic; using System.Linq; using AmongUs.GameOptions; +using BepInEx.Unity.IL2CPP.Utils.Collections; using HarmonyLib; using Hazel; +using UnityEngine; using TownOfHost.Roles.Core; using TownOfHost.Roles.Core.Interfaces; @@ -65,10 +68,11 @@ public static bool Prefix() { if (pc.GetRoleClass() is IAdditionalWinner additionalWinner) { - if (additionalWinner.CheckWin(out var winnerType)) + var winnerRole = pc.GetCustomRole(); + if (additionalWinner.CheckWin(ref winnerRole)) { CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); - CustomWinnerHolder.AdditionalWinnerTeams.Add(winnerType); + CustomWinnerHolder.AdditionalWinnerRoles.Add(winnerRole); } } } @@ -81,11 +85,13 @@ public static bool Prefix() } public static void StartEndGame(GameOverReason reason) { - var sender = new CustomRpcSender("EndGameSender", SendOption.Reliable, true); - sender.StartMessage(-1); // 5: GameData - MessageWriter writer = sender.stream; + AmongUsClient.Instance.StartCoroutine(CoEndGame(AmongUsClient.Instance, reason).WrapToIl2Cpp()); + } + private static IEnumerator CoEndGame(AmongUsClient self, GameOverReason reason) + { + // サーバー側のパケットサイズ制限によりCustomRpcSenderが利用できないため,遅延を挟むことで順番の整合性を保つ. - //ゴーストロール化 + // バニラ画面でのアウトロを正しくするためのゴーストロール化 List ReviveRequiredPlayerIds = new(); var winner = CustomWinnerHolder.WinnerTeam; foreach (var pc in Main.AllPlayerControls) @@ -106,58 +112,45 @@ void SetGhostRole(bool ToGhostImpostor) if (ToGhostImpostor) { Logger.Info($"{pc.GetNameWithRole()}: ImpostorGhostに変更", "ResetRoleAndEndGame"); - sender.StartRpc(pc.NetId, RpcCalls.SetRole) - .Write((ushort)RoleTypes.ImpostorGhost) - .EndRpc(); - pc.SetRole(RoleTypes.ImpostorGhost); + pc.RpcSetRole(RoleTypes.ImpostorGhost); } else { Logger.Info($"{pc.GetNameWithRole()}: CrewmateGhostに変更", "ResetRoleAndEndGame"); - sender.StartRpc(pc.NetId, RpcCalls.SetRole) - .Write((ushort)RoleTypes.CrewmateGhost) - .EndRpc(); - pc.SetRole(RoleTypes.Crewmate); + pc.RpcSetRole(RoleTypes.CrewmateGhost); } } } // CustomWinnerHolderの情報の同期 - sender.StartRpc(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.EndGame); - CustomWinnerHolder.WriteTo(sender.stream); - sender.EndRpc(); + var winnerWriter = self.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.EndGame, SendOption.Reliable); + CustomWinnerHolder.WriteTo(winnerWriter); + self.FinishRpcImmediately(winnerWriter); + + // 蘇生を確実にゴーストロール設定の後に届けるための遅延 + yield return new WaitForSeconds(EndGameDelay); - // GameDataによる蘇生処理 - writer.StartMessage(1); // Data + if (ReviveRequiredPlayerIds.Count > 0) { - writer.WritePacked(GameData.Instance.NetId); // NetId - foreach (var info in GameData.Instance.AllPlayers) + // 蘇生 パケットが膨れ上がって死ぬのを防ぐため,1送信につき1人ずつ蘇生する + for (int i = 0; i < ReviveRequiredPlayerIds.Count; i++) { - if (ReviveRequiredPlayerIds.Contains(info.PlayerId)) - { - // 蘇生&メッセージ書き込み - info.IsDead = false; - writer.StartMessage(info.PlayerId); - info.Serialize(writer); - writer.EndMessage(); - } + var playerId = ReviveRequiredPlayerIds[i]; + var playerInfo = GameData.Instance.GetPlayerById(playerId); + // 蘇生 + playerInfo.IsDead = false; + // 送信 + GameData.Instance.SetDirtyBit(0b_1u << playerId); + AmongUsClient.Instance.SendAllStreamedObjects(); } - writer.EndMessage(); - } - - sender.EndMessage(); - - // バニラ側のゲーム終了RPC - writer.StartMessage(8); //8: EndGame - { - writer.Write(AmongUsClient.Instance.GameId); //GameId - writer.Write((byte)reason); //GameoverReason - writer.Write(false); //showAd + // ゲーム終了を確実に最後に届けるための遅延 + yield return new WaitForSeconds(EndGameDelay); } - writer.EndMessage(); - sender.SendMessage(); + // ゲーム終了 + GameManager.Instance.RpcEndGame(reason, false); } + private const float EndGameDelay = 0.2f; public static void SetPredicateToNormal() => predicate = new NormalGameEndPredicate(); public static void SetPredicateToHideAndSeek() => predicate = new HideAndSeekGameEndPredicate(); @@ -304,6 +297,7 @@ public virtual bool CheckGameEndBySabotage(out GameOverReason reason) ISystemType sys = null; if (systems.ContainsKey(SystemTypes.Reactor)) sys = systems[SystemTypes.Reactor]; else if (systems.ContainsKey(SystemTypes.Laboratory)) sys = systems[SystemTypes.Laboratory]; + else if (systems.ContainsKey(SystemTypes.HeliSabotage)) sys = systems[SystemTypes.HeliSabotage]; ICriticalSabotage critical; if (sys != null && // サボタージュ存在確認 diff --git a/Patches/ClientOptionsPatch.cs b/Patches/ClientOptionsPatch.cs index 4e0eb897d..a8a78018d 100644 --- a/Patches/ClientOptionsPatch.cs +++ b/Patches/ClientOptionsPatch.cs @@ -16,7 +16,6 @@ public static class OptionsMenuBehaviourStartPatch private static ClientActionItem ShowLobbySummary; private static ClientActionItem CopyGameCodeOnCreateLobby; private static ClientActionItem HauntMenuFocusCrewmate; - private static ClientOptionItem PutBackWinTextPosition; public static void Postfix(OptionsMenuBehaviour __instance) { @@ -55,10 +54,6 @@ public static void Postfix(OptionsMenuBehaviour __instance) { HauntMenuFocusCrewmate = ClientOptionItem.Create("HauntFocusCrew", Main.HauntMenuFocusCrewmate, __instance); } - if (PutBackWinTextPosition == null || PutBackWinTextPosition.ToggleButton == null) - { - PutBackWinTextPosition = ClientOptionItem.Create("PutBackWinTextPosition", Main.PutBackWinTextPosition, __instance); - } if (UnloadMod == null || UnloadMod.ToggleButton == null) { UnloadMod = ClientActionItem.Create("UnloadMod", ModUnloaderScreen.Show, __instance); diff --git a/Patches/ClientPatch.cs b/Patches/ClientPatch.cs index bf5d04e05..0efba6fd3 100644 --- a/Patches/ClientPatch.cs +++ b/Patches/ClientPatch.cs @@ -20,9 +20,10 @@ public static bool Prefix(GameStartManager __instance) Logger.SendInGame(message); return false; } - if (ModUpdater.isBroken || ModUpdater.hasUpdate || !VersionChecker.IsSupported) + if (ModUpdater.isBroken || ModUpdater.hasUpdate || !VersionChecker.IsSupported || !Main.IsPublicAvailableOnThisVersion) { var message = ""; + if (!Main.IsPublicAvailableOnThisVersion) message = GetString("PublicNotAvailableOnThisVersion"); if (!VersionChecker.IsSupported) message = GetString("UnsupportedVersion"); if (ModUpdater.isBroken) message = GetString("ModBrokenMessage"); if (ModUpdater.hasUpdate) message = GetString("CanNotJoinPublicRoomNoLatest"); @@ -38,7 +39,7 @@ class MMOnlineManagerStartPatch { public static void Postfix(MMOnlineManager __instance) { - if (!(ModUpdater.hasUpdate || ModUpdater.isBroken || !VersionChecker.IsSupported)) return; + if (!(ModUpdater.hasUpdate || ModUpdater.isBroken || !VersionChecker.IsSupported || !Main.IsPublicAvailableOnThisVersion)) return; var obj = GameObject.Find("FindGameButton"); if (obj) { @@ -61,6 +62,10 @@ public static void Postfix(MMOnlineManager __instance) { message = GetString("UnsupportedVersion"); } + else if(!Main.IsPublicAvailableOnThisVersion) + { + message = GetString("PublicNotAvailableOnThisVersion"); + } textObj.text = $"{Utils.ColorString(Color.red, message)}"; } } diff --git a/Patches/ConstantsPatch.cs b/Patches/ConstantsPatch.cs new file mode 100644 index 000000000..4980186d0 --- /dev/null +++ b/Patches/ConstantsPatch.cs @@ -0,0 +1,27 @@ +using HarmonyLib; + +namespace TownOfHost.Patches; + +[HarmonyPatch(typeof(Constants), nameof(Constants.GetBroadcastVersion))] +public static class ConstantsGetBroadcastVersionPatch +{ + public static void Postfix(ref int __result) + { + if (GameStates.IsLocalGame) + { + return; + } + __result += 25; + } +} + +// AU side bug? +[HarmonyPatch(typeof(Constants), nameof(Constants.IsVersionModded))] +public static class ConstantsIsVersionModdedPatch +{ + public static bool Prefix(ref bool __result) + { + __result = true; + return false; + } +} diff --git a/Patches/ControlPatch.cs b/Patches/ControlPatch.cs index 726bc752b..dfc9c266a 100644 --- a/Patches/ControlPatch.cs +++ b/Patches/ControlPatch.cs @@ -178,10 +178,10 @@ public static void Postfix(ControllerManager __instance) //エアシップのトイレのドアを全て開ける if (Input.GetKeyDown(KeyCode.P)) { - ShipStatus.Instance.RpcRepairSystem(SystemTypes.Doors, 79); - ShipStatus.Instance.RpcRepairSystem(SystemTypes.Doors, 80); - ShipStatus.Instance.RpcRepairSystem(SystemTypes.Doors, 81); - ShipStatus.Instance.RpcRepairSystem(SystemTypes.Doors, 82); + ShipStatus.Instance.RpcUpdateSystem(SystemTypes.Doors, 79); + ShipStatus.Instance.RpcUpdateSystem(SystemTypes.Doors, 80); + ShipStatus.Instance.RpcUpdateSystem(SystemTypes.Doors, 81); + ShipStatus.Instance.RpcUpdateSystem(SystemTypes.Doors, 82); } //現在の座標を取得 if (Input.GetKeyDown(KeyCode.I)) diff --git a/Patches/CredentialsPatch.cs b/Patches/CredentialsPatch.cs index c442529ea..960c081bc 100644 --- a/Patches/CredentialsPatch.cs +++ b/Patches/CredentialsPatch.cs @@ -81,6 +81,12 @@ static void Postfix(VersionShower __instance) } VersionChecker.Check(); +#if DEBUG + if (OptionItem.IdDuplicated) + { + ErrorText.Instance.AddError(ErrorCode.OptionIDDuplicate); + } +#endif if (SpecialEventText == null && TohLogo != null) { diff --git a/Patches/ExilePatch.cs b/Patches/ExilePatch.cs index e619c7894..2a0818df3 100644 --- a/Patches/ExilePatch.cs +++ b/Patches/ExilePatch.cs @@ -56,7 +56,7 @@ static void WrapUpPostfix(GameData.PlayerInfo exiled) var role = exiled.GetCustomRole(); var info = role.GetRoleInfo(); //霊界用暗転バグ対処 - if (!AntiBlackout.OverrideExiledPlayer && (Main.ResetCamPlayerList.Contains(exiled.PlayerId) || (info?.RequireResetCam ?? false))) + if (!AntiBlackout.OverrideExiledPlayer && info?.IsDesyncImpostor == true) exiled.Object?.ResetPlayerCam(1f); exiled.IsDead = true; @@ -74,7 +74,7 @@ static void WrapUpPostfix(GameData.PlayerInfo exiled) { pc.ResetKillCooldown(); } - if (Options.RandomSpawn.GetBool()) + if (RandomSpawn.IsRandomSpawn()) { RandomSpawn.SpawnMap map; switch (Main.NormalOptions.MapId) @@ -91,6 +91,10 @@ static void WrapUpPostfix(GameData.PlayerInfo exiled) map = new RandomSpawn.PolusSpawnMap(); Main.AllPlayerControls.Do(map.RandomTeleport); break; + case 5: + map = new RandomSpawn.FungleSpawnMap(); + Main.AllPlayerControls.Do(map.RandomTeleport); + break; } } FallFromLadder.Reset(); @@ -122,7 +126,7 @@ static void WrapUpFinalizer(GameData.PlayerInfo exiled) { var player = Utils.GetPlayerById(x.Key); var roleClass = CustomRoleManager.GetByPlayerId(x.Key); - var requireResetCam = player?.GetCustomRole().GetRoleInfo()?.RequireResetCam; + var requireResetCam = player?.GetCustomRole().GetRoleInfo()?.IsDesyncImpostor == true; var state = PlayerState.GetByPlayerId(x.Key); Logger.Info($"{player.GetNameWithRole()}を{x.Value}で死亡させました", "AfterMeetingDeath"); state.DeathReason = x.Value; @@ -130,7 +134,7 @@ static void WrapUpFinalizer(GameData.PlayerInfo exiled) player?.RpcExileV2(); if (x.Value == CustomDeathReason.Suicide) player?.SetRealKiller(player, true); - if (Main.ResetCamPlayerList.Contains(x.Key) || (requireResetCam.HasValue && requireResetCam.Value)) + if (requireResetCam) player?.ResetPlayerCam(1f); if (roleClass is Executioner executioner && executioner.TargetId == x.Key) Executioner.ChangeRoleByTarget(x.Key); diff --git a/Patches/GameOptionsMenuPatch.cs b/Patches/GameOptionsMenuPatch.cs index fbe6a53fe..049b2e491 100644 --- a/Patches/GameOptionsMenuPatch.cs +++ b/Patches/GameOptionsMenuPatch.cs @@ -138,7 +138,7 @@ public static void Postfix(GameOptionsMenu __instance) for (var i = 0; i < tabs.Count; i++) { - tabs[i].transform.position = new(0.8f * (i - 1) - tabs.Count / 2f, tabs[i].transform.position.y, tabs[i].transform.position.z); + tabs[i].transform.localPosition = new(0.8f * (i - 1) - tabs.Count / 3f, tabs[i].transform.localPosition.y, tabs[i].transform.localPosition.z); var button = tabs[i].GetComponentInChildren(); if (button == null) continue; var copiedIndex = i; @@ -369,4 +369,4 @@ public static bool Prefix(NormalGameOptionsV07 __instance, int numPlayers, bool return false; } } -} \ No newline at end of file +} diff --git a/Patches/GameStartManagerPatch.cs b/Patches/GameStartManagerPatch.cs index 47781b40d..8dfab1959 100644 --- a/Patches/GameStartManagerPatch.cs +++ b/Patches/GameStartManagerPatch.cs @@ -110,7 +110,7 @@ public static void Postfix(GameStartManager __instance) if (!AmongUsClient.Instance.AmHost) return; // Make Public Button - if (ModUpdater.isBroken || ModUpdater.hasUpdate || !Main.AllowPublicRoom || !VersionChecker.IsSupported) + if (ModUpdater.isBroken || ModUpdater.hasUpdate || !Main.AllowPublicRoom || !VersionChecker.IsSupported || !Main.IsPublicAvailableOnThisVersion) { __instance.MakePublicButton.color = Palette.DisabledClear; __instance.privatePublicText.color = Palette.DisabledClear; @@ -269,12 +269,14 @@ private static void SelectRandomMap() MIRAHQ = 1 Polus = 2 Dleks = 3 - TheAirShip = 4*/ + TheAirShip = 4 + TheFungle = 5*/ if (Options.AddedTheSkeld.GetBool()) randomMaps.Add(0); if (Options.AddedMiraHQ.GetBool()) randomMaps.Add(1); if (Options.AddedPolus.GetBool()) randomMaps.Add(2); // if (Options.AddedDleks.GetBool()) RandomMaps.Add(3); if (Options.AddedTheAirShip.GetBool()) randomMaps.Add(4); + if (Options.AddedTheFungle.GetBool()) randomMaps.Add(5); if (randomMaps.Count <= 0) return; var mapsId = randomMaps[rand.Next(randomMaps.Count)]; diff --git a/Patches/GetBroadcastVersionPatch.cs b/Patches/GetBroadcastVersionPatch.cs deleted file mode 100644 index 088efa602..000000000 --- a/Patches/GetBroadcastVersionPatch.cs +++ /dev/null @@ -1,17 +0,0 @@ -using HarmonyLib; - -namespace TownOfHost.Patches; - -[HarmonyPatch(typeof(Constants), nameof(Constants.GetBroadcastVersion))] -public static class GetBroadcastVersionPatch -{ - public static bool Prefix(ref int __result) - { - if (GameStates.IsLocalGame) - { - return true; - } - __result = Constants.GetVersion(2222, 0, 0, 0); - return false; - } -} diff --git a/Patches/HauntMenuMinigamePatch.cs b/Patches/HauntMenuMinigamePatch.cs index 1002719a5..18115da90 100644 --- a/Patches/HauntMenuMinigamePatch.cs +++ b/Patches/HauntMenuMinigamePatch.cs @@ -13,13 +13,20 @@ public static void BeginPostfix(HauntMenuMinigame __instance) __instance.SetFilter((int)HauntMenuMinigame.HauntFilters.Crewmate); } } + } - [HarmonyPatch(typeof(HauntMenuMinigame), nameof(HauntMenuMinigame.SetFilterText)), HarmonyPrefix] - public static bool SetFilterTextPrefix(HauntMenuMinigame __instance) + [HarmonyPatch(typeof(HauntMenuMinigame), nameof(HauntMenuMinigame.SetFilterText))] + public static class HauntMenuMinigameSetFilterTextPatch + { + public static bool Prefix(HauntMenuMinigame __instance) { - __instance.FilterText.text = __instance.HauntTarget.GetTrueRoleName(); - - return false; + if (__instance.HauntTarget != null && Options.GhostCanSeeOtherRoles.GetBool()) + { + // 役職表示をカスタムロール名で上書き + __instance.FilterText.text = Utils.GetDisplayRoleName(PlayerControl.LocalPlayer, __instance.HauntTarget); + return false; + } + return true; } } } diff --git a/Patches/HudPatch.cs b/Patches/HudPatch.cs index 3e8cb8d6e..9ab983002 100644 --- a/Patches/HudPatch.cs +++ b/Patches/HudPatch.cs @@ -4,7 +4,6 @@ using TownOfHost.Roles.Core; using TownOfHost.Roles.Core.Interfaces; -using TownOfHost.Roles.Neutral; using static TownOfHost.Translator; namespace TownOfHost @@ -198,19 +197,9 @@ public static void Postfix(HudManager __instance, [HarmonyArgument(2)] bool isAc if (!isActive) return; var player = PlayerControl.LocalPlayer; - if (player == null) return; - switch (player.GetCustomRole()) - { - case CustomRoles.Sheriff: - case CustomRoles.Arsonist: - __instance.SabotageButton.ToggleVisible(false); - break; - case CustomRoles.Jackal: - Jackal.SetHudActive(__instance, isActive); - break; - } __instance.KillButton.ToggleVisible(player.CanUseKillButton()); __instance.ImpostorVentButton.ToggleVisible(player.CanUseImpostorVentButton()); + __instance.SabotageButton.ToggleVisible(player.CanUseSabotageButton()); } } [HarmonyPatch(typeof(MapBehaviour), nameof(MapBehaviour.Show))] @@ -223,7 +212,7 @@ public static void Prefix(MapBehaviour __instance, ref MapOptions opts) if (opts.Mode is MapOptions.Modes.Normal or MapOptions.Modes.Sabotage) { var player = PlayerControl.LocalPlayer; - if (player.Is(CustomRoleTypes.Impostor) || (player.Is(CustomRoles.Jackal) && Jackal.CanUseSabotage) || player.Is(CustomRoles.Egoist)) + if (player.GetRoleClass() is IKiller killer && killer.CanUseSabotageButton()) opts.Mode = MapOptions.Modes.Sabotage; else opts.Mode = MapOptions.Modes.Normal; @@ -293,7 +282,7 @@ public static void InputEnter() } public static void Send() { - ShipStatus.Instance.RpcRepairSystem((SystemTypes)SystemType, amount); + ShipStatus.Instance.RpcUpdateSystem((SystemTypes)SystemType, (byte)amount); Reset(); } public static void Reset() diff --git a/Patches/ISystemType/DoorsSystemTypePatch.cs b/Patches/ISystemType/DoorsSystemTypePatch.cs new file mode 100644 index 000000000..94abce2c2 --- /dev/null +++ b/Patches/ISystemType/DoorsSystemTypePatch.cs @@ -0,0 +1,26 @@ +using HarmonyLib; +using Hazel; +using TownOfHost.Roles.Core; +using TownOfHost.Roles.Core.Interfaces; + +namespace TownOfHost.Patches.ISystemType; + +[HarmonyPatch(typeof(DoorsSystemType), nameof(DoorsSystemType.UpdateSystem))] +public static class DoorsSystemTypeUpdateSystemPatch +{ + public static bool Prefix(DoorsSystemType __instance, [HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] MessageReader msgReader) + { + byte amount; + { + var newReader = MessageReader.Get(msgReader); + amount = newReader.ReadByte(); + newReader.Recycle(); + } + + if (player.GetRoleClass() is ISystemTypeUpdateHook systemTypeUpdateHook && !systemTypeUpdateHook.UpdateDoorsSystem(__instance, amount)) + { + return false; + } + return true; + } +} diff --git a/Patches/ISystemType/HeliSabotageSystemPatch.cs b/Patches/ISystemType/HeliSabotageSystemPatch.cs new file mode 100644 index 000000000..c719f7363 --- /dev/null +++ b/Patches/ISystemType/HeliSabotageSystemPatch.cs @@ -0,0 +1,42 @@ +using HarmonyLib; +using Hazel; +using TownOfHost.Roles.Core; +using TownOfHost.Roles.Core.Interfaces; + +namespace TownOfHost.Patches.ISystemType; + +[HarmonyPatch(typeof(HeliSabotageSystem), nameof(HeliSabotageSystem.UpdateSystem))] +public static class HeliSabotageSystemUpdateSystemPatch +{ + public static bool Prefix(HeliSabotageSystem __instance, [HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] MessageReader msgReader) + { + byte amount; + { + var newReader = MessageReader.Get(msgReader); + amount = newReader.ReadByte(); + newReader.Recycle(); + } + + if (player.GetRoleClass() is ISystemTypeUpdateHook systemTypeUpdateHook && !systemTypeUpdateHook.UpdateHeliSabotageSystem(__instance, amount)) + { + return false; + } + return true; + } +} + +//参考 +//https://github.com/Koke1024/Town-Of-Moss/blob/main/TownOfMoss/Patches/MeltDownBoost.cs + +[HarmonyPatch(typeof(HeliSabotageSystem), nameof(HeliSabotageSystem.Deteriorate))] +public static class HeliSabotageSystemPatch +{ + public static void Prefix(HeliSabotageSystem __instance) + { + if (!__instance.IsActive || !Options.SabotageTimeControl.GetBool()) + return; + if (AirshipStatus.Instance != null) + if (__instance.Countdown >= Options.AirshipReactorTimeLimit.GetFloat()) + __instance.Countdown = Options.AirshipReactorTimeLimit.GetFloat(); + } +} diff --git a/Patches/ISystemType/HqHudSystemTypePatch.cs b/Patches/ISystemType/HqHudSystemTypePatch.cs new file mode 100644 index 000000000..eb402b95e --- /dev/null +++ b/Patches/ISystemType/HqHudSystemTypePatch.cs @@ -0,0 +1,43 @@ +using HarmonyLib; +using Hazel; +using TownOfHost.Roles.Core; +using TownOfHost.Roles.Core.Interfaces; +using TownOfHost.Roles.Neutral; + +namespace TownOfHost.Patches.ISystemType; + +[HarmonyPatch(typeof(HqHudSystemType), nameof(HqHudSystemType.UpdateSystem))] +public static class HqHudSystemTypeUpdateSystemPatch +{ + public static bool Prefix(HqHudSystemType __instance, [HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] MessageReader msgReader) + { + byte amount; + { + var newReader = MessageReader.Get(msgReader); + amount = newReader.ReadByte(); + newReader.Recycle(); + } + + var tags = (HqHudSystemType.Tags)(amount & HqHudSystemType.TagMask); + var playerRole = player.GetRoleClass(); + var isMadmate = + player.Is(CustomRoleTypes.Madmate) || + // マッド属性化時に削除 + (playerRole is SchrodingerCat schrodingerCat && schrodingerCat.AmMadmate); + if (tags == HqHudSystemType.Tags.FixBit && isMadmate && !Options.MadmateCanFixComms.GetBool()) + { + return false; + } + + if (playerRole is ISystemTypeUpdateHook systemTypeUpdateHook && !systemTypeUpdateHook.UpdateHqHudSystem(__instance, amount)) + { + return false; + } + return true; + } + public static void Postfix() + { + Camouflage.CheckCamouflage(); + Utils.NotifyRoles(); + } +} diff --git a/Patches/ISystemType/HudOverrideSystemTypePatch.cs b/Patches/ISystemType/HudOverrideSystemTypePatch.cs new file mode 100644 index 000000000..39ff814b9 --- /dev/null +++ b/Patches/ISystemType/HudOverrideSystemTypePatch.cs @@ -0,0 +1,42 @@ +using HarmonyLib; +using Hazel; +using TownOfHost.Roles.Core; +using TownOfHost.Roles.Core.Interfaces; +using TownOfHost.Roles.Neutral; + +namespace TownOfHost.Patches.ISystemType; + +[HarmonyPatch(typeof(HudOverrideSystemType), nameof(HudOverrideSystemType.UpdateSystem))] +public static class HudOverrideSystemTypeUpdateSystemPatch +{ + public static bool Prefix(HudOverrideSystemType __instance, [HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] MessageReader msgReader) + { + byte amount; + { + var newReader = MessageReader.Get(msgReader); + amount = newReader.ReadByte(); + newReader.Recycle(); + } + + var playerRole = player.GetRoleClass(); + var isMadmate = + player.Is(CustomRoleTypes.Madmate) || + // マッド属性化時に削除 + (playerRole is SchrodingerCat schrodingerCat && schrodingerCat.AmMadmate); + if ((amount & HudOverrideSystemType.DamageBit) <= 0 && isMadmate && !Options.MadmateCanFixComms.GetBool()) + { + return false; + } + + if (playerRole is ISystemTypeUpdateHook systemTypeUpdateHook && !systemTypeUpdateHook.UpdateHudOverrideSystem(__instance, amount)) + { + return false; + } + return true; + } + public static void Postfix() + { + Camouflage.CheckCamouflage(); + Utils.NotifyRoles(); + } +} diff --git a/Patches/ISystemType/LifeSuppSystemTypePatch.cs b/Patches/ISystemType/LifeSuppSystemTypePatch.cs new file mode 100644 index 000000000..c94286110 --- /dev/null +++ b/Patches/ISystemType/LifeSuppSystemTypePatch.cs @@ -0,0 +1,26 @@ +using HarmonyLib; +using Hazel; +using TownOfHost.Roles.Core; +using TownOfHost.Roles.Core.Interfaces; + +namespace TownOfHost.Patches.ISystemType; + +[HarmonyPatch(typeof(LifeSuppSystemType), nameof(LifeSuppSystemType.UpdateSystem))] +public static class LifeSuppSystemUpdateSystemPatch +{ + public static bool Prefix(LifeSuppSystemType __instance, [HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] MessageReader msgReader) + { + byte amount; + { + var newReader = MessageReader.Get(msgReader); + amount = newReader.ReadByte(); + newReader.Recycle(); + } + + if (player.GetRoleClass() is ISystemTypeUpdateHook systemTypeUpdateHook && !systemTypeUpdateHook.UpdateLifeSuppSystem(__instance, amount)) + { + return false; + } + return true; + } +} diff --git a/Patches/ISystemType/MushroomMixupSabotageSystemPatch.cs b/Patches/ISystemType/MushroomMixupSabotageSystemPatch.cs new file mode 100644 index 000000000..98c3df467 --- /dev/null +++ b/Patches/ISystemType/MushroomMixupSabotageSystemPatch.cs @@ -0,0 +1,46 @@ +using HarmonyLib; +using Hazel; + +namespace TownOfHost.Patches.ISystemType; + +[HarmonyPatch(typeof(MushroomMixupSabotageSystem), nameof(MushroomMixupSabotageSystem.UpdateSystem))] +public static class MushroomMixupSabotageSystemUpdateSystemPatch +{ + public static void Prefix([HarmonyArgument(1)] MessageReader msgReader, ref byte __state /* amount */) + { + byte amount; + { + var newReader = MessageReader.Get(msgReader); + amount = newReader.ReadByte(); + newReader.Recycle(); + } + __state = amount; + } + public static void Postfix(MushroomMixupSabotageSystem __instance, byte __state /* amount */) + { + var operation = (MushroomMixupSabotageSystem.Operation)__state; + if (operation == MushroomMixupSabotageSystem.Operation.TriggerSabotage && Options.SabotageTimeControl.GetBool()) + { + __instance.currentSecondsUntilHeal = Options.FungleMushroomMixupDuration.GetFloat(); + } + // Desyncインポスター目線のプレイヤー名の表示/非表示を反映 + Utils.NotifyRoles(ForceLoop: true); + } +} +[HarmonyPatch(typeof(MushroomMixupSabotageSystem), nameof(MushroomMixupSabotageSystem.Deteriorate))] +public static class MushroomMixupSabotageSystemDeterioratePatch +{ + public static void Prefix(MushroomMixupSabotageSystem __instance, ref bool __state /* 本体処理前のIsActive */) + { + __state = __instance.IsActive; + } + public static void Postfix(MushroomMixupSabotageSystem __instance, bool __state) + { + // 本体処理でIsActiveが変わった場合 + if (__instance.IsActive != __state) + { + // Desyncインポスター目線のプレイヤー名の表示/非表示を反映 + Utils.NotifyRoles(ForceLoop: true); + } + } +} diff --git a/Patches/ISystemType/ReactorSystemTypePatch.cs b/Patches/ISystemType/ReactorSystemTypePatch.cs new file mode 100644 index 000000000..7f0717179 --- /dev/null +++ b/Patches/ISystemType/ReactorSystemTypePatch.cs @@ -0,0 +1,48 @@ +using HarmonyLib; +using Hazel; +using TownOfHost.Roles.Core; +using TownOfHost.Roles.Core.Interfaces; + +namespace TownOfHost.Patches.ISystemType; + +[HarmonyPatch(typeof(ReactorSystemType), nameof(ReactorSystemType.UpdateSystem))] +public static class ReactorSystemTypeUpdateSystemPatch +{ + public static bool Prefix(ReactorSystemType __instance, [HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] MessageReader msgReader, ref byte __state /* amount */) + { + byte amount; + { + var newReader = MessageReader.Get(msgReader); + amount = newReader.ReadByte(); + newReader.Recycle(); + } + __state = amount; + + if (player.GetRoleClass() is ISystemTypeUpdateHook systemTypeUpdateHook && !systemTypeUpdateHook.UpdateReactorSystem(__instance, amount)) + { + return false; + } + return true; + } + public static void Postfix(ReactorSystemType __instance, byte __state /* amount */ ) + { + // サボタージュ発動時 + if (__state == ReactorSystemType.StartCountdown) + { + if (!Options.SabotageTimeControl.GetBool()) + { + return; + } + var duration = (MapNames)Main.NormalOptions.MapId switch + { + MapNames.Polus => Options.PolusReactorTimeLimit.GetFloat(), + MapNames.Fungle => Options.FungleReactorTimeLimit.GetFloat(), + _ => float.NaN, + }; + if (!float.IsNaN(duration)) + { + __instance.Countdown = duration; + } + } + } +} diff --git a/Patches/ISystemType/SabotageSystemTypePatch.cs b/Patches/ISystemType/SabotageSystemTypePatch.cs new file mode 100644 index 000000000..2a667a72a --- /dev/null +++ b/Patches/ISystemType/SabotageSystemTypePatch.cs @@ -0,0 +1,96 @@ +using HarmonyLib; +using Hazel; +using TownOfHost.Attributes; +using TownOfHost.Modules; +using TownOfHost.Roles.Core; +using TownOfHost.Roles.Core.Interfaces; + +namespace TownOfHost.Patches.ISystemType; + +[HarmonyPatch(typeof(SabotageSystemType), nameof(SabotageSystemType.UpdateSystem))] +public static class SabotageSystemTypeUpdateSystemPatch +{ + private static bool isCooldownModificationEnabled; + private static float modifiedCooldownSec; + private static readonly LogHandler logger = Logger.Handler(nameof(SabotageSystemType)); + + [GameModuleInitializer] + public static void Initialize() + { + isCooldownModificationEnabled = Options.ModifySabotageCooldown.GetBool(); + modifiedCooldownSec = Options.SabotageCooldown.GetFloat(); + } + + public static bool Prefix([HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] MessageReader msgReader) + { + byte amount; + { + var newReader = MessageReader.Get(msgReader); + amount = newReader.ReadByte(); + newReader.Recycle(); + } + + var nextSabotage = (SystemTypes)amount; + logger.Info($"PlayerName: {player.GetNameWithRole()}, SabotageType: {nextSabotage}"); + + //HASモードではサボタージュ不可 + if (Options.CurrentGameMode == CustomGameMode.HideAndSeek || Options.IsStandardHAS) return false; + + if (!CustomRoleManager.OnSabotage(player, nextSabotage)) + { + return false; + } + var roleClass = player.GetRoleClass(); + if (roleClass is IKiller killer) + { + //そもそもサボタージュボタン使用不可ならサボタージュ不可 + if (!killer.CanUseSabotageButton()) return false; + //その他処理が必要であれば処理 + return roleClass.OnInvokeSabotage(nextSabotage); + } + else + { + return CanSabotage(player); + } + } + private static bool CanSabotage(PlayerControl player) + { + //サボタージュ出来ないキラー役職はサボタージュ自体をキャンセル + if (!player.Is(CustomRoleTypes.Impostor)) + { + return false; + } + return true; + } + public static void Postfix(SabotageSystemType __instance, bool __runOriginal /* Prefixの結果,本体処理が実行されたかどうか */ ) + { + if (!__runOriginal || !isCooldownModificationEnabled || !AmongUsClient.Instance.AmHost) + { + return; + } + // サボタージュクールダウンを変更 + __instance.Timer = modifiedCooldownSec; + __instance.IsDirty = true; + } +} + +[HarmonyPatch(typeof(ElectricTask), nameof(ElectricTask.Initialize))] +public static class ElectricTaskInitializePatch +{ + public static void Postfix() + { + Utils.MarkEveryoneDirtySettings(); + if (!GameStates.IsMeeting) + Utils.NotifyRoles(ForceLoop: true); + } +} +[HarmonyPatch(typeof(ElectricTask), nameof(ElectricTask.Complete))] +public static class ElectricTaskCompletePatch +{ + public static void Postfix() + { + Utils.MarkEveryoneDirtySettings(); + if (!GameStates.IsMeeting) + Utils.NotifyRoles(ForceLoop: true); + } +} diff --git a/Patches/ISystemType/SecurityCameraSystemTypePatch.cs b/Patches/ISystemType/SecurityCameraSystemTypePatch.cs new file mode 100644 index 000000000..e4f96927b --- /dev/null +++ b/Patches/ISystemType/SecurityCameraSystemTypePatch.cs @@ -0,0 +1,32 @@ +using HarmonyLib; +using Hazel; + +namespace TownOfHost.Patches.ISystemType; + +[HarmonyPatch(typeof(SecurityCameraSystemType), nameof(SecurityCameraSystemType.UpdateSystem))] +public static class SecurityCameraSystemTypeUpdateSystemPatch +{ + public static bool Prefix([HarmonyArgument(1)] MessageReader msgReader) + { + byte amount; + { + var newReader = MessageReader.Get(msgReader); + amount = newReader.ReadByte(); + newReader.Recycle(); + } + + // カメラ無効時,バニラプレイヤーはカメラを開けるので点滅させない + if (amount == SecurityCameraSystemType.IncrementOp) + { + var camerasDisabled = (MapNames)Main.NormalOptions.MapId switch + { + MapNames.Skeld => Options.DisableSkeldCamera.GetBool(), + MapNames.Polus => Options.DisablePolusCamera.GetBool(), + MapNames.Airship => Options.DisableAirshipCamera.GetBool(), + _ => false, + }; + return !camerasDisabled; + } + return true; + } +} diff --git a/Patches/ISystemType/SwitchSystemPatch.cs b/Patches/ISystemType/SwitchSystemPatch.cs new file mode 100644 index 000000000..904b95d1e --- /dev/null +++ b/Patches/ISystemType/SwitchSystemPatch.cs @@ -0,0 +1,74 @@ +using HarmonyLib; +using Hazel; +using TownOfHost.Roles.Core; +using TownOfHost.Roles.Core.Interfaces; +using TownOfHost.Roles.Neutral; +using UnityEngine; + +namespace TownOfHost.Patches.ISystemType; + +[HarmonyPatch(typeof(SwitchSystem), nameof(SwitchSystem.UpdateSystem))] +public static class SwitchSystemUpdateSystemPatch +{ + public static bool Prefix(SwitchSystem __instance, [HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] MessageReader msgReader) + { + byte amount; + { + var newReader = MessageReader.Get(msgReader); + amount = newReader.ReadByte(); + newReader.Recycle(); + } + + if (!AmongUsClient.Instance.AmHost) + { + return true; + } + // 停電サボタージュが鳴らされた場合は関係なし(ホスト名義で飛んでくるため誤爆注意) + if (amount.HasBit(SwitchSystem.DamageSystem)) + { + return true; + } + var isMadmate = + player.Is(CustomRoleTypes.Madmate) || + // マッド属性化時に削除 + (player.GetRoleClass() is SchrodingerCat schrodingerCat && schrodingerCat.AmMadmate); + if (isMadmate && !Options.MadmateCanFixLightsOut.GetBool()) + { + // 直せないならキャンセル + return false; + } + //Airshipの特定の停電を直せないならキャンセル + if ((MapNames)Main.NormalOptions.MapId == MapNames.Airship) + { + var truePosition = player.GetTruePosition(); + if (Options.DisableAirshipViewingDeckLightsPanel.GetBool() && Vector2.Distance(truePosition, new(-12.93f, -11.28f)) <= 2f) return false; + if (Options.DisableAirshipGapRoomLightsPanel.GetBool() && Vector2.Distance(truePosition, new(13.92f, 6.43f)) <= 2f) return false; + if (Options.DisableAirshipCargoLightsPanel.GetBool() && Vector2.Distance(truePosition, new(30.56f, 2.12f)) <= 2f) return false; + } + + // サボタージュによる破壊ではない && 配電盤を下げられなくするオプションがオン + if (Options.BlockDisturbancesToSwitches.GetBool()) + { + // amount分だけ1を左にずらす + // 各桁が各ツマミに対応する + // 一番左のツマミが操作されたら(amount: 0) 00001 + // 一番右のツマミが操作されたら(amount: 4) 10000 + // ref: SwitchSystem.RepairDamage, SwitchMinigame.FixedUpdate + var switchedKnob = (byte)(0b_00001 << amount); + // ExpectedSwitches: すべてONになっているときのスイッチの上下状態 + // ActualSwitches: 実際のスイッチの上下状態 + // 操作されたツマミについて,ExpectedとActualで同じならそのツマミは既に直ってる + if ((__instance.ActualSwitches & switchedKnob) == (__instance.ExpectedSwitches & switchedKnob)) + { + return false; + } + } + + // ロールの処理 + if (player.GetRoleClass() is ISystemTypeUpdateHook systemTypeUpdateHook && !systemTypeUpdateHook.UpdateSwitchSystem(__instance, amount)) + { + return false; + } + return true; + } +} diff --git a/Patches/IntroPatch.cs b/Patches/IntroPatch.cs index a86784fb9..9dc85318a 100644 --- a/Patches/IntroPatch.cs +++ b/Patches/IntroPatch.cs @@ -241,7 +241,7 @@ public static void Postfix(IntroCutscene __instance) PlayerControl.LocalPlayer.RpcExile(); PlayerState.GetByPlayerId(PlayerControl.LocalPlayer.PlayerId).SetDead(); } - if (Options.RandomSpawn.GetBool()) + if (RandomSpawn.IsRandomSpawn()) { RandomSpawn.SpawnMap map; switch (Main.NormalOptions.MapId) @@ -254,12 +254,20 @@ public static void Postfix(IntroCutscene __instance) map = new RandomSpawn.MiraHQSpawnMap(); Main.AllPlayerControls.Do(map.RandomTeleport); break; + case 2: + map = new RandomSpawn.PolusSpawnMap(); + Main.AllPlayerControls.Do(map.RandomTeleport); + break; + case 5: + map = new RandomSpawn.FungleSpawnMap(); + Main.AllPlayerControls.Do(map.RandomTeleport); + break; } } // そのままだとホストのみDesyncImpostorの暗室内での視界がクルー仕様になってしまう var roleInfo = PlayerControl.LocalPlayer.GetCustomRole().GetRoleInfo(); - var amDesyncImpostor = roleInfo?.RequireResetCam == true || Main.ResetCamPlayerList.Contains(PlayerControl.LocalPlayer.PlayerId); + var amDesyncImpostor = roleInfo?.IsDesyncImpostor == true; if (amDesyncImpostor) { PlayerControl.LocalPlayer.Data.Role.AffectedByLightAffectors = false; diff --git a/Patches/LadderPatch.cs b/Patches/LadderPatch.cs index 93a3a6633..4175c2b76 100644 --- a/Patches/LadderPatch.cs +++ b/Patches/LadderPatch.cs @@ -51,6 +51,7 @@ public static void FixedUpdate(PlayerControl player) .EndRpc(); sender.AutoStartRpc(player.NetId, (byte)RpcCalls.MurderPlayer) .WriteNetObject(player) + .Write((int)ExtendedPlayerControl.SucceededFlags) .EndRpc(); sender.SendMessage(); player.NetTransform.SnapTo(targetPos); diff --git a/Patches/MainMenuManagerPatch.cs b/Patches/MainMenuManagerPatch.cs index f1e6eb6c2..9c6f82557 100644 --- a/Patches/MainMenuManagerPatch.cs +++ b/Patches/MainMenuManagerPatch.cs @@ -1,27 +1,24 @@ using System; using HarmonyLib; -using TMPro; +using TownOfHost.Templates; using UnityEngine; -using Object = UnityEngine.Object; namespace TownOfHost { [HarmonyPatch(typeof(MainMenuManager))] public class MainMenuManagerPatch { - private static PassiveButton template; - private static PassiveButton discordButton; - public static PassiveButton UpdateButton { get; private set; } - private static PassiveButton gitHubButton; + private static SimpleButton discordButton; + public static SimpleButton UpdateButton { get; private set; } + private static SimpleButton gitHubButton; [HarmonyPatch(nameof(MainMenuManager.Start)), HarmonyPostfix, HarmonyPriority(Priority.Normal)] public static void StartPostfix(MainMenuManager __instance) { - if (template == null) template = __instance.quitButton; - if (template == null) return; + SimpleButton.SetBase(__instance.quitButton); //Discordボタンを生成 - if (discordButton == null) + if (SimpleButton.IsNullOrDestroyed(discordButton)) { discordButton = CreateButton( "DiscordButton", @@ -29,12 +26,12 @@ public static void StartPostfix(MainMenuManager __instance) new(88, 101, 242, byte.MaxValue), new(148, 161, byte.MaxValue, byte.MaxValue), () => Application.OpenURL(Main.DiscordInviteUrl), - "Discord"); + "Discord", + isActive: Main.ShowDiscordButton); } - discordButton.gameObject.SetActive(Main.ShowDiscordButton); // GitHubボタンを生成 - if (gitHubButton == null) + if (SimpleButton.IsNullOrDestroyed(gitHubButton)) { gitHubButton = CreateButton( "GitHubButton", @@ -46,7 +43,7 @@ public static void StartPostfix(MainMenuManager __instance) } //Updateボタンを生成 - if (UpdateButton == null) + if (SimpleButton.IsNullOrDestroyed(UpdateButton)) { UpdateButton = CreateButton( "UpdateButton", @@ -55,13 +52,13 @@ public static void StartPostfix(MainMenuManager __instance) new(60, 255, 255, byte.MaxValue), () => { - UpdateButton.gameObject.SetActive(false); + UpdateButton.Button.gameObject.SetActive(false); ModUpdater.StartUpdate(ModUpdater.downloadUrl); }, $"{Translator.GetString("updateButton")}\n{ModUpdater.latestTitle}", - new(2.5f, 1f)); + new(2.5f, 1f), + isActive: false); } - UpdateButton.gameObject.SetActive(false); #if RELEASE // フリープレイの無効化 @@ -82,42 +79,21 @@ public static void StartPostfix(MainMenuManager __instance) /// 押したときに発火するアクション /// ボタンのテキスト /// ボタンのサイズ 変更しないなら不要 - private static PassiveButton CreateButton(string name, Vector3 localPosition, Color32 normalColor, Color32 hoverColor, Action action, string label, Vector2? scale = null) + private static SimpleButton CreateButton( + string name, + Vector3 localPosition, + Color32 normalColor, + Color32 hoverColor, + Action action, + string label, + Vector2? scale = null, + bool isActive = true) { - var button = Object.Instantiate(template, CredentialsPatch.TohLogo.transform); - button.name = name; - Object.Destroy(button.GetComponent()); - button.transform.localPosition = localPosition; - - button.OnClick = new(); - button.OnClick.AddListener(action); - - var buttonText = button.transform.Find("FontPlacer/Text_TMP").GetComponent(); - buttonText.DestroyTranslator(); - buttonText.fontSize = buttonText.fontSizeMax = buttonText.fontSizeMin = 3.5f; - buttonText.enableWordWrapping = false; - buttonText.text = label; - var normalSprite = button.inactiveSprites.GetComponent(); - var hoverSprite = button.activeSprites.GetComponent(); - normalSprite.color = normalColor; - hoverSprite.color = hoverColor; - - // ラベルをセンタリング - var container = buttonText.transform.parent; - Object.Destroy(container.GetComponent()); - Object.Destroy(buttonText.GetComponent()); - container.SetLocalX(0f); - buttonText.transform.SetLocalX(0f); - buttonText.horizontalAlignment = HorizontalAlignmentOptions.Center; - - var buttonCollider = button.GetComponent(); + var button = new SimpleButton(CredentialsPatch.TohLogo.transform, name, localPosition, normalColor, hoverColor, action, label, isActive); if (scale.HasValue) { - normalSprite.size = hoverSprite.size = buttonCollider.size = scale.Value; + button.Scale = scale.Value; } - // 当たり判定のズレを直す - buttonCollider.offset = new(0f, 0f); - return button; } diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index 4feb392e9..917cb541e 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -196,7 +196,8 @@ public static void Postfix() if (AmongUsClient.Instance.AmHost) { AntiBlackout.SetIsDead(); - Main.AllPlayerControls.Do(pc => RandomSpawn.CustomNetworkTransformPatch.NumOfTP[pc.PlayerId] = 0); + Main.AllPlayerControls.Do(pc => RandomSpawn.FirstTP[pc.PlayerId] = true); + RandomSpawn.FastSpawnPosition.Clear(); } // MeetingVoteManagerを通さずに会議が終了した場合の後処理 MeetingVoteManager.Instance?.Destroy(); diff --git a/Patches/OneWayShadowsPatch.cs b/Patches/OneWayShadowsPatch.cs index 887444e1a..29df67390 100644 --- a/Patches/OneWayShadowsPatch.cs +++ b/Patches/OneWayShadowsPatch.cs @@ -9,7 +9,7 @@ public static class OneWayShadowsIsIgnoredPatch public static bool Prefix(OneWayShadows __instance, ref bool __result) { var roleInfo = PlayerControl.LocalPlayer.GetCustomRole().GetRoleInfo(); - var amDesyncImpostor = roleInfo?.RequireResetCam == true || Main.ResetCamPlayerList.Contains(PlayerControl.LocalPlayer.PlayerId); + var amDesyncImpostor = roleInfo?.IsDesyncImpostor == true; if (__instance.IgnoreImpostor && amDesyncImpostor) { __result = true; diff --git a/Patches/OutroPatch.cs b/Patches/OutroPatch.cs index 136beb085..cc8692371 100644 --- a/Patches/OutroPatch.cs +++ b/Patches/OutroPatch.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using HarmonyLib; +using TMPro; using UnityEngine; using TownOfHost.Modules; @@ -85,7 +86,7 @@ public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)] ref En else if (role == CustomRoles.HASFox && CustomWinnerHolder.WinnerTeam != CustomWinner.HASTroll && !pc.Data.IsDead) { winner.Add(pc); - CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.HASFox); + CustomWinnerHolder.AdditionalWinnerRoles.Add(CustomRoles.HASFox); } } } @@ -114,6 +115,8 @@ public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)] ref En class SetEverythingUpPatch { public static string LastWinsText = ""; + private static TextMeshPro roleSummary; + private static SimpleButton showHideButton; public static void Postfix(EndGameManager __instance) { @@ -123,18 +126,14 @@ public static void Postfix(EndGameManager __instance) //####################################### var WinnerTextObject = UnityEngine.Object.Instantiate(__instance.WinText.gameObject); - var WinnerText = WinnerTextObject.GetComponent(); - if (!Main.PutBackWinTextPosition.Value) - { - __instance.WinText.alignment = WinnerText.alignment = TMPro.TextAlignmentOptions.Right; - } - WinnerTextObject.transform.position = new(Main.PutBackWinTextPosition.Value ? __instance.WinText.transform.position.x : __instance.WinText.transform.position.x + 2.4f, __instance.WinText.transform.position.y - 0.5f, __instance.WinText.transform.position.z); + WinnerTextObject.transform.position = new(__instance.WinText.transform.position.x, __instance.WinText.transform.position.y - 0.5f, __instance.WinText.transform.position.z); WinnerTextObject.transform.localScale = new(0.6f, 0.6f, 0.6f); + var WinnerText = WinnerTextObject.GetComponent(); //WinTextと同じ型のコンポーネントを取得 WinnerText.fontSizeMin = 3f; WinnerText.text = ""; string CustomWinnerText = ""; - string AdditionalWinnerText = ""; + var AdditionalWinnerText = new StringBuilder(32); string CustomWinnerColor = Utils.GetRoleColorCode(CustomRoles.Crewmate); var winnerRole = (CustomRoles)CustomWinnerHolder.WinnerTeam; @@ -184,10 +183,9 @@ public static void Postfix(EndGameManager __instance) break; } - foreach (var additionalWinners in CustomWinnerHolder.AdditionalWinnerTeams) + foreach (var role in CustomWinnerHolder.AdditionalWinnerRoles) { - var addWinnerRole = (CustomRoles)additionalWinners; - AdditionalWinnerText += "&" + Utils.ColorString(Utils.GetRoleColor(addWinnerRole), Utils.GetRoleName(addWinnerRole)); + AdditionalWinnerText.Append('&').Append(Utils.ColorString(Utils.GetRoleColor(role), Utils.GetRoleName(role))); } if (CustomWinnerHolder.WinnerTeam is not CustomWinner.Draw and not CustomWinner.None) { @@ -201,7 +199,25 @@ public static void Postfix(EndGameManager __instance) // ==最終結果表示== //####################################### - var Pos = Camera.main.ViewportToWorldPoint(new Vector3(0f, 1f, Camera.main.nearClipPlane)); + var showInitially = Main.ShowResults.Value; + showHideButton = new SimpleButton( + __instance.transform, + "ShowHideResultsButton", + new(-4.5f, 2.6f, -14f), // BackgroundLayer(z=-13)より手前 + new(0, 136, 209, byte.MaxValue), + new(0, 196, byte.MaxValue, byte.MaxValue), + () => + { + var setToActive = !roleSummary.gameObject.activeSelf; + roleSummary.gameObject.SetActive(setToActive); + Main.ShowResults.Value = setToActive; + showHideButton.Label.text = GetString(setToActive ? "HideResults" : "ShowResults"); + }, + GetString(showInitially ? "HideResults" : "ShowResults")) + { + Scale = new(1.5f, 0.5f), + FontSize = 2f, + }; StringBuilder sb = new($"{GetString("RoleSummaryText")}"); List cloneRoles = new(PlayerState.AllPlayerStates.Keys); @@ -215,15 +231,16 @@ public static void Postfix(EndGameManager __instance) sb.Append($"\n  ").Append(EndGamePatch.SummaryText[id]); } var summary = sb.ToString(); - var RoleSummary = TMPTemplate.Create( + roleSummary = TMPTemplate.Create( "RoleSummaryText", summary, Color.white, 1.25f, - TMPro.TextAlignmentOptions.TopLeft, - setActive: true); - RoleSummary.transform.position = new Vector3(__instance.Navigation.ExitButton.transform.position.x + -0.05f, Pos.y - 0.13f, -15f); - RoleSummary.transform.localScale = new Vector3(1f, 1f, 1f); + TextAlignmentOptions.TopLeft, + setActive: showInitially, + parent: showHideButton.Button.transform); + roleSummary.transform.localPosition = new(1.7f, -0.4f, 0f); + roleSummary.transform.localScale = new Vector3(1.2f, 1.2f, 1f); LobbySummary.Save(summary); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -256,4 +273,4 @@ public static void Postfix(EndGameManager __instance) //Utils.ApplySuffix(); } } -} \ No newline at end of file +} diff --git a/Patches/PlayerContorolPatch.cs b/Patches/PlayerContorolPatch.cs index ca1dc52e2..b8bcf5841 100644 --- a/Patches/PlayerContorolPatch.cs +++ b/Patches/PlayerContorolPatch.cs @@ -53,7 +53,11 @@ public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] PlayerC if (!AmongUsClient.Instance.AmHost) return false; // 処理は全てCustomRoleManager側で行う - CustomRoleManager.OnCheckMurder(__instance, target); + if (!CustomRoleManager.OnCheckMurder(__instance, target)) + { + // キル失敗 + __instance.RpcMurderPlayer(target, false); + } return false; } @@ -124,16 +128,61 @@ public static bool CheckForInvalidMurdering(MurderInfo info) [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.MurderPlayer))] class MurderPlayerPatch { - public static void Prefix(PlayerControl __instance, [HarmonyArgument(0)] PlayerControl target) + private static readonly LogHandler logger = Logger.Handler(nameof(PlayerControl.MurderPlayer)); + public static void Prefix(PlayerControl __instance, [HarmonyArgument(0)] PlayerControl target, [HarmonyArgument(1)] MurderResultFlags resultFlags, ref bool __state /* 成功したキルかどうか */ ) { - Logger.Info($"{__instance.GetNameWithRole()} => {target.GetNameWithRole()}{(target.protectedByGuardian ? "(Protected)" : "")}", "MurderPlayer"); + logger.Info($"{__instance.GetNameWithRole()} => {target.GetNameWithRole()}({resultFlags})"); + var isProtectedByClient = resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.IsProtected(); + var isProtectedByHost = resultFlags.HasFlag(MurderResultFlags.FailedProtected); + var isFailed = resultFlags.HasFlag(MurderResultFlags.FailedError); + var isSucceeded = __state = !isProtectedByClient && !isProtectedByHost && !isFailed; + if (isProtectedByClient) + { + logger.Info("守護されているため,キルは失敗します"); + } + if (isProtectedByHost) + { + logger.Info("守護されているため,キルはホストによってキャンセルされました"); + } + if (isFailed) + { + logger.Info("キルはホストによってキャンセルされました"); + } - if (RandomSpawn.CustomNetworkTransformPatch.NumOfTP.TryGetValue(__instance.PlayerId, out var num) && num > 2) RandomSpawn.CustomNetworkTransformPatch.NumOfTP[__instance.PlayerId] = 3; - if (!target.protectedByGuardian) - Camouflage.RpcSetSkin(target, ForceRevert: true); + if (isSucceeded) + { + if (target.shapeshifting) + { + //シェイプシフトアニメーション中 + //アニメーション時間を考慮して1s、加えてクライアントとのラグを考慮して+0.5s遅延する + _ = new LateTask( + () => + { + if (GameStates.IsInTask) + { + target.RpcShapeshift(target, false); + } + }, + 1.5f, "RevertShapeshift"); + } + else + { + if (Main.CheckShapeshift.TryGetValue(target.PlayerId, out var shapeshifting) && shapeshifting) + { + //シェイプシフト強制解除 + target.RpcShapeshift(target, false); + } + } + Camouflage.RpcSetSkin(target, ForceRevert: true, RevertToDefault: true); + } } - public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)] PlayerControl target) + public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)] PlayerControl target, bool __state) { + // キルが成功していない場合,何もしない + if (!__state) + { + return; + } if (target.AmOwner) RemoveDisableDevicesPatch.UpdateDisableDevices(); if (!target.Data.IsDead || !AmongUsClient.Instance.AmHost) return; //以降ホストしか処理しない @@ -300,7 +349,7 @@ public static void Postfix(PlayerControl __instance) if (AmongUsClient.Instance.AmHost) {//実行クライアントがホストの場合のみ実行 - if (GameStates.IsLobby && (ModUpdater.hasUpdate || ModUpdater.isBroken || !Main.AllowPublicRoom || !VersionChecker.IsSupported) && AmongUsClient.Instance.IsGamePublic) + if (GameStates.IsLobby && (ModUpdater.hasUpdate || ModUpdater.isBroken || !Main.AllowPublicRoom || !VersionChecker.IsSupported || !Main.IsPublicAvailableOnThisVersion) && AmongUsClient.Instance.IsGamePublic) AmongUsClient.Instance.ChangeGamePublic(false); if (GameStates.IsInTask && ReportDeadBodyPatch.CanReport[__instance.PlayerId] && ReportDeadBodyPatch.WaitReport[__instance.PlayerId].Count > 0) @@ -601,14 +650,12 @@ public static bool Prefix(PlayerControl __instance, ref RoleTypes roleType) if (!ShipStatus.Instance.enabled) return true; if (roleType is RoleTypes.CrewmateGhost or RoleTypes.ImpostorGhost) { - var targetRequireResetCam = target.GetCustomRole().GetRoleInfo()?.RequireResetCam ?? false; - var targetIsKiller = target.Is(CustomRoleTypes.Impostor) || Main.ResetCamPlayerList.Contains(target.PlayerId) || targetRequireResetCam; + var targetIsKiller = target.GetRoleClass() is IKiller; var ghostRoles = new Dictionary(); foreach (var seer in Main.AllPlayerControls) { var self = seer.PlayerId == target.PlayerId; - var seerRequireResetCam = seer.GetCustomRole().GetRoleInfo()?.RequireResetCam ?? false; - var seerIsKiller = seer.Is(CustomRoleTypes.Impostor) || Main.ResetCamPlayerList.Contains(seer.PlayerId) || seerRequireResetCam; + var seerIsKiller = seer.GetRoleClass() is IKiller; if ((self && targetIsKiller) || (!seerIsKiller && target.Is(CustomRoleTypes.Impostor))) { @@ -653,4 +700,36 @@ public static void Postfix(PlayerControl __instance) __instance.GetRoleClass().OnDie(); } } + [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.MixUpOutfit))] + public static class PlayerControlMixupOutfitPatch + { + public static void Postfix(PlayerControl __instance) + { + if (!__instance.IsAlive()) + { + return; + } + // 自分がDesyncインポスターで,バニラ判定ではインポスターの場合,バニラ処理で名前が非表示にならないため,相手の名前を非表示にする + if ( + PlayerControl.LocalPlayer.Data.Role.IsImpostor && // バニラ判定でインポスター + !PlayerControl.LocalPlayer.Is(CustomRoleTypes.Impostor) && // Mod判定でインポスターではない + PlayerControl.LocalPlayer.GetCustomRole().GetRoleInfo()?.IsDesyncImpostor == true) // Desyncインポスター + { + // 名前を隠す + __instance.cosmetics.ToggleNameVisible(false); + } + } + } + [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.CheckSporeTrigger))] + public static class PlayerControlCheckSporeTriggerPatch + { + public static bool Prefix() + { + if (Options.DisableFungleSporeTrigger.GetBool()) + { + return false; + } + return true; + } + } } diff --git a/Patches/RandomSpawnPatch.cs b/Patches/RandomSpawnPatch.cs index aa3b12ae7..d957d982e 100644 --- a/Patches/RandomSpawnPatch.cs +++ b/Patches/RandomSpawnPatch.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using HarmonyLib; -using Hazel; using UnityEngine; using TownOfHost.Roles.Core; @@ -10,167 +9,412 @@ namespace TownOfHost { + public enum SpawnPoint + { + Cafeteria, + Weapons, + LifeSupp, + Nav, + Shields, + Comms, + Storage, + Admin, + Electrical, + LowerEngine, + UpperEngine, + Security, + Reactor, + MedBay, + Balcony, + Junction,//StringNamesにない文言 string.csvに追加 + LockerRoom, + Decontamination, + Laboratory, + Launchpad, + Office, + OfficeLeft,//StringNamesにない文言 string.csvに追加 + OfficeRight,//StringNamesにない文言 string.csvに追加 + Greenhouse, + BoilerRoom, + Dropship, + Rocket,//StringNamesにない文言 string.csvに追加 + Toilet,//StringNamesにない文言 string.csvに追加 + Specimens, + Brig, + Engine, + Kitchen, + CargoBay, + Records, + MainHall, + NapRoom,//StringNamesにない文言 string.csvに追加 AirShipメインホール左上の仮眠室 + MeetingRoom, + GapRoom, + VaultRoom, + Cockpit, + Armory, + ViewingDeck, + Medical, + Showers, + Beach, + RecRoom,//SplashZoneのこと + Bonfire,//StringNamesにない文言 string.csvに追加 Fungleの焚き火 + SleepingQuarters,//TheDorm 宿舎のこと + JungleTop,//StringNamesにない文言 string.csvに追加 + JungleBottom,//StringNamesにない文言 string.csvに追加 + Lookout, + MiningPit, + Highlands,//Fungleの高地 + Precipice,//StringNamesにない文言 string.csvに追加 + } class RandomSpawn { - [HarmonyPatch(typeof(CustomNetworkTransform), nameof(CustomNetworkTransform.SnapTo), typeof(Vector2), typeof(ushort))] - public class CustomNetworkTransformPatch + public static Dictionary FirstTP = new(); + public static Dictionary FastSpawnPosition = new(); + public static bool hostReady; + [HarmonyPatch(typeof(CustomNetworkTransform), nameof(CustomNetworkTransform.RpcSnapTo))] + public class RpcSnapToPatch { - public static Dictionary NumOfTP = new(); - public static void Postfix(CustomNetworkTransform __instance, [HarmonyArgument(0)] Vector2 position) + public static void Postfix(CustomNetworkTransform __instance, Vector2 position) { + var player = __instance.myPlayer; + //Logger.Info($"RpcSnapToPost:{player.name} pos:{position}", "RandomSpawn"); if (!AmongUsClient.Instance.AmHost) return; - if (position == new Vector2(-25f, 40f)) return; //最初の湧き地点ならreturn - if (GameStates.IsInTask) + if (Main.NormalOptions.MapId != 4) return;//AirShip以外無効 + if (FirstTP.TryGetValue(player.PlayerId, out var first) && first) { - var player = Main.AllPlayerControls.Where(p => p.NetTransform == __instance).FirstOrDefault(); - if (player == null) - { - Logger.Warn("プレイヤーがnullです", "RandomSpawn"); - return; - } - if (player.Is(CustomRoles.GM)) return; //GMは対象外に + hostReady = true; + //ホスト用処理 + //他視点へRPCを最初に送るのはスポーン位置選択後のため + //クライアントへRPCを発行するときにはすでにクライアントの初期配置は終わっている。 + AirshipSpawn(player); + } + } + } + [HarmonyPatch(typeof(CustomNetworkTransform), nameof(CustomNetworkTransform.HandleRpc))] + public class HandleRpcPatch + { + public static void Postfix(CustomNetworkTransform __instance) + { + var player = __instance.myPlayer; + //Logger.Info($"HandleRpcPost:{player.name}", "RandomSpawn"); - NumOfTP[player.PlayerId]++; + if (!AmongUsClient.Instance.AmHost) return; + if (Main.NormalOptions.MapId != 4) return;//AirShip以外無効 - if (NumOfTP[player.PlayerId] == 2) - { - if (Main.NormalOptions.MapId != 4) return; //マップがエアシップじゃなかったらreturn - if (player.Is(CustomRoles.Penguin)) - { - var penguin = player.GetRoleClass() as Penguin; - penguin?.OnSpawnAirship(); - } - player.RpcResetAbilityCooldown(); - if (Options.FixFirstKillCooldown.GetBool() && !MeetingStates.MeetingCalled) player.SetKillCooldown(Main.AllPlayerKillCooldown[player.PlayerId]); - if (!Options.RandomSpawn.GetBool()) return; //ランダムスポーンが無効ならreturn - new AirshipSpawnMap().RandomTeleport(player); - } + if (FirstTP.TryGetValue(player.PlayerId, out var first) && first) + { + //クライアント用処理 + //他視点へRPCを最初に送るのはスポーン位置選択後のため + //ランダムスポーン発生 + AirshipSpawn(player); } } } - public static void TP(CustomNetworkTransform nt, Vector2 location) + [HarmonyPatch(typeof(CustomNetworkTransform), nameof(CustomNetworkTransform.SnapTo), typeof(Vector2), typeof(ushort))] + public class SnapToPatch + { + public static void Postfix(CustomNetworkTransform __instance, Vector2 position, ushort minSid) + { + var player = __instance.myPlayer; + //Logger.Info($"SnapTo:{player.name} pos:{position} minSid={minSid}", "RandomSpawn"); + } + } + [HarmonyPatch(typeof(SpawnInMinigame), nameof(SpawnInMinigame.Begin))] + public class SpawnInMinigamePatch { - if (AmongUsClient.Instance.AmHost) nt.SnapTo(location); - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(nt.NetId, (byte)RpcCalls.SnapTo, SendOption.None); - NetHelpers.WriteVector2(location, writer); - writer.Write(nt.lastSequenceId); - AmongUsClient.Instance.FinishRpcImmediately(writer); + public static void Postfix() + { + Logger.Info($"BeginPost", "SpawnInMinigame"); + if (!AmongUsClient.Instance.AmHost) return; + hostReady = true; + } + } + public static void AirshipSpawn(PlayerControl player) + { + FirstTP[player.PlayerId] = false; + if (player.Is(CustomRoles.Penguin)) + { + var penguin = player.GetRoleClass() as Penguin; + penguin?.OnSpawnAirship(); + } + player.RpcResetAbilityCooldown(); + if (Options.FixFirstKillCooldown.GetBool() && !MeetingStates.MeetingCalled) player.SetKillCooldown(Main.AllPlayerKillCooldown[player.PlayerId]); + if (IsRandomSpawn()) + { + new AirshipSpawnMap().RandomTeleport(player); + } + else if (player.Is(CustomRoles.GM)) + { + new AirshipSpawnMap().FirstTeleport(player); + } + foreach (var (sp, pos) in FastSpawnPosition) + { + //早湧きした人を船外から初期位置に戻す + sp.RpcSnapToDesync(player, pos); + } + if (!hostReady) + { + //ホストのSpawnMiniGame開始までに湧いたプレイヤーを記録 + FastSpawnPosition[player] = player.transform.position; + } + } + public static bool IsRandomSpawn() + { + if (!Options.EnableRandomSpawn.GetBool()) return false; + switch (Main.NormalOptions.MapId) + { + case 0: + return Options.RandomSpawnSkeld.GetBool(); + case 1: + return Options.RandomSpawnMira.GetBool(); + case 2: + return Options.RandomSpawnPolus.GetBool(); + case 4: + return Options.RandomSpawnAirship.GetBool(); + case 5: + return Options.RandomSpawnFungle.GetBool(); + default: + Logger.Error($"MapIdFailed ID:{Main.NormalOptions.MapId}", "IsRandomSpawn"); + return false; + } + } + public static void SetupCustomOption() + { + // Skeld + Options.RandomSpawnSkeld = BooleanOptionItem.Create(103000, StringNames.MapNameSkeld, false, TabGroup.MainSettings, false).SetParent(Options.EnableRandomSpawn).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldCafeteria = BooleanOptionItem.Create(103001, StringNames.Cafeteria, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldWeapons = BooleanOptionItem.Create(103002, StringNames.Weapons, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldShields = BooleanOptionItem.Create(103003, StringNames.Shields, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldStorage = BooleanOptionItem.Create(103004, StringNames.Storage, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldLowerEngine = BooleanOptionItem.Create(103005, StringNames.LowerEngine, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldUpperEngine = BooleanOptionItem.Create(103006, StringNames.UpperEngine, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldLifeSupp = BooleanOptionItem.Create(103007, StringNames.LifeSupp, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldNav = BooleanOptionItem.Create(103008, StringNames.Nav, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldComms = BooleanOptionItem.Create(103009, StringNames.Comms, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldAdmin = BooleanOptionItem.Create(103010, StringNames.Admin, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldElectrical = BooleanOptionItem.Create(103011, StringNames.Electrical, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldSecurity = BooleanOptionItem.Create(103012, StringNames.Security, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldReactor = BooleanOptionItem.Create(103013, StringNames.Reactor, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + Options.RandomSpawnSkeldMedBay = BooleanOptionItem.Create(103014, StringNames.MedBay, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnSkeld).SetGameMode(CustomGameMode.All); + // Mira + Options.RandomSpawnMira = BooleanOptionItem.Create(103100, StringNames.MapNameMira, false, TabGroup.MainSettings, false).SetParent(Options.EnableRandomSpawn).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraCafeteria = BooleanOptionItem.Create(103101, StringNames.Cafeteria, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraComms = BooleanOptionItem.Create(103102, StringNames.Comms, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraDecontamination = BooleanOptionItem.Create(103103, StringNames.Decontamination, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraReactor = BooleanOptionItem.Create(103104, StringNames.Reactor, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraLaunchpad = BooleanOptionItem.Create(103105, StringNames.Launchpad, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraAdmin = BooleanOptionItem.Create(103106, StringNames.Admin, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraBalcony = BooleanOptionItem.Create(103107, StringNames.Balcony, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraStorage = BooleanOptionItem.Create(103108, StringNames.Storage, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraJunction = BooleanOptionItem.Create(103109, SpawnPoint.Junction, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraMedBay = BooleanOptionItem.Create(103110, StringNames.MedBay, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraLockerRoom = BooleanOptionItem.Create(103111, StringNames.LockerRoom, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraLaboratory = BooleanOptionItem.Create(103112, StringNames.Laboratory, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraOffice = BooleanOptionItem.Create(103113, StringNames.Office, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + Options.RandomSpawnMiraGreenhouse = BooleanOptionItem.Create(103114, StringNames.Greenhouse, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnMira).SetGameMode(CustomGameMode.All); + // Polus + Options.RandomSpawnPolus = BooleanOptionItem.Create(103200, StringNames.MapNamePolus, false, TabGroup.MainSettings, false).SetParent(Options.EnableRandomSpawn).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusOfficeLeft = BooleanOptionItem.Create(103201, SpawnPoint.OfficeLeft, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusBoilerRoom = BooleanOptionItem.Create(103202, StringNames.BoilerRoom, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusSecurity = BooleanOptionItem.Create(103203, StringNames.Security, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusDropship = BooleanOptionItem.Create(103204, StringNames.Dropship, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusLaboratory = BooleanOptionItem.Create(103205, StringNames.Laboratory, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusSpecimens = BooleanOptionItem.Create(103206, StringNames.Specimens, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusOfficeRight = BooleanOptionItem.Create(103207, SpawnPoint.OfficeRight, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusAdmin = BooleanOptionItem.Create(103208, StringNames.Admin, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusComms = BooleanOptionItem.Create(103209, StringNames.Comms, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusWeapons = BooleanOptionItem.Create(103210, StringNames.Weapons, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusLifeSupp = BooleanOptionItem.Create(103211, StringNames.LifeSupp, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusElectrical = BooleanOptionItem.Create(103212, StringNames.Electrical, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusStorage = BooleanOptionItem.Create(103213, StringNames.Storage, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusRocket = BooleanOptionItem.Create(103214, SpawnPoint.Rocket, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + Options.RandomSpawnPolusToilet = BooleanOptionItem.Create(103215, SpawnPoint.Toilet, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnPolus).SetGameMode(CustomGameMode.All); + // Airship + Options.RandomSpawnAirship = BooleanOptionItem.Create(103400, StringNames.MapNameAirship, false, TabGroup.MainSettings, false).SetParent(Options.EnableRandomSpawn).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipBrig = BooleanOptionItem.Create(103401, StringNames.Brig, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipEngine = BooleanOptionItem.Create(103402, StringNames.Engine, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipKitchen = BooleanOptionItem.Create(103403, StringNames.Kitchen, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipCargoBay = BooleanOptionItem.Create(103404, StringNames.CargoBay, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipRecords = BooleanOptionItem.Create(103405, StringNames.Records, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipMainHall = BooleanOptionItem.Create(103406, StringNames.MainHall, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipNapRoom = BooleanOptionItem.Create(103407, SpawnPoint.NapRoom, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipMeetingRoom = BooleanOptionItem.Create(103408, StringNames.MeetingRoom, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipGapRoom = BooleanOptionItem.Create(103409, StringNames.GapRoom, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipVaultRoom = BooleanOptionItem.Create(103410, StringNames.VaultRoom, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipComms = BooleanOptionItem.Create(103411, StringNames.Comms, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipCockpit = BooleanOptionItem.Create(103412, StringNames.Cockpit, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipArmory = BooleanOptionItem.Create(103413, StringNames.Armory, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipViewingDeck = BooleanOptionItem.Create(103414, StringNames.ViewingDeck, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipSecurity = BooleanOptionItem.Create(103415, StringNames.Security, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipElectrical = BooleanOptionItem.Create(103416, StringNames.Electrical, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipMedical = BooleanOptionItem.Create(103417, StringNames.Medical, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipToilet = BooleanOptionItem.Create(103418, SpawnPoint.Toilet, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + Options.RandomSpawnAirshipShowers = BooleanOptionItem.Create(103419, StringNames.Showers, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnAirship).SetGameMode(CustomGameMode.All); + // Fungle + Options.RandomSpawnFungle = BooleanOptionItem.Create(103500, StringNames.MapNameFungle, false, TabGroup.MainSettings, false).SetParent(Options.EnableRandomSpawn).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleKitchen = BooleanOptionItem.Create(103501, StringNames.Kitchen, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleBeach = BooleanOptionItem.Create(103502, StringNames.Beach, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleBonfire = BooleanOptionItem.Create(103503, SpawnPoint.Bonfire, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleGreenhouse = BooleanOptionItem.Create(103504, StringNames.Greenhouse, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleComms = BooleanOptionItem.Create(103505, StringNames.Comms, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleHighlands = BooleanOptionItem.Create(103506, StringNames.Highlands, true, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleCafeteria = BooleanOptionItem.Create(103507, StringNames.Cafeteria, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleRecRoom = BooleanOptionItem.Create(103508, StringNames.RecRoom, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleDropship = BooleanOptionItem.Create(103509, StringNames.Dropship, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleStorage = BooleanOptionItem.Create(103510, StringNames.Storage, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleMeetingRoom = BooleanOptionItem.Create(103511, StringNames.MeetingRoom, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleSleepingQuarters = BooleanOptionItem.Create(103512, StringNames.SleepingQuarters, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleLaboratory = BooleanOptionItem.Create(103513, StringNames.Laboratory, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleReactor = BooleanOptionItem.Create(103514, StringNames.Reactor, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleJungleTop = BooleanOptionItem.Create(103515, SpawnPoint.JungleTop, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleJungleBottom = BooleanOptionItem.Create(103516, SpawnPoint.JungleBottom, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleLookout = BooleanOptionItem.Create(103517, StringNames.Lookout, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleMiningPit = BooleanOptionItem.Create(103518, StringNames.MiningPit, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFungleUpperEngine = BooleanOptionItem.Create(103519, StringNames.UpperEngine, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); + Options.RandomSpawnFunglePrecipice = BooleanOptionItem.Create(103520, SpawnPoint.Precipice, false, TabGroup.MainSettings, false).SetParent(Options.RandomSpawnFungle).SetGameMode(CustomGameMode.All); } public abstract class SpawnMap { + public abstract Dictionary Positions { get; } public virtual void RandomTeleport(PlayerControl player) { - var location = GetLocation(); + Teleport(player, true); + } + public virtual void FirstTeleport(PlayerControl player) + { + Teleport(player, false); + } + + private void Teleport(PlayerControl player, bool isRadndom) + { + var location = GetLocation(!isRadndom); Logger.Info($"{player.Data.PlayerName}:{location}", "RandomSpawn"); - TP(player.NetTransform, location); + player.RpcSnapTo(location); + } + + public Vector2 GetLocation(Boolean first = false) + { + var EnableLocations = Positions.Where(o => o.Key.GetBool()).ToArray(); + var locations = EnableLocations.Length != 0 ? EnableLocations : Positions.ToArray(); + if (first) return locations[0].Value; + var location = locations.OrderBy(_ => Guid.NewGuid()).Take(1).FirstOrDefault(); + return location.Value; } - public abstract Vector2 GetLocation(); } public class SkeldSpawnMap : SpawnMap { - public Dictionary positions = new() - { - ["Cafeteria"] = new(-1.0f, 3.0f), - ["Weapons"] = new(9.3f, 1.0f), - ["O2"] = new(6.5f, -3.8f), - ["Navigation"] = new(16.5f, -4.8f), - ["Shields"] = new(9.3f, -12.3f), - ["Communications"] = new(4.0f, -15.5f), - ["Storage"] = new(-1.5f, -15.5f), - ["Admin"] = new(4.5f, -7.9f), - ["Electrical"] = new(-7.5f, -8.8f), - ["LowerEngine"] = new(-17.0f, -13.5f), - ["UpperEngine"] = new(-17.0f, -1.3f), - ["Security"] = new(-13.5f, -5.5f), - ["Reactor"] = new(-20.5f, -5.5f), - ["MedBay"] = new(-9.0f, -4.0f) - }; - public override Vector2 GetLocation() + public override Dictionary Positions { get; } = new() { - return positions.ToArray().OrderBy(_ => Guid.NewGuid()).Take(1).FirstOrDefault().Value; - } + [Options.RandomSpawnSkeldCafeteria] = new(-1.0f, 3.0f), + [Options.RandomSpawnSkeldWeapons] = new(9.3f, 1.0f), + [Options.RandomSpawnSkeldLifeSupp] = new(6.5f, -3.8f), + [Options.RandomSpawnSkeldNav] = new(16.5f, -4.8f), + [Options.RandomSpawnSkeldShields] = new(9.3f, -12.3f), + [Options.RandomSpawnSkeldComms] = new(4.0f, -15.5f), + [Options.RandomSpawnSkeldStorage] = new(-1.5f, -15.5f), + [Options.RandomSpawnSkeldAdmin] = new(4.5f, -7.9f), + [Options.RandomSpawnSkeldElectrical] = new(-7.5f, -8.8f), + [Options.RandomSpawnSkeldLowerEngine] = new(-17.0f, -13.5f), + [Options.RandomSpawnSkeldUpperEngine] = new(-17.0f, -1.3f), + [Options.RandomSpawnSkeldSecurity] = new(-13.5f, -5.5f), + [Options.RandomSpawnSkeldReactor] = new(-20.5f, -5.5f), + [Options.RandomSpawnSkeldMedBay] = new(-9.0f, -4.0f) + }; } public class MiraHQSpawnMap : SpawnMap { - public Dictionary positions = new() - { - ["Cafeteria"] = new(25.5f, 2.0f), - ["Balcony"] = new(24.0f, -2.0f), - ["Storage"] = new(19.5f, 4.0f), - ["ThreeWay"] = new(17.8f, 11.5f), - ["Communications"] = new(15.3f, 3.8f), - ["MedBay"] = new(15.5f, -0.5f), - ["LockerRoom"] = new(9.0f, 1.0f), - ["Decontamination"] = new(6.1f, 6.0f), - ["Laboratory"] = new(9.5f, 12.0f), - ["Reactor"] = new(2.5f, 10.5f), - ["Launchpad"] = new(-4.5f, 2.0f), - ["Admin"] = new(21.0f, 17.5f), - ["Office"] = new(15.0f, 19.0f), - ["Greenhouse"] = new(17.8f, 23.0f) - }; - public override Vector2 GetLocation() + public override Dictionary Positions { get; } = new() { - return positions.ToArray().OrderBy(_ => Guid.NewGuid()).Take(1).FirstOrDefault().Value; - } + [Options.RandomSpawnMiraCafeteria] = new(25.5f, 2.0f), + [Options.RandomSpawnMiraBalcony] = new(24.0f, -2.0f), + [Options.RandomSpawnMiraStorage] = new(19.5f, 4.0f), + [Options.RandomSpawnMiraJunction] = new(17.8f, 11.5f), + [Options.RandomSpawnMiraComms] = new(15.3f, 3.8f), + [Options.RandomSpawnMiraMedBay] = new(15.5f, -0.5f), + [Options.RandomSpawnMiraLockerRoom] = new(9.0f, 1.0f), + [Options.RandomSpawnMiraDecontamination] = new(6.1f, 6.0f), + [Options.RandomSpawnMiraLaboratory] = new(9.5f, 12.0f), + [Options.RandomSpawnMiraReactor] = new(2.5f, 10.5f), + [Options.RandomSpawnMiraLaunchpad] = new(-4.5f, 2.0f), + [Options.RandomSpawnMiraAdmin] = new(21.0f, 17.5f), + [Options.RandomSpawnMiraOffice] = new(15.0f, 19.0f), + [Options.RandomSpawnMiraGreenhouse] = new(17.8f, 23.0f) + }; } public class PolusSpawnMap : SpawnMap { - public Dictionary positions = new() - { - ["Office1"] = new(19.5f, -18.0f), - ["Office2"] = new(26.0f, -17.0f), - ["Admin"] = new(24.0f, -22.5f), - ["Communications"] = new(12.5f, -16.0f), - ["Weapons"] = new(12.0f, -23.5f), - ["BoilerRoom"] = new(2.3f, -24.0f), - ["O2"] = new(2.0f, -17.5f), - ["Electrical"] = new(9.5f, -12.5f), - ["Security"] = new(3.0f, -12.0f), - ["Dropship"] = new(16.7f, -3.0f), - ["Storage"] = new(20.5f, -12.0f), - ["Rocket"] = new(26.7f, -8.5f), - ["Laboratory"] = new(36.5f, -7.5f), - ["Toilet"] = new(34.0f, -10.0f), - ["SpecimenRoom"] = new(36.5f, -22.0f) - }; - public override Vector2 GetLocation() + public override Dictionary Positions { get; } = new() { - return positions.ToArray().OrderBy(_ => Guid.NewGuid()).Take(1).FirstOrDefault().Value; - } + + [Options.RandomSpawnPolusOfficeLeft] = new(19.5f, -18.0f), + [Options.RandomSpawnPolusOfficeRight] = new(26.0f, -17.0f), + [Options.RandomSpawnPolusAdmin] = new(24.0f, -22.5f), + [Options.RandomSpawnPolusComms] = new(12.5f, -16.0f), + [Options.RandomSpawnPolusWeapons] = new(12.0f, -23.5f), + [Options.RandomSpawnPolusBoilerRoom] = new(2.3f, -24.0f), + [Options.RandomSpawnPolusLifeSupp] = new(2.0f, -17.5f), + [Options.RandomSpawnPolusElectrical] = new(9.5f, -12.5f), + [Options.RandomSpawnPolusSecurity] = new(3.0f, -12.0f), + [Options.RandomSpawnPolusDropship] = new(16.7f, -3.0f), + [Options.RandomSpawnPolusStorage] = new(20.5f, -12.0f), + [Options.RandomSpawnPolusRocket] = new(26.7f, -8.5f), + [Options.RandomSpawnPolusLaboratory] = new(36.5f, -7.5f), + [Options.RandomSpawnPolusToilet] = new(34.0f, -10.0f), + [Options.RandomSpawnPolusSpecimens] = new(36.5f, -22.0f) + }; } public class AirshipSpawnMap : SpawnMap { - public Dictionary positions = new() - { - ["Brig"] = new(-0.7f, 8.5f), - ["Engine"] = new(-0.7f, -1.0f), - ["Kitchen"] = new(-7.0f, -11.5f), - ["CargoBay"] = new(33.5f, -1.5f), - ["Records"] = new(20.0f, 10.5f), - ["MainHall"] = new(15.5f, 0.0f), - ["NapRoom"] = new(6.3f, 2.5f), - ["MeetingRoom"] = new(17.1f, 14.9f), - ["GapRoom"] = new(12.0f, 8.5f), - ["Vault"] = new(-8.9f, 12.2f), - ["Communications"] = new(-13.3f, 1.3f), - ["Cockpit"] = new(-23.5f, -1.6f), - ["Armory"] = new(-10.3f, -5.9f), - ["ViewingDeck"] = new(-13.7f, -12.6f), - ["Security"] = new(5.8f, -10.8f), - ["Electrical"] = new(16.3f, -8.8f), - ["Medical"] = new(29.0f, -6.2f), - ["Toilet"] = new(30.9f, 6.8f), - ["Showers"] = new(21.2f, -0.8f) + public override Dictionary Positions { get; } = new() + { + [Options.RandomSpawnAirshipBrig] = new(-0.7f, 8.5f), + [Options.RandomSpawnAirshipEngine] = new(-0.7f, -1.0f), + [Options.RandomSpawnAirshipKitchen] = new(-7.0f, -11.5f), + [Options.RandomSpawnAirshipCargoBay] = new(33.5f, -1.5f), + [Options.RandomSpawnAirshipRecords] = new(20.0f, 10.5f), + [Options.RandomSpawnAirshipMainHall] = new(15.5f, 0.0f), + [Options.RandomSpawnAirshipNapRoom] = new(6.3f, 2.5f), + [Options.RandomSpawnAirshipMeetingRoom] = new(17.1f, 14.9f), + [Options.RandomSpawnAirshipGapRoom] = new(12.0f, 8.5f), + [Options.RandomSpawnAirshipVaultRoom] = new(-8.9f, 12.2f), + [Options.RandomSpawnAirshipComms] = new(-13.3f, 1.3f), + [Options.RandomSpawnAirshipCockpit] = new(-23.5f, -1.6f), + [Options.RandomSpawnAirshipArmory] = new(-10.3f, -5.9f), + [Options.RandomSpawnAirshipViewingDeck] = new(-13.7f, -12.6f), + [Options.RandomSpawnAirshipSecurity] = new(5.8f, -10.8f), + [Options.RandomSpawnAirshipElectrical] = new(16.3f, -8.8f), + [Options.RandomSpawnAirshipMedical] = new(29.0f, -6.2f), + [Options.RandomSpawnAirshipToilet] = new(30.9f, 6.8f), + [Options.RandomSpawnAirshipShowers] = new(21.2f, -0.8f) }; - public override Vector2 GetLocation() + } + public class FungleSpawnMap : SpawnMap + { + public override Dictionary Positions { get; } = new() { - return Options.AirshipAdditionalSpawn.GetBool() - ? positions.ToArray().OrderBy(_ => Guid.NewGuid()).Take(1).FirstOrDefault().Value - : positions.ToArray()[0..6].OrderBy(_ => Guid.NewGuid()).Take(1).FirstOrDefault().Value; - } + [Options.RandomSpawnFungleKitchen] = new(-17.8f, -7.3f), + [Options.RandomSpawnFungleBeach] = new(-21.3f, 3.0f), //海岸 + [Options.RandomSpawnFungleCafeteria] = new(-16.9f, 5.5f), + [Options.RandomSpawnFungleRecRoom] = new(-17.7f, 0.0f), + [Options.RandomSpawnFungleBonfire] = new(-9.7f, 2.7f), //焚き火 + [Options.RandomSpawnFungleDropship] = new(-7.6f, 10.4f), + [Options.RandomSpawnFungleStorage] = new(2.3f, 4.3f), + [Options.RandomSpawnFungleMeetingRoom] = new(-4.2f, -2.2f), + [Options.RandomSpawnFungleSleepingQuarters] = new(1.7f, -1.4f), //宿舎 + [Options.RandomSpawnFungleLaboratory] = new(-4.2f, -7.9f), + [Options.RandomSpawnFungleGreenhouse] = new(9.2f, -11.8f), + [Options.RandomSpawnFungleReactor] = new(21.8f, -7.2f), + [Options.RandomSpawnFungleJungleTop] = new(4.2f, -5.3f), + [Options.RandomSpawnFungleJungleBottom] = new(15.9f, -14.8f), + [Options.RandomSpawnFungleLookout] = new(6.4f, 3.1f), + [Options.RandomSpawnFungleMiningPit] = new(12.5f, 9.6f), + [Options.RandomSpawnFungleHighlands] = new(15.5f, 3.9f), //展望台右の高地 + [Options.RandomSpawnFungleUpperEngine] = new(21.9f, 3.2f), + [Options.RandomSpawnFunglePrecipice] = new(19.8f, 7.3f), //通信室下の崖 + [Options.RandomSpawnFungleComms] = new(20.9f, 13.4f), + }; } } } \ No newline at end of file diff --git a/Patches/SabotageSystemPatch.cs b/Patches/SabotageSystemPatch.cs deleted file mode 100644 index d674aebbc..000000000 --- a/Patches/SabotageSystemPatch.cs +++ /dev/null @@ -1,112 +0,0 @@ -using HarmonyLib; -using TownOfHost.Attributes; - -namespace TownOfHost -{ - //参考 - //https://github.com/Koke1024/Town-Of-Moss/blob/main/TownOfMoss/Patches/MeltDownBoost.cs - - [HarmonyPatch(typeof(ReactorSystemType), nameof(ReactorSystemType.Detoriorate))] - public static class ReactorSystemTypePatch - { - public static void Prefix(ReactorSystemType __instance) - { - if (!__instance.IsActive || !Options.SabotageTimeControl.GetBool()) - return; - if (ShipStatus.Instance.Type == ShipStatus.MapType.Pb) - { - if (__instance.Countdown >= Options.PolusReactorTimeLimit.GetFloat()) - __instance.Countdown = Options.PolusReactorTimeLimit.GetFloat(); - return; - } - return; - } - } - [HarmonyPatch(typeof(HeliSabotageSystem), nameof(HeliSabotageSystem.Detoriorate))] - public static class HeliSabotageSystemPatch - { - public static void Prefix(HeliSabotageSystem __instance) - { - if (!__instance.IsActive || !Options.SabotageTimeControl.GetBool()) - return; - if (AirshipStatus.Instance != null) - if (__instance.Countdown >= Options.AirshipReactorTimeLimit.GetFloat()) - __instance.Countdown = Options.AirshipReactorTimeLimit.GetFloat(); - } - } - [HarmonyPatch(typeof(SwitchSystem), nameof(SwitchSystem.RepairDamage))] - public static class SwitchSystemRepairDamagePatch - { - public static bool Prefix(SwitchSystem __instance, [HarmonyArgument(1)] byte amount) - { - if (!AmongUsClient.Instance.AmHost) - { - return true; - } - - // サボタージュによる破壊ではない && 配電盤を下げられなくするオプションがオン - if (!amount.HasBit(SwitchSystem.DamageSystem) && Options.BlockDisturbancesToSwitches.GetBool()) - { - // amount分だけ1を左にずらす - // 各桁が各ツマミに対応する - // 一番左のツマミが操作されたら(amount: 0) 00001 - // 一番右のツマミが操作されたら(amount: 4) 10000 - // ref: SwitchSystem.RepairDamage, SwitchMinigame.FixedUpdate - var switchedKnob = (byte)(0b_00001 << amount); - // ExpectedSwitches: すべてONになっているときのスイッチの上下状態 - // ActualSwitches: 実際のスイッチの上下状態 - // 操作されたツマミについて,ExpectedとActualで同じならそのツマミは既に直ってる - if ((__instance.ActualSwitches & switchedKnob) == (__instance.ExpectedSwitches & switchedKnob)) - { - return false; - } - } - return true; - } - } - [HarmonyPatch(typeof(ElectricTask), nameof(ElectricTask.Initialize))] - public static class ElectricTaskInitializePatch - { - public static void Postfix() - { - Utils.MarkEveryoneDirtySettings(); - if (!GameStates.IsMeeting) - Utils.NotifyRoles(ForceLoop: true); - } - } - [HarmonyPatch(typeof(ElectricTask), nameof(ElectricTask.Complete))] - public static class ElectricTaskCompletePatch - { - public static void Postfix() - { - Utils.MarkEveryoneDirtySettings(); - if (!GameStates.IsMeeting) - Utils.NotifyRoles(ForceLoop: true); - } - } - - // サボタージュを発生させたときに呼び出されるメソッド - [HarmonyPatch(typeof(SabotageSystemType), nameof(SabotageSystemType.RepairDamage))] - public static class SabotageSystemTypeRepairDamagePatch - { - private static bool isCooldownModificationEnabled; - private static float modifiedCooldownSec; - - [GameModuleInitializer] - public static void Initialize() - { - isCooldownModificationEnabled = Options.ModifySabotageCooldown.GetBool(); - modifiedCooldownSec = Options.SabotageCooldown.GetFloat(); - } - - public static void Postfix(SabotageSystemType __instance) - { - if (!isCooldownModificationEnabled || !AmongUsClient.Instance.AmHost) - { - return; - } - __instance.Timer = modifiedCooldownSec; - __instance.IsDirty = true; - } - } -} \ No newline at end of file diff --git a/Patches/ShipStatusPatch.cs b/Patches/ShipStatusPatch.cs index 87f8d4e6b..12e6d7a93 100644 --- a/Patches/ShipStatusPatch.cs +++ b/Patches/ShipStatusPatch.cs @@ -6,8 +6,6 @@ using UnityEngine; using TownOfHost.Modules; -using TownOfHost.Roles.Core; -using TownOfHost.Roles.Neutral; namespace TownOfHost { @@ -35,69 +33,22 @@ public static void Postfix(ShipStatus __instance) } } } - [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.RepairSystem))] - class RepairSystemPatch + [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.UpdateSystem), typeof(SystemTypes), typeof(PlayerControl), typeof(byte))] + class ShipStatusUpdateSystemPatch { - public static bool Prefix(ShipStatus __instance, + public static void Prefix(ShipStatus __instance, [HarmonyArgument(0)] SystemTypes systemType, [HarmonyArgument(1)] PlayerControl player, [HarmonyArgument(2)] byte amount) { - if (systemType == SystemTypes.Sabotage) + if (systemType != SystemTypes.Sabotage) { - Logger.Info("SystemType: " + systemType.ToString() + ", PlayerName: " + player.GetNameWithRole() + ", SabotageType: " + (SystemTypes)amount, "RepairSystem"); + Logger.Info("SystemType: " + systemType.ToString() + ", PlayerName: " + player.GetNameWithRole() + ", amount: " + amount, "UpdateSystem"); } - else - { - Logger.Info("SystemType: " + systemType.ToString() + ", PlayerName: " + player.GetNameWithRole() + ", amount: " + amount, "RepairSystem"); - } - if (RepairSender.enabled && AmongUsClient.Instance.NetworkMode != NetworkModes.OnlineGame) { Logger.SendInGame("SystemType: " + systemType.ToString() + ", PlayerName: " + player.GetNameWithRole() + ", amount: " + amount); } - if (!AmongUsClient.Instance.AmHost) return true; //以下、ホストのみ実行 - - if (systemType == SystemTypes.Sabotage) - { - var nextSabotage = (SystemTypes)amount; - //HASモードではサボタージュ不可 - if (Options.CurrentGameMode == CustomGameMode.HideAndSeek || Options.IsStandardHAS) return false; - var roleClass = player.GetRoleClass(); - if (roleClass != null) - { - return roleClass.OnInvokeSabotage(nextSabotage); - } - else - { - return CanSabotage(player, nextSabotage); - } - } - // カメラ無効時,バニラプレイヤーはカメラを開けるので点滅させない - else if (systemType == SystemTypes.Security && amount == 1) - { - var camerasDisabled = (MapNames)Main.NormalOptions.MapId switch - { - MapNames.Skeld => Options.DisableSkeldCamera.GetBool(), - MapNames.Polus => Options.DisablePolusCamera.GetBool(), - MapNames.Airship => Options.DisableAirshipCamera.GetBool(), - _ => false, - }; - return !camerasDisabled; - } - else - { - return CustomRoleManager.OnSabotage(player, systemType, amount); - } - } - public static void Postfix( - ShipStatus __instance, - [HarmonyArgument(0)] SystemTypes systemType, - [HarmonyArgument(1)] PlayerControl player, - [HarmonyArgument(2)] byte amount) - { - Camouflage.CheckCamouflage(); - DeviceTimer.HandleRepairSystem(systemType, player, amount); } public static void CheckAndOpenDoorsRange(ShipStatus __instance, int amount, int min, int max) { @@ -112,55 +63,9 @@ private static void CheckAndOpenDoors(ShipStatus __instance, int amount, params { if (DoorIds.Contains(amount)) foreach (var id in DoorIds) { - __instance.RpcRepairSystem(SystemTypes.Doors, id); + __instance.RpcUpdateSystem(SystemTypes.Doors, (byte)id); } } - private static bool CanSabotage(PlayerControl player, SystemTypes systemType) - { - //サボタージュ出来ないキラー役職はサボタージュ自体をキャンセル - if (!player.Is(CustomRoleTypes.Impostor)) - { - return false; - } - return true; - } - public static bool OnSabotage(PlayerControl player, SystemTypes systemType, byte amount) - { - // 停電サボタージュが鳴らされた場合は関係なし(ホスト名義で飛んでくるため誤爆注意) - if (systemType == SystemTypes.Electrical && amount.HasBit(SwitchSystem.DamageSystem)) - { - return true; - } - - var isMadmate = - player.Is(CustomRoleTypes.Madmate) || - // マッド属性化時に削除 - (player.GetRoleClass() is SchrodingerCat schrodingerCat && schrodingerCat.AmMadmate); - if (isMadmate) - { - if (systemType == SystemTypes.Comms) - { - //直せてしまったらキャンセル - return !(!Options.MadmateCanFixComms.GetBool() && amount is 0 or 16 or 17); - } - if (systemType == SystemTypes.Electrical) - { - //直せないならキャンセル - if (!Options.MadmateCanFixLightsOut.GetBool()) - return false; - } - } - - //Airshipの特定の停電を直せないならキャンセル - if (systemType == SystemTypes.Electrical && Main.NormalOptions.MapId == 4) - { - var truePosition = player.GetTruePosition(); - if (Options.DisableAirshipViewingDeckLightsPanel.GetBool() && Vector2.Distance(truePosition, new(-12.93f, -11.28f)) <= 2f) return false; - if (Options.DisableAirshipGapRoomLightsPanel.GetBool() && Vector2.Distance(truePosition, new(13.92f, 6.43f)) <= 2f) return false; - if (Options.DisableAirshipCargoLightsPanel.GetBool() && Vector2.Distance(truePosition, new(30.56f, 2.12f)) <= 2f) return false; - } - return true; - } } [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.CloseDoorsOfType))] class CloseDoorsPatch diff --git a/Patches/TaskAssignPatch.cs b/Patches/TaskAssignPatch.cs index d697cf204..4f3035fb3 100644 --- a/Patches/TaskAssignPatch.cs +++ b/Patches/TaskAssignPatch.cs @@ -105,7 +105,7 @@ public static void Prefix(GameData __instance, //割り当て可能なショートタスクのリスト Il2CppSystem.Collections.Generic.List ShortTasks = new(); - foreach (var task in ShipStatus.Instance.NormalTasks) + foreach (var task in ShipStatus.Instance.ShortTasks) ShortTasks.Add(task); Shuffle(ShortTasks); diff --git a/Patches/UsablesPatch.cs b/Patches/UsablesPatch.cs index 41499642c..dd88122e6 100644 --- a/Patches/UsablesPatch.cs +++ b/Patches/UsablesPatch.cs @@ -6,7 +6,6 @@ using TownOfHost.Modules; using TownOfHost.Roles.Core; -using TownOfHost.Roles.Neutral; namespace TownOfHost { [HarmonyPatch(typeof(Console), nameof(Console.CanUse))] @@ -35,51 +34,58 @@ public static bool Prefix(Vent __instance, [HarmonyArgument(0)] GameData.PlayerI [HarmonyArgument(2)] ref bool couldUse, ref float __result) { - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PlayerControl playerControl = pc.Object; - //####################################### - // ==ベント処理== - //####################################### - //参考:https://github.com/Eisbison/TheOtherRoles/blob/main/TheOtherRoles/Patches/UsablesPatch.cs + // 前半,Mod独自の処理 - bool VentForTrigger = false; - float num = float.MaxValue; + // カスタムロールを元にベントを使えるか判定 + // エンジニアベースの役職は常にtrue + couldUse = playerControl.CanUseImpostorVentButton() || pc.Role.Role == RoleTypes.Engineer; - var usableDistance = __instance.UsableDistance; - - if (pc.IsDead) return false; //死んでる人は強制的にfalseに。 - - canUse = couldUse = pc.Object.CanUseImpostorVentButton(); - switch (pc.GetCustomRole()) + canUse = couldUse; + // カスタムロールが使えなかったら使用不可 + if (!canUse) { - case CustomRoles.Arsonist: - if (Arsonist.IsDouseDone(pc.Object)) - VentForTrigger = true; - break; - default: - if (pc.Role.Role == RoleTypes.Engineer) // インポスター陣営ベースの役職とエンジニアベースの役職は常にtrue - canUse = couldUse = true; - break; + return false; } - if (!canUse) return false; - canUse = couldUse = (pc.Object.inVent || canUse) && (pc.Object.CanMove || pc.Object.inVent); + // ここまでMod独自の処理 + // ここからバニラ処理の置き換え + + IUsable usableVent = __instance.Cast(); + // ベントとプレイヤーの間の距離 + float actualDistance = float.MaxValue; + + couldUse = + // クラシックではtrue 多分バニラHnS用 + GameManager.Instance.LogicUsables.CanUse(usableVent, playerControl) && + // pc.Role.CanUse(usableVent) && バニラロールではなくカスタムロールを元に判定するので無視 + // 対象のベントにベントタスクがない もしくは今自分が対象のベントに入っている + (!playerControl.MustCleanVent(__instance.Id) || (playerControl.inVent && Vent.currentVent == __instance)) && + playerControl.IsAlive() && + (playerControl.CanMove || playerControl.inVent); - if (VentForTrigger && pc.Object.inVent) + // ベント掃除のチェック + if (ShipStatus.Instance.Systems.TryGetValue(SystemTypes.Ventilation, out var systemType)) { - canUse = couldUse = false; - return false; + VentilationSystem ventilationSystem = systemType.TryCast(); + // 誰かがベント掃除をしていたらそのベントには入れない + if (ventilationSystem != null && ventilationSystem.IsVentCurrentlyBeingCleaned(__instance.Id)) + { + couldUse = false; + } } + + canUse = couldUse; if (canUse) { - Vector2 truePosition = pc.Object.GetTruePosition(); - Vector3 position = __instance.transform.position; - num = Vector2.Distance(truePosition, position); - canUse &= num <= usableDistance && !PhysicsHelpers.AnythingBetween(truePosition, position, Constants.ShipOnlyMask, false); + Vector3 center = playerControl.Collider.bounds.center; + Vector3 ventPosition = __instance.transform.position; + actualDistance = Vector2.Distance(center, ventPosition); + canUse &= actualDistance <= __instance.UsableDistance && !PhysicsHelpers.AnythingBetween(playerControl.Collider, center, ventPosition, Constants.ShipOnlyMask, false); } - __result = num; + __result = actualDistance; return false; - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// } } diff --git a/Patches/onGameStartedPatch.cs b/Patches/onGameStartedPatch.cs index 0a9e07f9a..4753fe368 100644 --- a/Patches/onGameStartedPatch.cs +++ b/Patches/onGameStartedPatch.cs @@ -28,7 +28,6 @@ public static void Postfix(AmongUsClient __instance) Main.SKMadmateNowCount = 0; Main.AfterMeetingDeathPlayers = new(); - Main.ResetCamPlayerList = new(); Main.clientIdList = new(); Main.CheckShapeshift = new(); @@ -41,7 +40,9 @@ public static void Postfix(AmongUsClient __instance) Main.introDestroyed = false; - RandomSpawn.CustomNetworkTransformPatch.NumOfTP = new(); + RandomSpawn.FirstTP = new(); + RandomSpawn.FastSpawnPosition = new(); + RandomSpawn.hostReady = false; Main.DefaultCrewmateVision = Main.RealOptionsData.GetFloat(FloatOptionNames.CrewLightMod); Main.DefaultImpostorVision = Main.RealOptionsData.GetFloat(FloatOptionNames.ImpostorLightMod); @@ -84,7 +85,7 @@ public static void Postfix(AmongUsClient __instance) ReportDeadBodyPatch.WaitReport[pc.PlayerId] = new(); pc.cosmetics.nameText.text = pc.name; - RandomSpawn.CustomNetworkTransformPatch.NumOfTP.Add(pc.PlayerId, 0); + RandomSpawn.FirstTP.Add(pc.PlayerId, true); var outfit = pc.Data.DefaultOutfit; Camouflage.PlayerSkins[pc.PlayerId] = new GameData.PlayerOutfit().Set(outfit.PlayerName, outfit.ColorId, outfit.HatId, outfit.SkinId, outfit.VisorId, outfit.PetId); Main.clientIdList.Add(pc.GetClientId()); @@ -153,7 +154,7 @@ public static void Prefix() Dictionary<(byte, byte), RoleTypes> rolesMap = new(); foreach (var (role, info) in CustomRoleManager.AllRolesInfo) { - if (info.RequireResetCam) + if (info.IsDesyncImpostor) { AssignDesyncRole(role, AllPlayers, senders, rolesMap, BaseRole: info.BaseRoleType.Invoke()); } @@ -249,10 +250,10 @@ public static void Postfix() } else { - foreach (var role in CustomRolesHelper.AllRoles.Where(x => x < CustomRoles.NotAssigned)) + foreach (var role in CustomRolesHelper.AllStandardRoles) { if (role.IsVanilla()) continue; - if (CustomRoleManager.GetRoleInfo(role) is SimpleRoleInfo info && info.RequireResetCam) continue; + if (CustomRoleManager.GetRoleInfo(role)?.IsDesyncImpostor == true) continue; var baseRoleTypes = role.GetRoleTypes() switch { RoleTypes.Impostor => Impostors, @@ -444,9 +445,9 @@ private static void AssignLoversRoles(int RawCount = -1) public static int GetRoleTypesCount(RoleTypes roleTypes) { int count = 0; - foreach (var role in CustomRolesHelper.AllRoles.Where(x => x < CustomRoles.NotAssigned)) + foreach (var role in CustomRolesHelper.AllRoles) { - if (CustomRoleManager.GetRoleInfo(role) is SimpleRoleInfo info && info.RequireResetCam) continue; + if (CustomRoleManager.GetRoleInfo(role)?.IsDesyncImpostor == true) continue; if (role == CustomRoles.Egoist && Main.NormalOptions.GetInt(Int32OptionNames.NumImpostors) <= 1) continue; if (role.GetRoleTypes() == roleTypes) count += role.GetRealCount(); diff --git a/Resources/string.csv b/Resources/string.csv index bc9cd0a06..e9ba12a2b 100644 --- a/Resources/string.csv +++ b/Resources/string.csv @@ -1,717 +1,743 @@ -"id","0","11","13","14","5","2" +"id","0","11","13","14","5","2","3" "## Translators" "#5 RusTranslation: Tommy-XL" "#13 SChinese:fivefirex&ZeMingOH233 2T&MC-AS-Huier&xiaojin xyjx001(Edit: Tanakanira)" "#14 TChinese: NetherDragon_Tw (Edit: Soukunsandesu)" +"# TownOfHost-H" +"CamerasTimer","Cameras Timer","カメラの時間制限(全マップ)","","","","","" +"CamerasMaxTimer","Cameras Max Timer","使用可能時間","","","","","" +"AirshipAllOpenElectrical","AirshipAllOpenElectrical","電気室のドアを全開放する(エアシップ)","","","","","" +"ModdedMadCantOpen","Modded Mad Role Players Can't Use Those Consoles","Mod導入済マッド系プレイヤーは開けない","","","","","" +"EvilHackerCanSeeImpostorArrow",,"他のインポスターを指す矢印が見える","","","","","" +"EvilHackerInheritAbility","InheritAbility","死亡時、生存インポスターに能力を引き継ぐ","","","","","" +"EvilHackerSkipUnoccupiedRooms","EvilHackerSkipUnoccupiedRooms","アドミン情報で誰もいない部屋を省略する","","","","","" +"DiscordResult","Discord Result","Discordに試合結果を送信","","","","","" +"ShowLobbySummary","Show Lobby Summary","ロビーで前の試合の結果を表示","","","","","" +"CopyCode","Copy Code","部屋建て時にコードを自動でコピー","","","","","" +"HauntFocusCrew","Haunt Focus crew","憑依開始時に生存者にフォーカス","","","","","" + "## 役職" -"Neutral","Neutral","ニュートラル","独立阵营","中立陣營","Нейтрал","Neutro" +"Neutral","Neutral","ニュートラル","独立","中立陣營","Нейтралов","Neutro","" "# バニラ役職" -"Crewmate","Crewmate","クルーメイト","船员","船員","Член Экипажа","Tripulante" -"Engineer","Engineer","エンジニア","工程师","工程師","Инженер","Engenheiro" -"Scientist","Scientist","科学者","科学家","科學家","Ученый","Cientista" -"GuardianAngel","Guardian Angel","守護天使","守护天使","守護天使","Ангел-хранитель","Anjo da Guarda" -"Impostor","Impostor","インポスター","内鬼","偽裝者","Предатель","Impostor" -"Shapeshifter","Shapeshifter","シェイプシフター","变形者","變形者","Оборотень","Metamorfo" +"Crewmate","Crewmate","クルーメイト","船员","船員","Член Экипажа","Tripulante","Tripulante" +"Engineer","Engineer","エンジニア","工程师","工程師","Инженер","Engenheiro","Engenheiro" +"Scientist","Scientist","科学者","科学家","科學家","Ученый","Cientista","Cientista" +"GuardianAngel","Guardian Angel","守護天使","守护天使","守護天使","Ангел-хранитель","Anjo da Guarda","Anjo da Guarda" +"Impostor","Impostor","インポスター","内鬼","偽裝者","Предателей","Impostor","Impostor" +"Shapeshifter","Shapeshifter","シェイプシフター","变形者","變形者","Оборотень","Metamorfo","Metamorfo" "# 特殊インポスター役職" -"BountyHunter","Bounty Hunter","バウンティハンター","赏金猎人","賞金獵人","Охотник за головами","Caçador de Recompensas" -"Mare","Mare","メアー","梦魇","獵夢者","Ночной","Mare" -"FireWorks","Fireworker","花火職人","烟花商人","煙火工匠","Фейерверк","Fogos de Artifício" -"SerialKiller","Serial Killer","シリアルキラー","嗜血杀手","嗜血殺手","Серийный Убийца","Serial Killer" -"ShapeMaster","ShapeMaster","シェイプマスター","千面鬼","百變怪","Шпион","Metamorfo Mestre" -"Vampire","Vampire","ヴァンパイア","吸血鬼","吸血鬼","Вампир","Vampiro" -"Warlock","Warlock","ウォーロック","术士","術士","Колдун","Mago" -"Witch","Witch","ウィッチ","女巫","女巫","Ведьма","Bruxo" -"Mafia","Mafia","マフィア","黑手党","黑手黨","Мафиози","Mafioso" -"Puppeteer","Puppeteer","パペッティア","傀儡师","傀儡師","Кукловод","Marionetista" -"TimeThief","Time Thief","タイムシーフ","蚀时者","時間竊賊","Вор Времени","Ladrão de Tempo" -"Sniper","Sniper","スナイパー","狙击手","狙擊手","Снайпер","Sniper" -"EvilTracker","Evil Tracker","イビルトラッカー","邪恶的追踪者","邪惡的追蹤者","Злой Следопыт","Rastreador do Mal" -"Stealth","Stealth","ステルス","","","Хитрец","" -"NekoKabocha","Neko-Kabocha","ネコカボチャ","","","","" -"EvilHacker","Evil Hacker","イビルハッカー","","","Злой Хакер","" -"Penguin","Penguin","ペンギン","","","Пингвин","" -"Insider","Insider","インサイダー","","","Инсайдер","" +"BountyHunter","Bounty Hunter","バウンティハンター","赏金猎人","賞金獵人","Охотник за головами","Caçador de Recompensas","Caçador de Recompensas" +"Mare","Mare","メアー","梦魇","獵夢者","Ночной","Mare","Mare" +"FireWorks","Fireworker","花火職人","烟花商人","煙火工匠","Фейерверк","Fogos de Artifício","Fogos de Artifício" +"SerialKiller","Serial Killer","シリアルキラー","嗜血杀手","嗜血殺手","Серийный Убийца","Serial Killer","Serial Killer" +"ShapeMaster","ShapeMaster","シェイプマスター","千面鬼","百變怪","Шпион","Metamorfo Mestre","Metamorfo Mestre" +"Vampire","Vampire","ヴァンパイア","吸血鬼","吸血鬼","Вампир","Vampiro","Vampiro" +"Warlock","Warlock","ウォーロック","术士","術士","Колдун","Mago","Mago" +"Witch","Witch","ウィッチ","女巫","女巫","Ведьма","Bruxo","Bruxo" +"Mafia","Mafia","マフィア","黑手党","黑手黨","Мафиози","Mafioso","Mafioso" +"Puppeteer","Puppeteer","パペッティア","傀儡师","傀儡師","Кукловод","Marionetista","Marionetista" +"TimeThief","Time Thief","タイムシーフ","蚀时者","時間竊賊","Вор Времени","Ladrão de Tempo","Ladrão Temporal" +"Sniper","Sniper","スナイパー","狙击手","狙擊手","Снайпер","Sniper","Sniper" +"EvilTracker","Evil Tracker","イビルトラッカー","邪恶的追踪者","邪惡的追蹤者","Злой Следопыт","Rastreador do Mal","Rastreador do Mal" +"Stealth","Stealth","ステルス","暗杀者","","Хитрец","Furtivo","" +"NekoKabocha","Neko-Kabocha","ネコカボチャ","猫又","","Неко-Кабоча","Neko-Kabocha","" +"EvilHacker","Evil Hacker","イビルハッカー","邪恶黑客","","Злой Хакер","Hacker do Mal","" +"Penguin","Penguin","ペンギン","企鹅","","Пингвин","Pinguim","" +"Insider","Insider","インサイダー","内幕人","","Инсайдер","Informante","" "# マッドメイト系役職" -"Madmate","Madmate","マッドメイト","叛徒","叛徒","Безумец","Tripulante Louco" -"MadGuardian","Mad Guardian","マッドガーディアン","背叛的守卫","背叛的天使","Безумный Страж","Guardião Louco" -"MadSnitch","Mad Snitch","マッドスニッチ","背叛的告密者","背叛的告密者","Безумный Стукач","Dedo-Duro Louco" -"SKMadmate","Sidekick Madmate","サイドキックマッドメイト","叛徒跟班","叛徒跟班","Союзник Безумца","Tripulante Louco Ajudante" +"Madmate","Madmate","マッドメイト","叛徒","叛徒","Безумец","Tripulante Louco","Tripulante Louco" +"MadGuardian","Mad Guardian","マッドガーディアン","背叛的守卫","背叛的天使","Безумный Страж","Guardião Louco","Guardião Louco" +"MadSnitch","Mad Snitch","マッドスニッチ","背叛的告密者","背叛的告密者","Безумный Стукач","Dedo-Duro Louco","Dedo-Duro Louco" +"SKMadmate","Sidekick Madmate","サイドキックマッドメイト","叛徒跟班","叛徒跟班","Союзник Безумца","Tripulante Louco Ajudante","Tripulante Louco Ajudante" "# 特殊クルー役職" -"Bait","Bait","ベイト","诱饵","誘餌","Приманка","Isca" -"Lighter","Lighter","ライター","执灯人","持燈人","Фонарь","Lanterneiro" -"Mayor","Mayor","メイヤー","市长","市長","Мэр","Prefeito" -"SabotageMaster","Sabotage Master","サボタージュマスター","修理大师","修理工","Мастер Саботажа","Faz-Tudo" -"Sheriff","Sheriff","シェリフ","警长","警長","Шериф","Xerife" -"Arsonist","Arsonist","アーソニスト","纵火犯","縱火犯","Поджигатель","Incendiário" -"Snitch","Snitch","スニッチ","告密者","告密者","Стукач","Dedo-Duro" -"SpeedBooster","Speed Booster","スピードブースター","增速者","增速者","Ускоритель","Corredor" -"Doctor","Doctor","ドクター","医生","法醫","Доктор","Médico" -"Trapper","Beartrap","トラッパー","陷阱师","設陷者","Охотник","Armadilheiro" -"Dictator","Dictator","ディクテーター","独裁者","獨裁主義者","Диктатор","Ditador" -"Seer","Seer","シーア","灵媒","靈媒","Провидец","Vidente" -"TimeManager","TimeManager","タイムマネージャー","时间管理者","時間大師","Мастер Времени","Dono do Tempo" +"Bait","Bait","ベイト","诱饵","誘餌","Приманка","Isca","Isca" +"Lighter","Lighter","ライター","执灯人","持燈人","Фонарь","Lanterneiro","Isqueiro" +"Mayor","Mayor","メイヤー","市长","市長","Мэр","Prefeito","Prefeito" +"SabotageMaster","Sabotage Master","サボタージュマスター","修理大师","修理工","Мастер Саботажа","Faz-Tudo","Mestre Sabotador" +"Sheriff","Sheriff","シェリフ","警长","警長","Шериф","Xerife","Xerife" +"Arsonist","Arsonist","アーソニスト","纵火犯","縱火犯","Поджигатель","Incendiário","Incendiário" +"Snitch","Snitch","スニッチ","告密者","告密者","Стукач","Dedo-Duro","Dedo-Duro" +"SpeedBooster","Speed Booster","スピードブースター","增速者","增速者","Ускоритель","Corredor","Corredor" +"Doctor","Doctor","ドクター","医生","法醫","Доктор","Médico","Médico" +"Trapper","Beartrap","トラッパー","陷阱师","設陷者","Охотник","Armadilheiro","Armadilheiro" +"Dictator","Dictator","ディクテーター","独裁者","獨裁主義者","Диктатор","Ditador","Ditador" +"Seer","Seer","シーア","灵媒","靈媒","Провидец","Vidente","Vidente" +"TimeManager","TimeManager","タイムマネージャー","时间管理者","時間大師","Мастер Времени","Dono do Tempo","" "# ニュートラル役職" -"Jester","Jester","ジェスター","小丑","小丑","Шут","Bobo" -"Terrorist","Terrorist","テロリスト","恐怖分子","恐怖分子","Террорист","Terrorista" -"Executioner","Executioner","エクスキューショナー","处刑人","劊子手","Палач","Executor" -"SchrodingerCat","Schrödinger's Cat","シュレディンガーの猫","薛定谔的猫","薛丁格的貓","Пленник","Gato de Schrodinger" -"Opportunist","Opportunist","オポチュニスト","投机者","投機主義者","Выживший","Oportunista" -"Egoist","Egoist","エゴイスト","野心家","利己主義者","Эгоист","Egoísta" -"Lovers","Lovers","恋人","恋人","戀人","Любовники","Amantes" -"Jackal","Jackal","ジャッカル","豺狼","豺狼","Шакал","Chacal" -"PlagueDoctor","Plague Doctor","ペスト医師","","","Чумной Доктор","" +"Jester","Jester","ジェスター","小丑","小丑","Шут","Bobo","Bobo" +"Terrorist","Terrorist","テロリスト","恐怖分子","恐怖分子","Террорист","Terrorista","Terrorista" +"Executioner","Executioner","エクスキューショナー","处刑人","劊子手","Палач","Executor","Executor" +"SchrodingerCat","Schrödinger's Cat","シュレディンガーの猫","薛定谔的猫","薛丁格的貓","Пленник","Gato de Schrodinger","Gato de Schrodinger" +"Opportunist","Opportunist","オポチュニスト","投机者","投機主義者","Выживший","Oportunista","Oportunista" +"Egoist","Egoist","エゴイスト","野心家","利己主義者","Эгоист","Egoísta","Egoísta" +"Lovers","Lovers","恋人","恋人","戀人","Любовники","Amantes","Amantes" +"Jackal","Jackal","ジャッカル","豺狼","豺狼","Шакал","Lobo-Guará","Chacal" +"PlagueDoctor","Plague Doctor","ペスト医師","瘟疫医生","","Чумной Доктор","Médico da Peste","" "# HideAndSeek" -"HASFox","Fox","狐","狐狸","狐妖","Лис","Raposa" -"HASTroll","Troll","トロール","猎人","誘捕者","Тролль","Troll" +"HASFox","Fox","狐","狐狸","狐妖","Лис","Raposa","Raposa" +"HASTroll","Troll","トロール","猎人","誘捕者","Тролль","Troll","Troll" "# GM" -"GM","GM","GM","GM(管理员)","GM(遊戲大師)","Мастер Игры","GM(Fantasma)" +"GM","GM","GM","GM(管理员)","GM(遊戲大師)","Мастер Игры","MJ(Mestre de Jogo)","MJ(Mestre do Jogo)" "# 属性" -"LastImpostor","Last Impostor","ラストインポスター","绝境者","絕境者","Последний Предатель","Último Impostor" -"Watcher","Watcher","ウォッチャー","窥视者","觀察者","Наблюдатель","Observador" -"Workhorse","Workhorse","ワークホース","实干家","加班狂","Работник","Burro de Carga" +"LastImpostor","Last Impostor","ラストインポスター","绝境者","絕境者","Последний Предатель","Último Impostor","Último Impostor" +"Watcher","Watcher","ウォッチャー","窥视者","觀察者","Наблюдатель","Observador","Observador" +"Workhorse","Workhorse","ワークホース","实干家","加班狂","Работник","Burro de Carga","" "# その他" -"CustomRoleTypes.Crewmate","Crewmate","クルー","船员","","Членов Экипажа","Tripulante" -"CustomRoleTypes.Impostor","Impostor","インポスター","内鬼","","Предателей","Impostor" -"CustomRoleTypes.Neutral","Neutral","ニュートラル","独立","","Нейтралов","Neutro" -"CustomRoleTypes.Madmate","Mad","マッド","叛徒","","Безумцев","Louco" -"TeamCrewmate","Team Crewmates","クルー陣営","船员阵营","","Команда Членов Экипажа","Time Tripulantes" -"TeamImpostor","Team Impostors","インポスター陣営","内鬼阵营","幫助偽裝者","Команда Предателей","Time Impostor" -"TeamEgoist","Team Egoist","エゴイスト陣営","野心家阵营","利己主義者陣營","Команда Эгоистов","Time Egoísta" -"TeamJackal","Team Jackal","ジャッカル陣営","豺狼阵营","豺狼陣營","Команда Шакалов","Time Chacal" +"Team","Team","陣営","阵营","","Команда","","" +"CustomRoleTypes.Crewmate","Crewmate","クルー","船员","","Членов Экипажа","Tripulante","" +"CustomRoleTypes.Impostor","Impostor","インポスター","内鬼","偽裝者","Предателей","Impostor","Impostor" +"CustomRoleTypes.Neutral","Neutral","ニュートラル","独立","中立陣營","Нейтралов","Neutro","" +"CustomRoleTypes.Madmate","Mad","マッド","叛徒","","Безумцев","Louco","" +"TeamCrewmate","Team Crewmates","クルー陣営","船员阵营","","Команда Членов Экипажа","Time Tripulantes","" +"TeamImpostor","Team Impostors","インポスター陣営","内鬼阵营","幫助偽裝者","Команда Предателей","Time Impostor","Time Impostor" +"TeamEgoist","Team Egoist","エゴイスト陣営","野心家阵营","利己主義者陣營","Команда Эгоистов","Time Egoísta","Time Egoísta" +"TeamJackal","Team Jackal","ジャッカル陣営","豺狼阵营","豺狼陣營","Команда Шакалов","Time Guará","Time Chacal" "## 役職説明ショート" -"NeutralInfo","Independent forces unaffiliated with any other teams","他のどの陣営にも属さない孤高の陣営","不属于其他阵营的独立阵营","你屬於一個獨立陣營且不受其他陣營或職業的獲勝條件引響","Команда которая не принадлежит ни к каким другим командам","Forças independentes que não pertencem a nenhuma outra equipe" +"NeutralInfo","Independent forces unaffiliated with any other teams","他のどの陣営にも属さない孤高の陣営","不属于其他阵营的独立阵营","你屬於一個獨立陣營且不受其他陣營或職業的獲勝條件引響","Команда которая не принадлежит ни к каким другим командам","Forças independentes que não pertencem a nenhuma outra equipe","Um time solitário que não pertence a nenhum outro time" "# 特殊インポスター役職" -"BountyHunterInfo","Eliminate your target","標的を確実に仕留めよう","猎杀你的赏金目标","拿下你的目標","Охотьтесь за своей целью","Elimine o seu alvo" -"MareInfo","Darkness aids you. Kill","闇の世界で暗殺せよ","在黑暗中作案","你恐懼黑暗嗎?","Убивайте в темноте","O escuro está do seu lado. Mate" -"FireWorksInfo","Go out with a BANG","最後に一華咲かせよう","一起来看最后的烟花吧","在最後時刻來放煙火吧","Морфитесь чтобы поставить фейерверк","Termine com uma EXPLOSÃO" -"SerialKillerInfo","Unleash your bloodlust","殺人衝動が抑えられない","你渴望鲜血,抑制不住你的冲动","你無法抑制你的殺人衝動","Убивай чтобы ты не умер","Liberte sua sede por sangue" -"ShapeMasterInfo","Shapeshift and confuse others","変身して敵を混乱させよう","变成他人,迷惑对手","你知道我是誰嗎?","Морфитесь чтобы запутать всех Членов Экипажа","Troque de corpos e confunda outros" -"VampireInfo","Bite and annihilate","クルーを噛んで全滅させよう","吸干他们的血","吸取他們的血","Прокусите всех Членов Экипажа","Morda e aniquile" -"WarlockInfo","Cast your curses","敵を呪い殺そう","给你的敌人下咒","借刀殺人","Прокляните всех Членов Экипажа","Lance sua maldição" -"WitchInfo","Cast your spells","敵に魔術をかけよう","诅咒你的敌人","施展你的魔法來讓你的敵人死亡","Заклинайте Членов Экипажа","Lance o seu feitiço" -"MafiaInfo","Support the imposters and kill","インポスターの援助をしよう","帮助其他内鬼杀掉所有人","幫助偽裝者們殺死所有人","Помогите Предателям убить всех","Ajude os impostores e mate" -"BeforeMafiaInfo","You can't kill yet","今はキルをすることができない","潜伏以等候时机","潛伏以等待時機","Вы еще не можете убивать","Você ainda não pode matar" -"AfterMafiaInfo","It's time to kill","サボを活用して皆殺しにしよう","杀光所有人","殺光所有人吧","Время убивать","Está na hora de matar" -"PuppeteerInfo","Force the Crewmate to kill","敵を操って皆殺しにしよう","操纵敌人,杀光船员","操控對象並殺光所有人","Заставьте Члена Экипажа убить","Force os tripulantes a matar" -"TimeThiefInfo","Kill to steal meeting time","敵を殺し、会議時間を奪おう","不仅夺走他们的性命,还有他们的时间","殺光你的敵人並將會議時間縮短","Убивайте игроков чтобы сократить время встречи","Mate para roubar tempo de reunião" -"SniperInfo","Snipe","狙い撃つぜ","瞄准敌人,射击!","讓你的敵人在你的狙擊下死亡","Стреляйте в Членов Экипажа на расстоянии","Hora de atirar" -"LastImpostorInfo","You are the last of us","残ったのはあなただけ","你是狼村最后的希望…","你是狼村最後的希望...","Ты последний предатель","Você é o último Impostor" -"EvilTrackerInfo","Track others","プレイヤーを追跡しろ","让我看看,我的小目标在哪","你不要過來啊啊阿","Отслеживайте Игроков","Localize os outros" -"StealthInfo","Act unseen in the dark world...","闇の世界で暗躍せよ","","","Действуйте незаметно в темноте","" -"NekoKabochaInfo","Take your killer to your grave","死なばもろとも","","","Отведи своего убийцу в могилу","" -"EvilHackerInfo","Hack systems","システムをハッキングせよ","","","Взломайте систему","" -"PenguinInfo","Let's drag and kill!","ぺちぺち!","","","Давайте тащить и убивать!","" -"InsiderInfo","Knowledge is power","知は力なり","","","Знание - сила!","" +"BountyHunterInfo","Eliminate your target","標的を確実に仕留めよう","猎杀你的赏金目标","拿下你的目標","Охотьтесь за своей целью","Elimine o seu alvo","Elimine o seu alvo" +"MareInfo","Darkness aids you. Kill","闇の世界で暗殺せよ","在黑暗中作案","你恐懼黑暗嗎?","Убивайте в темноте","A escuridão está do seu lado. Mate!","O escuro está do seu lado. Mate" +"FireWorksInfo","Go out with a BANG","最後に一華咲かせよう","一起来看最后的烟花吧","在最後時刻來放煙火吧","Морфитесь чтобы поставить фейерверк","Termine com uma EXPLOSÃO","Termine com uma EXPLOSÃO" +"SerialKillerInfo","Unleash your bloodlust","殺人衝動が抑えられない","你渴望鲜血,抑制不住你的冲动","你無法抑制你的殺人衝動","Убивай чтобы ты не умер","Liberte sua sede por sangue","Liberte sua sede por sangue" +"ShapeMasterInfo","Shapeshift and confuse others","変身して敵を混乱させよう","变成他人,迷惑对手","你知道我是誰嗎?","Морфитесь чтобы запутать всех Членов Экипажа","Troque de corpos e confunda outros","Troque de corpos e confunda outros" +"VampireInfo","Bite and annihilate","クルーを噛んで全滅させよう","吸干他们的血","吸取他們的血","Прокусите всех Членов Экипажа","Morda e aniquile","Morda e aniquile" +"WarlockInfo","Cast your curses","敵を呪い殺そう","给你的敌人下咒","借刀殺人","Прокляните всех Членов Экипажа","Lance sua maldição","Lance sua maldição" +"WitchInfo","Cast your spells","敵に魔術をかけよう","诅咒你的敌人","施展你的魔法來讓你的敵人死亡","Заклинайте Членов Экипажа","Lance o seu feitiço","Lance o seu feitiço" +"MafiaInfo","Help the Impostors","インポスターの援助をしよう","帮助内鬼","幫助偽裝者","Помогите Предателям","Ajude os Impostores","Ajude os Impostores" +"BeforeMafiaInfo","You can't kill yet","今はキルをすることができない","潜伏以等候时机","潛伏以等待時機","Вы еще не можете убивать","Você ainda não pode matar","Você ainda não pode matar" +"AfterMafiaInfo","It's time to kill","サボを活用して皆殺しにしよう","杀光所有人","殺光所有人吧","Время убивать","Está na hora de matar","Está na hora de matar" +"PuppeteerInfo","Force the Crewmate to kill","敵を操って皆殺しにしよう","操纵敌人,杀光船员","操控對象並殺光所有人","Заставьте Члена Экипажа убить","Force os tripulantes a matar","Force os tripulantes a matar" +"TimeThiefInfo","Kill to steal meeting time","敵を殺し、会議時間を奪おう","不仅夺走他们的性命,还有他们的时间","殺光你的敵人並將會議時間縮短","Убивайте игроков чтобы сократить время встречи","Mate para roubar tempo de reunião","Mate para roubar tempo de reunião" +"SniperInfo","Snipe","狙い撃つぜ","瞄准敌人,射击!","讓你的敵人在你的狙擊下死亡","Стреляйте в Членов Экипажа на расстоянии","Hora de atirar","Hora de atirar" +"LastImpostorInfo","You are the last of us","残ったのはあなただけ","你是狼村最后的希望…","你是狼村最後的希望...","Ты последний предатель","Você é o último Impostor","Você é o último Impostor" +"EvilTrackerInfo","Track others","プレイヤーを追跡しろ","让我看看,我的小目标在哪","你不要過來啊啊阿","Отслеживайте Игроков","Localize os outros","Localize os outros" +"StealthInfo","Act unseen in the dark world...","闇の世界で暗躍せよ","降下黑暗!","","Действуйте незаметно в темноте","Tabalhe sem ser visto no mundo escuro...","" +"NekoKabochaInfo","Take your killer to your grave","死なばもろとも","带走杀死你的人","","Отведи своего убийцу в могилу","Leve seu assassino para o túmulo","" +"EvilHackerInfo","Hack systems","システムをハッキングせよ","破解系统","","Взломайте систему","Hackeie sistemas","" +"PenguinInfo","Let's drag and kill!","ぺちぺち!","扑哧扑哧!","","Давайте тащить и убивать!","Vamos arrastar e matar!","" +"InsiderInfo","Knowledge is power","知は力なり","知识就是力量","","Знание - сила!","Conhecimento é poder","" "# マッドメイト系役職" -"MadmateInfo","Help the Impostors","インポスターの援助をしよう","帮助内鬼","幫助偽裝者","Помогите Предателям","Ajude os Impostores" -"BeforeMadmateInfo","Finish your tasks","タスクを済ませろ","完成你的任务","完成你的任務","Завершите свои задания","Termine as suas tarefas" -"MadGuardianInfo","Finish your tasks and help the Impostors","タスクを済ませ、インポスターの援助をしよう","完成任务后来帮助内鬼","完成你的任務並幫助偽裝者","Завершите свои задания чтобы помочь Предателям","Termine as suas tarefas e ajude os Impostores" -"MadSnitchInfo","Finish your tasks and help the Impostors","タスクを済ませ、インポスターの援助をしよう","完成任务后来帮助内鬼","完成你的任務並幫助偽裝者","Завершите свои задания чтобы помочь Предателям","Termine as suas tarefas e ajude os Impostores" -"SKMadmateInfo","You are now on Team Impostors","サイドキックにされた","你是内鬼的好帮手","你現在屬於偽裝者們了,盡你所能幫助他們","Теперь вы в команде Предателя","Você agora está no Time Impostor" +"MadmateInfo","","インポスターの援助をしよう","帮助其他内鬼杀掉所有人","幫助偽裝者","Помогите Предателям убить всех","Ajude os Impostores","Ajude os Impostores" +"BeforeMadmateInfo","Finish your tasks","タスクを済ませろ","完成你的任务","完成你的任務","Завершите свои задания","Termine as suas tarefas","Termine as suas tarefas" +"MadGuardianInfo","Finish your tasks and help the Impostors","タスクを済ませ、インポスターの援助をしよう","完成任务后来帮助内鬼","完成你的任務並幫助偽裝者","Завершите свои задания чтобы помочь Предателям","Termine as suas tarefas e ajude os Impostores","Termine as suas tarefas e ajude os Impostores" +"MadSnitchInfo","","タスクを済ませ、インポスターの援助をしよう","完成任务后来帮助内鬼","完成你的任務並幫助偽裝者","Завершите свои задания, чтобы помочь Предателям","Termine as suas tarefas e ajude os Impostores","Termine as suas tarefas e ajude os Impostores" +"SKMadmateInfo","You are now on Team Impostors","サイドキックにされた","你是内鬼的好帮手","你現在屬於偽裝者們了,盡你所能幫助他們","Теперь вы в команде Предателя","Você agora está no Time Impostor","Você agora está no Time Impostor" "# 特殊クルー役職" -"BaitInfo","Bait your enemies","敵を罠にはめよう","诱骗敌人,让敌人落入你的陷阱","犧牲小我,完成大我","Убийца моментально зарепортит ваш труп","Engane os seus inimigos" -"LighterInfo","Light 'em up","すべてを照らそう","驱散黑暗,照亮前路","夜空中最亮的星","Увеличьте свой обзор выполнив свои задания","Luz para todo lado" -"MayorInfo","Your vote has more influence","自分の票が何倍もの力を持っている","你的投票更有分量","你的票數更多更有料","У вас больше голосов","O seu voto tem mais influência" -"SabotageMasterInfo","Fix sabotages faster","より早くサボタージュを直そう","你是一个修理匠,修理本领强","更快的修理破壞","Устраните саботаж в одиночку","Conserte as sabotagens mais rápido" -"SheriffInfo","Shoot the Impostors","インポスターを撃ち抜け","把内鬼统统执法掉","執法偽裝者及中立陣營玩家們","Убивайте Предателей и Нейтралов","Atire nos Impostores" -"SnitchInfo","Finish your tasks to find the Impostors","タスクを早く済ませよう","完成任务 揪出内鬼","完成你的所有任務來找到偽裝者","Завершите свои задания чтобы узнать Предателей","Termine as suas tarefas para dedurar os Impostores" -"SpeedBoosterInfo","Boost others' speed","走らせろ","让其他人跑起来吧","讓其他人跑得更快","Ускорьте случайного игрока","Impulsione a velocidade dos jogadores" -"DoctorInfo","And thus they died...","斯くして奴は死んだ","调查他人死因","查看他們的死因","Вы знаете причину смерти игроков","Veja os sinais vitais e causa da morte" -"TrapperInfo","Trap your enemies","敵を罠にはめよう","引诱敌人落入你的陷阱当中","誘捕你的敵人","Ловите своих врагов","Prenda os seus inimigos" -"DictatorInfo","Decide who to eject","独裁政治をしよう","让我统治世界,拥有无上的权利","讓所有人臣服於你","Повесьте своего врага на страх и риск","Escolha quem será exilado" -"SeerInfo","You see the moment someone dies","他人の死んだ瞬間がわかる","你能感知死亡","你擁有陰陽眼","Вы видите когда умирают игроки","Veja o momento em que alguém morre" -"TimeManagerInfo","Do the tasks and extend meeting time","タスクをして会議時間を延ばそう","任务搞快点,不就有时间开会了嘛","完成你的任務來延長會議時間","Выполняйте задания чтобы увеличить время встречи","Faça as tarefas e aumente o tempo de reunião" +"BaitInfo","Trap your enemies","敵を罠にはめよう","引诱敌人落入你的陷阱当中","誘捕你的敵人","Ловите своих врагов","Prenda os seus inimigos","Prenda os seus inimigos" +"LighterInfo","Light 'em up","すべてを照らそう","驱散黑暗,照亮前路","夜空中最亮的星","Увеличьте свой обзор выполнив свои задания","Luz para todo lado","Luz para todo lado" +"MayorInfo","Your vote has more influence","自分の票が何倍もの力を持っている","你的投票更有分量","你的票數更多更有料","У вас больше голосов","O seu voto tem mais influência","O seu voto tem mais influência" +"SabotageMasterInfo","Fix sabotages faster","より早くサボタージュを直そう","你是一个修理匠,修理本领强","更快的修理破壞","Устраните саботаж в одиночку","Conserte as sabotagens mais rápido","Conserte as sabotagens mais rápido" +"SheriffInfo","Shoot the Impostors","インポスターを撃ち抜け","把内鬼统统执法掉","執法偽裝者及中立陣營玩家們","Убивайте Предателей и Нейтралов","Atire nos Impostores","Atire nos Impostores" +"SnitchInfo","Finish your tasks to find the Impostors","タスクを早く済ませよう","完成任务 揪出内鬼","完成你的所有任務來找到偽裝者","Завершите свои задания чтобы узнать Предателей","Termine as suas tarefas para dedurar os Impostores","Termine as suas tarefas para dedurar os Impostores" +"SpeedBoosterInfo","Boost others' speed","走らせろ","让其他人跑起来吧","讓其他人跑得更快","Ускорьте случайного игрока","Impulsione a velocidade dos jogadores","Impulsione a velocidade dos jogadores" +"DoctorInfo","And thus they died...","斯くして奴は死んだ","调查他人死因","查看他們的死因","Вы знаете причину смерти игроков","Veja os sinais vitais e causa da morte","Veja os sinais vitais e causa da morte" +"TrapperInfo","","敵を罠にはめよう","诱骗敌人,让敌人落入你的陷阱","誘捕你的敵人","Убийца моментально зарепортит ваш труп","Prenda os seus inimigos","Prenda os seus inimigos" +"DictatorInfo","Decide who to eject","独裁政治をしよう","让我统治世界,拥有无上的权利","讓所有人臣服於你","Повесьте своего врага на страх и риск","Escolha quem será exilado","Escolha quem será exilado" +"SeerInfo","You see the moment someone dies","他人の死んだ瞬間がわかる","你能感知死亡","你擁有陰陽眼","Вы видите когда умирают игроки","Veja o momento em que alguém morre","Veja o momento em que alguém morre" +"TimeManagerInfo","Do the tasks and extend meeting time","タスクをして会議時間を延ばそう","任务搞快点,不就有时间开会了嘛","完成你的任務來延長會議時間","Выполняйте задания чтобы увеличить время встречи","Faça as tarefas e aumente o tempo de reunião","" "# ニュートラル役職" -"ArsonistInfo","Burn them to crisps","燃やせ","火焰给我燃烧起来吧!","燒吧,燒吧,燃燒吧","Облейте и подожгите всех игроков","Queime tudo em pedacinhos" -"JesterInfo","Get voted out","追放されよう","让他们把你这个假货投出去","想個辦法讓你被投出去","Заставь игроков изгнать себя","Seja exilado" -"OpportunistInfo","Just stay alive","とにかく生き残りましょう","苟着吧,苟也是个不错的选择","盡你所能地活下去吧!","Останьтесь в живых","Sobreviva a qualquer custo" -"SchrodingerCatInfo","Get killed and join your killer's team","殺されて陣営に所属しろ","加入击杀者的阵营","打不過就加入","Дайте убить себя и присоединитесь к команде убийц","Ao morrer entre para o time que lhe matou" -"CSchrodingerCatInfo","You are now on Team Crewmate","クルーの味方になった","你加入了船员阵营,帮助船员找出内鬼","你現在變成船員了, 幫助船員們找出偽裝者們和中立帶刀者","Теперь вы за Членов Экипажа","Você agora é do Time Tripulante" -"MSchrodingerCatInfo","You are now on Team Impostor","インポスターの味方になった","你加入了内鬼阵营,掩护自己的内鬼同伴,并且一起胜利","你現在變成偽裝者了,但是你不能殺人,盡量掩護你的隊友的身分","Теперь вы за Предателей","Você agora é do Time Impostor" -"EgoSchrodingerCatInfo","You are now on Team Egoist","エゴイストの味方になった","你成为了野心家的伙伴","你是利己主義者的跟班","Теперь вы за Эгоистов","Você agora é do Time Egoísta" -"JSchrodingerCatInfo","You are now on Team Jackal","ジャッカルの味方になった","你是豺狼的跟班,传宗接代是个好选择","你是豺狼的跟班,但可惜的是你不能殺人","Теперь вы за Шакалов","Você agora é do Time Chacal" -"TerroristInfo","Finish your tasks, THEN die","タスクを済ませ、自爆しよう","都别想活着,和我一起同归于尽!","我要開飛船撞Mira總部","Умрите после выполнения всех заданий","Termine suas tarefas, DEPOIS morra" -"ExecutionerInfo","Get your target voted out","ターゲットを追放させよう","不惜一切代价把你的目标票出去!","想盡一切辦法來投出你的目標","Заставь игроков изгнать свою цель","Faça o seu alvo ser exilado" -"EgoistInfo","Take over the Impostors' victory","インポスター勝利を独占しよう","夺走内鬼的胜利","讓我們來奪取偽裝者的勝利","Не дай Предателям победить","Ganhe no lugar do Impostor" -"LoversInfo","Live happily ever after, together","恋人と生きて幸せを掴もう","你们坠入了爱河,成为了一对恋人,一起活到最后吧!","你墜入了愛河","Выживите со своим Любовником","Vivam felizes para sempre, juntos" -"JackalInfo","Kill Everyone","すべてを殺せ","快去杀光所有人,一只苍蝇都不要剩下!","殺光所有人不留活口","Убей всех игроков","Mate todos" -"PlagueDoctorInfo","Spread disease to wipe out the crew","ペストをばらまけ","","","Распространите чуму","" +"ArsonistInfo","Burn them to crisps","燃やせ","火焰给我燃烧起来吧!","燒吧,燒吧,燃燒吧","Облейте и подожгите всех игроков","Queime tudo em pedacinhos","Queime tudo em pedacinhos" +"JesterInfo","Get voted out","追放されよう","让他们把你这个假货投出去","想個辦法讓你被投出去","Заставь игроков изгнать себя","Seja exilado","Seja exilado" +"OpportunistInfo","Just stay alive","とにかく生き残りましょう","活下去吧!活到最后你就成为了赢家!","盡你所能地活下去吧!","Останьтесь в живых","Sobreviva a qualquer custo","Sobreviva a qualquer custo" +"SchrodingerCatInfo","Get killed and join your killer's team","殺されて陣営に所属しろ","加入击杀者的阵营","打不過就加入","Дайте убить себя и присоединитесь к команде убийц","Ao morrer entre para o time que lhe matou","Ao morrer entre para o time que lhe matou" +"CSchrodingerCatInfo","You are now on Team Crewmate","クルーの味方になった","你加入了船员阵营,帮助船员找出内鬼","你現在變成船員了, 幫助船員們找出偽裝者們和中立帶刀者","Теперь вы за Членов Экипажа","Você agora é do Time Tripulante","" +"MSchrodingerCatInfo","You are now on Team Impostor","インポスターの味方になった","你加入了内鬼阵营,掩护自己的内鬼同伴,并且一起胜利","你現在變成偽裝者了,但是你不能殺人,盡量掩護你的隊友的身分","Теперь вы за Предателей","Você agora é do Time Impostor","Você agora é do Time Impostor" +"EgoSchrodingerCatInfo","You are now on Team Egoist","エゴイストの味方になった","你成为了野心家的伙伴","你是利己主義者的跟班","Теперь вы за Эгоистов","Você agora é do Time Egoísta","Você agora é do Time Egoísta" +"JSchrodingerCatInfo","You are now on Team Jackal","ジャッカルの味方になった","你是豺狼的跟班,传宗接代是个好选择","你是豺狼的跟班,但可惜的是你不能殺人","Теперь вы за Шакалов","Você agora é do Time Guará","Você agora é do Time Chacal" +"TerroristInfo","Finish your tasks, THEN die","タスクを済ませ、自爆しよう","都别想活着,和我一起同归于尽!","我要開飛船撞Mira總部","Умрите после выполнения всех заданий","Termine suas tarefas, DEPOIS morra","Termine suas tarefas, DEPOIS morra" +"ExecutionerInfo","Get your target voted out","ターゲットを追放させよう","不惜一切代价把你的目标票出去!","想盡一切辦法來投出你的目標","Заставь игроков изгнать свою цель","Faça o seu alvo ser exilado","Faça o seu alvo ser exilado" +"EgoistInfo","Take over the Impostors' victory","インポスター勝利を独占しよう","夺走内鬼的胜利","讓我們來奪取偽裝者的勝利","Не дай Предателям победить","Ganhe no lugar do Impostor","Ganhe no lugar do Impostor" +"LoversInfo","Live happily ever after, together","恋人と生きて幸せを掴もう","你们坠入了爱河,成为了一对恋人,一起活到最后吧!","你墜入了愛河","Выживите со своим Любовником","Vivam felizes para sempre, juntos","Vivam felizes para sempre, juntos" +"JackalInfo","Kill Everyone","すべてを殺せ","快去杀光所有人,一只苍蝇都不要剩下!","殺光所有人不留活口","Убей всех игроков","Mate todos","Mate todos" +"PlagueDoctorInfo","Spread disease to wipe out the crew","ペストをばらまけ","在船员之中散布瘟疫","","Распространите чуму","Espalhe doenças para acabar com a tripulação","" "# HideAndSeek" -"HASFoxInfo","Just stay alive","とにかく生き残りましょう","活下去吧!活到最后你就成为了赢家!","盡你所能地活下去吧!","Останьтесь в живых","Sobreviva a qualquer custo" -"HASTrollInfo","Get killed","自爆しよう","轰!你为了同伴牺牲了!","盡可能讓獵人殺死你","Будьте убитым","Seja morto" +"HASFoxInfo","","とにかく生き残りましょう","苟着吧,苟也是个不错的选择","","Останьтесь в живых","Sobreviva a qualquer custo","Sobreviva a qualquer custo" +"HASTrollInfo","Get killed","自爆しよう","轰!你为了同伴牺牲了!","盡可能讓獵人殺死你","Будьте убитым","Seja morto","Seja morto" "# GM" -"GMInfo","You are the Game Master","ゲームマスターだ","你是管理员,在旁边看戏吧","當吃瓜群眾在一旁看戲吧","Вы Мастер Игры","Você é o Mestre do Jogo (GM)" +"GMInfo","You are the Game Master","ゲームマスターだ","你是管理员,在旁边看戏吧","當吃瓜群眾在一旁看戲吧","Вы Мастер Игры","Você é o Mestre de Jogo (MJ)","Você é o Mestre do Jogo (GM)" "# 属性" -"WatcherInfo","Gaze upon all votes","みんなの投票に目を光らせよう","你可以看见所有人的投票,注意他们的选择","注意所有人的投票","Вы видите цвета голосов","Veja todos os votos" -"WorkhorseInfo","You have Extra tasks","タスクはまだ終わらない","这活还得继续肝","感覺不如做任務","Теперь у вас дополнительные задания","Você tem tarefas adicionais" +"WatcherInfo","Gaze upon all votes","みんなの投票に目を光らせよう","你可以看见所有人的投票,注意他们的选择","注意所有人的投票","Вы видите цвета голосов","Veja todos os votos","Veja todos os votos" +"WorkhorseInfo","You have Extra tasks","タスクはまだ終わらない","这活还得继续肝","感覺不如做任務","Теперь у вас дополнительные задания","Você tem tarefas adicionais","" "## 役職説明ロング" "# 特殊インポスター役職" -"BountyHunterInfoLong","(Impostors):\nIf you kill your indicated target, your next kill cooldown will be greatly reduced.\nIf you kill anyone other than your target, your next kill cooldown will increase.\nThe target swaps after a configurable amount of time.","(インポスター陣営):\n表示されたターゲットをキルした場合、次のキルクールがとても短くなる。\nターゲットではないプレイヤーをキルした場合は、キルクールが伸びてしまう。\nターゲットは一定時間ごとに変更される。","(内鬼阵营):\n如果赏金猎人击杀了赏金目标,击杀冷却缩短。\n反之,则冷却增加。","(偽裝者陣營):\n如果賞金獵人殺了賞金目標,下一次的殺人冷卻會變得非常的短\n如果賞金獵人殺了非賞金目標,下一次的殺人冷卻將會被延長或不變\n目標將會重置在時間結束或殺死賞金目標後。","(Предатель):\nЕсли Охотник за головами убьет назначенную ему цель, время отката убийства будет намного меньше чем обычно. \n При убийстве игрока который не является его целью, приводит к стандартному времени отката убийства. \nЦели меняются спустя какое-то время","(Impostores):\nSe você matar o seu alvo, o tempo de recarga (abate) será mais rápido.\nMatar um jogador que não seja seu alvo, resulta em um tempo de recarga (abate) maior.\nOs alvos são alterados em intervalos regulares." -"FireWorksInfoLong","(Impostors):\nWhenever you shapeshift, you can place a firework.\n After placing all fireworks, you can set them all off at once when you shapeshift once you become the last impostor.\nOnce you start placing fireworks, you cannot kill until the fireworks go off.","(インポスター陣営):\n変身することで花火を最大3か所に設置可能。\nすべて設置した後、インポスター最後の生き残りになった時に再度変身することで一斉起爆できる。\n花火が設置されている間はキルできない。","(内鬼阵营):\n烟花商人可以安放烟花炸弹,造成范围性杀伤。\n烟花商人在变形状态下可以安放至多三个烟花炸弹。\n当存活的内鬼阵营玩家仅剩烟花商人且烟花炸弹安置完毕时,烟花商人可以在变形状态下引爆炸弹。\n烟花商人不可以进行常规击杀,除非安放并引爆所有烟花炸弹。\n烟花炸弹可以同样炸死烟花商人自己。\n如果烟花炸弹炸死所有存活玩家,内鬼阵营将获胜。","(偽裝者陣營):\n煙火工匠可以放置煙火來造成範圍性傷害,煙火工匠可以以變形來放置煙火,最多可以放置三個煙火,但是在煙火工匠放置完所有煙火前無法殺人\n如果他的隊友都已經死亡的話,煙火工匠便可以引爆煙火。\n如果煙火工匠也在爆炸中被炸死,偽裝者陣營同樣獲勝。","(Предатель):\nФейерверк может поставить несколько фейерверков с помощью Морфлинга. \nПосле установки всех фейерверков он может поджечь их все сразу с помощью использования Морфлинга, когда он является последним Предателем. \nТолько после этого он сможет убивать."," (Impostores):\nAo mudar de corpo, você coloca um fogo de artifício.\nDepois de colocar todos, você pode acender tudo de uma vez ao trocar de corpo sendo o último Impostor.\nQuando começar a colocar fogos de artifício, você não poderá matar até eles explodirem." -"SerialKillerInfoLong","(Impostors):\nYou have a shorter kill cooldown than other impostors', but you will die if you don't kill before the end of your shapeshift cooldown.","(インポスター陣営):\nキルクールが他のインポスターに比べて短い反面、変身のクールタイムが明ける前にキルしないと自爆する。","(内鬼阵营):\n嗜血杀手的冷却因为他的杀戮欲望而更快。\n嗜血杀手若不能在一定时间内进行击杀,\n就会因承受不了自己的杀戮欲望而自杀。","(偽裝者陣營):\n嗜血殺手的殺人冷卻是所有偽裝者中最短的一個。\n但是如果嗜血殺手沒有在自殺倒數歸零前再殺人,嗜血殺手將會自殺。","(Предатель):\nУ Серийного Убийцы еще более короткий откат убийства. \nНо если он не совершит убийство в установленный срок, то он мгновенно убьет сам себя.","(Impostores):\nO seu tempo de recarga (abate) é menor, porém, se você não matar no prazo morrerá no fim do seu tempo de mutação." -"ShapeMasterInfoLong","(Impostors):\nShapeMaster has no Shapeshift cooldown.\nOn the other hand, their default Shapeshift duration is shorter (default: 10s).","(インポスター陣営):\nシェイプシフトに特化したインポスター。\n変身持続時間が通常のシェイプシフターよりも短い半面、変身解除後のクールダウンがなく無限に変身することができる。\n変身継続時間は設定で変更が可能。","(内鬼阵营):\n千面鬼没有变形冷却与次数限制。\n在默认设置下,千面鬼变形的持续时间更短(10秒)。","(偽裝者陣營):\n百變怪相較於普通變形者,百變怪沒有變形冷卻,但是相較於普通變形者他的變形時間會更短。","(Предатель):\nШпион не имеет отката восстановления Морфа. \nС другой стороны, длительность Морфа по умолчанию стоит куда меньше. \nПродолжительность трансформации можно изменить в настройках.","(Impostores):\nVocê não tem tempo de recarga (mutação).\nPor outro lado, o tempo padrão de mutação é menor (padrão: 10s)." -"MareInfoLong","(Impostors):\nYou can only kill during Fix Lights.\nWhen you make a kill, your next kill cooldown will be halved.\nDuring Fix Lights, you can move faster, but your name will also be displayed in red for all.","(インポスター陣営):\n停電の時だけキルができるインポスター。\nキルに成功すると次のキルクールが半分になる。\n移動速度も停電中は早くなるが名前が赤くなる。","(内鬼阵营):\n梦魇只能在停电时下进行击杀。\n若夜魇击杀成功,夜魇的击杀冷却将变成原来的一半。\n此外,停电续时间内夜魇的移动速度也会增加,\n并且,这段时间内所有玩家都能看到夜魇的名字以红色表示。","(偽裝者陣營):\n獵夢者只可在關燈狀態下殺人,但是移動速度將會變快,並且如果殺人成功冷卻將會減半。\n不過請注意,獵夢者關燈狀態下的名字會變成紅色,並且所有玩家可見。","(Предатель):\nПредатель, который может убивать только при выключенном свете. \nКаждое его убийство вдвое снижает откат убийства. \nСкорость движения также будет выше при выключенном свете, но его никнейм станет красным и будет виден всем игрокам.","(Impostores):\nVocê só pode matar durante apagões.\nQuando você mata, o seu próximo tempo de recarga (abate) será reduzido.\nDurante apagões, você se move mais rápido, mas o seu nome aparecerá em vermelho para todos." -"VampireInfoLong","(Impostors):\nYou can make delayed kills by biting others.\n(Bitten players will die either after the delay is over or the moment a meeting is called before then.)\nIf you bite a [Bait](#Bait), there is no delay, and you are forced to self-report.","(インポスター陣営):\nプレイヤーを噛む事で遅延キルが可能。\n(時間が経過or会議が始まると死亡する。)\nベイトの場合即死亡し、強制的に通報が入る。","(内鬼阵营):\n在吸血鬼的吸血的对象不会立刻死亡,而是延迟一段时间发生。\n若在这段时间内进入会议阶段,该目标将立即死亡。\n当被击杀目标为诱饵时,吸血鬼的击杀被视为普通击杀。","(偽裝者陣營):\n當吸血鬼嘗試殺人時,嘗試被殺的目標並不會立刻死亡,而是過一段時間之後突然暴斃,\n如果在這段時間內進入會議,被吸血的玩家將會立刻死亡。\n請注意: 如果吸血鬼的目標為誘餌,吸血鬼仍舊會報告誘餌的屍體。","(Предатель):\nВампир может кусать игроков, а не убивать. \nУкушенный игрок умирает спустя какое-то время, или же умирает сразу как только произойдёт срочное собрание. \nНо если он укусит [Приманку](#Bait) он совершит обычное убийство и он вынужден сразу зарепортить труп.","(Impostores):\nVocê pode matar ao morder outros.\nJogadores mordidos vão morrer depois de um tempo, ou caso uma reunião seja chamada antes.\nSe você morder uma [Isca], não terá delay e você será obrigado a reportar o corpo imediatamente." -"WarlockInfoLong","(Impostors):\nThe Warlocks can curse another players before Shapeshift.\nWhen they Shapeshift after cursing, the player next to the cursed is killed no matter how far away they are.\nWhile Shpashifting, you can perform normal kills.\nAlso, only one player can be cursed per turn.","(インポスター陣営):\nキルをすると一番近くの人に呪いがかかる。\n次から変身ボタンを押すと、呪われた人が一番近い人をキルする。\n変身した場合、普通のキルになる。\nまた、1ターンに1人しか呪うことはできない。","(内鬼阵营):\n术士在不变形状态下击杀时,目标会被下咒。\n当术士变形时,最靠近被下咒玩家的船员将会被击杀。","(偽裝者陣營):\n術士可以通過殺人鍵下咒一個目標,如果目標身上出現盾牌破碎的效果代表下咒成功。\n如果術士變形,那麼被下咒的玩家將會立刻殺死一名離他最近的玩家,\n如果術士未在進入會議前咒殺,則詛咒失效。","(Предатель):\nКолдун может проклинать игроков. \nКогда он Морфится после проклинания какого либо игрока, то проклятый игрок убивает любого игрока который находится по близости, независимо от того насколько далеко он находится. \nТак же во время Морфа он совершает обычные убийства.","(Impostores):\nVocê pode amaldiçoar outros jogadores ao mutar.\nQuando você se transformar novamente depois de amaldiçoar alguém, a pessoa mais próxima do jogador amaldiçoado será morta, não importa o quão longe esteja.\nEnquanto alguém etiver amaldiçoado você pode matar normalmente.\nAlém disso, apenas uma pessoa pode ser amaldiçoada por rodada." -"WitchInfoLong","(Impostors):\nYour kill alternates between ""Kill"" and ""Spell"" by actions.\nWhen you kill with your ""Spell"", you bewitch the player.\nPlayers who have been bewitched have a † mark displayed next to them and must vote out the Witch within that meeting, or they will die after the meeting ends.","(インポスター陣営):\nキルする、またはベントに入ると「Kill」と「Spell」が切り変わる。\nSpellの表示でキルすると呪うことが出来る。\n呪いをかけられた人は会議で「†」が付き魔女を追放しなければ会議後に死亡する。","(内鬼阵营):\n女巫会在击杀模式与诅咒模式间来回切换,在诅咒模式下可以对目标进行诅咒。\n会议时,被诅咒的目标会带有对全员可见的诅咒标记「†」。如果女巫未在会议时被投票放逐,被诅咒的目标将会在会议结束后死亡。","(偽裝者陣營):\n女巫擁有兩種殺人方式: 詛咒與殺人。會議時,全部玩家都可看到被詛咒的玩家的名字旁有個「†」符號。如果會議結束時女巫沒有被逐出或被殺死,則被詛咒的玩家死亡。\n根據房主設定,女巫的攻擊模式分為通風管切換以及殺人切換。","(Предатель):\nВедьма может совершить убийство или заклинание по очереди. \nИгроки, которые были проклятые Ведьма на голосовании помечаются красным крестом справа от никнейма, \nИ если Ведьма не изгнан, все проклятые им игроки умирают сразу же после голосования.","(Impostores):\nVocê pode alternar entre ""Matar"" e ""Feitiço"".\nQuando você usa seu ""Feitiço"", você enfeitiça o jogador.\nQuem for enfeitiçado tem uma marca de † ao lado do nome.\nÉ necessário exilar a bruxa antes da reunião acabar, se não o enfeitiçado morrerá ao fim da reunião." -"MafiaInfoLong","(Impostors):\nAt the start of the game, you are unable to kill even though you will see the kill button.\nAfter your fellow impostors are dead, you will be able to kill.","(インポスター陣営):\n初期状態ではキルが封じられている。\n仲間が全員死亡後、自身もキルが可能になる。","(内鬼阵营):\n黑手党在游戏中有其他内鬼存活时不可以进行击杀。","(偽裝者陣營):\n當黑手黨在場且有其他偽裝者存活時不可以殺人,但是當隊友都死亡時他就可以開始殺人。","(Предатель):\nИзначально Мафиози не может убивать. \nОн сможет убивать после того, как все Предатели кроме него будут убиты или изгнаны.","(Impostores):\nNo começo do jogo, você não pode matar.\nDepois que seus companheiros Impostores forem mortos, você será capaz de matar." -"PuppeteerInfoLong","(Impostors):\nInstead of killing someone, ""kills"" can force a player to kill the next non-impostor they approach.\nIf the player your puppet killed has an ability that activates upon death, the effect will land on the puppet instead of yourself.\nYou cannot perform a normal kill.","(インポスター陣営):\nキルした対象に、対象が次に近づいたクルーをキルさせる事ができる。\n対象がキルした相手がキルされた瞬間に発動するものであった場合、対象にその効果が反映される。\n普通のキルを行うことはできない。","(内鬼阵营):\n傀儡师的操控对象会击杀其遇到的下一个船员。\n若后者在接触的同时死亡,则前者死亡。\n傀儡师不可以执行正常击杀。","(偽裝者陣營):\n傀儡師按下殺人按鈕時將會操控離他最近的玩家(與術士下咒差不多),接著如果在殺人範圍內碰到非偽裝者陣營的人,被操控的玩家將會直接殺死被害者,並且效果不再持續。","(Предатель):\nКукловод может совершить убийство с помощью помеченного игрока, но не может убивать как обычный Предатель. \nЕсли цель убивает противника и он срабатывает в момент убийства цели, эффект отразится на цели. \nВы не можете совершать обычные убийства.","(Impostores):\nInvés de matar alguém, ""matar"" força um jogador a matar o próximo (não impostor).\nSe o jogador que a sua marionete matou tem uma habilidade que ativa durante a morte, o efeito irá para a marionete ao invés de você.\nVocê não pode matar normalmente." -"TimeThiefInfoLong","(Impostors):\nEvery kill reduces discussion and voting time in meeting.\nIf you are voted out or killed, the lost time returns to meetings.","(インポスター陣営):\nキルを行うと会議時間が減少する。\nタイムシーフが追放または殺害されると、失われた会議時間が返ってきます。","(内鬼阵营):\n蚀时者每击杀一个人,会议时间就将减少一定时间。\n如果蚀时者死亡,被偷取的时间的会议时间将返还。","(偽裝者陣營):\n時間竊賊每殺死一個人,那麼會議時間就會減少一定時間,如果時間竊賊死亡,那麼會議時間將會恢復。","(Предатель):\nКаждое убийство Вора Времени сокращает время обсуждения и голосования на собраниях. \nВ зависимости от настроек потерянное время возвращается после того, как он будет убит или изгнан.","(Impostores):\nCada morte reduz tempo de votação e discussão.\nSe você for exilado ou morto, o tempo perdido retorna para as reuniões." -"SniperInfoLong","(Impostors):\nYou can shoot players from afar.\nYour line of fire continues as a straight line in the direction of the spot where you shapeshift to where you unshift.\nYou cannot perform normal kills until you use up all of your ammo.","(インポスター陣営):\n遠くの敵を狙撃できるインポスター。\nシェイプシフトした地点から解除した地点の延長線上の敵を一人撃ち抜く。\n撃ちきるまで通常キル出来ない。","(内鬼阵营):\n狙击手拥有远距离射杀的能力。\n方法为:变形的位置及解除变形位置的连线及其延长线上的一名玩家将被射杀。\n在狙击手的子弹耗尽前,狙击手无法进行常规击杀。","(偽裝者陣營):\n狙擊手擁有遠距離狙殺的技能,當狙擊手變形時會標記一個點(標記A),解除變形時也會標記一個點(標記B),標記A到標記B即為彈道,\n子彈將由標記A穿越彈道後再從標記B打出,並狙殺離這條彈道上最近的人(若有人處於彈道中則不會被狙殺),狙擊手在子彈用完之後可以正常殺人。","(Предатель):\nСнайпер может стрелять в игроков на расстоянии. \nОн убивает игрока который, находится с ним на одной линии, от точки Морфа до точки возвращения в свой облик. \nОн может совершать обычные убийства после того, как все его патроны закончатся.","(Impostor):\nVocê pode atirar em jogadores de longe.\nSua linha de fogo continua numa linha reta, do ponto que você se metamorfa ao ponto que você volta ao normal.\nVocê não pode fazer mortes normais até usar toda a sua munição." -"EvilTrackerInfoLong","(Impostors):\nYou can see where others are.\nYou will see arrows pointing to the other impostors and anyone you shapeshift into.\nYou will see a ""kill flash"" when your fellow impostors kill.","(インポスター陣営):\n他人の位置が分かるインポスター。\n味方のインポスターとシェイプシフトで選んだ一人の位置が矢印で表示される。\n味方インポスターのキルによるキルフラッシュも見える。","(内鬼阵营):\n邪恶追踪者可以追踪其他内鬼以及其所变形的玩家。\n玩家名称下面的箭头代表着目标的方向。\n当内鬼队友杀人时,邪恶追踪者将会看到击杀闪烁提示。","(偽裝者陣營):\n邪惡追蹤者可以通過變形來指定一個追蹤目標,變形後將會立刻解除變形,並且玩家名稱會出現一個箭頭指向目標,當隊友殺人時,邪惡追蹤者將會看到螢幕閃爍為提示。","(Предатель):\nЗлой Следопыт может отслеживать других игроков. \nОн может видеть стрелки, которые указывают на Предателей или на того, в кого он превратился. \nОн может видеть когда было совершено убийство с помощью вспышки убийства.","(Impostores):\nVocê consegue ver a posição de outros.\nÉ possível ver setas apontando para outros Impostores e qualquer um em que você se transforme.\nVocê também verá uma piscada de tela quando um aliado Impostor matar alguém." -"StealthInfoLong","(Impostors):\nWhen the Stealth kills, players in the same room are blinded for a short while.","(インポスター陣営):\nキルを行うと、同じ部屋にいる他のプレイヤーの視界が少しの間暗転する。","","","(Предатель):\nКогда Хитрец убивает, игроки в одной локации будут ненадолго ослеплены","" -"NekoKabochaInfoLong","(Impostors):\nThe Neko-Kabocha kills back their killer.","(インポスター陣営):\nキルされた際、自分を殺してきたプレイヤーを殺し返す。","","","(Предатель):\nНеко-Кабоча убивает своего убийцу","" -"EvilHackerInfoLong","(Impostors):\nThe EvilHacker can get the last-minute admin information at the meeting beginning.\nUnoccupied rooms are not shown.\nA '★' marks rooms with impostors.\nRooms with dead-bodies are marked with the number of bodies.\ne.g.)★Cafeteria: 3(DEAD×1)","(インポスター陣営):\n会議直前のアドミン情報を見ることができる。\n誰もいない部屋は省略される。\nインポスターがいる部屋には★印が付く。\n死体がある部屋には死体数が表記される。\n例)★カフェテリア: 3(死体×1)","","","(Предатель):\nЗлой Хакер может получить последнюю информацию на админке в начале встречи.\nНезанятые локации не показаны.\nA '★' Помечает локации с Предателями.\nЛокации с трупами помечаются количеством трупом) \nНапример: \n★Кафетерия: 3 (Мертвы×1)""","" -"PenguinInfoLong","(Impostors):\nPenguins can restrain target by pressing the kill button, and drag around.\While dragging, the target dies by pressing the kill button again or after a certain period of time.\nPress the kill button twice for a direct kill.","(インポスター陣営):\nキルボタン一回で相手を拘束して連れまわすことが出来る。\nもう一度キルボタンを押すか一定時間経過でキルする。\nキルボタン2回で直接キル可能。","","","(Предатель):\nПингвины могут удерживать цель, нажимая кнопку убийства, и перетаскивать ее.\Во время перетаскивания цель умирает, нажав кнопку убийства еще раз или через определенный промежуток времени.\nНажмите кнопку убийства дважды, чтобы убить напрямую.","" -"InsiderInfoLong","(Impostors):\nYou can see roles whose you killed.\nYou can also see roles and abilities of all Impostors.\nKIlling specified times tells you Madmate as well.","(インポスター陣営):\nキルした相手の役職が分かる。\nまた、味方インポスターの役職と能力が見える。\nさらに、特定回数キルするとマッドメイトも分かる。","","","(Предатель):\nВы можете увидеть роли, которых Вы убили\nВы также можете увидеть роли и способности всех Предателей.\nУбийство в указанное время также сообщает вам о Безумце.","" +"BountyHunterInfoLong","If you kill your indicated target, your next kill cooldown will be greatly reduced.\nIf you kill anyone other than your target, your next kill cooldown will increase.\nThe target swaps after a configurable amount of time.","表示されたターゲットをキルした場合、次のキルクールがとても短くなる。\nターゲットではないプレイヤーをキルした場合は、キルクールが伸びてしまう。\nターゲットは一定時間ごとに変更される。","如果赏金猎人击杀了赏金目标,击杀冷却缩短。\n反之,则冷却增加。","如果賞金獵人殺了賞金目標,下一次的殺人冷卻會變得非常的短\n如果賞金獵人殺了非賞金目標,下一次的殺人冷卻將會被延長或不變\n目標將會重置在時間結束或殺死賞金目標後。","Если Охотник за головами убьет назначенную ему цель, время отката убийства будет намного меньше чем обычно. \n При убийстве игрока который не является его целью, приводит к стандартному времени отката убийства. \nЦели меняются спустя какое-то время","Se você matar o seu alvo, o tempo de recarga (abate) será mais rápido.\nMatar um jogador que não seja seu alvo, resulta em um tempo de recarga (abate) maior.\nOs alvos são alterados em intervalos regulares."," Se você matar o seu alvo, o tempo de morte será mais rápido.\nMatar um jogador que não seja seu alvo, resulta em um tempo de morte maior.\nOs alvos são alterados em intervalos regulares." +"FireWorksInfoLong","Whenever you shapeshift, you can place a firework.\n After placing all fireworks, you can set them all off at once when you shapeshift once you become the last impostor.\nOnce you start placing fireworks, you cannot kill until the fireworks go off.","変身することで花火を最大3か所に設置可能。\nすべて設置した後、インポスター最後の生き残りになった時に再度変身することで一斉起爆できる。\n花火が設置されている間はキルできない。","烟花商人可以安放烟花炸弹,造成范围性杀伤。\n烟花商人在变形状态下可以安放至多三个烟花炸弹。\n当存活的内鬼阵营玩家仅剩烟花商人且烟花炸弹安置完毕时,烟花商人可以在变形状态下引爆炸弹。\n烟花商人不可以进行常规击杀,除非安放并引爆所有烟花炸弹。\n烟花炸弹可以同样炸死烟花商人自己。\n如果烟花炸弹炸死所有存活玩家,内鬼阵营将获胜。","煙火工匠可以放置煙火來造成範圍性傷害,煙火工匠可以以變形來放置煙火,最多可以放置三個煙火,但是在煙火工匠放置完所有煙火前無法殺人\n如果他的隊友都已經死亡的話,煙火工匠便可以引爆煙火。\n如果煙火工匠也在爆炸中被炸死,偽裝者陣營同樣獲勝。","Фейерверк может поставить несколько фейерверков с помощью Морфлинга. \nПосле установки всех фейерверков он может поджечь их все сразу с помощью использования Морфлинга, когда он является последним Предателем. \nТолько после этого он сможет убивать."," Ao mudar de corpo, você coloca um fogo de artifício.\nDepois de colocar todos, você pode acender tudo de uma vez ao trocar de corpo sendo o último Impostor.\nQuando começar a colocar fogos de artifício, você não poderá matar até eles explodirem."," Ao mudar de corpo, você coloca um fogo de artifício.\nDepois de colocar todos, você pode acender tudo de uma vez ao trocar de corpo sendo o último Impostor.\nQuando começar a colocar fogos de artifício, você não poderá matar até eles explodirem." +"SerialKillerInfoLong","You have a shorter kill cooldown than other impostors', but you will die if you don't kill before the end of your shapeshift cooldown.","キルクールが他のインポスターに比べて短い反面、変身のクールタイムが明ける前にキルしないと自爆する。","嗜血杀手的冷却因为他的杀戮欲望而更快。\n嗜血杀手若不能在一定时间内进行击杀,\n就会因承受不了自己的杀戮欲望而自杀。","嗜血殺手的殺人冷卻是所有偽裝者中最短的一個。\n但是如果嗜血殺手沒有在自殺倒數歸零前再殺人,嗜血殺手將會自殺。","У Серийного Убийцы еще более короткий откат убийства. \nНо если он не совершит убийство в установленный срок, то он мгновенно убьет сам себя.","O seu tempo de recarga (abate) é menor, porém, se você não matar no prazo morrerá no fim do seu tempo de mutação.","O seu tempo de morte é menor, porém, se você não matar no prazo morrerá no fim do seu tempo de mutação." +"ShapeMasterInfoLong","ShapeMaster has no Shapeshift cooldown.\nOn the other hand, their default Shapeshift duration is shorter (default: 10s).","シェイプシフトに特化したインポスター。\n変身持続時間が通常のシェイプシフターよりも短い半面、変身解除後のクールダウンがなく無限に変身することができる。\n変身継続時間は設定で変更が可能。","千面鬼没有变形冷却与次数限制。\n在默认设置下,千面鬼变形的持续时间更短(10秒)。","百變怪相較於普通變形者,百變怪沒有變形冷卻,但是相較於普通變形者他的變形時間會更短。","Шпион не имеет отката восстановления Морфа. \nС другой стороны, длительность Морфа по умолчанию стоит куда меньше. \nПродолжительность трансформации можно изменить в настройках.","Você não tem tempo de recarga (mutação).\nPor outro lado, o tempo padrão de mutação é menor (padrão: 10s).","Você não tem tempo para trocar de corpo.\nPor outro lado, o tempo padrão de metamorfose é menor (padrão: 10s)." +"MareInfoLong","You can only kill during Fix Lights.\nWhen you make a kill, your next kill cooldown will be halved.\nDuring Fix Lights, you can move faster, but your name will also be displayed in red for all.","停電の時だけキルができるインポスター。\nキルに成功すると次のキルクールが半分になる。\n移動速度も停電中は早くなるが名前が赤くなる。","梦魇只能在停电时下进行击杀。\n若夜魇击杀成功,夜魇的击杀冷却将变成原来的一半。\n此外,停电续时间内夜魇的移动速度也会增加,\n并且,这段时间内所有玩家都能看到夜魇的名字以红色表示。","獵夢者只可在關燈狀態下殺人,但是移動速度將會變快,並且如果殺人成功冷卻將會減半。\n不過請注意,獵夢者關燈狀態下的名字會變成紅色,並且所有玩家可見。","Предатель, который может убивать только при выключенном свете. \nКаждое его убийство вдвое снижает откат убийства. \nСкорость движения также будет выше при выключенном свете, но его никнейм станет красным и будет виден всем игрокам.","Você só pode matar durante apagões.\nQuando você mata, o seu próximo tempo de recarga (abate) será reduzido.\nDurante apagões, você se move mais rápido, mas o seu nome aparecerá em vermelho para todos.","Você só pode matar durante apagões.\nQuando você mata, o seu próximo tempo de morte será reduzido.\nDurante apagões, você se move mais rápido, mas o seu nome aparecerá em vermelho para todos." +"VampireInfoLong","You can make delayed kills by biting others.\n(Bitten players will die either after the delay is over or the moment a meeting is called before then.)\nIf you bite a [Bait](#Bait), there is no delay, and you are forced to self-report.","プレイヤーを噛む事で遅延キルが可能。\n(時間が経過or会議が始まると死亡する。)\nベイトの場合即死亡し、強制的に通報が入る。","在吸血鬼的吸血的对象不会立刻死亡,而是延迟一段时间发生。\n若在这段时间内进入会议阶段,该目标将立即死亡。\n当被击杀目标为诱饵时,吸血鬼的击杀被视为普通击杀。","當吸血鬼嘗試殺人時,嘗試被殺的目標並不會立刻死亡,而是過一段時間之後突然暴斃,\n如果在這段時間內進入會議,被吸血的玩家將會立刻死亡。\n請注意: 如果吸血鬼的目標為誘餌,吸血鬼仍舊會報告誘餌的屍體。","Вампир может кусать игроков, а не убивать. \nУкушенный игрок умирает спустя какое-то время, или же умирает сразу как только произойдёт срочное собрание. \nНо если он укусит [Приманку](#Bait) он совершит обычное убийство и он вынужден сразу зарепортить труп.","Você pode matar ao morder outros.\nJogadores mordidos vão morrer depois de um tempo, ou caso uma reunião seja chamada antes.\nSe você morder uma [Isca], não terá delay e você será obrigado a reportar o corpo imediatamente.","Você pode matar ao morder outros.\nJogadores mordidos vão morrer depois de um tempo, ou caso uma reunião seja chamada antes.\nSe você morder um [Isca], não terá delay e você será obrigado a reportar o corpo imediatamente." +"WarlockInfoLong","The Warlocks can curse another players before Shapeshift.\nWhen they Shapeshift after cursing, the player next to the cursed is killed no matter how far away they are.\nWhile Shpashifting, you can perform normal kills.\nAlso, only one player can be cursed per turn.","キルをすると一番近くの人に呪いがかかる。\n次から変身ボタンを押すと、呪われた人が一番近い人をキルする。\n変身した場合、普通のキルになる。\nまた、1ターンに1人しか呪うことはできない。","术士在不变形状态下击杀时,目标会被下咒。\n当术士变形时,最靠近被下咒玩家的船员将会被击杀。","術士可以通過殺人鍵下咒一個目標,如果目標身上出現盾牌破碎的效果代表下咒成功。\n如果術士變形,那麼被下咒的玩家將會立刻殺死一名離他最近的玩家,\n如果術士未在進入會議前咒殺,則詛咒失效。","Колдун может проклинать игроков. \nКогда он Морфится после проклинания какого либо игрока, то проклятый игрок убивает любого игрока который находится по близости, независимо от того насколько далеко он находится. \nТак же во время Морфа он совершает обычные убийства.","Você pode amaldiçoar outros jogadores ao mutar.\nQuando você se transformar novamente depois de amaldiçoar alguém, a pessoa mais próxima do jogador amaldiçoado será morta, não importa o quão longe esteja.\nEnquanto alguém etiver amaldiçoado você pode matar normalmente.\nAlém disso, apenas uma pessoa pode ser amaldiçoada por rodada.","Você pode amaldiçoar outros jogadores ao mutar.\nQuando você se transformar novamente depois de amaldiçoar alguém, a pessoa mais próxima do jogador amaldiçoado será morta, não importa o quão longe esteja.\nEnquanto alguém etiver amaldiçoado você pode matar normalmente.\nAlém disso, apenas uma pessoa pode ser amaldiçoada por rodada." +"WitchInfoLong","Your kill alternates between ""Kill"" and ""Spell"" by actions.\nWhen you kill with your ""Spell"", you bewitch the player.\nPlayers who have been bewitched have a † mark displayed next to them and must vote out the Witch within that meeting, or they will die after the meeting ends.","キルする、またはベントに入ると「Kill」と「Spell」が切り変わる。\nSpellの表示でキルすると呪うことが出来る。\n呪いをかけられた人は会議で「†」が付き魔女を追放しなければ会議後に死亡する。","女巫会在击杀模式与诅咒模式间来回切换,在诅咒模式下可以对目标进行诅咒。\n会议时,被诅咒的目标会带有对全员可见的诅咒标记「†」。如果女巫未在会议时被投票放逐,被诅咒的目标将会在会议结束后死亡。","女巫擁有兩種殺人方式: 詛咒與殺人。會議時,全部玩家都可看到被詛咒的玩家的名字旁有個「†」符號。如果會議結束時女巫沒有被逐出或被殺死,則被詛咒的玩家死亡。\n根據房主設定,女巫的攻擊模式分為通風管切換以及殺人切換。","Ведьма может совершить убийство или заклинание по очереди. \nИгроки, которые были проклятые Ведьма на голосовании помечаются красным крестом справа от никнейма, \nИ если Ведьма не изгнан, все проклятые им игроки умирают сразу же после голосования.","Você pode alternar entre ""Matar"" e ""Feitiço"".\nQuando você usa seu ""Feitiço"", você enfeitiça o jogador.\nQuem for enfeitiçado tem uma marca de † ao lado do nome.\nÉ necessário exilar a bruxa antes da reunião acabar, se não o enfeitiçado morrerá ao fim da reunião.","" +"MafiaInfoLong","At the start of the game, you are unable to kill even though you will see the kill button.\nAfter your fellow impostors are dead, you will be able to kill.","初期状態ではキルが封じられている。\n仲間が全員死亡後、自身もキルが可能になる。","黑手党在游戏中有其他内鬼存活时不可以进行击杀。","當黑手黨在場且有其他偽裝者存活時不可以殺人,但是當隊友都死亡時他就可以開始殺人。","Изначально Мафиози не может убивать. \nОн сможет убивать после того, как все Предатели кроме него будут убиты или изгнаны.","No começo do jogo, você não pode matar.\nDepois que seus companheiros Impostores forem mortos, você será capaz de matar.","No começo do jogo, você não pode matar.\nDepois que seus companheiros Impostores forem mortos, você será capaz de matar." +"PuppeteerInfoLong","Instead of killing someone, ""kills"" can force a player to kill the next non-impostor they approach.\nIf the player your puppet killed has an ability that activates upon death, the effect will land on the puppet instead of yourself.\nYou cannot perform a normal kill.","キルした対象に、対象が次に近づいたクルーをキルさせる事ができる。\n対象がキルした相手がキルされた瞬間に発動するものであった場合、対象にその効果が反映される。\n普通のキルを行うことはできない。","傀儡师的操控对象会击杀其遇到的下一个船员。\n若后者在接触的同时死亡,则前者死亡。\n傀儡师不可以执行正常击杀。","傀儡師按下殺人按鈕時將會操控離他最近的玩家(與術士下咒差不多),接著如果在殺人範圍內碰到非偽裝者陣營的人,被操控的玩家將會直接殺死被害者,並且效果不再持續。","Кукловод может совершить убийство с помощью помеченного игрока, но не может убивать как обычный Предатель. \nЕсли цель убивает противника и он срабатывает в момент убийства цели, эффект отразится на цели. \nВы не можете совершать обычные убийства.","Invés de matar alguém, ""matar"" força um jogador a matar o próximo (não impostor).\nSe o jogador que a sua marionete matou tem uma habilidade que ativa durante a morte, o efeito irá para a marionete ao invés de você.\nVocê não pode matar normalmente.","Invés de matar alguém, ""matar"" força um jogador a matar o próximo (não impostor).\nSe o jogador que a sua marionete matou tem uma habilidade que ativa durante a morte, o efeito irá para a marionete ao invés de você.\nVocê não pode matar normalmente." +"TimeThiefInfoLong","Every kill reduces discussion and voting time in meeting.\nIf you are voted out or killed, the lost time returns to meetings.","キルを行うと会議時間が減少する。\nタイムシーフが追放または殺害されると、失われた会議時間が返ってきます。","蚀时者每击杀一个人,会议时间就将减少一定时间。\n如果蚀时者死亡,被偷取的时间的会议时间将返还。","時間竊賊每殺死一個人,那麼會議時間就會減少一定時間,如果時間竊賊死亡,那麼會議時間將會恢復。","Каждое убийство Вора Времени сокращает время обсуждения и голосования на собраниях. \nВ зависимости от настроек потерянное время возвращается после того, как он будет убит или изгнан.","Cada morte reduz tempo de votação e discussão.\nSe você for exilado ou morto, o tempo perdido retorna para as reuniões.","Cada morte reduz tempo de votação e discussão.\nSe você for exilado ou morto, o tempo perdido retorna para as reuniões." +"SniperInfoLong","You can shoot players from afar.\nYour line of fire continues as a straight line in the direction of the spot where you shapeshift to where you unshift.\nYou cannot perform normal kills until you use up all of your ammo.","遠くの敵を狙撃できるインポスター。\nシェイプシフトした地点から解除した地点の延長線上の敵を一人撃ち抜く。\n撃ちきるまで通常キル出来ない。","狙击手拥有远距离射杀的能力。\n方法为:变形的位置及解除变形位置的连线及其延长线上的一名玩家将被射杀。\n在狙击手的子弹耗尽前,狙击手无法进行常规击杀。","狙擊手擁有遠距離狙殺的技能,當狙擊手變形時會標記一個點(標記A),解除變形時也會標記一個點(標記B),標記A到標記B即為彈道,\n子彈將由標記A穿越彈道後再從標記B打出,並狙殺離這條彈道上最近的人(若有人處於彈道中則不會被狙殺),狙擊手在子彈用完之後可以正常殺人。","Снайпер может стрелять в игроков на расстоянии. \nОн убивает игрока который, находится с ним на одной линии, от точки Морфа до точки возвращения в свой облик. \nОн может совершать обычные убийства после того, как все его патроны закончатся.","Você pode atirar em jogadores de longe.\nSua linha de fogo continua numa linha reta, do ponto que você se metamorfa ao ponto que você volta ao normal.\nVocê não pode fazer mortes normais até usar toda a sua munição.","Você pode atirar em jogadores de longe.\nSua linha de fogo continua numa linha reta, do ponto que você se metamorfa ao ponto que você volta ao normal.\nVocê não pode fazer mortes normais até usar toda a sua munição." +"EvilTrackerInfoLong","You can see where others are.\nYou will see arrows pointing to the other impostors and anyone you shapeshift into.\nYou will see a ""kill flash"" when your fellow impostors kill.","他人の位置が分かるインポスター。\n味方のインポスターとシェイプシフトで選んだ一人の位置が矢印で表示される。\n味方インポスターのキルによるキルフラッシュも見える。","邪恶追踪者可以追踪其他内鬼以及其所变形的玩家。\n玩家名称下面的箭头代表着目标的方向。\n当内鬼队友杀人时,邪恶追踪者将会看到击杀闪烁提示。","邪惡追蹤者可以通過變形來指定一個追蹤目標,變形後將會立刻解除變形,並且玩家名稱會出現一個箭頭指向目標,當隊友殺人時,邪惡追蹤者將會看到螢幕閃爍為提示。","Злой Следопыт может отслеживать других игроков. \nОн может видеть стрелки, которые указывают на Предателей или на того, в кого он превратился. \nОн может видеть когда было совершено убийство с помощью вспышки убийства.","Você consegue ver a posição de outros.\nÉ possível ver setas apontando para outros Impostores e qualquer um em que você se transforme.\nVocê também verá uma piscada de tela quando um aliado Impostor matar alguém.","Você consegue ver a posição de outros.\nÉ possível ver setas apontando para outros Impostores e qualquer um em que você se transforme.\nVocê também verá uma piscada de tela quando um aliado Impostor matar alguém." +"StealthInfoLong","When the Stealth kills, players in the same room are blinded for a short while.","キルを行うと、同じ部屋にいる他のプレイヤーの視界が少しの間暗転する。","当暗杀者完成击杀后,与暗杀者处于同一房间内的玩家会短暂失明。","","Когда Хитрец убивает, игроки в одной локации будут ненадолго ослеплены","Quando o Furtivo mata, os jogadores na mesma sala ficam cegos por um curto período.","" +"NekoKabochaInfoLong","The Neko-Kabocha kills back their killer.","キルされた際、自分を殺してきたプレイヤーを殺し返す。","当猫又被击杀时,猫又也会击杀凶手。","","Неко-Кабоча убивает своего убийцу","O Neko-Kabocha mata de volta o seu assassino.","" +"EvilHackerInfoLong","The EvilHacker can get the last-minute admin information at the meeting beginning.\nUnoccupied rooms are not shown.\nA '★' marks rooms with impostors.\nRooms with dead-bodies are marked with the number of bodies.\ne.g.)★Cafeteria: 3(DEAD×1)","会議直前のアドミン情報を見ることができる。\n誰もいない部屋は省略される。\nインポスターがいる部屋には★印が付く。\n死体がある部屋には死体数が表記される。\n例)★カフェテリア: 3(死体×1)","邪恶黑客可以在会议上看到会议前的管理信息。\n无人的房间将被省略。\n有内鬼的房间标有★。\n有尸体的房间显示尸体数量\n例:★大厅: 3(尸体×1)","","Злой Хакер может получить последнюю информацию на админке в начале встречи.\nНезанятые локации не показаны.\nA '★' Помечает локации с Предателями.\nЛокации с трупами помечаются количеством трупом) \nНапример: \n★Кафетерия: 3 (Мертвы×1)""","O Hacker do Mal pode obter as informações recentes do admin no início da reunião.\nAs salas desocupadas não são mostradas.\nUm '★' marca as salas com impostores.\nAs salas com mortos são marcadas com o número de corpos.\nEx:)★Cafeteria: 3( MORTO×1)","" +"PenguinInfoLong","Penguins can restrain target by pressing the kill button, and drag around.\While dragging, the target dies by pressing the kill button again or after a certain period of time.\nPress the kill button twice for a direct kill.","キルボタン一回で相手を拘束して連れまわすことが出来る。\nもう一度キルボタンを押すか一定時間経過でキルする。\nキルボタン2回で直接キル可能。","企鹅使用击杀键可以拖拽目标。\n等到倒计时结束或再次按下击杀键则可以正常击杀目标。","","Пингвины могут удерживать цель, нажимая кнопку убийства, и перетаскивать ее.\Во время перетаскивания цель умирает, нажав кнопку убийства еще раз или через определенный промежуток времени.\nНажмите кнопку убийства дважды, чтобы убить напрямую.","Os Pinguins podem prender o alvo pressionando o botão de matar e arrastá-lo.\nEnquanto estiver arrastando, o alvo morre ao pressionar o botão de matar novamente ou após um determinado período de tempo.\nPressione o botão de matar duas vezes para matar imediatamente.","" +"InsiderInfoLong","You can see roles whose you killed.\nYou can also see roles and abilities of all Impostors.\nKIlling specified times tells you Madmate as well.","キルした相手の役職が分かる。\nまた、味方インポスターの役職と能力が見える。\nさらに、特定回数キルするとマッドメイトも分かる。","内幕人可以看到自己击杀的人的职业。\n还可以看到队友的位置和能力。\n此外,如果内幕人完成了指定的击杀数,则可以看到谁是叛徒。","","Вы можете увидеть роли, которых Вы убили\nВы также можете увидеть роли и способности всех Предателей.\nУбийство в указанное время также сообщает вам о Безумце.","Você pode ver as classes de quem matou.\nVocê também pode ver as classes e as habilidades de todos os Impostores.\nAo matar em horários específicos, você verá o Tripulante Louco.","" "# マッドメイト系役職" -"MadmateInfoLong","(Impostors):\nYou are a crewmate, but you act as the impostors' ally.\nYou and the impostors cannot recognize each other.\nYou can vent, and you have no tasks.","(インポスター陣営):\nクルーだがインポスターの味方をする。\nインポスターと互いに認識できない。\nベントが使え、タスクはない。","(内鬼阵营):\n叛徒伪装成船员来帮助内鬼阵营,但是他们并不知道谁是内鬼。\n叛徒不可以进行击杀或者破坏,可以跳管道且没有任务。","(偽裝者陣營):\n叛徒屬於偽裝者陣營。\n偽裝者無法知道叛徒是誰,他可以使用通風口且沒有任務。","(Предатель):\nЧлен Экипажа, который на стороне Предателей. \nНо он не знает кто является Предателем в игре. \nМожет использовать вентиляцию, но нет заданий.","(Impostores):\nVocê é um tripulante que se aliou ao impostor.\nOs Impostores não sabem quem você é e vice-versa.\nPode usar a ventilação, e não tem tarefas." -"MadGuardianInfoLong","(Impostors):\nYou are a crewmate, but you act as the impostors' ally.\nYou and the impostors cannot recognize each other.\nOnce you finish your tasks, you will be shielded from impostor kills.","(インポスター陣営):\nクルーだがインポスターの味方をする。\nインポスターと互いに認識できない。\nタスクを全て完了させるとシールドが付与される。","(内鬼阵营):\n背叛的守卫不可以击杀、破坏或使用管道。\n背叛的守卫完成任务之后,可以免疫所有击杀。","(狼人陣營):\n船員,但是他們屬於狼人陣營.\n狼人和他們無法互相知道誰是叛徒,誰是狼\n當背叛的天使完成所有任務時,他將有一個盾牌。","(Предатель):\nЧлен Экипажа, который на стороне Предателей. \nНо он не знает кто является Предателем в игре. \nЕсли он выполнит все задания, то он получит щит, с помощью которого он может предотвратить свое убийство.","(Impostores):\nVocê é um tripulante que se aliou ao impostor.\nOs Impostores não sabem quem você é e vice-versa.\nQuando terminar as suas tarefas, o Impostor não poderá te matar." -"MadSnitchInfoLong","(Impostors):\nYou are a crewmate, but you act as the impostors' ally.\nYou and the impostors cannot recognize each other, but once you finish all tasks, you can see who the impostors are.","(インポスター陣営):\nクルーだがインポスターの味方をする。\nインポスターと互いに認識できないが、タスクを全て完了させるとマッドスニッチからインポスターを認識できるようになる。","(内鬼阵营):\n背叛的告密者与内鬼不互认,不可以击杀、破坏或者使用管道。\n背叛的告密者完成所有任务之后,其与内鬼互认。","(狼人陣營):\n船員,但是他們屬於狼人陣營.\n狼人和他們無法互相知道誰是叛徒,誰是狼\n當背叛的告密者做完任務時,狼人和背叛的告密者將可以互相知道對方。","(Предатель):\nЧлен Экипажа, который на стороне Предателей. \nОн может увидеть, кто является Предателем, после того как выполнит все свои задания. \nВ зависимости от настроек он может использовать вентиляцию.","(Impostores):\nVocê é um tripulante que se aliou ao impostor.\nOs Impostores não sabem quem você é e vice-versa.\nQuando terminar as suas tarefas, você poderá ver quem é o Impostor." -"SKMadmateInfoLong","(Impostors):\nThe player closest to a Shapeshifter when they shapeshift will become a Sidekick Madmate. You have no tasks.","(インポスター陣営):\n変身した際に一番近かった人がなれる役職。タスクはない。","(内鬼阵营):\n一些内鬼职业变形时,其附近的与其距离最近的玩家的职业有概率转化为该职业。\n叛徒跟班与内鬼不互认。","(狼人陣營):\n當狼人變形時\n離狼人變形最近的一個人將有機率轉化為叛徒跟班\n他沒有任務,且叛徒跟班與狼人無法互相知道。","(Предатель):\nРоль, которую может занять ближайший к вам игрок, когда вы используете Морф. Нет заданий.","(Impostores):\nO jogador mais próximo de um Metamorfo quando eles se transformam virará um Tripulante Louco Ajudante. Você não tem tarefas." +"MadmateInfoLong","You are a crewmate, but you act as the impostors' ally.\nYou and the impostors cannot recognize each other.\nYou can vent, and you have no tasks.","クルーだがインポスターの味方をする。\nインポスターと互いに認識できない。\nベントが使え、タスクはない。","叛徒伪装成船员来帮助内鬼阵营,但是他们并不知道谁是内鬼。\n叛徒不可以进行击杀或者破坏,可以跳管道且没有任务。","叛徒屬於偽裝者陣營。\n偽裝者無法知道叛徒是誰,他可以使用通風口且沒有任務。","Член Экипажа, который на стороне Предателей. \nНо он не знает кто является Предателем в игре. \nМожет использовать вентиляцию, но нет заданий.","Você é um tripulante que se aliou ao impostor.\nOs Impostores não sabem quem você é e vice-versa.\nPode usar a ventilação, e não tem tarefas.","Você é um tripulante que se aliou ao impostor.\nOs Impostores não sabem quem você é e vice-versa.\nPode usar a ventilação, e não tem tarefas." +"MadGuardianInfoLong","You are a crewmate, but you act as the impostors' ally.\nYou and the impostors cannot recognize each other.\nOnce you finish your tasks, you will be shielded from impostor kills.","クルーだがインポスターの味方をする。\nインポスターと互いに認識できない。\nタスクを全て完了させるとシールドが付与される。","背叛的守卫不可以击杀、破坏或使用管道。\n背叛的守卫完成任务之后,可以免疫所有击杀。","船員,但是他們屬於狼人陣營.\n狼人和他們無法互相知道誰是叛徒,誰是狼\n當背叛的天使完成所有任務時,他將有一個盾牌。","Член Экипажа, который на стороне Предателей. \nНо он не знает кто является Предателем в игре. \nЕсли он выполнит все задания, то он получит щит, с помощью которого он может предотвратить свое убийство.","Você é um tripulante que se aliou ao impostor.\nOs Impostores não sabem quem você é e vice-versa.\nQuando terminar as suas tarefas, o Impostor não poderá te matar.","Você é um tripulante que se aliou ao impostor.\nOs Impostores não sabem quem você é e vice-versa.\nQuando terminar as suas tarefas, o Impostor não poderá te matar." +"MadSnitchInfoLong","You are a crewmate, but you act as the impostors' ally.\nYou and the impostors cannot recognize each other, but once you finish all tasks, you can see who the impostors are.","クルーだがインポスターの味方をする。\nインポスターと互いに認識できないが、タスクを全て完了させるとマッドスニッチからインポスターを認識できるようになる。","背叛的告密者与内鬼不互认,不可以击杀、破坏或者使用管道。\n背叛的告密者完成所有任务之后,其与内鬼互认。","船員,但是他們屬於狼人陣營.\n狼人和他們無法互相知道誰是叛徒,誰是狼\n當背叛的告密者做完任務時,狼人和背叛的告密者將可以互相知道對方。","Член Экипажа, который на стороне Предателей. \nОн может увидеть, кто является Предателем, после того как выполнит все свои задания. \nВ зависимости от настроек он может использовать вентиляцию.","Você é um tripulante que se aliou ao impostor.\nOs Impostores não sabem quem você é e vice-versa.\nQuando terminar as suas tarefas, você poderá ver quem é o Impostor.","Você é um tripulante que se aliou ao impostor.\nOs Impostores não sabem quem você é e vice-versa.\nQuando terminar as suas tarefas, você poderá ver quem é o Impostor." +"SKMadmateInfoLong","(Impostors):\nThe player closest to a Shapeshifter when they shapeshift will become a Sidekick Madmate. You have no tasks.","(インポスター陣営):\n変身した際に一番近かった人がなれる役職。タスクはない。","(内鬼阵营):\n一些内鬼职业变形时,其附近的与其距离最近的玩家的职业有概率转化为该职业。\n叛徒跟班与内鬼不互认。","(狼人陣營):\n當狼人變形時\n離狼人變形最近的一個人將有機率轉化為叛徒跟班\n他沒有任務,且叛徒跟班與狼人無法互相知道。","(Предатель):\nРоль, которую может занять ближайший к вам игрок, когда вы используете Морф. Нет заданий.","(Impostores):\nO jogador mais próximo de um Metamorfo quando eles se transformam virará um Tripulante Louco Ajudante. Você não tem tarefas.","(Impostores):\nO jogador mais próximo de um Metamorfo quando eles se transformam virará um Tripulante Louco Ajudante. Você não tem tarefas." "# 特殊クルー役職" -"BaitInfoLong","(Crewmates):\nWhen you are killed, you force your killer to immediately self-report.","(クルー陣営):\nキルされた時に、自身をキルした人に\n強制的にセルフレポートさせる事ができる。","(船员阵营):\n诱饵被击杀时,击杀诱饵的凶手将被迫报告。","(船員):\n當被殺時,兇手將強制自行舉報","(Член Экипажа):\nКогда Приманку убивают, он заставляет убившего игрока \nмоментально зарепортить ваш труп.","(Tripulantes):\nQuando morrer, você força o seu assassino a reportar o seu corpo imediatamente." -"LighterInfoLong","(Crewmates):\nYour vision will increase.","(クルー陣営):\n条件を満たすと、自分の視界を広げることができる。","(船员阵营):\n执灯人完成任务后,视野会扩大,且不受照明破坏影响。","(船員):\n當做完所有任務時可以增大視野且不受關燈影響","(Член Экипажа):\nПосле выполнения всех заданий Фонарик сможет увеличить свое поле зрения.","(Tripulantes):\nQuando você terminar todas as suas tarefas, sua visão irá aumentar." -"MayorInfoLong","(Crewmates):\nYou have multiple votes, which will all go towards the one person you vote or skip.","(クルー陣営):\n会議の票を複数持ち、まとめて一人または\nスキップに入れることができる。(設定有)","(船员阵营):\n市长在投票时可投下多票 (票数多寡根据设置),市长还拥有以跳通风管来召开紧急会议的能力。","(船員):\n可以一次投很多票並可以將它灌到一個人身上或跳過 (可以設定票數)","(Член Экипажа):\nМэр можете иметь несколько голосов на собрании, и проголосовать за любого игрока этими доп. голосами или \nпросто отдать все голоса в скип.","(Tripulantes):\nVocê tem votos adicionais, todo eles vão para a pessoa em que você votar ou pular." -"SabotageMasterInfoLong","(Crewmates):\nYou can Fix Reactors, O2, Communications by yourself.\nLights can be fixed with the flick of one switch.\nOpening a door will open all doors to that room.","(クルー陣営):\nリアクター・O2・通信妨害を一人で修理可能。\n停電は一箇所のレバーに触れる事で全て直る。\nドアを開けるとその部屋の全てのドアが開く。","(船员阵营):\n氧气泄露、核反应堆熔毁以及米拉总部的通讯破坏维修大师只需要修复一边则另一边即可同时被修复。\n维修大师只需要按一个开关便可以修复照明破坏。\n维修大师打开波鲁斯与飞艇地图的门时维修大师所在房间的所有门同时打开。","(船員):\n反應堆, 氧氣, 通訊以及其他破壞可以修理工獨自修復。","(Член Экипажа):\nМастер Саботажа может в одиночку починить саботажи такие, как: Саботаж Реактора, Саботаж O2 и Саботаж Связи. \nТак же он может починить Саботаж Света коснувшись лишь одного рычага. \nОткрытие одной двери позволяет открыть все двери в этой комнате.","(Tripulantes):\nVocê pode consertar Reatores, O2, Comunicações sozinho.\nAs luzes podem ser reparadas apertando apenas um botão.\nAbrir uma porta, abre todas as outras daquela mesma sala." -"SheriffInfoLong","(Crewmates):\nYou can kill anyone on Team Impostors.\nThe ability to kill Neutrals is a configurable setting.\nIf you try to kill a crew, you will kill yourself instead. You have no tasks.","(クルー陣営):\nインポスター陣営をキルすることができる。\n※ニュートラルをキル可能にする設定有。\nクルーメイトをキルすると自殺する。タスクはない。","(船员阵营):\n警长可以击杀内鬼(根据房间设置,警长也可以击杀独立阵营玩家)。\n警长若尝试击杀船员阵营的玩家,警长将会走火自杀。\n警长没有任务。","(船員):\n警長可以殺死狼人陣營的人,\n*可以設定中立陣營是否可以被殺\n警長若槍到好人陣營將會自殺,警長沒有任務。","(Член Экипажа):\nШериф может убивать Предателей. \nЕсть настройка, позволяющая убить даже Нейтралов. \nОднако если Шериф попытается убить Члена Экипажа, то это приведёт к его смерти. У него нет заданий.","(Tripulantes):\nVocê pode matar qualquer um, no Time Impostor.\nA habilidade de matar neutros é configurável.\nSe você tentar matar um Tripulante, irá morrer no lugar dele. Você não tem tarefas." -"SnitchInfoLong","(Crewmates):\nOnce you finish all your tasks, the Impostors' names will be displayed in red to you.\nWhen you have only one task left, a ★ mark will be displayed next to your name and you will be exposed to the Impostors.","(クルー陣営):\nタスク完了と同時にインポスターの名前の色が\n赤く表示され逆に自身のタスクが少なくなると\n名前の横に★が表示されインポスターに暴かれる。","(船员阵营):\n告密者完成所有任务后,将可以看到以红色昵称表示的所有内鬼。\n当告密者仅剩一个任务的时候,内鬼将会看到自己以及告密者的昵称旁边有「★」作为提示。\n(注:告密者同时作为恋人时,其技能将失效)","(船員):\n當告密者做完所有任務,狼人的名字將會變成紅色(僅告密者可見),\n但是當他剩下最後一個任務時,\n狼人會看見告密者頭上會有個星號標記。(中立或船員不可見)\n(P.S:戀人告密者技能失效)","(Член Экипажа):\nКак только Стукач выполнит все задания, то никнейм у всех Предателей будет отображаться \nкрасным цветом, и наоборот, если количество ваших заданий будет близка к завершению, \nто рядом с никнеймом Стукача появится звёздочка★, которая будет видна Предателям.","(Tripulantes): \nAssim que você terminar suas tarefas, os nomes dos Impostores serão revelados em vermelho para você. \nQuando estiver com apenas uma tarefa faltando, um símbolo (★) será mostrado no seu nome e você estará exposto aos Impostores." -"LoversInfoLong","(Neutrals),\nLovers is an additional Neutrals role assigned to 2 random players on top of their defined roles.\nIf you and your lover both survive til the end, you both will win.\nIf one of you dies, the other will also die. If crewmates get a task victory, you both will lose.","(ニュートラル):\n全プレイヤーの中からランダムで2人に他の役職と重複して配役されるニュートラル。\n恋人が2人とも生き残っている状態で試合終了すれば勝利。\n恋人の片方が死んだときにもう片方も死ぬ。クルーのタスクが完了すると敗北。","恋人为两名玩家的组合。\n恋人存活到游戏结束则恋人独自获胜。\n船员阵营的恋人玩家没有任务。\n从恋人阵营玩家可以看到两名恋人玩家昵称旁有心形标志。\n恋人一同赴死。\n恋人中的一方在会议中被放逐时,另一方将死亡并变为不可以被报告的尸体。","(中立),\n遊戲開始系統會隨機抽取兩人當戀人,該職業可以和其他職業屬於同一個玩家身上\n戀人將獲勝在遊戲結束時如果兩個戀人都活著.\n但是當一個戀人死去,另一個戀人也會跟著死去\n如果船員完成所有任務,戀人即宣告失敗。","(Нейтралы):\nДополнительно с какой-либо ролью Любовники назначаются 2 случайным игрокам. \nЕсли оба Любовника останутся живы, то они выиграют. \nКогда умрёт хотя бы один Любовник, то моментально умрёт и второй. Они проиграют когда Члены Экипажа выполнят все задания.","(Neutros): \nOs Amantes, é um papel dado a 2 jogadores aleatórios, somado a sua classe já definida. \nSe você e seu amante sobreviverem até o final, vocês ganharão. \nCaso um dos dois morra, o outro também morre. Se os Tripulantes ganharem por tarefas, os dois perdem." -"SpeedBoosterInfoLong","(Crewmates):\nOnce you finish your tasks, a random living player will get a speed boost.","(クルー陣営):\nタスクを終わらせると、ランダムな生存者のスピードを上げる事ができる。","(船员阵营):\n增速者完成所有任务后,场上任意一名存活玩家的速度将被加快。","(船員):加速器做完任務時可以讓隨機的活著玩家速度加快","(Член Экипажа):\nОн может увеличить скорость случайных живых игроков, если он будет выполнять задания.","(Tripulantes): \nAssim que você finalizar suas tarefas, um jogador vivo aleatório irá receber um aumento de velocidade." -"DoctorInfoLong","(Crewmates):\nYou can see the cause of death for all players.\nIn addition, you can access vitals wherever you are while you still have battery.","(クルー陣営):\nプレイヤーの死因を知ることができる。\nまた、充電がある限りどこでもバイタルを確認できる。","(船员阵营):\n医生可以查看生命监测装置并确认死亡玩家的死因。\n查看的时间长短与设备的充能数挂钩。","(船員):\n醫生可以知道每個玩家的死因\n還有醫生有一個跟科學家一樣的心電圖,它也需要充電。","(Член Экипажа):\nДоктор может узнать причину смерти игрока. \nКроме того, он имеет портативные пульсы как у учёного, которые он может использовать в любом месте, пока у него не закончится зарядка.","(Tripulantes): \nVocê pode ver a causa da morte de todos os jogadores. \nAdicionalmente, você tem acesso aos sinais vitais de todos enquanto tiver bateria." -"TrapperInfoLong","(Crewmates):\nWhen you are killed, you immobilize your killer for a configurable amount of time.","(クルー陣営):\nキルされると、キルした人を数秒間移動不可にすることができる。(設定有)","(船员阵营):\n陷阱师被击杀时,凶手一段时间内将不能移动。","(船員):\n如果設限者被殺,殺他的兇手將會被困在原地幾秒(可以設定)","(Член Экипажа):\nПосле того как Охотника убьют, то его убийца будет обездвижен на несколько секунд. (время зависит от настроек)","(Tripulantes): \nAo ser morto, imobilize o seu assassino por um certo período. (configurável)" -"DictatorInfoLong","(Crewmates):\nWhen you vote someone, the meeting will end on the spot and the player you voted will be ejected.\nThe moment you vote someone out, you will also die.\nWhen you vote for someone in a meeting, they forcibly break that meeting and exile the player they vote for.\nAfter exercising the force, the Dictators die just after meeting.","(クルー陣営):\n会議中に誰かに投票をすると、会議を強制終了させて投票先を吊る事ができる。\n投票したタイミングでディクテーターは死ぬ。","(船员阵营):\n当独裁者在会议阶段投票给玩家后,会议会被强制结束并放逐其投票对象。\n该技能发动后独裁者将会死亡。","(船員):\n如果獨裁主義者在會議上投票給某位玩家,\n獨裁主義者將強制結束會議並無視場上票數將獨裁主義者投的那個人投出\n但是獨裁主義者將死於他投票的瞬間。","(Член Экипажа):\nЕсли Диктатор проголосует за любого игрока во время собрания, он сможет принудительно завершить собрание и кикнуть игрока за которого он отдал голос \nТак же Диктатор умрёт после собрания когда он отдаст голос.","(Tripulantes): \nQuando votar em alguém, durante uma reunião, a reunião será encerrada e a pessoa em que você votou, será exilada. \nAo exilar alguém, você morre-rá." -"SeerInfoLong","(Crewmates):\nWhenever someone dies, you will see a ""kill flash"".","(クルー陣営):\nプレイヤーが死亡するごとに、シーアはキルフラッシュを見ることができる。","(船员阵营):\n每当玩家死亡时,灵媒将会看到击杀闪烁。","(船員):當有玩家死亡時,靈媒將看到閃光","(Член Экипажа):\nПровидец видит ''Вспышку Убийства'' каждый раз, когда игроки умирают.","(Tripulantes): \nQuando alguém morre, você vê um ""clarão de abate"" (a sua tela piscará brevemente)." -"TimeManagerInfoLong","(Crewmates):The more tasks you do, the longer your meeting will last. When you die, the meeting time will be restored.","(クルー陣営):\nタスクを終わらせるごとに会議時間が延びる。死亡すると会議時間は元に戻る。","(船员阵营):\n你做的任务越多,会议时间就会越长。在你死后,会议时间会复原。","(船員陣營):\n時間大師做越多任務,那麼會議時間就會延長越多(直到上限)。如果時間大師死亡,那麼會議時間就會恢復原狀。","(Член Экипажа):\nМастер Времени может увеличить время встречи, увеличивается время по мере выполнения заданий. Но когда он умрет, время встречи будет сброшено по умолчанию.","(Tripulantes):\nAo fazer tarefas você aumenta o tempo de reunião. Quando você morre, o tempo de reunião volta ao normal." +"BaitInfoLong","When you are killed, you force your killer to immediately self-report.","キルされた時に、自身をキルした人に\n強制的にセルフレポートさせる事ができる。","诱饵被击杀时,击杀诱饵的凶手将被迫报告。","當被殺時,兇手將強制自行舉報","Когда Приманку убивают, он заставляет убившего игрока \nмоментально зарепортить ваш труп.","Quando morrer, você força o seu assassino a reportar o seu corpo imediatamente.","" +"LighterInfoLong","Your vision will increase.","条件を満たすと、自分の視界を広げることができる。","执灯人完成任务后,视野会扩大,且不受照明破坏影响。","當做完所有任務時可以增大視野且不受關燈影響","После выполнения всех заданий Фонарик сможет увеличить свое поле зрения.","Quando você terminar todas as suas tarefas, sua visão irá aumentar.","" +"MayorInfoLong","You have multiple votes, which will all go towards the one person you vote or skip.","会議の票を複数持ち、まとめて一人または\nスキップに入れることができる。(設定有)","市长在投票时可投下多票 (票数多寡根据设置),市长还拥有以跳通风管来召开紧急会议的能力。","可以一次投很多票並可以將它灌到一個人身上或跳過 (可以設定票數)","Мэр можете иметь несколько голосов на собрании, и проголосовать за любого игрока этими доп. голосами или \nпросто отдать все голоса в скип.","Você tem votos adicionais, todo eles vão para a pessoa em que você votar ou pular.","" +"SabotageMasterInfoLong","You can Fix Reactors, O2, Communications by yourself.\nLights can be fixed with the flick of one switch.\nOpening a door will open all doors to that room.","リアクター・O2・通信妨害を一人で修理可能。\n停電は一箇所のレバーに触れる事で全て直る。\nドアを開けるとその部屋の全てのドアが開く。","氧气泄露、核反应堆熔毁以及米拉总部的通讯破坏维修大师只需要修复一边则另一边即可同时被修复。\n维修大师只需要按一个开关便可以修复照明破坏。\n维修大师打开波鲁斯与飞艇地图的门时维修大师所在房间的所有门同时打开。","反應堆, 氧氣, 通訊以及其他破壞可以修理工獨自修復。","Мастер Саботажа может в одиночку починить саботажи такие, как: Саботаж Реактора, Саботаж O2 и Саботаж Связи. \nТак же он может починить Саботаж Света коснувшись лишь одного рычага. \nОткрытие одной двери позволяет открыть все двери в этой комнате.","Você pode consertar Reatores, O2, Comunicações sozinho.\nAs luzes podem ser reparadas apertando apenas um botão.\nAbrir uma porta, abre todas as outras daquela mesma sala.","" +"SheriffInfoLong","You can kill anyone on Team Impostors.\nThe ability to kill Neutrals is a configurable setting.\nIf you try to kill a crew, you will kill yourself instead. You have no tasks.","インポスター陣営をキルすることができる。\n※ニュートラルをキル可能にする設定有。\nクルーメイトをキルすると自殺する。タスクはない。","警长可以击杀内鬼(根据房间设置,警长也可以击杀独立阵营玩家)。\n警长若尝试击杀船员阵营的玩家,警长将会走火自杀。\n警长没有任务。","警長可以殺死狼人陣營的人,\n*可以設定中立陣營是否可以被殺\n警長若槍到好人陣營將會自殺,警長沒有任務。","Шериф может убивать Предателей. \nЕсть настройка, позволяющая убить даже Нейтралов. \nОднако если Шериф попытается убить Члена Экипажа, то это приведёт к его смерти. У него нет заданий.","Você pode matar qualquer um, no Time Impostor.\nA habilidade de matar neutros é configurável.\nSe você tentar matar um Tripulante, irá morrer no lugar dele. Você não tem tarefas.","" +"SnitchInfoLong","Once you finish all your tasks, the Impostors' names will be displayed in red to you.\nWhen you have only one task left, a ★ mark will be displayed next to your name and you will be exposed to the Impostors.","タスク完了と同時にインポスターの名前の色が\n赤く表示され逆に自身のタスクが少なくなると\n名前の横に★が表示されインポスターに暴かれる。","告密者完成所有任务后,将可以看到以红色昵称表示的所有内鬼。\n当告密者仅剩一个任务的时候,内鬼将会看到自己以及告密者的昵称旁边有「★」作为提示。\n(注:告密者同时作为恋人时,其技能将失效)","當告密者做完所有任務,狼人的名字將會變成紅色(僅告密者可見),\n但是當他剩下最後一個任務時,\n狼人會看見告密者頭上會有個星號標記。(中立或船員不可見)\n(P.S:戀人告密者技能失效)","Как только Стукач выполнит все задания, то никнейм у всех Предателей будет отображаться \nкрасным цветом, и наоборот, если количество ваших заданий будет близка к завершению, \nто рядом с никнеймом Стукача появится звёздочка★, которая будет видна Предателям.","Assim que você terminar suas tarefas, os nomes dos Impostores serão revelados em vermelho para você. \nQuando estiver com apenas uma tarefa faltando, um símbolo (★) será mostrado no seu nome e você estará exposto aos Impostores.","" +"LoversInfoLong","(Neutrals):\nLovers is an additional Neutrals role assigned to 2 random players on top of their defined roles.\nIf you and your lover both survive til the end, you both will win.\nIf one of you dies, the other will also die. If crewmates get a task victory, you both will lose.","(ニュートラル):\n全プレイヤーの中からランダムで2人に他の役職と重複して配役されるニュートラル。\n恋人が2人とも生き残っている状態で試合終了すれば勝利。\n恋人の片方が死んだときにもう片方も死ぬ。クルーのタスクが完了すると敗北。","恋人为两名玩家的组合。\n恋人存活到游戏结束则恋人独自获胜。\n船员阵营的恋人玩家没有任务。\n从恋人阵营玩家可以看到两名恋人玩家昵称旁有心形标志。\n恋人一同赴死。\n恋人中的一方在会议中被放逐时,另一方将死亡并变为不可以被报告的尸体。","(中立),\n遊戲開始系統會隨機抽取兩人當戀人,該職業可以和其他職業屬於同一個玩家身上\n戀人將獲勝在遊戲結束時如果兩個戀人都活著.\n但是當一個戀人死去,另一個戀人也會跟著死去\n如果船員完成所有任務,戀人即宣告失敗。","(Нейтралы):\nДополнительно с какой-либо ролью Любовники назначаются 2 случайным игрокам. \nЕсли оба Любовника останутся живы, то они выиграют. \nКогда умрёт хотя бы один Любовник, то моментально умрёт и второй. Они проиграют когда Члены Экипажа выполнят все задания.","(Neutros): \nOs Amantes, é um papel dado a 2 jogadores aleatórios, somado a sua classe já definida. \nSe você e seu amante sobreviverem até o final, vocês ganharão. \nCaso um dos dois morra, o outro também morre. Se os Tripulantes ganharem por tarefas, os dois perdem.","" +"SpeedBoosterInfoLong","Once you finish your tasks, a random living player will get a speed boost.","タスクを終わらせると、ランダムな生存者のスピードを上げる事ができる。","增速者完成所有任务后,场上任意一名存活玩家的速度将被加快。","加速器做完任務時可以讓隨機的活著玩家速度加快","Он может увеличить скорость случайных живых игроков, если он будет выполнять задания.","Assim que você finalizar suas tarefas, um jogador vivo aleatório irá receber um aumento de velocidade.","" +"DoctorInfoLong","You can see the cause of death for all players.\nIn addition, you can access vitals wherever you are while you still have battery.","プレイヤーの死因を知ることができる。\nまた、充電がある限りどこでもバイタルを確認できる。","医生可以查看生命监测装置并确认死亡玩家的死因。\n查看的时间长短与设备的充能数挂钩。","醫生可以知道每個玩家的死因\n還有醫生有一個跟科學家一樣的心電圖,它也需要充電。","Доктор может узнать причину смерти игрока. \nКроме того, он имеет портативные пульсы как у учёного, которые он может использовать в любом месте, пока у него не закончится зарядка.","Você pode ver a causa da morte de todos os jogadores. \nAdicionalmente, você tem acesso aos sinais vitais de todos enquanto tiver bateria.","" +"TrapperInfoLong","When you are killed, you immobilize your killer for a configurable amount of time.","キルされると、キルした人を数秒間移動不可にすることができる。(設定有)","陷阱师被击杀时,凶手一段时间内将不能移动。","如果設限者被殺,殺他的兇手將會被困在原地幾秒(可以設定)","После того как Охотника убьют, то его убийца будет обездвижен на несколько секунд. (время зависит от настроек)","Ao ser morto, imobilize o seu assassino por um certo período. (configurável)","" +"DictatorInfoLong","When you vote someone, the meeting will end on the spot and the player you voted will be ejected.\nThe moment you vote someone out, you will also die.\nWhen you vote for someone in a meeting, they forcibly break that meeting and exile the player they vote for.\nAfter exercising the force, the Dictators die just after meeting.","会議中に誰かに投票をすると、会議を強制終了させて投票先を吊る事ができる。\n投票したタイミングでディクテーターは死ぬ。","当独裁者在会议阶段投票给玩家后,会议会被强制结束并放逐其投票对象。\n该技能发动后独裁者将会死亡。","如果獨裁主義者在會議上投票給某位玩家,\n獨裁主義者將強制結束會議並無視場上票數將獨裁主義者投的那個人投出\n但是獨裁主義者將死於他投票的瞬間。","Если Диктатор проголосует за любого игрока во время собрания, он сможет принудительно завершить собрание и кикнуть игрока за которого он отдал голос \nТак же Диктатор умрёт после собрания когда он отдаст голос.","Quando votar em alguém, durante uma reunião, a reunião será encerrada e a pessoa em que você votou, será exilada. \nAo exilar alguém, você morre-rá.","" +"SeerInfoLong","Whenever someone dies, you will see a ""Kill Flash"".","プレイヤーが死亡するごとに、シーアはキルフラッシュを見ることができる。","每当玩家死亡时,灵媒将会看到击杀闪烁。","當有玩家死亡時,靈媒將看到閃光","Провидец видит ''Вспышку Убийства'' каждый раз, когда игроки умирают.","Quando alguém morre, você vê um ""clarão de abate"" (a sua tela piscará brevemente).","" +"TimeManagerInfoLong","The more tasks you do, the longer your meeting will last. When you die, the meeting time will be restored.","タスクを終わらせるごとに会議時間が延びる。死亡すると会議時間は元に戻る。","你做的任务越多,会议时间就会越长。在你死后,会议时间会复原。","時間大師做越多任務,那麼會議時間就會延長越多(直到上限)。如果時間大師死亡,那麼會議時間就會恢復原狀。","Мастер Времени может увеличить время встречи, увеличивается время по мере выполнения заданий. Но когда он умрет, время встречи будет сброшено по умолчанию.","Ao fazer tarefas você aumenta o tempo de reunião. Quando você morre, o tempo de reunião volta ao normal.","" "# ニュートラル役職" -"ArsonistInfoLong","(Neutrals):\nWhen you use the kill button, you douse your target in oil.\nAfter dousing all living players, you set everything ablaze and win by venting.","(ニュートラル):\nキルボタンを使おうとすると、ターゲットに油を塗れる。\nすべてのクルーに油を塗ったら船を燃やして勝利する。","(独立阵营):\n纵火犯可以通过对玩家点击击杀按钮并在跟随其数秒来完成涂油行为。\n当所有存活玩家都被纵火犯涂油后,纵火犯可以通过跳通风管来点火,并单独获得胜利。","(中立):\n當縱火犯嘗試使用殺人鍵,它將對他身邊最近的一個人澆上油,\n當他對所有玩家澆上油之後,縱火縱火犯點火即勝利。","(Нейтрал):\nПоджигатель может обливать игроков, нажав на кнопку 'Убить' и находясь рядом с игроком в течение нескольких секунд. \nПосле того как он обольёт всех живых игроков Поджигатель сможет запрыгнуть в вентиляцию, чтобы поджечь всех игроков, что приводит к победе Поджигателя.","(Neutros):\nAo apertar o botão de matar, você encharca o seu inimigo em óleo.\nApós ter encharcado todos os jogadores vivos, você coloca tudo em chamas e ganha entrando na ventilação." -"JesterInfoLong","(Neutrals):\nYou solo victory by getting voted out during a meeting.\nIf the game ends without you getting voted out, or if you are killed, you lose.","(ニュートラル):\n会議で追放された時に単独勝利できる。\n追放されず試合が終了orキルされると敗北。","(独立阵营):\n小丑被放逐则小丑单独游戏胜利。\n游戏结束时若小丑仍存活则小丑输掉游戏。","(中立):\n當小丑被丟出時即宣告小丑獲勝,\n但是小丑在遊戲結束時活下來或是在遊戲中被殺即宣告失敗","(Нейтрал):\nШут сможет выиграть игру, если за него проголосуют во время голосования и он будет изгнан.\nВ противном случае Шут проиграет.","(Neutros): \nVocê ganha sozinho ao ser exilado durante uma reunião. \nSe o jogo terminar com você não sendo votado, ou você for morto, você perde." -"TerroristInfoLong","(Neutrals):\nYou solo victory by getting killed or voted out AFTER you finish your tasks.","(ニュートラル):\n自身のタスクを全て完了させた状態で\nキル・追放された時に単独勝利する。","(独立阵营):\n恐怖分子完成所有任务后死亡,则恐怖分子单独获得胜利。","(中立):\n恐怖分子只要做完它的所有任務並且死亡即可獲勝","(Нейтрал):\nТеррорист побеждает в одиночку, если его убивают или изгоняют. \nНо только если все его задания будут выполнены.","(Neutros): \nVocê ganha sozinho se for morto ou exilado DEPOIS de finalizar suas tarefas." -"ExecutionerInfoLong","(Neutrals):\nYou are assigned a target who has a diamond mark only you can see next to their name.\nYou solo victory by having that target voted out. If your target is killed, your own role changes into something else.\nIf your target is Jester, you will win alongside Jester.","(ニュートラル):\nターゲットに対してこちらからのみ視認できるダイヤのマークがついている。\n投票でダイヤが付いている人を追放すれば単独勝利。ターゲットがキルされた場合は役職が変化する。\nターゲットがジェスターの場合は追加勝利する。","(独立阵营):\n游戏开始时处刑人会被分配到一个处刑目标,并在其昵称旁用菱形「♦」表示。\n若处刑目标被击杀,则处刑的职业会根据设置变为船员,小丑或投机者。\n如果处刑目标在会议中被放逐则处行者独自获得胜利。\n若小丑作为处刑目标被放逐时,小丑与处刑人共同获得胜利。","(中立):\n劊子手的目標會有一個鑽石符號,這個符號僅劊子手可見,\n如果他的目標被投出,他將獨自勝利。\n如果目標被殺,劊子手將轉職(可以設定).\n如果目標是小丑,那麼被丟出時,劊子手和小丑會一起獲勝。","(Нейтрал):\nПалач назначается цель с темно-фиолетовым значком «Ромба» рядом с именем игрока.\nОн побеждает когда его цель будет изгнана на голосовании.\nЕсли его цель умирает до того как он был изгнан, Палач меняет свою роль и становится Членом Экипажа, Шутом или Выжившим в зависимости от настроек игры.\nЕсли его цель является Шут, то Палач выиграет вместе с ним.","(Neutros): \nVocê recebe um alvo com um símbolo de diamante próximo ao seu nome, que só você pode ver. \n Você ganha sozinho se o seu alvo for exilado. Caso o alvo seja morto, a sua classe muda para algo diferente. \nSe o seu alvo é um Bobo, você ganha com ele." -"SchrodingerCatInfoLong","(Neutrals):\nWhen you are killed, you block the kill and join the attacker's team.","(ニュートラル):\nキルされると、キルを防いでキルをしてきた陣営に所属する。","(独立阵营):游戏开始时薛定谔的猫没有胜利条件。\n薛定谔的猫被击杀后,其会复活且加入凶手阵营,获得与凶手所在阵营相同的胜利条件。\n 注:1.被放逐的薛定谔的猫死亡并不再变更胜利条件。\n2.被术士击杀的薛定谔的猫死亡并不再变更胜利条件。\n3.除吸血鬼的击杀外,一切显示为玩家自杀的情况内薛定谔的猫死亡并不再变更胜利条件。","(中立陣營):\n當薛定諤的貓第一次被殺時,他將轉入殺他的那個陣營。\n(P.S:1.薛定諤的貓如果被丟出則陣營不更改(可以在設定中更改)\n2.薛定諤的貓如果被術士下咒對象咒死時則陣營不更改\n3.如果薛定諤的貓有戀人且戀人死亡受到戀人共死影響自殺則陣營不更改)","(Нейтрал):\nКогда Пленника пытаются убить, он предотвращает убийство и переходит к команде убийцы.","(Neutros): \nQuando morto, você bloqueia a morte e se junta ao time do seu assassino." -"OpportunistInfoLong","(Neutrals):\nSo long as you are alive at the end of the game, you will win alongside whoever the victor is.","(ニュートラル):\n試合終了時に生存していれば追加勝利となる。","(独立阵营):\n若投机者在游戏结束时存活,则投机者跟随获胜玩家一同获得胜利。","(中立):\n如果投機主義者活到最後,他將跟著遊戲結束獲勝的陣營一起獲勝。","(Нейтрал):\nВыживший выигрывает игру с любыми другими ролями, но только если он выжил.","(Neutros): \nContanto que você esteja vivo no final do jogo, você dividirá a vitória ao lado do vencedor." -"EgoistInfoLong","(Neutrals):\nWhen all impostors are dead, you solo victory by fulfilling impostor victory conditions.\nYou and the impostors know who each other are.","(ニュートラル):\n味方がすべて死んだ状態でインポスターが勝つと単独勝利する。\nインポスターは誰がエゴイストか分かる。エゴイストも、誰がインポスターか分かる。","(独立阵营):\n原则上野心家属于内鬼阵营。野心家与内鬼阵营玩家互认但不可以击杀对方。\n当其他内鬼阵营玩家全部死亡后,若野心家存活且内鬼阵营达成胜利条件,则野心家单独获得胜利。","(中立陣營):\n如果所有的偽裝者都死亡,且利己主義者存活,利己主義者將獨自獲勝。\n(偽裝者和利己主義者互相知道對方但是不可以互刀對方)","(Нейтрал):\nПосле того, как все Предатели умрут то Эгоист побеждает вместо Предателей. \nПредатели и Эгоист видят друг друга.","(Neutros): \nQuando todos os Impostores morrerem, você ganha sozinho assumindo o papel de Impostor. \nVocê e os Impostores sabem quem são uns aos outros." -"JackalInfoLong","(Neutrals):\nJackal can kill all Crewmates, Impostors and Neutrals.\nTeam Jackal wins when living Jackal outnumbers living Crewmates and when there are no Impostors alive.","(ニュートラル):\nジャッカルはすべてのプレイヤーを殺すことができる。\nインポスターが残っておらず、生き残っているジャッカルの人数がクルーと同じかそれ以上でジャッカルが勝利する。","(独立阵营):\n豺狼需要击杀所有人。\n存活的玩家只剩豺狼和一名其他船员时,豺狼获得胜利。","(中立):\n豺狼需要殺死所有人來獲得勝利,\n如果場上存活的玩家只剩下豺狼和另一名不帶刀職業時,豺狼將獲勝。","(Нейтрал):\nШакал может убить всех Членов Экипажа, Нейтралов и Предателей тоже. \nШакал может победить, когда в живых остался только 1 Шакал и 1 Член Экипажа.","(Neutros): \nVocê pode matar todos os jogadores. \nGanhe quando não existirem mais Impostores, e o número de Chacais for maior que o de Tripulantes." -"PlagueDoctorInfoLong","(Neutrals):\nThe Plague Doctor's goal is to infect every living player.\nThey start by choosing one player to infect, after which anyone who spends a set\namount of time in range of the infected player becomes infected themselves.\nInfection progress is cumulative, and does not reset with distance or after meetings.","(第三陣営):\nペスト医師ははすべてのプレイヤーに感染を広げることが目的。\nキルで最初の感染者を選ぶ。接触感染していきすべてのプレイヤーに感染させたら勝利。会議では感染はリセットされない","","","(Нейтрал):\nЦель Чумного Доктора - заразить всех живых игроков.\nОн выбирает одного игрока для заражения с помощью кнопки 'Убить', после чего игрок который будет в радиусе зараженного игрока в течении X времени, станет зараженным.\nПрогресс заражения не сбрасывается после встреч и т.д.","" +"ArsonistInfoLong","When you use the kill button, you douse your target in oil.\nAfter dousing all living players, you set everything ablaze and win by venting.","キルボタンを使おうとすると、ターゲットに油を塗れる。\nすべてのクルーに油を塗ったら船を燃やして勝利する。","纵火犯可以通过对玩家点击击杀按钮并在跟随其数秒来完成涂油行为。\n当所有存活玩家都被纵火犯涂油后,纵火犯可以通过跳通风管来点火,并单独获得胜利。","當縱火犯嘗試使用殺人鍵,它將對他身邊最近的一個人澆上油,\n當他對所有玩家澆上油之後,縱火縱火犯點火即勝利。","Поджигатель может обливать игроков, нажав на кнопку 'Убить' и находясь рядом с игроком в течение нескольких секунд. \nПосле того как он обольёт всех живых игроков Поджигатель сможет запрыгнуть в вентиляцию, чтобы поджечь всех игроков, что приводит к победе Поджигателя.","Ao apertar o botão de matar, você encharca o seu inimigo em óleo.\nApós ter encharcado todos os jogadores vivos, você coloca tudo em chamas e ganha entrando na ventilação.","" +"JesterInfoLong","You solo victory by getting voted out during a meeting.\nIf the game ends without you getting voted out, or if you are killed, you lose.","会議で追放された時に単独勝利できる。\n追放されず試合が終了orキルされると敗北。","小丑被放逐则小丑单独游戏胜利。\n游戏结束时若小丑仍存活则小丑输掉游戏。","當小丑被丟出時即宣告小丑獲勝,\n但是小丑在遊戲結束時活下來或是在遊戲中被殺即宣告失敗","Шут сможет выиграть игру, если за него проголосуют во время голосования и он будет изгнан.\nВ противном случае Шут проиграет.","Você ganha sozinho ao ser exilado durante uma reunião. \nSe o jogo terminar com você não sendo votado, ou você for morto, você perde.","" +"TerroristInfoLong","You solo victory by getting killed or voted out AFTER you finish your tasks.","自身のタスクを全て完了させた状態で\nキル・追放された時に単独勝利する。","恐怖分子完成所有任务后死亡,则恐怖分子单独获得胜利。","恐怖分子只要做完它的所有任務並且死亡即可獲勝","Террорист побеждает в одиночку, если его убивают или изгоняют. \nНо только если все его задания будут выполнены.","Você ganha sozinho se for morto ou exilado DEPOIS de finalizar suas tarefas.","" +"ExecutionerInfoLong","You are assigned a target who has a diamond mark only you can see next to their name.\nYou solo victory by having that target voted out. If your target is killed, your own role changes into something else.\nIf your target is Jester, you will win alongside Jester.","ターゲットに対してこちらからのみ視認できるダイヤのマークがついている。\n投票でダイヤが付いている人を追放すれば単独勝利。ターゲットがキルされた場合は役職が変化する。\nターゲットがジェスターの場合は追加勝利する。","游戏开始时处刑人会被分配到一个处刑目标,并在其昵称旁用菱形「♦」表示。\n若处刑目标被击杀,则处刑的职业会根据设置变为船员,小丑或投机者。\n如果处刑目标在会议中被放逐则处行者独自获得胜利。\n若小丑作为处刑目标被放逐时,小丑与处刑人共同获得胜利。","劊子手的目標會有一個鑽石符號,這個符號僅劊子手可見,\n如果他的目標被投出,他將獨自勝利。\n如果目標被殺,劊子手將轉職(可以設定).\n如果目標是小丑,那麼被丟出時,劊子手和小丑會一起獲勝。","Палач назначается цель с темно-фиолетовым значком «Ромба» рядом с именем игрока.\nОн побеждает когда его цель будет изгнана на голосовании.\nЕсли его цель умирает до того как он был изгнан, Палач меняет свою роль и становится Членом Экипажа, Шутом или Выжившим в зависимости от настроек игры.\nЕсли его цель является Шут, то Палач выиграет вместе с ним.","Você recebe um alvo com um símbolo de diamante próximo ao seu nome, que só você pode ver. \n Você ganha sozinho se o seu alvo for exilado. Caso o alvo seja morto, a sua classe muda para algo diferente. \nSe o seu alvo é um Bobo, você ganha com ele.","" +"SchrodingerCatInfoLong","When you are killed, you block the kill and join the attacker's team.","キルされると、キルを防いでキルをしてきた陣営に所属する。","游戏开始时薛定谔的猫没有胜利条件。\n薛定谔的猫被击杀后,其会复活且加入凶手阵营,获得与凶手所在阵营相同的胜利条件。\n 注:1.被放逐的薛定谔的猫死亡并不再变更胜利条件。\n2.被术士击杀的薛定谔的猫死亡并不再变更胜利条件。\n3.除吸血鬼的击杀外,一切显示为玩家自杀的情况内薛定谔的猫死亡并不再变更胜利条件。","當薛定諤的貓第一次被殺時,他將轉入殺他的那個陣營。\n(P.S:1.薛定諤的貓如果被丟出則陣營不更改(可以在設定中更改)\n2.薛定諤的貓如果被術士下咒對象咒死時則陣營不更改\n3.如果薛定諤的貓有戀人且戀人死亡受到戀人共死影響自殺則陣營不更改)","Когда Пленника пытаются убить, он предотвращает убийство и переходит к команде убийцы.","Quando morto, você bloqueia a morte e se junta ao time do seu assassino.","" +"OpportunistInfoLong","So long as you are alive at the end of the game, you will win alongside whoever the victor is.","試合終了時に生存していれば追加勝利となる。","若投机者在游戏结束时存活,则投机者跟随获胜玩家一同获得胜利。","如果投機主義者活到最後,他將跟著遊戲結束獲勝的陣營一起獲勝。","Выживший выигрывает игру с любыми другими ролями, но только если он выжил.","Contanto que você esteja vivo no final do jogo, você dividirá a vitória ao lado do vencedor.","" +"EgoistInfoLong","When all impostors are dead, you solo victory by fulfilling impostor victory conditions.\nYou and the impostors know who each other are.","味方がすべて死んだ状態でインポスターが勝つと単独勝利する。\nインポスターは誰がエゴイストか分かる。エゴイストも、誰がインポスターか分かる。","原则上野心家属于内鬼阵营。野心家与内鬼阵营玩家互认但不可以击杀对方。\n当其他内鬼阵营玩家全部死亡后,若野心家存活且内鬼阵营达成胜利条件,则野心家单独获得胜利。","如果所有的偽裝者都死亡,且利己主義者存活,利己主義者將獨自獲勝。\n(偽裝者和利己主義者互相知道對方但是不可以互刀對方)","После того, как все Предатели умрут то Эгоист побеждает вместо Предателей. \nПредатели и Эгоист видят друг друга.","Quando todos os Impostores morrerem, você ganha sozinho assumindo o papel de Impostor. \nVocê e os Impostores sabem quem são uns aos outros.","" +"JackalInfoLong","Jackal can kill all Crewmates, Impostors and Neutrals.\nTeam Jackal wins when living Jackal outnumbers living Crewmates and when there are no Impostors alive.","ジャッカルはすべてのプレイヤーを殺すことができる。\nインポスターが残っておらず、生き残っているジャッカルの人数がクルーと同じかそれ以上でジャッカルが勝利する。","豺狼需要击杀所有人。\n存活的玩家只剩豺狼和一名其他船员时,豺狼获得胜利。","豺狼需要殺死所有人來獲得勝利,\n如果場上存活的玩家只剩下豺狼和另一名不帶刀職業時,豺狼將獲勝。","Шакал может убить всех Членов Экипажа, Нейтралов и Предателей тоже. \nШакал может победить, когда в живых остался только 1 Шакал и 1 Член Экипажа.","Você pode matar todos os jogadores. \nGanhe quando não existirem mais Impostores, e o número de Guarás for maior que o de Tripulantes.","" +"PlagueDoctorInfoLong","The Plague Doctor's goal is to infect every living player.\nThey start by choosing one player to infect, after which anyone who spends a set\namount of time in range of the infected player becomes infected themselves.\nInfection progress is cumulative, and does not reset with distance or after meetings.","ペスト医師ははすべてのプレイヤーに感染を広げることが目的。\nキルで最初の感染者を選ぶ。接触感染していきすべてのプレイヤーに感染させたら勝利。会議では感染はリセットされない","瘟疫医生的目标是感染所有玩家。\n使用击杀键选择第一个要感染的人。瘟疫通过接触传染,并且不会因为会议打断进度。","","Цель Чумного Доктора - заразить всех живых игроков.\nОн выбирает одного игрока для заражения с помощью кнопки 'Убить', после чего игрок который будет в радиусе зараженного игрока в течении X времени, станет зараженным.\nПрогресс заражения не сбрасывается после встреч и т.д.","O objetivo do Médico da Peste é infectar todos os jogadores vivos.\nEles começam escolhendo um jogador para infectar e, depois disso, qualquer pessoa que passar um período de tempo definido próxima do jogador infectado será infectada também.\nO progresso da infecção é cumulativo e não é reiniciado com a distância ou após reuniões.","" "# HideAndSeek" -"HASFoxInfoLong","(HideAndSeek):\nThey win the game with other Roles (except Troll) only if they are alive at the game end.","(かくれんぼ):\nトロールを除くいずれかの陣営が勝利したときに生き残っていれば、勝利した陣営に追加で勝利することができる。","(躲猫猫):\n狐狸活到最后便与获胜阵营一同获胜。","(躲貓貓):\n除了其他的中立陣營以外,只要狐妖活下來即可獲勝\n他們將跟著遊戲結束的獲勝陣營一起獲勝。","(Прятки):\nЕсли какая-либо роль, кроме Тролля побеждает и выживает, то победившая роль может одержать дополнительную победу.","(Esconde-Esconde): \nEles ganham adicionalmente com qualquer classe (exceto Troll), se estiverem vivos no final." -"HASTrollInfoLong","(Hide and Seek):\nYou solo victory when you are killed. In this case, even if the Fox is alive, they will lose.","(かくれんぼ):\n鬼にキルされたときに単独勝利となる。この場合、狐が生き残っていても狐は敗北となる。","(躲猫猫):\n若猎人被抓,则猎人单独获胜。\n在这种情况下,即使狐狸未被抓,狐狸也会输掉游戏。","(躲貓貓):\n當誘捕者被偽裝者殺死,被殺的誘捕者就宣告獲勝\n在這個例子中,狐妖即使活著也宣告失敗。","(Прятки):\nТролль побеждает в одиночку если его убивают.","(Esconde-Esconde): \nVocê ganha sozinho se for morto. Neste caso, mesmo que a Rapoza esteja viva, ela perderá." +"HASFoxInfoLong","(HideAndSeek):\nThey win the game with other Roles (except Troll) only if they are alive at the game end.","(かくれんぼ):\nトロールを除くいずれかの陣営が勝利したときに生き残っていれば、勝利した陣営に追加で勝利することができる。","(躲猫猫):\n狐狸活到最后便与获胜阵营一同获胜。","(躲貓貓):\n除了其他的中立陣營以外,只要狐妖活下來即可獲勝\n他們將跟著遊戲結束的獲勝陣營一起獲勝。","(Прятки):\nЕсли какая-либо роль, кроме Тролля побеждает и выживает, то победившая роль может одержать дополнительную победу.","(Esconde-Esconde): \nEles ganham adicionalmente com qualquer classe (exceto Troll), se estiverem vivos no final.","" +"HASTrollInfoLong","(Hide and Seek):\nYou solo victory when you are killed. In this case, even if the Fox is alive, they will lose.","(かくれんぼ):\n鬼にキルされたときに単独勝利となる。この場合、狐が生き残っていても狐は敗北となる。","(躲猫猫):\n若猎人被抓,则猎人单独获胜。\n在这种情况下,即使狐狸未被抓,狐狸也会输掉游戏。","(躲貓貓):\n當誘捕者被偽裝者殺死,被殺的誘捕者就宣告獲勝\n在這個例子中,狐妖即使活著也宣告失敗。","(Прятки):\nТролль побеждает в одиночку если его убивают.","(Esconde-Esconde): \nVocê ganha sozinho se for morto. Neste caso, mesmo que a Rapoza esteja viva, ela perderá.","" "# GM" -"GMInfoLong","(None):\nThe GM (Game Master) is an observer role.\nTheir presence has no effect on the game, and all players know who the GM is. The GM role will be assigned to the host, who will automatically become a ghost at the start of the game.","(なし):\nGM(ゲームマスター)はオブザーバー役職である。\nGMはゲーム自体には何の影響も与えず、すべてのプレイヤーは誰がGMであるかがわかる。必ずホストに割り当てられ、始めから幽霊状態になっている。","(无阵营):\nGM(管理员)是房主专属的观察者职业。从游戏开始时便以幽灵状态在旁边观战。 \n该职业本身对游戏没有影响,且会议中对所有玩家可见。 ","(不屬於任何陣營):\nGM(遊戲大師)是遊戲的觀察者, \nGM對遊戲進行過程沒有影響,所有玩家都知道GM是誰,該職業絕對會分配給房主。\nGM(遊戲大師)從開始遊戲時就處於死亡狀態。","(Никто):\nМастер Игры — это роль, которая наблюдает за игрой в роле призрака. \nОн не влияет на игру, и все игроки всегда знают кто Мастер Игры. Всегда назначается только хосту лобби и становится призраком в самом начале игры.","(Nenhum): \nO GM (Mestre do Jogo) é uma classe de observação. \nA sua presença não afeta o jogo, e todos os jogadores sabem quem é o GM. A classe de GM será dada ao anfitrião, que será um fantasma desde o começo do jogo." +"GMInfoLong","(None):\nThe GM (Game Master) is an observer role.\nTheir presence has no effect on the game, and all players know who the GM is. The GM role will be assigned to the host, who will automatically become a ghost at the start of the game.","(なし):\nGM(ゲームマスター)はオブザーバー役職である。\nGMはゲーム自体には何の影響も与えず、すべてのプレイヤーは誰がGMであるかがわかる。必ずホストに割り当てられ、始めから幽霊状態になっている。","(无阵营):\nGM(管理员)是房主专属的观察者职业。从游戏开始时便以幽灵状态在旁边观战。 \n该职业本身对游戏没有影响,且会议中对所有玩家可见。 ","(不屬於任何陣營):\nGM(遊戲大師)是遊戲的觀察者, \nGM對遊戲進行過程沒有影響,所有玩家都知道GM是誰,該職業絕對會分配給房主。\nGM(遊戲大師)從開始遊戲時就處於死亡狀態。","(Никто):\nМастер Игры — это роль, которая наблюдает за игрой в роле призрака. \nОн не влияет на игру, и все игроки всегда знают кто Мастер Игры. Всегда назначается только хосту лобби и становится призраком в самом начале игры.","(Nenhum): \nO Mestre de Jogo é uma classe de observação. \nA sua presença não afeta o jogo, e todos os jogadores sabem quem é o MJ. A classe de MJ será dada ao anfitrião, que será um fantasma desde o começo do jogo.","" "# 属性" -"LastImpostorInfoLong","(Add-ons):\nAn Add-on granted to the last Impostor remaining.\nKill cooldown is reduced according to this setting.\nNot granted to Bounty Hunters, Serial Killers, or Vampires.","(属性):\n最後のインポスターに付与される属性。\nキルクールが設定した時間まで短くなる。\nバウンティハンター、シリアルキラー、ヴァンパイアには付与されない。","(效果):\n这个效果在内鬼仅剩一人时赋予该内鬼。\n使其击杀冷却缩短。\n该效果对赏金猎人、嗜血杀手以及吸血鬼不适用。","(附加效果):\n該效果給予在場上的最後一位偽裝者,擁有該效果的偽裝者刀人冷卻時間會變短,該效果不會給予:\n賞金獵人, 連環殺手或吸血鬼 (可以設定冷卻)","(Атрибут):\nАтрибут, присваивается последнему Предателю. \nВремя отката убийства становится меньше, чем обычно. \nНе назначается Охотнику за головами, Серийному убийце или Вампиру.","(Atributos): \nUm Atributo dado ao último Impostor. \nO tempo de recarga (abate) é reduzido segundo a configuração. \nNão é dado para Caçadores de Recompensas, Serial Killers ou Vampiros." -"WatcherInfoLong","(Add-ons):\nYou can see everyone's votes even if anonymous voting is on.","(属性):\n全員の投票先を見ることができる。","(效果):\n会议时窥视者可以看到所有人的投票。","(附加效果):\n會議時觀察者可以看到所有人的投票。","(Атрибут):\nНаблюдатель может видеть все цвета голосов несмотря на анонимное голосование.","(Atributos):\nVocê consegue ver os votos de todos, mesmo que os votos anônimos estejam ligados." -"WorkhorseInfoLong","(Add-ons):\nAn Add-on granted to the first living Crewmate finishing all the tasks.\nYou are assigned additional tasks necessary for the tasks win.\nNot granted to roles with no tasks or with abilities triggered by finishing tasks.","(属性):\n最初に生きてタスクを終えたクルーに付与される属性。\n追加タスクが割り当てられる。\nクルーメイト以外にも割り当てる設定でもタスクが無い、タスク完了で能力が発動する役職には付与されない。","(附加效果):\n该附加效果赋予给第一个完成所有任务并存活的船员。\n你需要完成额外的任务来达成任务胜利。\n此效果不会赋予那些没有任务或者通过完成任务才能使用能力的诸多职业。","(屬性):\n此效果會被賦予在第一個完成任務且存活的船員。\n實習生必須做完附加任務來獲勝,\n此效果不會賦予在那些需要通過完成任務來觸發技能的職業上。","(Атрибут):\nДополнительные задания присваиваются первому живому Члену Экипажа, который выполнит все задания. \nЕму назначаются дополнительные задания, необходимые для победы с помощью заданий. \nНе может присваиваться ролям которые не имеют заданий, или ролям со способностями которые активируются после выполнения заданий.","(Atributos):\nUm atributo dado ao primeiro tripulante que finalizar todas as tarefas.\nVocê recebe tarefas adicionais necessárias para vencer. \nNão é dado a classes quem não têm tarefas, ou com habilidades ativadas ao finalizar todas as tarefas." +"LastImpostorInfoLong","(Add-ons):\nAn Add-on granted to the last Impostor remaining.\nKill cooldown is reduced according to this setting.\nNot granted to Bounty Hunters, Serial Killers, or Vampires.","(属性):\n最後のインポスターに付与される属性。\nキルクールが設定した時間まで短くなる。\nバウンティハンター、シリアルキラー、ヴァンパイアには付与されない。","(效果):\n这个效果在内鬼仅剩一人时赋予该内鬼。\n使其击杀冷却缩短。\n该效果对赏金猎人、嗜血杀手以及吸血鬼不适用。","(附加效果):\n該效果給予在場上的最後一位偽裝者,擁有該效果的偽裝者刀人冷卻時間會變短,該效果不會給予:\n賞金獵人, 連環殺手或吸血鬼 (可以設定冷卻)","(Атрибут):\nАтрибут, присваивается последнему Предателю. \nВремя отката убийства становится меньше, чем обычно. \nНе назначается Охотнику за головами, Серийному убийце или Вампиру.","(Atributos): \nUm Atributo dado ao último Impostor. \nO tempo de recarga (abate) é reduzido segundo a configuração. \nNão é dado para Caçadores de Recompensas, Serial Killers ou Vampiros.","" +"WatcherInfoLong","(Add-ons):\nYou can see everyone's votes even if anonymous voting is on.","(属性):\n全員の投票先を見ることができる。","(效果):\n会议时窥视者可以看到所有人的投票。","(附加效果):\n會議時觀察者可以看到所有人的投票。","(Атрибут):\nНаблюдатель может видеть все цвета голосов несмотря на анонимное голосование.","(Atributos):\nVocê consegue ver os votos de todos, mesmo que os votos anônimos estejam ligados.","" +"WorkhorseInfoLong","(Add-ons):\nAn Add-on granted to the first living Crewmate finishing all the tasks.\nYou are assigned additional tasks necessary for the tasks win.\nNot granted to roles with no tasks or with abilities triggered by finishing tasks.","(属性):\n最初に生きてタスクを終えたクルーに付与される属性。\n追加タスクが割り当てられる。\nクルーメイト以外にも割り当てる設定でもタスクが無い、タスク完了で能力が発動する役職には付与されない。","(附加效果):\n该附加效果赋予给第一个完成所有任务并存活的船员。\n你需要完成额外的任务来达成任务胜利。\n此效果不会赋予那些没有任务或者通过完成任务才能使用能力的诸多职业。","(屬性):\n此效果會被賦予在第一個完成任務且存活的船員。\n實習生必須做完附加任務來獲勝,\n此效果不會賦予在那些需要通過完成任務來觸發技能的職業上。","(Атрибут):\nДополнительные задания присваиваются первому живому Члену Экипажа, который выполнит все задания. \nЕму назначаются дополнительные задания, необходимые для победы с помощью заданий. \nНе может присваиваться ролям которые не имеют заданий, или ролям со способностями которые активируются после выполнения заданий.","(Atributos):\nUm atributo dado ao primeiro tripulante que finalizar todas as tarefas.\nVocê recebe tarefas adicionais necessárias para vencer. \nNão é dado a classes quem não têm tarefas, ou com habilidades ativadas ao finalizar todas as tarefas.","" + +"# ヘルプ" +"Basis","Basis","判定","基于","","Вердикт","","" "#モードオプション" -"HideAndSeek","Hide and Seek","かくれんぼ","躲猫猫","躲貓貓","Прятки","Esconde-Esconde" -"NoGameEnd","NoGameEnd","ゲームを終了しない","测试模式","遊戲不會結束模式","Игра не закончится","Jogo Sem Fim" -"SyncButtonMode","SyncButtonMode","ボタン回数同期モード","所有船员共享会议次数","全場拍桌數同步模式","Общая кнопка встречи","Modo de Botão Sincronizado" -"RandomMapsMode","RandomMapsMode","ランダムマップモード","随机地图模式","隨機地圖模式","Случайная карта","Modo de Mapas Aleatórios" -"SyncedButtonCount","Max Number of Buttons Allowed","合計ボタン使用可能回数","紧急会议可用次数","全場最大拍桌數量","Максимальное количество нажатий на Кнопку","Número Máximo de Usos do Botão" -"AddedTheSkeld","Include TheSkeld","TheSkeldを追加","添加骷髅舰地图","將The Skeld地圖列入選項","Добавить TheSkeld","Incluir TheSkeld" -"AddedMIRAHQ","Include MIRAHQ","MIRAHQを追加","添加米拉总部地图","將MIRA HQ地圖列入選項","Добавить MiraHQ","Incluir MIRAHQ" -"AddedPolus","Include Polus","Polusを追加","添加波鲁斯地图","將Polus地圖列入選項","Добавить Polus","Incluir Polus" -"AddedTheAirShip","Include TheAirShip","TheAirShipを追加","添加飞艇地图","將The AirShip列入選項","Добавить TheAirship","Incluir TheAirShip" -"AllAliveMeeting","Meeting When No One is Dead","全員生存時の会議","全员存活时的会议设定","全員存活會議","Все живые на собрании","Reunião Com Todos Vivos" -"AllAliveMeetingTime","Meeting Time When No One is Dead","全員生存時の会議時間","全员存活时的会议时间","全員存活會議時間","Время когда все живы на собрании","Tempo de Reunião Com Todos Vivos" -"AdditionalEmergencyCooldown","Additional Emergency Cooldown","追加の緊急ボタンクールダウン","附加紧急会议冷却时间","附加緊急會議冷卻時間","Дополнительный откат Собраний","Tempo de Emergência Adicional" -"AdditionalEmergencyCooldownThreshold","Applied Living Player Count","適用する生存人数","适用存活人数","適用於幾個存活人數","Применяемый Порог Выживания","Número de Vivos Para Aplicar" -"AdditionalEmergencyCooldownTime","Additional Cooldown","追加するクールダウン","附加冷却时间","附加冷卻時間","Дополнительный откат","Tempo Adicional" -"LadderDeath","Fall From Ladders","ハシゴから転落","从梯子上下滑时有可能摔死","從梯子上下滑時可能會摔死","Падение с Лестницы","Queda de Escadas" -"LadderDeathChance","Fall To Death Chance","転落する確率","从梯子上摔死的概率","從梯子上下滑摔死機率","Шанс разбиться насмерть","Chance de Queda" -"DisableTasks","Disable Tasks","タスクを無効化する","禁用某些任务","禁用某些特定的任務","Отключить Задания","Desativar Tarefas" -"DisableSwipeCardTask","Disable SwipeCard Tasks","カードタスク","禁用刷卡任务","禁用刷卡任務","Отключить Задание ''Провести Карту''","Desativar Tarefas de Passar Cartão" -"DisableSubmitScanTask","Disable SubmitScan Tasks","医務室のスキャンタスク","禁用扫描任务","禁用掃描任務","Отключить Задание ''Сканирование''","Desativar Tarefas de Raio-X" -"DisableUnlockSafeTask","Disable UnlockSafe Tasks","金庫タスク","禁用解除歧管任务","禁用保險櫃任務","Отключить Задание ''Открытие Сейфа''","Desativar Tarefas de Abrir Cofre" -"DisableUploadDataTask","Disable UploadData Tasks","ダウンロードタスク","禁用上传数据任务","禁用上傳任務","Отключить Задание ''Загрузка Данных''","Desativar Tarefas de Baixar Dados" -"DisableStartReactorTask","Disable StartReactor Tasks","原子炉起動タスク","禁用启动反应堆任务","禁用啟動反應堆任務","Отключить Задание ''Запустить Реактор''","Desativar Tarefas de Acionar Reator" -"DisableResetBreakerTask","Disable ResetBreaker Tasks","ブレーカーリセットタスク","禁用重置反应堆任务","禁用重置斷路器任務","Отключить Задание ''Рычаги(Airship)''","Desativar Reiniciar Disjuntores" -"SuffixMode","Suffix","名前の二行目","附加标签","名字標籤","Суффикс","Sufixo" -"SuffixMode.None","None","なし","无","無","Ничего","Nenhum" -"SuffixMode.Version","Version","バージョン","版本","版本","Версия","Versão" -"SuffixMode.Streaming","Streaming","配信中","直播中","直播中","Стримит","Ao Vivo" -"SuffixMode.Recording","Recording","録画中","录制中","錄影中","Записывает","Gravando" -"SuffixMode.RoomHost","Room Host","部屋主","房主","房主","Хост Румы","Anfitrião" -"SuffixMode.OriginalName","OriginalName","元の名前","玩家名称","玩家原名稱","Настоящий Никнейм","Nome Original" -"ColorNameMode","Color Name Mode","色名前モード","显示颜色名称","名字將被顏色名稱替換","Режим: Никнейм соответствует цвету","Modo Nomes de Cores" -"FixFirstKillCooldown","Normalize First Kill Cooldown","初期スポーン時のクールダウン修正","修正首刀冷却时间","修正開場時的殺人冷卻時間","Нормализовать откат убийства в начале Игры","Normalizar Tempo de Recarga do Primeiro Abate" -"GhostCanSeeOtherRoles","Ghosts Can See Other Roles","幽霊が他人の役職を見ることができる","幽灵可见他人职业","幽靈可以看見所有玩家職業","Призраки могут видеть все Роли","Fantasmas Podem Ver Outros Papéis" -"GhostCanSeeOtherTasks","Ghosts Can See Other Tasks","幽霊が他人のタスク進捗を見ることができる","幽灵可见他人任务进度","","Призраки могут видеть прогресс заданий других игроков","Fantasmas Podem Ver Tarefas de Outros" -"GhostCanSeeOtherVotes","Ghosts Can See Other Votes","幽霊が他人の投票先を見ることができる","幽灵可见投票情况","幽靈可以看見所有玩家的投票","Призраки могут видеть цвета Голосов","Fantasmas Podem Ver Outros Votos" -"GhostCanSeeDeathReason","Ghost Can See Cause Of Death","幽霊が死因を見ることができる","幽灵可以看见死因","幽靈可以看見死因","Призраки могут видеть Причины Смерти","Fantasmas Podem Ver Causa da Morte" -"GhostIgnoreTasks","Ghosts Exempt From Tasks","死人のタスクを免除する","幽灵无视任务","幽靈可以免除任務","Призраки игнорируют Задания","Fantasmas Isentos de Tarefas" -"DisableTaskWin","Disable Task Win","タスク勝利を無効化","禁用任务胜利","禁用任務勝利","Отключить победу по Заданиям","Desativar Vitória por Tarefas" -"HideGameSettings","Hide Game Settings","ゲーム設定を隠す","隐藏游戏设置","隱藏遊戲設定","Скрыть настройки Игры","Esconder Configurações" -"RoleOptions","Role Options","役職設定","职业设置","職業設定","Настройка Ролей","Opções de Classe" -"ModeOptions","Mode Options","モード設定","模组设置","模式設定","Настройка Режима","Opções de Modos" -"AutoDisplayLastResult","Auto Display Last Result","自動的に試合結果を表示","自动显示最终结果","自動顯示上一回合結果","Отображать результат последней игры в чате","Mostrar Ultimo Resultado" -"AutoDisplayKillLog","Auto Display KillLog","自動的にキルログを表示","自动显示击杀日志","自動顯示擊殺紀錄","Отображать историю убийств в чате","Mostrar Log de Mortes Automaticamente" -"VoteMode","Voting Mode","投票モード","投票相关设定","投票設定","Режим Голосования","Modo de Votação" -"WhenSkipVote","When Skip Vote","スキップ時","跳过投票相当于投给自己","當跳過投票時","Когда пропускаете Голосование","Quando Pular Voto" -"WhenSkipVoteIgnoreFirstMeeting","Ignore First Meeting","初回会議を除く","忽略首次会议","忽略首次會議","Игнорировать первое собрание","Ignorar Primeira Reunião" -"WhenSkipVoteIgnoreNoDeadBody","Ignore When No DeadBody","死体がない時を除く","无尸体时忽略","沒有屍體時無法選擇","Игнорировать когда Нет Трупов","Ignorar Se Não Tiver Corpo" -"WhenSkipVoteIgnoreEmergency","Ignore When Emergency Button","緊急ボタンを除く","忽略紧急会议按钮","緊急會議中無法選擇","Игнорировать кнопку собраний","Ignorar Se Tiver Botão de Emergência" -"WhenNonVote","When No Votes","無投票時","不投票相当于投给自己","沒有投票時","Когда никто не Голосовал","Quando Sem Votos" -"Default","Default","デフォルト","默认","預設","Обычное","Padrão" -"Suicide","Suicide","自殺","自杀","自殺","Самоубийство","Suicídio" -"SelfVote","Self Vote","自投票","自票","投自己","Само Голос","Votar Em Si" -"Skip","Skip","スキップ","跳过","跳過","Пропуск","Pular" -"WhenTie","When Tied Vote","同数投票時","平票时","平票時","Когда ничья","Quando Empatar Votos" -"TieMode.Default","Default","デフォルト","默认","預設","Обычное","Padrão" -"TieMode.All","Eject All","全員追放","全体放逐","全員放逐","Изгнать всех","Exilar Todos" -"TieMode.Random","Eject Random","ランダムに追放","随机放逐","隨機放逐","Изгнать случайно","Exilar Aleatório" -"DisableDevices","Disable Devices","デバイスを無効化","禁用设备","禁用裝置","Отключить устройства","Desativar Dispositivos" -"CamerasTimer","Cameras Timer","カメラの時間制限(全マップ)","","","","" -"CamerasMaxTimer","Cameras Max Timer","使用可能時間","","","","" -"DisableSkeldDevices","Disable Skeld Devices","スケルドのデバイス無効化","禁用骷髅舰设备","禁用The Skeld中的設備","Отключить устройства на TheSkeld","Desativar Dispositivos de Skeld" -"DisableMiraHQDevices","Disable MiraHQ Device","ミラHQのデバイス無効化","禁用米拉总部设备","禁用Mira HQ中的設備","Отключить устройства на MiraHQ","Desativar Dispositivos de MiraHQ" -"DisablePolusDevices","Disable Polus Device","ポーラスのデバイス無効化","禁用波鲁斯设备","禁用Polus中的設備","Отключить устройства на Polus","Desativar Dispositivos de Polus" -"DisableAirshipDevices","Disable Airship Device","エアシップのデバイス無効化","禁用飞艇设备","禁用The Airship中的設備","Отключить устройства на Airship","Desativar Dispositivos de Airship" -"DisableSkeldAdmin","Disable Admin","アドミン無効化","禁用管理室地图","禁用管理室地圖","Отключить Стол Администратора","Desativar Admin" -"DisableMiraHQAdmin","Disable Admin","アドミン無効化","禁用管理室地图","禁用管理室地圖","Отключить Стол Администратора","Desativar Admin" -"DisablePolusAdmin","Disable Admin","アドミン無効化","禁用管理室地图","禁用管理室地圖","Отключить Стол Администратора","Desativar Admin" -"DisableAirshipCockpitAdmin","Disable Cockpit Admin","アドミン無効化(コックピット)","禁用飞艇(驾驶舱)管理室地图","禁用The Airship(駕駛艙)的管理室地圖","Отключить Стол Администратора В Кабине","Desativar Admin da Cabine" -"DisableAirshipRecordsAdmin","Disable Records Admin","アドミン無効化(アーカイブ)","禁用飞艇(档案室)管理室地图","禁用The Airship(檔案室)的管理室地圖","Отключить Стол Администратора В Архиве","Desativar Admin da Sala de Arquivos" -"DisableSkeldCamera","Disable Camera","カメラ無効化","禁用骷髅舰监控","禁用骷髅舰监控设备","Отключить Камеры","Desativar Câmeras" -"DisablePolusCamera","Disable Camera","カメラ無効化","禁用波鲁斯监控设备","禁用波鲁斯监控设备","Отключить Камеры","Desativar Câmeras" -"DisableAirshipCamera","Disable Camera","カメラ無効化","禁用飞艇监控","禁用飞艇监控设备","Отключить Камеры","Desativar Câmeras" -"DisableMiraHQDoorLog","Disable DoorLog","ドアログ無効化","禁用米拉总部门禁记录","禁用米拉总部门禁日志","Отключить Журналы","Desativar Portaria" -"DisablePolusVital","Disable Vital","バイタル無効化","禁用波鲁斯生命监测装置","禁用波鲁斯生命监测装置","Отключить Пульсы","Desativar Vitais" -"DisableAirshipVital","Disable Vital","バイタル無効化","禁用飞艇生命监测装置","禁用飞艇生命监测装置","Отключить Пульсы","Desativar Vitais" -"IgnoreConditions","Ignore Conditions","除外条件","忽略条件","忽略條件","Игнорируют условия","Ignorar Condições" -"IgnoreImpostors","Ignore Impostors","インポスターを除く","忽略内鬼","偽裝者陣營除外","Игнорируют Предатели","Ignorar Impostores" -"IgnoreMadmates","Ignore Madmates","マッドメイト系を除く","忽略叛徒","叛徒職業除外","Игнорируют Безумцы","Ignorar Tripulantes Loucos" -"IgnoreNeutrals","Ignore Neutrals","ニュートラルを除く","忽略独立阵营","中立陣營除外","Игнорируют Нейтралы","Ignorar Neutros" -"IgnoreCrewmates","Ignore Crewmates","クルーを除く","忽略船员","船員陣營除外","Игнорируют Члены Экипажа","Ignorar Tripulantes" -"IgnoreAfterAnyoneDied","Ignore After Anyone Has Died","死人が出た後を除く","忽略幽灵","當所有人都死亡時除外","Игнорируют умершие игроки","Ignorar Depois de Alguém Morrer" -"LightsOutSpecialSettings","Fix Lights Special Settings","停電の特殊設定","停电特殊设定","關燈特殊設定","Специальные настройки при отключении Света","Configuração Especial de Consertar Luzes" -"DisableAirshipViewingDeckLightsPanel","Disable Viewing Deck Lights Panel(Airship)","展望の配電盤を無効化(エアシップ)","禁用瞭望台配电箱(飞艇地图)","關閉觀景台的配電箱 (Airship)","Отключить починку Света на Смотровой Палубе (Airship)","Desativar Painel de Luzes do Deck Panorâmico(Airship)" -"DisableAirshipGapRoomLightsPanel","Disable Gap Room Lights Panel(Airship)","昇降機の配電盤を無効化(エアシップ)","禁用升降机配电箱(飞艇地图)","關閉間隙室右側的配電箱 (Airship)","Отключить починку Света в Комнате Пролета (Airship)","Desativar Painel de Luzes da Sala Suspensa(Airship)" -"DisableAirshipCargoLightsPanel","Disable Cargo Lights Panel(Airship)","貨物室の配電盤を無効化(エアシップ)","禁用货舱配电箱(飞艇地图)","關閉貨艙的配電箱 (Airship)","Отключить починку Света в Грузовом Отсеке (Airship)","Desativar Painel de Luzes das Cargas(Airship)" -"BlockDisturbancesToSwitches","Block Switches When They Are Up","配電盤妨害を無効化","","","Блокировать переключатели когда они подняты","" -"MapModification","Map Modifications","マップ改造","地图修改","","Модификации карты","Modificações do Mapa" -"DisableAirshipMovingPlatform","Disable Moving Platform(Airship)","昇降機のリフトを無効化(エアシップ)","禁用升降机(飞艇地图)","","Отключить движущуюся платформу (Airship)","Desativar Plataformas Voadoras(Airship)" -"AirShipVariableElectrical","Variable Electrical(AirShip)","電気室の構造変化(エアシップ)","改变配电室构造(飞艇地图)","電力室構造變化 (Airship)","Двери в Электрощитовой меняются случайно (Airship)","Elétrica Varia(AirShip)" -"AirshipAllOpenElectrical","AirshipAllOpenElectrical","電気室のドアを全開放する(エアシップ)","","","","" -"ResetDoorsEveryTurns","Reset Doors After Meeting(Airship/Polus)","会議後にドア状況をリセットする(エアシップ・ポーラス)","","","Сбросить статус дверей после собраний","" -"DoorsResetMode","Reset Mode","リセットモード","","","Режим сброса дверей","" -"AllOpen","All Open","全て開放","","","Все Открыты","" -"AllClosed","All Closed","全て閉鎖","","","Все Закрыты","" -"RandomByDoor","Random By Door","ドアごとにランダム","","","Случайно для каждой двери","" -"RandomSpawn","Random Spawn","ランダムスポーン","随机出生点","隨機出生點","Случайный спавн","Spawn Aleatório" -"AirshipAdditionalSpawn","Additional Spawn(Airship)","追加スポーン位置(エアシップ)","额外出生点(飞艇地图)","額外出生點(The Airship地圖)","Дополнительный спавн(Airship)","Spawn Adicional(Airship)" -"CommsCamouflage","Camouflage During Comms","コミュサボ時のカモフラージュ","通信破坏时伪装","通訊破壞時所有玩家變成小灰人","Камуфляж при Саботаже Связи","Camuflagem Durante Comunicação" -"EnableDebugMode","Enable Debug Mode","デバッグモードを有効化する","开启调试模式","啟用偵錯模式","Включить режим отладки","Ativar Modo de Depuração" -"ChangeNameToRoleInfo","Show Role Descriptions to Unmodded Client","役職説明を非modクライアントにも表示する","对未安装本mod的玩家显示职业说明","對未安裝本模組的玩家顯示職業說明","Показать описания Ролей игрокам играющие без Мода","Mostrar Descrição de Classe para Clientes Não Modificados" -"RoleAssigningAlgorithm","Role Assigning Algorithm","役職割り当てのアルゴリズム","职业分配算法","職業分配算法","Алгоритм назначения Ролей","Algoritmo de Atribuição de Classes" -"RoleAssigningAlgorithm.Default","Default","デフォルト","默认随机算法","預設算法","По умолчанию","Padrão" -"RoleAssigningAlgorithm.NetRandom",".NET System.Random",".NET System.Random",".NET 系统随机算法","NET系統隨機算法","Случайный","Sistema Aleatório .NET" -"RoleAssigningAlgorithm.HashRandom","HashRandom","HashRandom","哈希随机算法","Hash值隨機算法","HashRandom","HashRandom" -"RoleAssigningAlgorithm.Xorshift","Xorshift","Xorshift","Xorshift随机算法","Xorshift隨機算法","Xorshift","Xorshift" -"RoleAssigningAlgorithm.MersenneTwister","Mersenne Twister","Mersenne Twister","Mersenne Twister随机算法","Mersenne Twister隨機算法","MersenneTwister","Mersenne Twister" -"ApplyDenyNameList","Apply DenyName List","DenyNameリストを適用する","启用违禁昵称名单","自動禁止具有不良名字的人加入","Применить файл запрещённых имён (DenyName)","Aplicar Lista de Nomes Proibidos (DenyName)" -"KickPlayerFriendCodeNotExist","Kick Players Whose Friend Code Does Not Exist","フレンドコードが存在しないプレイヤーをキックする","踢出好友编号无效的玩家","將沒有好友代碼的玩家自動踢出","Кикнуть игроков у которых нет Кода Друга","Expulsar Jogadores Sem Código de Amigo" -"ApplyBanList","Apply BanList","BANリストを適用する","启用封禁名单","啟用封禁名單","Применить файл с забаненными игроками (BanList)","Aplicar Lista de Banimentos (BanList)" +"HideAndSeek","Hide and Seek","かくれんぼ","躲猫猫","躲貓貓","Прятки","Esconde-Esconde","" +"NoGameEnd","No Game End","ゲームを終了しない","测试模式","遊戲不會結束模式","Игра Не Закончится","Jogo Sem Fim","" +"SyncButtonMode","Sync Button Mode","ボタン回数同期モード","所有船员共享会议次数","全場拍桌數同步模式","Общая кнопка встречи","Modo de Botão Sincronizado","" +"RandomMapsMode","Random Maps Mode","ランダムマップモード","随机地图模式","隨機地圖模式","Случайная карта","Modo de Mapas Aleatórios","" +"SyncedButtonCount","Max Number of Buttons Allowed","合計ボタン使用可能回数","紧急会议可用次数","全場最大拍桌數量","Максимальное количество нажатий на Кнопку","Número Máximo de Usos do Botão","" +"AddedTheSkeld","Include TheSkeld","TheSkeldを追加","添加骷髅舰地图","將The Skeld地圖列入選項","Добавить The Skeld","Incluir TheSkeld","" +"AddedMIRAHQ","Include MIRAHQ","MIRAHQを追加","添加米拉总部地图","將MIRA HQ地圖列入選項","Добавить MIRA HQ","Incluir MIRAHQ","" +"AddedPolus","Include Polus","Polusを追加","添加波鲁斯地图","將Polus地圖列入選項","Добавить Polus","Incluir Polus","" +"AddedTheAirShip","Include TheAirShip","TheAirShipを追加","添加飞艇地图","將The AirShip列入選項","Добавить The Airship","Incluir TheAirShip","" +"AddedTheFungle","Include TheFungle","TheFungleを追加","添加蘑菇岛地图","","Добавить The Fungle","","" +"AllAliveMeeting","Meeting When No One is Dead","全員生存時の会議","全员存活时的会议设定","全員存活會議","Все живые на собрании","Reunião Com Todos Vivos","" +"AllAliveMeetingTime","Meeting Time When No One is Dead","全員生存時の会議時間","全员存活时的会议时间","全員存活會議時間","Время когда все живы на собрании","Tempo de Reunião Com Todos Vivos","" +"AdditionalEmergencyCooldown","Additional Emergency Cooldown","追加の緊急ボタンクールダウン","附加紧急会议冷却时间","附加緊急會議冷卻時間","Дополнительный откат собраний","Tempo de Emergência Adicional","" +"AdditionalEmergencyCooldownThreshold","Applied Living Player Count","適用する生存人数","适用存活人数","適用於幾個存活人數","Применяемый Порог Выживания","Número de Vivos Para Aplicar","" +"AdditionalEmergencyCooldownTime","Additional Cooldown","追加するクールダウン","附加冷却时间","附加冷卻時間","Дополнительный откат","Tempo Adicional","" +"LadderDeath","Fall From Ladders","ハシゴから転落","从梯子上下滑时有可能摔死","從梯子上下滑時可能會摔死","Падение с Лестницы","Queda de Escadas","" +"LadderDeathChance","Fall To Death Chance","転落する確率","从梯子上摔死的概率","從梯子上下滑摔死機率","Шанс разбиться насмерть","Chance de Queda","" +"DisableTasks","Disable Tasks","タスクを無効化する","禁用某些任务","禁用某些特定的任務","Отключить задания","Desativar Tarefas","" +"DisableSwipeCardTask","Disable SwipeCard Tasks","カードタスク","禁用刷卡任务","禁用刷卡任務","Отключить задание ''Провести Карту''","Desativar Tarefas de Passar Cartão","" +"DisableSubmitScanTask","Disable SubmitScan Tasks","医務室のスキャンタスク","禁用扫描任务","禁用掃描任務","Отключить задание ''Сканирование''","Desativar Tarefas de Raio-X","" +"DisableUnlockSafeTask","Disable UnlockSafe Tasks","金庫タスク","禁用解除歧管任务","禁用保險櫃任務","Отключить задание ''Открытие Сейфа''","Desativar Tarefas de Abrir Cofre","" +"DisableUploadDataTask","Disable UploadData Tasks","ダウンロードタスク","禁用上传数据任务","禁用上傳任務","Отключить задание ''Загрузка Данных''","Desativar Tarefas de Baixar Dados","" +"DisableStartReactorTask","Disable StartReactor Tasks","原子炉起動タスク","禁用启动反应堆任务","禁用啟動反應堆任務","Отключить задание ''Запустить Реактор''","Desativar Tarefas de Acionar Reator","" +"DisableResetBreakerTask","Disable ResetBreaker Tasks","ブレーカーリセットタスク","禁用重置反应堆任务","禁用重置斷路器任務","Отключить задание ''Рычаги(Airship)''","Desativar Reiniciar Disjuntores","" +"SuffixMode","Suffix","名前の二行目","附加标签","名字標籤","Суффикс","Sufixo","" +"SuffixMode.None","Never","なし","不更换","關閉","Никогда","Nunca","" +"SuffixMode.Version","Version","バージョン","版本","版本","Версия","Versão","" +"SuffixMode.Streaming","Streaming","配信中","直播中","直播中","Стримит","Ao Vivo","" +"SuffixMode.Recording","Recording","録画中","录制中","錄影中","Записывает","Gravando","" +"SuffixMode.RoomHost","Room Host","部屋主","房主","房主","Хост Румы","Anfitrião","" +"SuffixMode.OriginalName","OriginalName","元の名前","玩家名称","玩家原名稱","Настоящий Никнейм","Nome Original","" +"ColorNameMode","Color Name Mode","色名前モード","显示颜色名称","名字將被顏色名稱替換","Режим: Никнейм соответствует цвету","Modo Nomes de Cores","" +"FixFirstKillCooldown","Normalize First Kill Cooldown","初期スポーン時のクールダウン修正","修正首刀冷却时间","修正開場時的殺人冷卻時間","Нормализовать откат убийства в начале Игры","Normalizar Tempo de Recarga do Primeiro Abate","" +"GhostCanSeeOtherRoles","Ghosts Can See Other Roles","幽霊が他人の役職を見ることができる","幽灵可见他人职业","幽靈可以看見所有玩家職業","Призраки могут видеть все роли","Fantasmas Podem Ver Outros Papéis","" +"GhostCanSeeOtherTasks","Ghosts Can See Other Tasks","幽霊が他人のタスク進捗を見ることができる","幽灵可见他人任务进度","","Призраки могут видеть прогресс заданий других игроков","Fantasmas Podem Ver Tarefas de Outros","" +"GhostCanSeeOtherVotes","Ghosts Can See Other Votes","幽霊が他人の投票先を見ることができる","幽灵可见投票情况","幽靈可以看見所有玩家的投票","Призраки могут видеть цвета голосов","Fantasmas Podem Ver Outros Votos","" +"GhostCanSeeDeathReason","Ghost Can See Cause Of Death","幽霊が死因を見ることができる","幽灵可以看见死因","幽靈可以看見死因","Призраки могут видеть причины смерти","Fantasmas Podem Ver Causa da Morte","" +"GhostIgnoreTasks","Ghosts Exempt From Tasks","死人のタスクを免除する","幽灵无视任务","幽靈可以免除任務","Призраки игнорируют задания","Fantasmas Isentos de Tarefas","" +"DisableTaskWin","Disable Task Win","タスク勝利を無効化","禁用任务胜利","禁用任務勝利","Отключить победу по заданиям","Desativar Vitória por Tarefas","" +"HideGameSettings","Hide Game Settings","ゲーム設定を隠す","隐藏游戏设置","隱藏遊戲設定","Скрыть настройки Игры","Esconder Configurações","" +"RoleOptions","Role Options","役職設定","职业设置","職業設定","Настройка Ролей","Opções de Classe","" +"ModeOptions","Mode Options","モード設定","模组设置","模式設定","Настройка режима","Opções de Modos","" +"AutoDisplayLastResult","Auto Display Last Result","自動的に試合結果を表示","自动显示最终结果","自動顯示上一回合結果","Отображать результат последней игры в чате","Mostrar Ultimo Resultado","" +"AutoDisplayKillLog","Auto Display Kill Log","自動的にキルログを表示","自动显示击杀日志","自動顯示擊殺紀錄","Отображать историю убийств в чате","Mostrar Log de Mortes Automaticamente","" +"VoteMode","Voting Mode","投票モード","投票相关设定","投票設定","Режим голосования","Modo de Votação","" +"WhenSkipVote","When Skip Vote","スキップ時","跳过投票相当于投给自己","當跳過投票時","Когда пропускаете Голосование","Quando Pular Voto","" +"WhenSkipVoteIgnoreFirstMeeting","Ignore First Meeting","初回会議を除く","忽略首次会议","忽略首次會議","Игнорировать первое собрание","Ignorar Primeira Reunião","" +"WhenSkipVoteIgnoreNoDeadBody","Ignore When No DeadBody","死体がない時を除く","无尸体时忽略","沒有屍體時無法選擇","Игнорировать когда Нет Трупов","Ignorar Se Não Tiver Corpo","" +"WhenSkipVoteIgnoreEmergency","Ignore When Emergency Button","緊急ボタンを除く","忽略紧急会议按钮","緊急會議中無法選擇","Игнорировать кнопку собраний","Ignorar Se Tiver Botão de Emergência","" +"WhenNonVote","When No Votes","無投票時","不投票相当于投给自己","沒有投票時","Когда никто не Голосовал","Quando Sem Votos","" +"Default","Default","デフォルト","默认随机算法","預設算法","По умолчанию","Padrão","" +"Suicide","Suicide","自殺","自杀","自殺","Суицид","Suicídio","" +"SelfVote","Self Vote","自投票","自票","投自己","Само Голос","Votar Em Si","" +"Skip","Skip","スキップ","跳过","跳過","Пропуск","Pular","" +"WhenTie","When Tied Vote","同数投票時","平票时","平票時","Когда ничья","Quando Empatar Votos","" +"TieMode.Default","","デフォルト","默认随机算法","預設","По умолчанию","","" +"TieMode.All","Eject All","全員追放","全体放逐","全員放逐","Изгнать всех","Exilar Todos","" +"TieMode.Random","Eject Random","ランダムに追放","随机放逐","隨機放逐","Изгнать случайно","Exilar Aleatório","" +"DisableDevices","Disable Devices","デバイスを無効化","禁用设备","禁用裝置","Отключить устройства","Desativar Dispositivos","" +"DisableSkeldDevices","Disable Skeld Devices","スケルドのデバイス無効化","禁用骷髅舰设备","禁用The Skeld中的設備","Отключить устройства на The Skeld","Desativar Dispositivos de Skeld","" +"DisableMiraHQDevices","Disable MiraHQ Device","ミラHQのデバイス無効化","禁用米拉总部设备","禁用Mira HQ中的設備","Отключить устройства на MIRA HQ","Desativar Dispositivos de MiraHQ","" +"DisablePolusDevices","Disable Polus Device","ポーラスのデバイス無効化","禁用波鲁斯设备","禁用Polus中的設備","Отключить устройства на Polus","Desativar Dispositivos de Polus","" +"DisableAirshipDevices","Disable Airship Device","エアシップのデバイス無効化","禁用飞艇设备","禁用The Airship中的設備","Отключить устройства на The Airship","Desativar Dispositivos de Airship","" +"DisableFungleDevices","Disable Fungle Devices","ファングルのデバイス無効化","禁用蘑菇岛设备","","Отключить устройства на The Fangle","","" +"DisableSkeldAdmin","Disable Admin","アドミン無効化","禁用管理室地图","禁用管理室地圖","Отключить стол администратора","Desativar Admin","" +"DisableMiraHQAdmin","","アドミン無効化","禁用管理室地图","禁用管理室地圖","Отключить стол администратора","","" +"DisablePolusAdmin","","アドミン無効化","禁用管理室地图","禁用管理室地圖","Отключить стол администратора","","" +"DisableAirshipCockpitAdmin","Disable Cockpit Admin","アドミン無効化(コックピット)","禁用飞艇(驾驶舱)管理室地图","禁用The Airship(駕駛艙)的管理室地圖","Отключить стол администратора в ''Кабине''","Desativar Admin da Cabine","" +"DisableAirshipRecordsAdmin","Disable Records Admin","アドミン無効化(アーカイブ)","禁用飞艇(档案室)管理室地图","禁用The Airship(檔案室)的管理室地圖","Отключить стол администратора в ''Архиве''","Desativar Admin da Sala de Arquivos","" +"DisableSkeldCamera","Disable Camera","カメラ無効化","禁用飞艇监控","禁用飞艇监控设备","Отключить камеры","Desativar Câmeras","" +"DisablePolusCamera","","カメラ無効化","禁用监控","禁用波鲁斯监控设备","Отключить камеры","Desativar Câmeras","" +"DisableAirshipCamera","","カメラ無効化","禁用监控","禁用飞艇监控设备","Отключить камеры","Desativar Câmeras","" +"DisableMiraHQDoorLog","Disable DoorLog","ドアログ無効化","禁用米拉总部门禁记录","禁用米拉总部门禁日志","Отключить журналы","Desativar Portaria","" +"DisablePolusVital","Disable Vital","バイタル無効化","禁用飞艇生命监测装置","禁用飞艇生命监测装置","Отключить пульсы","Desativar Vitais","" +"DisableAirshipVital","","バイタル無効化","禁用蘑菇岛生命监测装置","","Отключить пульсы","Desativar Vitais","" +"DisableFungleVital","","バイタル無効化","禁用生命监测装置","","Отключить пульсы","","" +"IgnoreConditions","Ignore Conditions","除外条件","忽略条件","忽略條件","Игнорируют условия","Ignorar Condições","" +"IgnoreImpostors","Ignore Impostors","インポスターを除く","忽略内鬼","偽裝者陣營除外","Игнорируют Предатели","Ignorar Impostores","" +"IgnoreMadmates","Ignore Madmates","マッドメイト系を除く","忽略叛徒","叛徒職業除外","Игнорируют Безумцы","Ignorar Tripulantes Loucos","" +"IgnoreNeutrals","Ignore Neutrals","ニュートラルを除く","忽略独立阵营","中立陣營除外","Игнорируют Нейтралы","Ignorar Neutros","" +"IgnoreCrewmates","Ignore Crewmates","クルーを除く","忽略船员","船員陣營除外","Игнорируют Члены Экипажа","Ignorar Tripulantes","" +"IgnoreAfterAnyoneDied","Ignore After Anyone Has Died","死人が出た後を除く","忽略幽灵","當所有人都死亡時除外","Игнорируют умершие игроки","Ignorar Depois de Alguém Morrer","" +"LightsOutSpecialSettings","Fix Lights Special Settings","停電の特殊設定","停电特殊设定","關燈特殊設定","Специальные настройки при саботаже света","Configuração Especial de Consertar Luzes","" +"DisableAirshipViewingDeckLightsPanel","Disable Viewing Deck Lights Panel(Airship)","展望の配電盤を無効化(エアシップ)","禁用瞭望台配电箱(飞艇地图)","關閉觀景台的配電箱 (Airship)","Отключить починку света на ''Смотровой Палубе'' (Airship)","Desativar Painel de Luzes do Deck Panorâmico(Airship)","" +"DisableAirshipGapRoomLightsPanel","Disable Gap Room Lights Panel(Airship)","昇降機の配電盤を無効化(エアシップ)","禁用升降机配电箱(飞艇地图)","關閉間隙室右側的配電箱 (Airship)","Отключить починку света в ''Комнате Пролета'' (Airship)","Desativar Painel de Luzes da Sala Suspensa(Airship)","" +"DisableAirshipCargoLightsPanel","Disable Cargo Lights Panel(Airship)","貨物室の配電盤を無効化(エアシップ)","禁用货舱配电箱(飞艇地图)","關閉貨艙的配電箱 (Airship)","Отключить починку света в ''Грузовом Отсеке'' (Airship)","Desativar Painel de Luzes das Cargas(Airship)","" +"BlockDisturbancesToSwitches","Block Switches When They Are Up","配電盤妨害を無効化","禁用电力破坏","","Блокировать переключатели когда они подняты","Interruptores Sempre Ligados","" +"MapModification","Map Modifications","マップ改造","地图修改","","Модификации для карт","Modificações do Mapa","" +"DisableAirshipMovingPlatform","Disable Moving Platform(Airship)","昇降機のリフトを無効化(エアシップ)","禁用升降机(飞艇地图)","","Отключить движущуюся платформу (Airship)","Desativar Plataformas Voadoras(Airship)","" +"AirShipVariableElectrical","Variable Electrical(AirShip)","電気室の構造変化(エアシップ)","改变配电室构造(飞艇地图)","電力室構造變化 (Airship)","Двери в Электрощитовой меняются случайно (Airship)","Elétrica Varia(AirShip)","" +"ResetDoorsEveryTurns","Reset Doors After Meeting(Airship/Polus/Fungle)","会議後にドア状況をリセットする(エアシップ・ポーラス・ファングル)","每次会议后重置门的状态(飞艇/波鲁斯/蘑菇岛)","","Сбросить статус дверей после собраний","Redefinir Portas Após Reunião(Airship/Polus)","" +"DoorsResetMode","Reset Mode","リセットモード","重置门状态为","","Режим сброса дверей","Modo de Redefinição","" +"AllOpen","All Open","全て開放","全部开启","","Все Открыты","Todas Abertas","" +"AllClosed","All Closed","全て閉鎖","全部关闭","","Все Закрыты","Todas Fechadas","" +"RandomByDoor","Random By Door","ドアごとにランダム","随机开启或关闭","","Случайно для каждой двери","Aleatoriamente Por Porta","" +"DisableFungleSporeTrigger","Prevent Spores Trigger(Fungle)","きのこの胞子を無効化(ファングル)","禁用蘑菇混合(蘑菇岛地图)","","Отключить триггер спор (Fungle)","","" +"RandomSpawn","Random Spawn","ランダムスポーン","随机出生点","隨機出生點","Случайный спавн","Spawn Aleatório","" +"CommsCamouflage","Camouflage During Comms Sabotage","コミュサボ時のカモフラージュ","通信破坏时伪装","通訊破壞時所有玩家變成小灰人","Камуфляж при Саботаже Связи","Camuflagem Durante Comunicação","" +"EnableDebugMode","Enable Debug Mode","デバッグモードを有効化する","开启调试模式","啟用偵錯模式","Включить режим отладки","Ativar Modo de Depuração","" +"ChangeNameToRoleInfo","Show Role Descriptions to Unmodded Client","役職説明を非modクライアントにも表示する","对未安装本mod的玩家显示职业说明","對未安裝本模組的玩家顯示職業說明","Показать краткую информацию о роли в начале игры","Mostrar Descrição de Classe para Clientes Sem Mod","" +"RoleAssigningAlgorithm","Role Assigning Algorithm","役職割り当てのアルゴリズム","职业分配算法","職業分配算法","Алгоритм назначения Ролей","Algoritmo de Atribuição de Classes","" +"RoleAssigningAlgorithm.Default","","デフォルト","默认","預設算法","По умолчанию","Padrão","" +"RoleAssigningAlgorithm.NetRandom","",".NET System.Random",".NET 系统随机算法","NET系統隨機算法","Случайный","Sistema Aleatório .NET","" +"RoleAssigningAlgorithm.HashRandom","","HashRandom","哈希随机算法","Hash值隨機算法","HashRandom","","" +"RoleAssigningAlgorithm.Xorshift","","Xorshift","Xorshift随机算法","Xorshift隨機算法","Xorshift","","" +"RoleAssigningAlgorithm.MersenneTwister","","Mersenne Twister","Mersenne Twister随机算法","Mersenne Twister隨機算法","MersenneTwister","","" +"ApplyDenyNameList","Apply DenyName List","DenyNameリストを適用する","启用违禁昵称名单","自動禁止具有不良名字的人加入","Применить файл запрещённых имён (DenyName)","Aplicar Lista de Nomes Proibidos (DenyName)","" +"KickPlayerFriendCodeNotExist","Kick Players Whose Friend Code Does Not Exist","フレンドコードが存在しないプレイヤーをキックする","踢出好友编号无效的玩家","將沒有好友代碼的玩家自動踢出","Кикнуть игроков у которых нет Кода Друга","Expulsar Jogadores Sem Código de Amigo","" +"ApplyBanList","Apply BanList","BANリストを適用する","启用封禁名单","啟用封禁名單","Применить файл с забаненными игроками (BanList)","Aplicar Lista de Banimentos (BanList)","" "## モード説明" -"HideAndSeekInfo","Hide and Seek:\nNo emergency meetings. Crewmates (Blue) can only win by finishing tasks\nand impostors (Red) can only win by killing all crewmates.","かくれんぼ:\n会議を開くことはできず、クルーはタスク完了、インポスターは全クルー殺害でのみ勝利することができる。\nインポスターは赤、クルーは青に体の色が変更される。","躲猫猫:\n不能开会;船员只能通过完成任务获得胜利;\n内鬼需要杀死所有船员来获得胜利;\n内鬼的皮肤颜色将变为红色,船员变为蓝色,以示区分。","躲貓貓:\n不能拍桌或舉報,船員只能做任務\n偽裝者的勝利條件為殺光所有船員\n狼人的顏色會轉變為紅色,船員則會變為藍色。","Прятки:\nНикто не может созвать срочное собрание, Члены Экипажа могут победить, только выполняя задания. \nПредатели меняют цвет тела на красный, а экипажи на синий.","Esconde-Esconde:\nNão há reuniões de emergência. Tripulantes (Azul) podem ganhar apenas ao completar todas as tarefas,\ne Impostores (Vermelho) ganham apenas quando matarem todos os tripulantes." -"StandardHASInfo","Hide and Seek with Roles:\nNo emergency meetings. Crewmates can only win by finishing tasks and impostors can only win by killing all crewmates.","役職入りでかくれんぼ:\n会議を開くことはできず、クルーはタスク完了、インポスターは全クルー殺害でのみ勝利することができる。","躲猫猫(模组):\n没有会议阶段;船员只能完成任务;\n内鬼需要杀死所有船员来获得胜利。","躲貓貓(附加普通模式職業):\n不能拍桌或舉報,船員只能做任務\n偽裝者的勝利條件為殺光所有船員\n偽裝者的顏色會轉變為紅色,船員則會變為藍色。","Прятки с Ролями:\nНикто не может созвать срочное собрание, Члены Экипажа могут победить, только выполнив задания, А Предатели могут победить, только убив всех Членов Экипажа.","Esconde-Esconde com Classes:\nNão há reuniões de emergência. Tripulantes podem ganhar apenas ao completar todas as tarefas, e Impostores ganham apenas quando matarem todos os tripulantes." -"NoGameEndInfo","NoGameEnd:\nA mode for debugging with no victory. The only way to end the game is to have the host SHIFT+L+ENTER.","ゲームを終了しない:\n勝利判定が存在しないデバッグ用のモード。ホストのSHIFT+L+ENTER以外でのゲーム終了ができない。","测试模式:\n本模式用于进行测试。本模式没有游戏结束判定,房主按下shift+L+enter可以结束游戏。","遊戲不會結束模式:\n該模式僅限用來測試,這裡沒有勝利條件\n該模式無法通過正常勝利條件結束,僅能讓房主使用SHIFT+L+ENTER或指令/dis來結束遊戲。","Игра Не Заканчивается:\nРежим отладки, в котором нет основы для выигрыша.\nИгра не может быть завершена, если Хосту не использует команду в лобби SHIFT+L+ENTER.","Jogo Sem Fim:\nUm modo para testes, sem vitória. A única forma de finalizar o jogo é o anfitrião usar SHIFT+L+ENTER." -"SyncButtonModeInfo","SyncButtonMode:\nA mode where all players share the total buttons available.","ボタン回数同期モード:\nプレイヤー全員のボタン回数が同期されているモード。(設定有)","所有船员共享会议次数:\n游戏中存在全员共有的最大紧急会议次数。(可以进行设置)","全場拍桌數同步模式:\n該模式會讓全場玩家的拍桌數同步。(可以設定拍桌數)","Синхронизация Кнопки:\nЭтот режим ограничивает максимальное количество срочных собраний, которые можно созвать в целом. (в настройках)","Modo de Botão Sincronizado:\nUm modo em que os jogadores dividem o número total de botões disponíveis. (configurável)" -"SabotageTimeControlInfo","Sabotage Duration Control:\nCan alter the duration of certain sabotages.","サボタージュの時間制御:\n一部サボタージュの制限時間を変更することができる。(設定有)","破坏调整:\n可以调整一部分破坏的限制时间。(可以进行设置)","控制緊急任務時間:\n該模式可以讓某些地圖的破壞時間重新設定。 (可以設定)","Изменить Время Саботажа:\nВозможность изменить время саботажа реактора. (в настройках)","Controle de Duração da Sabotagem:\nPode alterar a duração de algumas sabotagens. (configurável)" -"RandomMapsModeInfo","RandomMapsMode:\nA mode in which the map changes randomly.","ランダムマップモード:\nランダムにマップが変わるモード。(設定有)","随机地图:\n随机选择地图游玩的模式。(可以进行设置)","隨機地圖模式:\n該模式會隨機挑選地圖。 (可以設定)","Случайная Карта:\nРежим в котором карты случайным образом меняются в каждой игре. (в настройках)","Modo de Mapas Aleatórios:\nUm modo em que o mapa muda aleatoriamente a cada partida. (configurável)" -"DisableDevicesInfo","DisableDevices:\nVarious devices can be disabled.","デバイスを無効化:\n各種機器を無効化することができる。","禁用设备:\n可以禁用各种设备。","禁用設備:\n可以禁用各種設備。","Отключенные Устройства:\nРазличные устройства могут быть отключены","Desativar Dispositivos:\nDiversos dispositivos podem ser desativados." +"HideAndSeekInfo","Hide and Seek:\nNo emergency meetings. Crewmates (Blue) can only win by finishing tasks\nand impostors (Red) can only win by killing all crewmates.","かくれんぼ:\n会議を開くことはできず、クルーはタスク完了、インポスターは全クルー殺害でのみ勝利することができる。\nインポスターは赤、クルーは青に体の色が変更される。","躲猫猫:\n不能开会;船员只能通过完成任务获得胜利;\n内鬼需要杀死所有船员来获得胜利;\n内鬼的皮肤颜色将变为红色,船员变为蓝色,以示区分。","躲貓貓:\n不能拍桌或舉報,船員只能做任務\n偽裝者的勝利條件為殺光所有船員\n狼人的顏色會轉變為紅色,船員則會變為藍色。","Прятки:\nНикто не может созвать срочное собрание, Члены Экипажа могут победить, только выполняя задания. \nПредатели меняют цвет тела на красный, а экипажи на синий.","Esconde-Esconde:\nNão há reuniões de emergência. Tripulantes (Azul) podem ganhar apenas ao completar todas as tarefas,\ne Impostores (Vermelho) ganham apenas quando matarem todos os tripulantes.","" +"StandardHASInfo","Hide and Seek with Roles:\nNo emergency meetings. Crewmates can only win by finishing tasks and impostors can only win by killing all crewmates.","役職入りでかくれんぼ:\n会議を開くことはできず、クルーはタスク完了、インポスターは全クルー殺害でのみ勝利することができる。","躲猫猫(模组):\n没有会议阶段;船员只能完成任务;\n内鬼需要杀死所有船员来获得胜利。","躲貓貓(附加普通模式職業):\n不能拍桌或舉報,船員只能做任務\n偽裝者的勝利條件為殺光所有船員\n偽裝者的顏色會轉變為紅色,船員則會變為藍色。","Прятки с Ролями:\nНикто не может созвать срочное собрание, Члены Экипажа могут победить, только выполнив задания, А Предатели могут победить, только убив всех Членов Экипажа.","Esconde-Esconde com Classes:\nNão há reuniões de emergência. Tripulantes podem ganhar apenas ao completar todas as tarefas, e Impostores ganham apenas quando matarem todos os tripulantes.","" +"NoGameEndInfo","No Game End:\nA mode for debugging with no victory. The only way to end the game is to have the host SHIFT+L+ENTER.","ゲームを終了しない:\n勝利判定が存在しないデバッグ用のモード。ホストのSHIFT+L+ENTER以外でのゲーム終了ができない。","测试模式:\n本模式用于进行测试。本模式没有游戏结束判定,房主按下shift+L+enter可以结束游戏。","遊戲不會結束模式:\n該模式僅限用來測試,這裡沒有勝利條件\n該模式無法通過正常勝利條件結束,僅能讓房主使用SHIFT+L+ENTER或指令/dis來結束遊戲。","Игра Не Закаончится:\nРежим отладки, в котором нет основы для выигрыша.\nИгра не может быть завершена, если Хосту не использует команду в лобби SHIFT+L+ENTER.","Jogo Sem Fim:\nUm modo para testes, sem vitória. A única forma de finalizar o jogo é o anfitrião usar SHIFT+L+ENTER.","" +"SyncButtonModeInfo","Sync Button Mode:\nA mode where all players share the total buttons available.","ボタン回数同期モード:\nプレイヤー全員のボタン回数が同期されているモード。(設定有)","所有船员共享会议次数:\n游戏中存在全员共有的最大紧急会议次数。(可以进行设置)","全場拍桌數同步模式:\n該模式會讓全場玩家的拍桌數同步。(可以設定拍桌數)","Синхронизация Кнопки:\nЭтот режим ограничивает максимальное количество срочных собраний, которые можно созвать в целом. (в настройках)","Modo de Botão Sincronizado:\nUm modo em que os jogadores dividem o número total de botões disponíveis. (configurável)","" +"SabotageTimeControlInfo","Sabotage Duration Control:\nCan alter the duration of certain sabotages.","サボタージュの時間制御:\n一部サボタージュの制限時間を変更することができる。(設定有)","破坏调整:\n可以调整一部分破坏的限制时间。(可以进行设置)","控制緊急任務時間:\n該模式可以讓某些地圖的破壞時間重新設定。 (可以設定)","Изменить Время Саботажа:\nВозможность изменить время саботажа реактора. (в настройках)","Controle de Duração da Sabotagem:\nPode alterar a duração de algumas sabotagens. (configurável)","" +"RandomMapsModeInfo","Random Maps Mode:\nA mode in which the map changes randomly.","ランダムマップモード:\nランダムにマップが変わるモード。(設定有)","随机地图:\n随机选择地图游玩的模式。(可以进行设置)","隨機地圖模式:\n該模式會隨機挑選地圖。 (可以設定)","Случайная Карта:\nРежим в котором карты случайным образом меняются в каждой игре. (в настройках)","Modo de Mapas Aleatórios:\nUm modo em que o mapa muda aleatoriamente a cada partida. (configurável)","" +"DisableDevicesInfo","DisableDevices:\nVarious devices can be disabled.","デバイスを無効化:\n各種機器を無効化することができる。","禁用设备:\n可以禁用各种设备。","禁用設備:\n可以禁用各種設備。","Отключенные Устройства:\nРазличные устройства могут быть отключены","Desativar Dispositivos:\nDiversos dispositivos podem ser desativados.","" "## 詳細設定" -"Cooldown","Cooldown","クールダウン","冷却时间","冷卻時間","Откат","Tempo de Recarga" -"KillCooldown","Kill Cooldown","キルクール","击杀冷却时间","殺人冷卻時間","Откат убийства","Tempo de Recarga (abate)" -"CanVent","Can Vent","ベントを使える","可以使用通风管道","可以使用通風口","Может использовать Вентиляцию","Pode Usar Dutos" -"ImpostorVision","Impostor Vision","インポスター視界","拥有内鬼视野","擁有偽裝者的視野","Имеет дальность Обзора Предателя","Visão (Impostor)" -"CanUseSabotage","Can Sabotage","サボタージュを使用できる","可以破坏","可以破壞","Может использовать Саботаж","Pode Sabotar" -"CanCreateMadmate","Can Make SideKick Madmate","マッドメイトを指名できる","可以指名叛徒","可以招募叛徒","Он может назначить Безумцев","Pode Criar Tripulante Louco Ajudante" - -"AssignMode","Assign Algorithm Mode","アサインモード","分配算法","","Алгоритм назначения","Atribuir Modo do Algorítimo" -"AssignAlgorithm.Fixed","Fixed","固定","固定","","Фиксированный","Fixo" -"AssignAlgorithm.Random","Random","ランダム","随机","","Случайный","Aleatório" -"RoleTypeMin","Minimum %roleType% Roles","%roleType%役職の最小人数","%roleType%职业的最小人数","","Минимум ролей для %roleType% ","Mínimo de Classes %roleType%" -"RoleTypeMax","Maximum %roleType% Roles","%roleType%役職の最大人数","%roleType%职业的最大人数","","Максимум ролей для %roleType%","Máximo de Classes %roleType%" -"%roleTypes%Maximum","Max Players In %roleTypes%","%roleTypes%の最大アサイン数","%roleTypes%最大玩家数","","Максимум %roleTypes%","Máximo de Jogadores em %roleTypes%" -"FixedRole","Fixed Role","役職を固定","固定职业","","Фиксированная Роль","Classe Fixa" -"Role","Role","役職","职业","","Роль","Classe" - -"BountyTargetChangeTime","Time Until Target Swaps","ターゲット変更時間","赏金目标切换时间","賞金目標切換時間","Время смены цели","Tempo Para Troca de Alvos" -"BountySuccessKillCooldown","Kill Cooldown After Killing Bounty","ターゲット殺害時のキルクール","赏金猎人击杀赏金目标的奖励冷却时间","賞金獵人殺死賞金目標冷卻","Перезарядка после убийства цели","Tempo de Recarga ao Matar Alvo" -"BountyFailureKillCooldown","Kill Cooldown After Killing Others","ターゲット以外殺害時のキルクール","赏金猎人击杀赏金目标以外玩家的惩罚冷却时间","賞金獵人殺死非賞金目標冷卻","Перезарядка после обычного Убийства","Tempo de Recarga ao Matar Outros" -"BountyShowTargetArrow","Bounty hunter show arrow pointing to target","ターゲットへの矢印を表示する","赏金猎人的目标以箭头显示位置","賞金獵人獲得指向目標的箭頭","Показывать стрелку указывающую на Цель","Mostrar Seta Apontando ao Alvo" -"DefaultShapeshiftCooldown","Default Shapeshift Cooldown","デフォルトの変身クールダウン","默认变形冷却时间","預設變身時間","Обычная перезарядка Оборотня","Tempo de Recarga Padrão (Mutar)" -"VampireKillDelay","Kill Delay(S)","殺害までの時間(秒)","吸血目标延迟死亡时间","殺人延遲","Длительность укуса(Секунды)","Atraso do Abate (S)" -"MareAddSpeedInLightsOut","Mare Add Player Add Speed In Lights Out","停電時のメアーの加速値","梦魇熄灯时的额外速度","關燈時黑暗博士的額外速度","Скорость Ночного при Саботаже Света","Velocidade Adicional do Mare em Apagão" -"MareKillCooldownInLightsOut","Kill Cooldown During Fix Lights","停電時のメアーのキルクール","梦魇熄灯时的击杀冷却","關燈時黑暗博士的殺人冷卻","Откат убийства Ночного при Саботаже Света","Tempo de Recarga (abate) Durante Apagão" -"SabotageMasterSkillLimit","Ability Use Count(Excluding Doors)","修理能力を使用できる回数(ドア閉鎖は除く)","修理大师修理破坏的最大次数","修理工修理破壞次數上限","Лимит способности (Кроме открытия дверей)","Máximo de Usos da Habilidade (Menos Portas)" -"CanMakeMadmateCount","Sidekick Madmate Max Count","サイドキックマッドメイト(人)","变形者可以招募叛徒的数量","變形者招募叛徒最大人數","Максимум союзников Безумца","Máximo de Tripulantes Loucos Ajudantes" -"MadSnitchTasks","Mad Snitch Tasks","マッドスニッチのタスク数","背叛的告密者任务数","背叛告密者任務數量","Задания Безумного Стукача","Tarefas do Dedo-Duro Louco" -"MadSnitchCanAlsoBeExposedToImpostor","Known to Impostors","インポスターからも視認できる","对内鬼同样可见","對偽裝者同樣可見","Также не защищен от Предателей","Visível Para Impostor" -"MadSnitchTaskTrigger","Tasks Until Boost Activated","効果を発動するタスク数","","","","" -"MadGuardianCanSeeWhoTriedToKill","Can See Attempted Murderer","自身の殺害未遂者を知ることができる","背叛的守卫可以得知尝试对其击杀的玩家","背叛天使可以看到是誰嘗試殺害自己","Может видеть кто пытался его убить","Pode Ver Quem Tentou Matar" -"MadmateCanFixLightsOut","Mad Roles Can Fix Lights","マッドメイト系役職が停電を直せる","叛徒系职业可以修理照明破坏","叛徒職業的玩家可以修理電燈","Безумцы могут чинить Свет","Loucos Podem Consertar Luzes" -"MadmateCanFixComms","Mad Roles Can Fix Comms","マッドメイト系役職が通信障害を直せる","叛徒系职业可以修理通讯","叛徒職業的玩家可以修理通訊","Безумцы могут чинить Связь","Loucos Podem Consertar Comunicações" -"ModdedMadCantOpen","Modded Mad Role Players Can't Use Those Consoles","Mod導入済マッド系プレイヤーは開けない","","","","" -"MadmateHasImpostorVision","Mad Roles Have Impostor Vision","マッドメイト系役職がインポスター視界を持つ","叛徒系职业拥有内鬼视野","叛徒職業的玩家有跟偽裝者一樣的視野","Безумцы имеют дальность Обзора Предателя","Loucos Têm Visão de Impostor" -"MadmateCanSeeKillFlash","Mad Roles Can See ""Kill Flash""","マッドメイト系役職にキルフラッシュが見える","叛徒系职业可以看到击杀闪光","叛徒職業的玩家可以看到殺人閃光","Безумцы могут видеть Вспышку Убийства","Loucos Podem Ver ""Clarão de Abate""" -"MadmateCanSeeOtherVotes","Mad Roles Can See Votes","マッドメイト系役職に他人の投票先が分かる","叛徒系职业可以看到其他人所投的票","叛徒可以看到所有人的投票","Безумцы могут видеть цвета Голосов","Loucos Podem Ver Votos" -"MadmateCanSeeDeathReason","Madmates Can See Cause Of Death","マッドメイト系役職に死因が分かる","叛徒系职业可以看见死因","叛徒職業的玩家可以看到死因","Безумцы могут видеть Причины Смерти","Loucos Podem Ver Causa da Morte" -"MadmateExileCrewmate","Madmates Revenge A Crewmate When Exiled","マッドメイト系役職が追放時クルーを道連れにする","叛徒系职业在被放逐时复仇, 带走一名船员垫背","叛徒被丟出時會隨機拖一個船員一起下水","Безумцы сохраняют команду при изгнании","Loucos Vingam um Tripulante Quando Exilados" -"MadmateVentCooldown","Mad Roles Vent Cooldown","マッドメイト系役職のベントクールダウン","叛徒系职业跳管道冷却时间","叛徒職業的玩家跳管道冷卻","Откат вентиляции Безумцев","Tempo de Recarga de Dutos dos Loucos" -"MadmateVentMaxTime","Mad Roles Max Vent Duration","マッドメイト系役職のベント内での最大時間","叛徒系职业在管道中停留的最大时间","叛徒職業的玩家在管道中可以停留的最大時間","Время использования вентиляции Безумцев","Tempo Máximo de Loucos nos Dutos" -"LighterMaxVision","Max Vision","最大視界","完成任务后的视野","做完任務的視野","Дальность обзора","Aumento de Visão" -"LighterTaskCompletedDisableLightOut","Ignore Fix Lights Effect","タスク完了時に停電を無効にする","完成任务的执灯人不受熄灯影响","完成任務的小燈人視野不受關燈影響","Имеет дальность Обзора Предателя","Ignorar Efeitos do Apagão" -"LighterTriggerType","Ability Activation Condition","能力発動条件","","","Условие активации способности","" -"LighterTaskTrigger","Tasks Until Boost Activated","効果を発動するタスク数","","","Количество задач повышающие скорость","" -"SabotageMasterFixesDoors","Can Open Multiple Doors","1度に複数のドアを開けられる","修理大师打开多扇关闭的门","修理工可以一次性修理多扇門","Может открыть все двери","Pode Abrir Múltiplas Portas" -"SabotageMasterFixesReactors","Can Fix Both Reactors","リアクターに対して能力を使える","修理大师可以一人修理核反应堆","修理工可以獨自修理兩邊的反應堆","Может починить саботаж Реактора","Pode Consertar Reatores" -"SabotageMasterFixesOxygens","Can Fix Both O2","酸素妨害に対して能力を使える","修理大师修理氧气破坏时另一边的氧气设备将会被同时修理","修理工可以獨自修理兩邊的氧氣","Может починить саботаж O2","Pode Consertar Ambos O2" -"SabotageMasterFixesCommunications","Can Fix Both Comms In MIRA HQ","MIRA HQの通信妨害に対して能力を使える","修理大师在米拉总部地图修理通信破坏时另一边的通信破坏同时被修理","修理工可以獨自修理兩邊的通訊(Mira HQ)","Может починить саботаж Связи на MiraHQ","Pode Concertar Ambas Comunicações (Mira HQ)" -"SabotageMasterFixesElectrical","Can Fix Lights With One Switch","停電に対して能力を使える","修理大师按一个按钮就可以修复照明破坏","修理工可以快速完成修理電燈","Может починить саботаж Света одним кликом","Pode Consertar Luzes Com um Botão" -"SheriffCanKill%role%","Can Kill %role%","%role%をキルできる","警长可以执法%role%","警長可以槍死%role%","Может убить %role%","Pode Matar %role%" -"SheriffCanKillNeutrals","Can Kill Neutrals","ニュートラルをキルできる","警长可以执法独立阵营","警長可以槍死中立","Может убить Нейтралов","Pode Matar Neutros" -"SheriffCanKillAll","All ON","全てオン","全开","全開","Все ВКЛ","Todos Ligados" -"SheriffCanKillSeparately","Individual Settings","個別に設定","个别设定","個別設定","Выбрать кого","Configuração Individual" -"In%team%","(Team %team%)","(%team%陣営)","(%team%阵营)","(%team%陣營)","(Команда %team%)","(Time %team%)" -"SheriffMisfireKillsTarget","Misfire Kills Target","誤爆時、ターゲットも死ぬ","警长误杀好人会同时击杀目标","警長誤殺好人會同時擊殺目標","Шериф убивает цель вместе с собой","Pode Matar Alvo Incorreto" -"SheriffShotLimit","Ammo","キル可能回数","最大执法次数","最大子彈數量","Количество выстрелов","Balas" -"SheriffCanKillAllAlive","Can Kill With All Alive","全員生存時にキルできる","全员存活时可以执法","全員存活時可以執法","Может убить всех живых","Pode Matar Com Todos Vivos" -"DoctorTaskCompletedBatteryCharge","Battery Duration","充電持続時間","医生每次完成任务增加的设备充能数","醫生完成任務充電秒數","Длительность батарейки","Duração da Bateria" -"SnitchEnableTargetArrow","Can See Arrow To Target","ターゲットを示す矢印が見える","告密者完成任务后可以通过箭头确认所有被发现目标","告密者完成任務後可以看到指向目標的箭頭","Может видеть стрелку цели","Pode Ver Seta Para o Alvo" -"SnitchCanGetArrowColor","Can See Target Team Colored Arrow","矢印の色で陣営がわかる","对不同阵营的目标以不同颜色的箭头表示","對不同陣營的目標使用不同顏色的箭頭標示","Может видеть цвета стрелок","Pode Ver Seta Colorida Para Time Alvo" -"SnitchCanFindNeutralKiller","Can Find Neutral Killers","ニュートラルのキル可能役職を見つけることが出来る","告密者也可以和拥有击杀能力的独立阵营玩家互相发现","告密者也可以和中立陣營帶刀職業互認","Может видеть Нейтральных Убийц","Pode Achar Assassinos Neutros" -"SnitchRemainingTaskFound","Remaining tasks to be found","敵陣営に見つかるタスク残量","剩余任务数对敌对阵营可见","帶刀職業可見告密者剩餘任務數","Оставшиеся задания при которых он будет виден","Tarefas restantes para encontrar" -"SpeedBoosterUpSpeed","Random Player's Speed Boost","加速値","增速者加速时的移动速度","被加速器加速的玩家的移動速度","Повысить скорость игрока на","Impulso de Velocidade" -"SpeedBoosterTaskTrigger","Tasks Until Boost Activated","効果を発動するタスク数","效果发动所需任务数","效果發動所需任務數","Задачи повышающие скорость","Tarefas Para Ativar Impulso" -"MayorAdditionalVote","Additional Votes Count","追加投票の個数","附加票数","附加票數","Дополнительные голоса","Votos Adicionais" -"MayorHasPortableButton","Mayor Has Mobile Emergency Button","ポータブルボタンを持っている","市长跳通风管触发紧急会议","可以隨時拍桌","У Мэра есть портативная Кнопка","Botão Móvel de Emergência" -"MayorNumOfUseButton","Number Of Mobile Emergency Button","ポータブルボタンの使用可能回数","市长紧急会议最大次数","隨時拍桌最大次數","Количество портативных Кнопок","Número de Botões de Emergência" -"CanBeforeSchrodingerCatWinTheCrewmate","Can Win With Crewmates If No Team","役職変化前であれば、クルー陣営と勝利できる","薛定谔的猫未加入其他阵营前可以跟随船员阵营获胜","薛定諤的貓的陣營轉變前可以隨船員一起獲勝","Без команды он может победить с Членами Экипажа","Pode Ganhar Com Tripulantes" -"SchrodingerCatExiledTeamChanges","Team Changes When Ejected","吊られた際、陣営が変化する","薛定谔的猫被放逐时会加入其他阵营","薛定諤的貓被丟出時陣營會轉變","Команда меняется после его Изгнания","Time Muda Quando Exilado" -"SchrodingerCatCanSeeKillableTeammate","Can See Killable Teammate","変化した陣営のキル役職が分かる","变更阵营后可见带刀职业","陣營轉變後可以看見帶刀的隊友","Может видеть всю команду в которой он состоит","Vê Aliado que Pode Ser Morto" -"ExecutionerCanTargetImpostor","Can Target Impostors","インポスターもターゲットにできる","内鬼阵营玩家可以成为处刑人的目标","劊子手的目標可以是偽裝者陣營的玩家","Может иметь цель изгнать Предателя","Alvo Pode Ser Impostor" -"ExecutionerCanTargetNeutralKiller","Can Target Neutrals","キルできるニュートラルもターゲットにできる","独立阵营玩家可以成为处刑人的目标","劊子手的目標可以是中立陣營的玩家","Может иметь цель изгнать Нейтрального Убийцу","Pode Ter Alvo Neutro" -"ExecutionerChangeRolesAfterTargetKilled","Role Changes When Target Dies","ターゲットがキルされた後に変化する役職","处刑人目标死亡后将变为的职业","劊子手的目標被殺後轉變的職業","Роль после смерти его цели","Mudar Classe Caso Alvo Morra" -"SerialKillerLimit","Time Until Suicide","自殺までの秒数","嗜血杀手自杀倒计时","連環殺手自殺倒數","Лимит времени до Самоубийства","Tempo Para Suicídio" -"ArsonistDouseTime","Time Until Douse Complete","塗り時間","涂油所需时间","澆油所需時間","Длительность обливания","Tempo Para Completar Encharque" -"CanTerroristSuicideWin","Can Win By Suicide","テロリストの自殺勝ち","恐怖分子可以通过自杀获胜","恐怖分子可以自殺來獲勝","Для победы достаточно сделать Суицид","Pode Ganhar Por Suicídio" -"ShapeMasterShapeshiftDuration","Shapeshift Duration","シェイプマスターの変身持続時間","千面鬼变形持续时间","變形大師變形持續時間","Продолжительность морфа","Duração da Mutação" -"FireWorksMaxCount","Fireworks Count","花火の所持数","烟花商人可放置烟花的最大数量","煙火工匠可設定煙火最大數量","Максимальное количество фейерверков","Número de Fogos de Artifício" -"FireWorksRadius","Firework Explosion Radius","花火の爆発半径","烟花商人烟花爆炸半径","煙火工匠的煙火爆炸半徑","Радиус фейерверка","Alcance da Explosão" -"SniperBulletCount","Ammo","所持弾数","最大子弹数量","子彈最大數量","Количество пуль","Balas" -"SniperPrecisionShooting","Precision Firing","精密射撃モード","精准射击模式","精準射擊模式","Точный выстрел","Tiro Com Precisão" -"SniperAimAssist","Aim Assist","エイムアシスト","瞄准辅助","瞄準輔助","Помощь в прицеливании","Assistência de Mira" -"SniperAimAssistOneshot","One shot Assist","単発アシスト","一枪爆头辅助","單發瞄準","Помощь только с одним выстрелом","Assistência de One Shot" -"NumberOfLovers","Number Of Lovers (Pairs)","ラバーズの組数(x2人数)","恋人对数","戀人最大數量(2位玩家)","Количество Любовников (x2участника)","Quantidade de Amantes (Pares)" -"TimeThiefDecreaseMeetingTime","Time Thief Time Stolen","減少する会議時間","蚀时者每次击杀缩短的会议时间","時間小偷每次殺人減少的會議時間","Уменьшить длительность обсуждения на","Tempo Roubado de Reunião" -"TimeThiefLowerLimitVotingTime","Minimum Voting Time","投票時間の下限","蚀时者存活时会议时间最低下限","時間小偷在場時會議時間最低下限","Уменьшить длительность голосования на","Tempo Mínimo de Votação" -"TimeThiefReturnStolenTimeUponDeath","Return Stolen Time After Death","死亡後に盗んだ時間を返す","蚀时者死亡后会议时间重置","時間小偷死亡後將會議時間重設","Вернуть украденное время после его смерти","Devolver Tempo Roubado ao Morrer" -"EvilTrackerCanSeeKillFlash","Can See ""Kill Flash"" for Impostor Kills","インポスターキル時にフラッシュが見える","内鬼进行击杀时邪恶的追踪者可见击杀闪光","當狼人隊友殺人時邪惡的追蹤者可以看到閃光","Может видеть ''Вспышку Убийства''","Pode Ver ""Clarão"" Quando Impostor Matar" -"EvilTrackerTargetMode","Can Set Target","ターゲットの設定タイミング","目标更换时点","可以更換目標","Может установить цель","Pode Definir Alvos" -"EvilTrackerTargetMode.Never","Never","なし","不更换","關閉","Никогда","Nunca" -"EvilTrackerTargetMode.OnceInGame","Once In Game","試合毎","每局游戏一次","每局遊戲一次","В каждой игре","Uma Vez no Jogo" -"EvilTrackerTargetMode.EveryMeeting","Every Meeting","ターン毎","每次会议","每回合","На каждой встрече","Toda Reunião" -"EvilTrackerTargetMode.Always","Always","常時","一直","隨時","Всегда","Sempre" -"EvilTrackerCanSeeLastRoomInMeeting","Can See Target's Last Room In Meeting","会議中、追跡対象の最終位置を表示する","可以在会议时知晓追踪目标的最后停留房间","可以在會議中看到目標最後位置","Может видеть местоположение Целей во время Собрания","Pode Ver a Última Posição do Alvo na Reunião" -"KillFlashDuration","""Kill Flash"" Duration","キルフラッシュの長さ","击杀闪烁持续时间","殺人閃光持續時間","Длительность ""Вспышки Убийства""","Duração do ""Clarão de Abate""" -"WitchModeSwitchAction","Mode Switch Action","モード変更アクション","切换击杀模式","切換模式操作","Действие для Смены Режима","Trocar de Modo" -"TriggerKill","Kill","キル","击杀","殺人鍵","Убийство","Matar" -"TriggerVent","Vent","ベント","通风管","通風口","Вентиляция","Duto" -"TimeManagerIncreaseMeetingTime","Increase voting time","伸びる会議時間","延长投票的时间","延長時間","Увеличить время встреч на","Aumentar Tempo de Votação" -"TimeManagerLimitMeetingTime","Increase limit","会議時間の伸びる限界","增加限制","會議時間延長上限","Лимит увеличения времени встреч","Aumentar Limite" -"TriggerDouble","Double Click","ダブルクリック","双击","同時觸發兩個技能冷卻","Двойное нажатие","Duplo Clique" -"AssignOnlyTo%role%","Assign Only To %role%","%role%のみに割り当てる","只赋予%role%","","Назначить только для %role%","Atribuir Apenas a %role%" -"WorkhorseNumLongTasks","Additional Long Tasks","追加ロングタスクの個数","额外长任务数","增加的長任務數量","Дополнительные долгие задания","Adicionar Tarefas Longas" -"WorkhorseNumShortTasks","Additional Short Tasks","追加ショートタスクの個数","额外短任务数","增加的短任務數量","Дополнительные короткие задания","Adicionar Tarefas Curtas" -"StealthExcludeImpostors","Exclude Impostors From Blindness","暗転効果の対象からインポスターを除外する","","","Исключить Предателей из слепоты","" -"StealthDarkenDuration","Blindness Duration","暗転の持続時間","","","Длительность слепоты","" -"NekoKabochaImpostorsGetRevenged","Impostors Get Revenged","インポスターを道連れにする","","","Предатели могут мстить","" -"NekoKabochaMadmatesGetRevenged","Madmates Get Revenged","マッドメイトを道連れにする","","","Безумцы могут мстить","" -"NekoKabochaRevengeOnExile","Revenge When Exiled","追放された時に誰かを道連れにする","","","Месть во время изгнания","" -"EvilHackerCanSeeDeadMark","Can See The Location of Dead-bodies","死体位置がわかる","","","Может видеть местоположение трупов","" -"EvilHackerCanSeeImpostorMark","Can See The Location of Other Impostors","他のインポスターの位置がわかる","","","Может видеть местоположение других Предателей","" -"EvilHackerCanSeeKillFlash","Can See The Kill-flash for Impostor Kills","インポスターキル時にフラッシュが見える","","","Может видеть ''Вспышку Убийства''","" -"EvilHackerCanSeeImpostorArrow",,"他のインポスターを指す矢印が見える","","","","" -"EvilHackerCanSeeMurderRoom","Can See The Murder Location","キルの発生場所がわかる","","","Может увидеть место убийства","" -"EvilHackerInheritAbility","InheritAbility","死亡時、生存インポスターに能力を引き継ぐ","","","","" -"EvilHackerSkipUnoccupiedRooms","EvilHackerSkipUnoccupiedRooms","アドミン情報で誰もいない部屋を省略する","","","","" -"PenguinAbductTimerLimit","Dragging Time","引き摺れる時間","","","Время перетаскивания","" -"PenguinMeetingKill","Kill if meeting starts during dragging.","会議開始時に引き摺り中ならキルする","","","Убить если встреча начнется во время перетаскивания","" -"InsiderCanSeeImpostorAbilities","Can See Impostor Abilities","味方インポスターの能力が分かる","","","Может видеть роли других Предателей","" -"InsiderCanSeeAllGhostsRoles","Can See All Ghost's Roles","幽霊全員の役職が分かる","","","Может видеть все роли Призраков","" -"InsiderCanSeeMadmates","Can See Madmates","マッドメイトが分かる","","","Может видеть Безумцев","" -"InsiderKillCountToSeeMadmates","Kill Count To See Madmates","必要なキル数","","","Количество убийств при котором будет виден Безумец","" -"PlagueDoctorInfectLimit","Infect Count","感染回数","","","Количество заражений","" -"PlagueDoctorInfectWhenKilled","Infect When Killed","キルされた時に感染させる","","","Заразить убийцу при смерти заражённого","" -"PlagueDoctorInfectTime","Infect Time","感染に必要な時間","","","Время заражения","" -"PlagueDoctorInfectDistance","Infect Distance","感染する距離","","","Радиус заражения","" -"PlagueDoctorInfectInactiveTime","Infect Invalid Time","行動開始から感染しない時間","","","Недействительное время заражения","" -"PlagueDoctorCanInfectSelf","Can Infect Self","自身も感染する","","","Может заразить себя","" -"PlagueDoctorCanInfectVent","Can Infect in Vent","ベント内外でも感染する","","","Может заразить в вентиляции","" +"Cooldown","Cooldown","クールダウン","冷却时间","冷卻時間","Откат","Tempo de Recarga","" +"KillCooldown","Kill Cooldown","キルクール","击杀冷却时间","殺人冷卻時間","Откат убийства","Tempo de Recarga (abate)","" +"CanVent","Can Vent","ベントを使える","可以使用通风管道","可以使用通風口","Может использовать Вентиляцию","Pode Usar Dutos","" +"ImpostorVision","Impostor Vision","インポスター視界","拥有内鬼视野","擁有偽裝者的視野","Имеет дальность Обзора Предателя","Visão (Impostor)","" +"CanUseSabotage","Can Sabotage","サボタージュを使用できる","可以破坏","可以破壞","Может использовать Саботаж","Pode Sabotar","" +"CanCreateMadmate","Can Make SideKick Madmate","マッドメイトを指名できる","可以指名叛徒","可以招募叛徒","Он может назначить Безумцев","Pode Criar Tripulante Louco Ajudante","" + +"AssignMode","Assign Algorithm Mode","アサインモード","分配算法","","Алгоритм назначения","Atribuir Modo do Algorítimo","" +"AssignAlgorithm.Fixed","Fixed","固定","固定","","Фиксированный","Fixo","" +"AssignAlgorithm.Random","Random","ランダム","随机","","Случайный","Aleatório","" +"RoleTypeMin","Minimum %roleType% Roles","%roleType%役職の最小人数","%roleType%职业的最小人数","","Минимум ролей для %roleType% ","Mínimo de Classes %roleType%","" +"RoleTypeMax","Maximum %roleType% Roles","%roleType%役職の最大人数","%roleType%职业的最大人数","","Максимум ролей для %roleType%","Máximo de Classes %roleType%","" +"%roleTypes%Maximum","Max Players In %roleTypes%","%roleTypes%の最大アサイン数","%roleTypes%最大玩家数","","Максимум для %roleTypes%","Máximo de Jogadores em %roleTypes%","" +"FixedRole","Fixed Role","役職を固定","固定职业","","Фиксированная Роль","Classe Fixa","" +"Role","Roles","役職","职业","職業","Роли","Classes","" + +"BountyTargetChangeTime","Time Until Target Swaps","ターゲット変更時間","赏金目标切换时间","賞金目標切換時間","Время смены цели","Tempo Para Troca de Alvos","" +"BountySuccessKillCooldown","Kill Cooldown After Killing Bounty","ターゲット殺害時のキルクール","赏金猎人击杀赏金目标的奖励冷却时间","賞金獵人殺死賞金目標冷卻","Перезарядка после убийства цели","Tempo de Recarga ao Matar Alvo","" +"BountyFailureKillCooldown","Kill Cooldown After Killing Others","ターゲット以外殺害時のキルクール","赏金猎人击杀赏金目标以外玩家的惩罚冷却时间","賞金獵人殺死非賞金目標冷卻","Перезарядка после обычного Убийства","Tempo de Recarga ao Matar Outros","" +"BountyShowTargetArrow","Show Arrow Pointing To Target","ターゲットへの矢印を表示する","赏金猎人的目标以箭头显示位置","賞金獵人獲得指向目標的箭頭","Показывать стрелку указывающую на Цель","Mostrar Seta Apontando ao Alvo","" +"DefaultShapeshiftCooldown","Default Shapeshift Cooldown","デフォルトの変身クールダウン","默认变形冷却时间","預設變身時間","Обычная перезарядка Оборотня","Tempo de Recarga Padrão (Mutar)","" +"VampireKillDelay","Kill Delay","殺害までの時間(秒)","吸血目标延迟死亡时间","殺人延遲","Длительность укуса(Секунды)","Atraso do Abate (S)","" +"MareAddSpeedInLightsOut","Add Mare Speed With Lights Out","停電時のメアーの加速値","梦魇熄灯时的额外速度","關燈時黑暗博士的額外速度","Добавить скорость Ночному при саботаже света","Velocidade Adicional do Mare em Apagão","" +"MareKillCooldownInLightsOut","Kill Cooldown During Fix Lights","停電時のメアーのキルクール","梦魇熄灯时的击杀冷却","關燈時黑暗博士的殺人冷卻","Откат убийства Ночного при Саботаже Света","Tempo de Recarga (abate) Durante Apagão","" +"SabotageMasterSkillLimit","Ability Use Count(Excluding Doors)","修理能力を使用できる回数(ドア閉鎖は除く)","修理大师修理破坏的最大次数","修理工修理破壞次數上限","Лимит способности (Кроме открытия дверей)","Máximo de Usos da Habilidade (Menos Portas)","" +"CanMakeMadmateCount","Sidekick Madmate Max Count","サイドキックマッドメイト(人)","变形者可以招募叛徒的数量","變形者招募叛徒最大人數","Максимум союзников Безумца","Máximo de Tripulantes Loucos Ajudantes","" +"MadSnitchTasks","Mad Snitch Tasks","マッドスニッチのタスク数","背叛的告密者任务数","背叛告密者任務數量","Задания Безумного Стукача","Tarefas do Dedo-Duro Louco","" +"MadSnitchCanAlsoBeExposedToImpostor","Known to Impostors","インポスターからも視認できる","对内鬼同样可见","對偽裝者同樣可見","Также не защищен от Предателей","Visível Para Impostor","" +"MadSnitchTaskTrigger","Tasks Until Boost Activated","効果を発動するタスク数","效果发动所需任务数","效果發動所需任務數","Задачи повышающие скорость","Tarefas Para Ativar Impulso","" +"MadGuardianCanSeeWhoTriedToKill","Can See Attempted Murderer","自身の殺害未遂者を知ることができる","背叛的守卫可以得知尝试对其击杀的玩家","背叛天使可以看到是誰嘗試殺害自己","Может видеть кто пытался его убить","Pode Ver Quem Tentou Matar","" +"MadmateCanFixLightsOut","Mad Roles Can Fix Lights","マッドメイト系役職が停電を直せる","叛徒系职业可以修理照明破坏","叛徒職業的玩家可以修理電燈","Безумцы могут чинить Свет","Loucos Podem Consertar Luzes","" +"MadmateCanFixComms","Mad Roles Can Fix Comms","マッドメイト系役職が通信障害を直せる","叛徒系职业可以修理通讯","叛徒職業的玩家可以修理通訊","Безумцы могут чинить Связь","Loucos Podem Consertar Comunicações","" +"MadmateHasImpostorVision","Mad Roles Have Impostor Vision","マッドメイト系役職がインポスター視界を持つ","叛徒系职业拥有内鬼视野","叛徒職業的玩家有跟偽裝者一樣的視野","Безумцы имеют дальность Обзора Предателя","Loucos Têm Visão de Impostor","" +"MadmateCanSeeKillFlash","Mad Roles Can See ''Kill Flash''","マッドメイト系役職にキルフラッシュが見える","叛徒系职业可以看到击杀闪光","叛徒職業的玩家可以看到殺人閃光","Безумцы могут видеть Вспышку Убийства","Loucos Podem Ver ""Clarão de Abate""","" +"MadmateCanSeeOtherVotes","Mad Roles Can See Votes","マッドメイト系役職に他人の投票先が分かる","叛徒系职业可以看到其他人所投的票","叛徒可以看到所有人的投票","Безумцы могут видеть цвета Голосов","Loucos Podem Ver Votos","" +"MadmateCanSeeDeathReason","Mad Roles Can See Cause Of Death","マッドメイト系役職に死因が分かる","叛徒系职业可以看见死因","叛徒職業的玩家可以看到死因","Безумцы могут видеть Причины Смерти","Loucos Podem Ver Causa da Morte","" +"MadmateExileCrewmate","Madmates Revenge A Crewmate When Exiled","マッドメイト系役職が追放時クルーを道連れにする","叛徒系职业在被放逐时复仇, 带走一名船员垫背","叛徒被丟出時會隨機拖一個船員一起下水","Безумцы сохраняют команду при изгнании","Loucos Vingam um Tripulante Quando Exilados","" +"MadmateVentCooldown","Mad Roles Vent Cooldown","マッドメイト系役職のベントクールダウン","叛徒系职业跳管道冷却时间","叛徒職業的玩家跳管道冷卻","Откат вентиляции Безумцев","Tempo de Recarga de Dutos dos Loucos","" +"MadmateVentMaxTime","Mad Roles Max Vent Duration","マッドメイト系役職のベント内での最大時間","叛徒系职业在管道中停留的最大时间","叛徒職業的玩家在管道中可以停留的最大時間","Время использования вентиляции Безумцев","Tempo Máximo de Loucos nos Dutos","" +"LighterMaxVision","Max Vision","最大視界","完成任务后的视野","做完任務的視野","Дальность обзора","Aumento de Visão","" +"LighterTaskCompletedDisableLightOut","Ignore Fix Lights Effect","タスク完了時に停電を無効にする","完成任务的执灯人不受熄灯影响","完成任務的小燈人視野不受關燈影響","Имеет дальность Обзора Предателя","Ignorar Efeitos do Apagão","" +"LighterTriggerType","Ability Activation Condition","能力発動条件","技能发动条件","","Условие активации способности","Condição de Ativação da Habilidade","" +"LighterTaskTrigger","","効果を発動するタスク数","加速所需任务数","","Задачи повышающие скорость","Tarefas Para Ativar Impulso","" +"SabotageMasterFixesDoors","Can Open Multiple Doors","1度に複数のドアを開けられる","修理大师打开多扇关闭的门","修理工可以一次性修理多扇門","Может открыть все двери","Pode Abrir Múltiplas Portas","" +"SabotageMasterFixesReactors","Can Fix Both Reactors","リアクターに対して能力を使える","修理大师可以一人修理核反应堆","修理工可以獨自修理兩邊的反應堆","Может починить саботаж Реактора","Pode Consertar Reatores","" +"SabotageMasterFixesOxygens","Can Fix Both O2","酸素妨害に対して能力を使える","修理大师修理氧气破坏时另一边的氧气设备将会被同时修理","修理工可以獨自修理兩邊的氧氣","Может починить саботаж O2","Pode Consertar Ambos O2","" +"SabotageMasterFixesCommunications","Can Fix Both Comms In MIRA HQ","MIRA HQの通信妨害に対して能力を使える","修理大师在米拉总部地图修理通信破坏时另一边的通信破坏同时被修理","修理工可以獨自修理兩邊的通訊(Mira HQ)","Может починить саботаж Связи на MiraHQ","Pode Concertar Ambas Comunicações (Mira HQ)","" +"SabotageMasterFixesElectrical","Can Fix Lights With One Switch","停電に対して能力を使える","修理大师按一个按钮就可以修复照明破坏","修理工可以快速完成修理電燈","Может починить саботаж Света одним кликом","Pode Consertar Luzes Com um Botão","" +"SheriffCanKill%role%","Can Kill %role%","%role%をキルできる","警长可以执法%role%","警長可以槍死%role%","Может убить %role%","Pode Matar %role%","" +"SheriffCanKillNeutrals","Can Kill Neutrals","ニュートラルをキルできる","警长可以执法独立阵营","警長可以槍死中立","Может убить Нейтралов","Pode Matar Neutros","" +"SheriffCanKillAll","All ON","全てオン","全开","全開","Все ВКЛ","Todos Ligados","" +"SheriffCanKillSeparately","Individual Settings","個別に設定","个别设定","個別設定","Выбрать кого","Configuração Individual","" +"In%team%","(Team %team%)","(%team%陣営)","(%team%阵营)","(%team%陣營)","(Команда %team%)","(Time %team%)","" +"SheriffMisfireKillsTarget","Misfire Kills Target","誤爆時、ターゲットも死ぬ","警长误杀好人会同时击杀目标","警長誤殺好人會同時擊殺目標","Шериф убивает цель вместе с собой","Pode Matar Alvo Incorreto","" +"SheriffShotLimit","Ammo","キル可能回数","最大执法次数","最大子彈數量","Количество выстрелов","Balas","" +"SheriffCanKillAllAlive","Can Kill When No One Is Dead","全員生存時にキルできる","全员存活时可以执法","全員存活時可以執法","Может убивать когда никто не умер","Pode Matar Com Todos Vivos","" +"DoctorTaskCompletedBatteryCharge","Battery Duration","充電持続時間","医生每次完成任务增加的设备充能数","醫生完成任務充電秒數","Длительность батарейки","Duração da Bateria","" +"SnitchEnableTargetArrow","Can See Arrow To Target","ターゲットを示す矢印が見える","告密者完成任务后可以通过箭头确认所有被发现目标","告密者完成任務後可以看到指向目標的箭頭","Может видеть стрелку цели","Pode Ver Seta Para o Alvo","" +"SnitchCanGetArrowColor","Can See Target Team Colored Arrow","矢印の色で陣営がわかる","对不同阵营的目标以不同颜色的箭头表示","對不同陣營的目標使用不同顏色的箭頭標示","Может видеть цвета стрелок","Pode Ver Seta Colorida Para Time Alvo","" +"SnitchCanFindNeutralKiller","Can Find Neutral Killers","ニュートラルのキル可能役職を見つけることが出来る","告密者也可以和拥有击杀能力的独立阵营玩家互相发现","告密者也可以和中立陣營帶刀職業互認","Может видеть Нейтральных Убийц","Pode Achar Assassinos Neutros","" +"SnitchRemainingTaskFound","Remaining tasks to be found","敵陣営に見つかるタスク残量","剩余任务数对敌对阵营可见","帶刀職業可見告密者剩餘任務數","Оставшиеся задания при которых он будет виден","Tarefas restantes para encontrar","" +"SpeedBoosterUpSpeed","Random Player's Speed Boost","加速値","增速者加速时的移动速度","被加速器加速的玩家的移動速度","Повысить скорость игрока на","Impulso de Velocidade","" +"SpeedBoosterTaskTrigger","","効果を発動するタスク数","发动技能所需完成任务数量","效果發動所需任務數","Задачи повышающие скорость","Tarefas Para Ativar Impulso","" +"MayorAdditionalVote","Additional Votes Count","追加投票の個数","附加票数","附加票數","Дополнительные голоса","Votos Adicionais","" +"MayorHasPortableButton","Mayor Has Mobile Emergency Button","ポータブルボタンを持っている","市长跳通风管触发紧急会议","可以隨時拍桌","У Мэра есть портативная Кнопка","Botão Móvel de Emergência","" +"MayorNumOfUseButton","Number Of Mobile Emergency Button","ポータブルボタンの使用可能回数","市长紧急会议最大次数","隨時拍桌最大次數","Количество портативных Кнопок","Número de Botões de Emergência","" +"CanBeforeSchrodingerCatWinTheCrewmate","Can Win With Crewmates If No Team","役職変化前であれば、クルー陣営と勝利できる","薛定谔的猫未加入其他阵营前可以跟随船员阵营获胜","薛定諤的貓的陣營轉變前可以隨船員一起獲勝","Без команды он может победить с Членами Экипажа","Pode Ganhar Com Tripulantes","" +"SchrodingerCatExiledTeamChanges","Team Changes When Ejected","吊られた際、陣営が変化する","薛定谔的猫被放逐时会加入其他阵营","薛定諤的貓被丟出時陣營會轉變","Команда меняется после его Изгнания","Time Muda Quando Exilado","" +"SchrodingerCatCanSeeKillableTeammate","Can See Killable Teammate","変化した陣営のキル役職が分かる","变更阵营后可见带刀职业","陣營轉變後可以看見帶刀的隊友","Может видеть всю команду в которой он состоит","Vê Aliado que Pode Ser Morto","" +"ExecutionerCanTargetImpostor","Can Target Impostors","インポスターもターゲットにできる","内鬼阵营玩家可以成为处刑人的目标","劊子手的目標可以是偽裝者陣營的玩家","Может иметь цель изгнать Предателя","Alvo Pode Ser Impostor","" +"ExecutionerCanTargetNeutralKiller","Can Target Neutrals","キルできるニュートラルもターゲットにできる","独立阵营玩家可以成为处刑人的目标","劊子手的目標可以是中立陣營的玩家","Может иметь цель изгнать Нейтрального Убийцу","Pode Ter Alvo Neutro","" +"ExecutionerChangeRolesAfterTargetKilled","Role Changes When Target Dies","ターゲットがキルされた後に変化する役職","处刑人目标死亡后将变为的职业","劊子手的目標被殺後轉變的職業","Роль после смерти его цели","Mudar Classe Caso Alvo Morra","" +"SerialKillerLimit","Time Until Suicide","自殺までの秒数","嗜血杀手自杀倒计时","連環殺手自殺倒數","Лимит времени до Самоубийства","Tempo Para Suicídio","" +"ArsonistDouseTime","Time Until Douse Complete","塗り時間","涂油所需时间","澆油所需時間","Длительность обливания","Tempo Para Completar Encharque","" +"CanTerroristSuicideWin","Can Win By Suicide","テロリストの自殺勝ち","恐怖分子可以通过自杀获胜","恐怖分子可以自殺來獲勝","Для победы достаточно сделать Суицид","Pode Ganhar Por Suicídio","" +"ShapeMasterShapeshiftDuration","Shapeshift Duration","シェイプマスターの変身持続時間","千面鬼变形持续时间","變形大師變形持續時間","Продолжительность морфа","Duração da Mutação","" +"FireWorksMaxCount","Fireworks Count","花火の所持数","烟花商人可放置烟花的最大数量","煙火工匠可設定煙火最大數量","Максимальное количество фейерверков","Número de Fogos de Artifício","" +"FireWorksRadius","Firework Explosion Radius","花火の爆発半径","烟花商人烟花爆炸半径","煙火工匠的煙火爆炸半徑","Радиус фейерверка","Alcance da Explosão","" +"SniperBulletCount","Ammo","所持弾数","最大子弹数量","子彈最大數量","Количество пуль","Balas","" +"SniperPrecisionShooting","Precision Firing","精密射撃モード","精准射击模式","精準射擊模式","Точный выстрел","Tiro Com Precisão","" +"SniperAimAssist","Aim Assist","エイムアシスト","瞄准辅助","瞄準輔助","Помощь в прицеливании","Assistência de Mira","" +"SniperAimAssistOneshot","One shot Assist","単発アシスト","一枪爆头辅助","單發瞄準","Помощь только с одним выстрелом","Assistência de One Shot","" +"NumberOfLovers","Number Of Lovers (Pairs)","ラバーズの組数(x2人数)","恋人对数","戀人最大數量(2位玩家)","Количество Любовников (x2участника)","Quantidade de Amantes (Pares)","" +"TimeThiefDecreaseMeetingTime","Time Thief Time Stolen","減少する会議時間","蚀时者每次击杀缩短的会议时间","時間小偷每次殺人減少的會議時間","Уменьшить длительность обсуждения на","Tempo Roubado de Reunião","" +"TimeThiefLowerLimitVotingTime","Minimum Voting Time","投票時間の下限","蚀时者存活时会议时间最低下限","時間小偷在場時會議時間最低下限","Уменьшить длительность голосования на","Tempo Mínimo de Votação","" +"TimeThiefReturnStolenTimeUponDeath","Return Stolen Time After Death","死亡後に盗んだ時間を返す","蚀时者死亡后会议时间重置","時間小偷死亡後將會議時間重設","Вернуть украденное время после его смерти","Devolver Tempo Roubado ao Morrer","" +"EvilTrackerCanSeeKillFlash","Can See ''Kill Flash''","インポスターキル時にフラッシュが見える","内鬼进行击杀时邪恶的追踪者可见击杀闪光","當狼人隊友殺人時邪惡的追蹤者可以看到閃光","Может видеть ''Вспышку Убийства''","Pode Ver ""Clarão"" Quando Impostor Matar","" +"EvilTrackerTargetMode","Can Set Target","ターゲットの設定タイミング","目标更换时点","可以更換目標","Может установить цель","Pode Definir Alvos","" +"EvilTrackerTargetMode.Never","","なし","无","關閉","Никто","Nunca","" +"EvilTrackerTargetMode.OnceInGame","Once In Game","試合毎","每局游戏一次","每局遊戲一次","В каждой игре","Uma Vez no Jogo","" +"EvilTrackerTargetMode.EveryMeeting","Every Meeting","ターン毎","每次会议","每回合","На каждой встрече","Toda Reunião","" +"EvilTrackerTargetMode.Always","Always","常時","一直","隨時","Всегда","Sempre","" +"EvilTrackerCanSeeLastRoomInMeeting","Can See Target's Last Room In Meeting","会議中、追跡対象の最終位置を表示する","可以在会议时知晓追踪目标的最后停留房间","可以在會議中看到目標最後位置","Может видеть местоположение Целей во время Собрания","Pode Ver a Última Posição do Alvo na Reunião","" +"KillFlashDuration","""Kill Flash"" Duration","キルフラッシュの長さ","击杀闪烁持续时间","殺人閃光持續時間","Длительность ""Вспышки Убийства""","Duração do ""Clarão de Abate""","" +"WitchModeSwitchAction","Mode Switch Action","モード変更アクション","切换击杀模式","切換模式操作","Действие для Смены Режима","Trocar de Modo","" +"TriggerKill","Kill","キル","被杀","殺死","Убить","Abatido","" +"TriggerVent","Vent","ベント","通风管","通風口","Вентиляция","Duto","" +"TimeManagerIncreaseMeetingTime","Increase voting time","伸びる会議時間","延长投票的时间","延長時間","Увеличить время встреч на","Aumentar Tempo de Votação","" +"TimeManagerLimitMeetingTime","Increase limit","会議時間の伸びる限界","增加限制","會議時間延長上限","Лимит увеличения времени встреч","Aumentar Limite","" +"TriggerDouble","Double Click","ダブルクリック","双击","同時觸發兩個技能冷卻","Двойное нажатие","Duplo Clique","" +"AssignOnlyTo%role%","Assign Only To %role%","%role%のみに割り当てる","只赋予%role%","","Назначить только для %role%","Atribuir Apenas a %role%","" +"WorkhorseNumLongTasks","Additional Long Tasks","追加ロングタスクの個数","额外长任务数","增加的長任務數量","Дополнительные долгие задания","Adicionar Tarefas Longas","" +"WorkhorseNumShortTasks","Additional Short Tasks","追加ショートタスクの個数","额外短任务数","增加的短任務數量","Дополнительные короткие задания","Adicionar Tarefas Curtas","" +"StealthExcludeImpostors","Exclude Impostors From Blindness","暗転効果の対象からインポスターを除外する","内鬼不会失明","","Исключить Предателей из слепоты","Excluir Impostores da Cegueira","" +"StealthDarkenDuration","Blindness Duration","暗転の持続時間","失明持续时间","","Длительность слепоты","Duração da Cegueira","" +"NekoKabochaImpostorsGetRevenged","Impostors Get Revenged","インポスターを道連れにする","内鬼可以被反杀","","Предатели могут мстить","Impostores São Vingados","" +"NekoKabochaMadmatesGetRevenged","Madmates Get Revenged","マッドメイトを道連れにする","叛徒可以被反杀","","Безумцы могут мстить","Tripulantes Loucos São Vingados","" +"NekoKabochaRevengeOnExile","Revenge When Exiled","追放された時に誰かを道連れにする","被驱逐时复仇","","Месть во время изгнания","Vinagança Quando Exilado","" +"EvilHackerCanSeeDeadMark","Can See The Location of Dead-bodies","死体位置がわかる","可以知道尸体的位置","","Может видеть местоположение трупов","Pode Ver Local dos Corpos","" +"EvilHackerCanSeeImpostorMark","Can See The Location of Other Impostors","他のインポスターの位置がわかる","可以知道内鬼的位置","","Может видеть местоположение других Предателей","Pode Ver o Local de Outros Impostores","" +"EvilHackerCanSeeKillFlash","","インポスターキル時にフラッシュが見える","当内鬼击杀时你可以看到杀戮闪烁","","Может видеть ''Вспышку Убийства''","","" +"EvilHackerCanSeeMurderRoom","Can See The Murder Location","キルの発生場所がわかる","可以知道某个房间发生了击杀","","Может увидеть место убийства","Pode Ver Localidade do Assassinato","" +"PenguinAbductTimerLimit","Dragging Time","引き摺れる時間","最长拖拽时间","","Время перетаскивания","Tempo Para Arrastar","" +"PenguinMeetingKill","Kill If Meeting Starts During Dragging","会議開始時に引き摺り中ならキルする","拖拽时开会将立刻击杀目标","","Убить если встреча начнется во время перетаскивания","Matar Se Reunião Começar Enquanto Arrasta","" +"InsiderCanSeeImpostorAbilities","Can See Impostor Abilities","味方インポスターの能力が分かる","可以看到内鬼技能状态","","Может видеть роли других Предателей","Pode Ver Habilidades de Impostor","" +"InsiderCanSeeAllGhostsRoles","Can See All Ghost's Roles","幽霊全員の役職が分かる","可以看到所有幽灵的职业","","Может видеть все роли Призраков","Pode Ver Todas As Classes dos Fantasmas","" +"InsiderCanSeeMadmates","Can See Madmates","マッドメイトが分かる","可以看到叛徒","","Может видеть Безумцев","Pode Ver Tripulantes Loucos","" +"InsiderKillCountToSeeMadmates","Kill Count To See Madmates","必要なキル数","看到叛徒所需击杀数","","Количество убийств при котором будет виден Безумец","Quantas Kills Para Ver Tripulante Louco","" +"PlagueDoctorInfectLimit","Infect Count","感染回数","可主动感染次数","","Количество заражений","Limite de Infectados","" +"PlagueDoctorInfectWhenKilled","Infect When Killed","キルされた時に感染させる","被击杀时感染凶手","","Заразить убийцу при смерти заражённого","Infectar Ao Ser Morto","" +"PlagueDoctorInfectTime","Infect Time","感染に必要な時間","感染所需时间","","Время заражения","Tempo de Infecção","" +"PlagueDoctorInfectDistance","Infect Distance","感染する距離","感染有效传播距离","","Радиус заражения","Distância de Infecção","" +"PlagueDoctorInfectInactiveTime","Infect Invalid Time","行動開始から感染しない時間","主动感染冷却时间","","Недействительное время заражения","Tempo Inativo da Infecção","" +"PlagueDoctorCanInfectSelf","Can Infect Self","自身も感染する","可以感染自己","","Может заразить себя","Pode Infectar A Si Mesmo","" +"PlagueDoctorCanInfectVent","Can Infect in Vent","ベント内外でも感染する","可以感染管道内的玩家","","Может заразить в вентиляции","Pode Infectar no Duto","" + +"## ランダムスポーンポイント" +"Junction","Junction","三叉路","三岔路","","Перекресток","","" +"OfficeLeft","Office Left","オフィス左","办公室(左)","","Левый Офис","","" +"OfficeRight","Office Right","オフィス右","办公室(右)","","Правый Офис","","" +"Rocket","Rocket","ロケット","火箭","","Ракета","","" +"Toilet","Toilet","トイレ","厕所","","Туалет","","" +"NapRoom","Nap Room","仮眠室","休息室","","Комната для сна","","" +"Bonfire","Bonfire","焚き火","篝火","","Костер","","" +"JungleTop","Jungle Top","ジャングル上","丛林上","","Вершина Джунглей","","" +"JungleBottom","Jungle Bottom","ジャングル下","丛林下","","Дно Джунглей","","" +"Precipice","Precipice","崖","悬崖","","Скала","","" "## 能力発動条件" -"TaskProgressRate","Task Progress","タスク進捗率","","","Прогресс заданий","" -"TaskCount","Task Count","タスク数","","","Количество заданий","" +"TaskProgressRate","Task Progress","タスク進捗率","任务进度","","Прогресс заданий","Progresso das Tarefas","" +"TaskCount","Task Count","タスク数","任务数量","","Количество заданий","Número de Tarefas","" "## かくれんぼ設定" -"HideAndSeekOptions","Hide and Seek Settings","かくれんぼの設定","躲猫猫设置","躲貓貓設定","Настройки Пряток","Configurações do Esconde-Esconde" -"AllowCloseDoors","Allow Door Sabotage","ドア閉鎖を許可する","允许关门","可以關門","Разрешить закрытие дверей","Permitir Sabotar Portas" -"TrapperBlockMoveTime","Trapped Duration","移動を封じる時間","陷阱师技能封锁凶手移动时长","設陷者封鎖兇手移動時間","Продолжительность заморозки","Tempo Preso" -"HideAndSeekWaitingTime","Impostor Standby Time(S)","インポスターの待機時間(秒)","内鬼阵营在游戏开始后的等待时间","偽裝者在遊戲開始後的等待時間(秒)","Время ожидания Предателей(Секунды)","Tempo de Espera do Impostor (S)" -"IgnoreVent","Disable Vents","ベントの使用を禁止する","禁止使用管道","禁止使用管道","Отключить Вентиляцию","Desativar Dutos" -"HideAndSeekRoles","Hide and Seek Roles","かくれんぼの役職","躲猫猫职业","躲貓貓職業","Роли в Прятках","Classes do Esconde-Esconde" +"HideAndSeekOptions","Hide and Seek Settings","かくれんぼの設定","躲猫猫设置","躲貓貓設定","Настройки Пряток","Configurações do Esconde-Esconde","" +"AllowCloseDoors","Allow Door Sabotage","ドア閉鎖を許可する","允许关门","可以關門","Разрешить закрытие дверей","Permitir Sabotar Portas","" +"TrapperBlockMoveTime","Trapped Duration","移動を封じる時間","陷阱师技能封锁凶手移动时长","設陷者封鎖兇手移動時間","Продолжительность заморозки","Tempo Preso","" +"HideAndSeekWaitingTime","Impostor Standby Time(S)","インポスターの待機時間(秒)","内鬼阵营在游戏开始后的等待时间","偽裝者在遊戲開始後的等待時間(秒)","Время ожидания Предателей(Секунды)","Tempo de Espera do Impostor (S)","" +"IgnoreVent","Disable Vents","ベントの使用を禁止する","禁止使用管道","禁止使用管道","Отключить вентиляцию","Desativar Dutos","" +"HideAndSeekRoles","Hide and Seek Roles","かくれんぼの役職","躲猫猫职业","躲貓貓職業","Роли в Прятках","Classes do Esconde-Esconde","" "## 死因" -"DeathReason.Kill","Kill","キル","被杀","被殺","Убит","Abatido" -"DeathReason.Vote","Ejected","追放","放逐","被丟","Изгнан","Exilado" -"DeathReason.Suicide","Suicide","自殺","自杀","自殺","Суицид","Suicídio" -"DeathReason.Spell","Spell","呪殺","咒杀","咒殺","Заколдован","Feitiço" -"DeathReason.Bite","Bitten","噛殺","吸血","咬死","Укушен","Mordido" -"DeathReason.FollowingSuicide","Lover's Suicide","後追い","为爱而死","戀人共死","♡Суицид♡","Suicídio por Amor" -"DeathReason.Bombed","Explosion","爆死","炸死","炸死","Взорван","Explosão" -"DeathReason.Misfire","Misfire","誤爆","走火","走火","Убился","Alvo Incorreto" -"DeathReason.Torched","Torched","焼殺","烧死","燒死","Сожжён","Queimado" -"DeathReason.Sniped","Sniped","狙撃","狙杀","被狙","Застрелен","Baleado" -"DeathReason.Execution","Execution","処刑","处决","處刑","Казнен","Executado" -"DeathReason.Disconnected","Disconnected","回線切断","断连","斷線","Вышел","Desconectado" -"DeathReason.Fall","Fall","転落","摔死","摔死","Упал","Queda" -"DeathReason.Revenge","Revenge","道連れ","复仇","復仇","Месть","Vingança" -"DeathReason.Infected","Infected","感染","","","Заражён","" -"DeathReason.etc","Other","その他","其他","其他","Другое","Outros" -"Alive","Alive","生存","存活","存活","Выжил","Vivo" -"Win"," Wins","勝利","胜利","勝利"," Победили","Vitória" -"Last-","Last ","ラスト","仅存","獨活","Последний ","Último" +"DeathReason.Kill","","キル","击杀","被殺","Убит","Abatido","" +"DeathReason.Vote","Ejected","追放","放逐","被丟","Изгнан","Exilado","" +"DeathReason.Suicide","","自殺","自杀","自殺","Суицид","Suicídio","" +"DeathReason.Spell","Spell","呪殺","咒杀","咒殺","Заколдован","Feitiço","" +"DeathReason.Bite","Bitten","噛殺","吸血","咬死","Укушен","Mordido","" +"DeathReason.FollowingSuicide","Lover's Suicide","後追い","为爱而死","戀人共死","♡Суицид♡","Suicídio por Amor","" +"DeathReason.Bombed","Explosion","爆死","炸死","炸死","Взорван","Explosão","" +"DeathReason.Misfire","Misfire","誤爆","走火","走火","Убился","Alvo Incorreto","" +"DeathReason.Torched","Torched","焼殺","烧死","燒死","Сожжён","Queimado","" +"DeathReason.Sniped","Snipe","狙撃","狙击","狙擊","Застрелить","Atirar","" +"DeathReason.Execution","Execution","処刑","处决","處刑","Казнен","Executado","" +"DeathReason.Disconnected","Disconnected","回線切断","断连","斷線","Вышел","Desconectado","" +"DeathReason.Fall","Fall","転落","摔死","摔死","Упал","Queda","" +"DeathReason.Revenge","Revenge","道連れ","复仇","復仇","Месть","Vingança","" +"DeathReason.Infected","Infected","感染","感染","","Заражён","Infectado","" +"DeathReason.etc","Other","その他","其他","其他","Другое","Outros","" +"Alive","Alive","生存","存活","存活","Выжил","Vivo","" +"Win"," Wins","勝利","胜利","勝利"," Победили","Vitória","" +"Last-","Last ","ラスト","仅存","獨活","Последний ","Último","" "## リアクターの時間制御" -"SabotageTimeControl","Sabotage Duration Control","サボタージュの時間制御","更改修理时限","重新設定緊急任務時間","Изменить время Саботажа","Controle da Duração de Sabotagem" -"PolusReactorTimeLimit","Polus Reactor Duration","ポーラスのリアクター制限時間","波鲁斯抗震稳定器修理时限","Polus地震抑制器破壞最大時間","Polus время саботажа Реактора","Duração do Reator em Polus" -"AirshipReactorTimeLimit","Airship Reactor Duration","エアシップのリアクター制限時間","飞艇坠毁路线修理时限","Airship間隙室破壞最大時間","Airship время саботажа Реактора","Duração do Reator em Airship" +"SabotageTimeControl","Sabotage Duration Control","サボタージュの時間制御","更改修理时限","重新設定緊急任務時間","Изменить время саботажа","Controle da Duração de Sabotagem","" +"PolusReactorTimeLimit","Polus Reactor Duration","ポーラスのリアクター制限時間","波鲁斯抗震稳定器修理时限","Polus地震抑制器破壞最大時間","Polus время саботажа Реактора","Duração do Reator em Polus","" +"AirshipReactorTimeLimit","Airship Reactor Duration","エアシップのリアクター制限時間","飞艇坠毁路线修理时限","Airship間隙室破壞最大時間","Airship время саботажа Реактора","Duração do Reator em Airship","" +"FungleReactorTimeLimit","Fungle Reactor Duration","ファングルのリアクター制限時間","反应堆时间限制(蘑菇岛地图)","","Время саботажа Реактора на The Fungle","","" +"FungleMushroomMixupDuration","Fungle Mushroom Mixup Duration","ファングルのキノコカオス効果時間","蘑菇混合持续时间(蘑菇岛地图)","","Длительность грибного переполоха на The Fungle","","" "## サボタージュのクールダウン変更" -"ModifySabotageCooldown","Sabotage Cooldown Control","サボタージュのクールダウン制御","","","Контролировать откат саботажа","" -"SabotageCooldown","Sabotage Cooldown","サボタージュのクールダウン","","","Откат саботажа","" +"ModifySabotageCooldown","Sabotage Cooldown Control","サボタージュのクールダウン制御","修改破坏冷却时间","","Контролировать откат саботажа","Controle do Tepo de Recarga da Sabotagem","" +"SabotageCooldown","Sabotage Cooldown","サボタージュのクールダウン","破坏冷却时间","","Откат саботажа","Tempo de Recarga da Sabotagem","" "## クライアント設定" -"Close","Close","閉じる","关闭","","Закрыть","Fechar" -"TOHOptions","TOH Options","TOHの設定","TOH 选项","","Настройки TOH","TOH Opções" -"ForceJapanese","Force Japanese","日本語に強制","强制使用日语","強制使用日語","Принудительный Японский","Forçar Japonês" -"JapaneseRoleName","Japanese Role Name","日本語の役職名","用日语显示职业名","","Японские названия ролей","Nomes de Classes Japoneses" -"DiscordResult","Discord Result","Discordに試合結果を送信","","","","" -"ShowLobbySummary","Show Lobby Summary","ロビーで前の試合の結果を表示","","","","" -"CopyCode","Copy Code","部屋建て時にコードを自動でコピー","","","","" -"HauntFocusCrew","Haunt Focus crew","憑依開始時に生存者にフォーカス","","","","" -"PutBackWinTextPosition","PutBackWinTextPosition","勝敗テキストの位置を中央に戻す","","","","" -"UnloadMod","Disable The Mod","Modを無効化","切换为原版","","Отключить мод","Desabilitar o Mod" -"UnloadWarning","Warning\n\nTo re-enable the mod, you must restart the game. Would you like to continue anyway?","警告\n\nModを再び有効化するにはゲームの再起動が必要です。本当に無効化しますか?","警告:\n\n切换后需要重启游戏来恢复模组,确定要切换原版吗?","","Внимание!\n\nДля повторного включения мода необходимо перезапустить игру. Вы все равно хотите продолжить?","Aviso\n\nPara reativar o mod, você precisará reiniciar o jogo. Deseja continuar assim mesmo?" -"CannotUnloadDuringGame","The mod cannot be disabled during a game.","試合中はModを無効化できません","游戏中不能切换原版","","Мод нельзя отключить во время игры.","O mod não pode ser desabilitado durante um jogo." -"Cancel","Cancel","キャンセル","取消","","Отменить","Cancelar" -"Unload","DISABLE","無効化","确认","","ОТКЛЮЧИТЬ","DESATIVAR" -"DumpLog","Output Log","ログを出力","输出日志","","Вывести Журнал","Log de Saída" +"Close","Close","閉じる","关闭","","Закрыть","Fechar","" +"TOHOptions","TOH Options","TOHの設定","TOH 选项","","Настройки TOH","TOH Opções","" +"ForceJapanese","Force Japanese","日本語に強制","强制使用日语","強制使用日語","Принудительный Японский","Forçar Japonês","" +"JapaneseRoleName","Japanese Role Name","日本語の役職名","用日语显示职业名","","Японские названия ролей","Nomes de Classes Japoneses","" +"UnloadMod","Disable The Mod","Modを無効化","切换为原版","","Отключить мод","Desabilitar o Mod","" +"UnloadWarning","Warning\n\nTo re-enable the mod, you must restart the game. Would you like to continue anyway?","警告\n\nModを再び有効化するにはゲームの再起動が必要です。本当に無効化しますか?","警告:\n\n切换后需要重启游戏来恢复模组,确定要切换原版吗?","","Внимание!\n\nДля повторного включения мода необходимо перезапустить игру. Вы все равно хотите продолжить?","Aviso\n\nPara reativar o mod, você precisará reiniciar o jogo. Deseja continuar assim mesmo?","" +"CannotUnloadDuringGame","The mod cannot be disabled during a game.","試合中はModを無効化できません","游戏中不能切换原版","","Мод нельзя отключить во время игры","O mod não pode ser desabilitado durante um jogo.","" +"Cancel","Cancel","キャンセル","取消","","Отменить","Cancelar","" +"Unload","DISABLE","無効化","确认","","ОТКЛЮЧИТЬ","DESATIVAR","" +"DumpLog","Output Log","ログを出力","输出日志","","Вывести Журнал","Log de Saída","" "## ヘルプテキスト" -"CommandList","Command List:","コマンド一覧:","指令列表:","指令列表:","Список команд:","Lista de Comandos:" -"Command.winner","Display Winners","勝者を表示","显示获胜玩家","顯示上一局的獲勝玩家","Показать победителей","Mostrar Ganhadores" -"Command.lastresult","Display Match Result","試合結果を表示","显示上局游戏结果","顯示上一局的遊戲結果","Показать результат матча","Mostrar Resultado da Partida" -"Command.rename","Change Host Name","ホストの名前を変更","更改房主昵称","更改房主名字","Изменить никнейм Хоста","Mudar Nome do Anfitrião" -"Command.now","Display Active Settings","現在有効な設定を表示","显示当前设置","顯示目前設定","Показать активные настройки","Mostrar Configurações Ativas" -"Command.h_now","Display Active Settings Description","現在有効な設定の説明を表示","显示当前设置帮助","顯示現在設定的模式和職業說明","Показать описание текущих активных настроек","Mostrar Descrição das Configurações Ativas" -"Command.h_roles"," - Display Role Description","<役職名> - 役職の説明を表示","<职业名> - 显示职业介绍","<職業名> - 顯示關於該職業的介紹","<ИмяРоли> — Показать описание Роли"," — Mostrar Descrição da Classe" -"Command.h_addons"," - Display Add-on Description","<属性名> - 属性の説明を表示","<附加效果名> - 显示附加效果说明","<屬性名> - 顯示關於該屬性的介紹","<ИмяАтрибута> — Показать описание Атрибута"," — Mostrar Descrição do Atributo" -"Command.h_modes"," - Display Mode Description","<モード名> - モードの説明を表示","<模式名> - 显示模式介绍","<模式名> - 顯示關於該模式的介紹","<ИмяРежима> - Показать описание Режима"," - Mostrar Nome do Modo" -"Command.dump","Output Log to Desktop","デスクトップにログを出力","将游戏运行日志输出到桌面","在桌面上建立遊戲輸出日誌","Вывод журнала на Рабочий Стол","Enviar Registro (log) Para Área de Trabalho" -"Command.h_args","Available Args (Clipped)","使用可能な引数 (略称)","可用参数(简写)","可用的參數(簡稱)","Доступные Аргументы (Ограниченные)","Argumentos Disponíveis (Atalho)" - -"CantUse.lastresult","Unable to use /lastresult during a game.","試合中に/lastresultを使用することはできません。","不可以在游戏中使用/lastresult命令","不可以在遊戲中使用/lastresult指令","Нельзя использовать /lastresult во время игры","Impossível usar /lastresult durante um jogo." -"CantUse.killlog","Unable to use /killlog during a game.","試合中に/killlogを使用することはできません。","不可以在游戏中使用/killlog命令","不可以在遊戲中使用/killlog指令","Нельзя использовать /killlog во время игры","Impossível usar /killlog durante um jogo." +"CommandList","Command List:","コマンド一覧:","指令列表:","指令列表:","Список команд:","Lista de Comandos:","" +"Command.winner","Display Winners","勝者を表示","显示获胜玩家","顯示上一局的獲勝玩家","Показать победителей","Mostrar Ganhadores","" +"Command.lastresult","Display Match Result","試合結果を表示","显示上局游戏结果","顯示上一局的遊戲結果","Показать результат матча","Mostrar Resultado da Partida","" +"Command.rename","Change Host Name","ホストの名前を変更","更改房主昵称","更改房主名字","Изменить никнейм Хоста","Mudar Nome do Anfitrião","" +"Command.now","Display Active Settings","現在有効な設定を表示","显示当前设置","顯示目前設定","Показать активные настройки","Mostrar Configurações Ativas","" +"Command.h_now","Display Active Settings Description","現在有効な設定の説明を表示","显示当前设置帮助","顯示現在設定的模式和職業說明","Показать описание текущих активных настроек","Mostrar Descrição das Configurações Ativas","" +"Command.h_roles"," - Display Role Description","<役職名> - 役職の説明を表示","<职业名> - 显示职业介绍","<職業名> - 顯示關於該職業的介紹","<ИмяРоли> — Показать описание Роли"," — Mostrar Descrição da Classe","" +"Command.h_addons"," - Display Add-on Description","<属性名> - 属性の説明を表示","<附加效果名> - 显示附加效果说明","<屬性名> - 顯示關於該屬性的介紹","<ИмяАтрибута> — Показать описание Атрибута"," — Mostrar Descrição do Atributo","" +"Command.h_modes"," - Display Mode Description","<モード名> - モードの説明を表示","<模式名> - 显示模式介绍","<模式名> - 顯示關於該模式的介紹","<ИмяРежима> - Показать описание Режима"," - Mostrar Nome do Modo","" +"Command.dump","Output Log to Desktop","デスクトップにログを出力","将游戏运行日志输出到桌面","在桌面上建立遊戲輸出日誌","Вывод журнала на Рабочий Стол","Enviar Registro (log) Para Área de Trabalho","" +"Command.h_args","Available Args (Clipped)","使用可能な引数 (略称)","可用参数(简写)","可用的參數(簡稱)","Доступные Аргументы (Ограниченные)","Argumentos Disponíveis (Atalho)","" + +"CantUse.lastresult","Unable to use /lastresult during a game.","試合中に/lastresultを使用することはできません。","不可以在游戏中使用/lastresult命令","不可以在遊戲中使用/lastresult指令","Нельзя использовать /lastresult во время игры","Impossível usar /lastresult durante um jogo.","" +"CantUse.killlog","Unable to use /killlog during a game.","試合中に/killlogを使用することはできません。","不可以在游戏中使用/killlog命令","不可以在遊戲中使用/killlog指令","Нельзя использовать /killlog во время игры","Impossível usar /killlog durante um jogo.","" "## メッセージ" -"Message.SetToSeconds","Set to {0} seconds.","{0}秒に設定されました","信息发送冷却时间为{0}秒","訊息發送冷卻已設定為{0}秒","Установлен на {0} секунды","Definido para {0} segundos." -"Message.MessageWaitHelp","Specify the first argument in seconds.","第一引数を秒数で指定します。","指定一个数设定秒数","在指令後方指定一個數字來設定秒數","Укажите первый аргумент в секундах.","Especifique o primeiro argumento em segundos." -"Message.TemplateNotFoundHost","No messages matching ""{0}"" were found.\nPlease add to template.txt like so:\n{0}:contents\n\nDefined Tags:\n{1}","「{0}」に該当するメッセージが見つかりませんでした。\n{0}:内容\nのようにtemplate.txtに追記してください。\n\n定義されているタグ:\n{1}","未找到与“{0}”对应的消息。 \n{0}: 内容\n 请添加到 template.txt。 \n\n标签定义:\n{1}","沒有找到與「{0}」相關的訊息,\n請將內容加入template.txt作為{0}的定義內容。\n\n定義標籤:\n{1}","Сообщения соответствующие ''{0}'' не найдены. \n{0}:Содержание\nПожалуйста добавьте в template.txt \n\nОпределенные теги:\n{1}","Nenhuma mensagem correspondente a ""{0}"" foi encontrada.\nPor favor, adicione ao ""template.txt"" da seguinte forma:\n{0}:conteúdo \n\nTags Definidas:\n{1}" -"Message.TemplateNotFoundClient","No messages matching ""{0}"" were found.","「{0}」に該当するメッセージが見つかりませんでした。","未找到与“{0}”相关的消息。","沒有找到與「{0}」相關的訊息","Сообщения соответствующие ''{0}'' не найдены.","Nenhuma mensagem correspondente a ""{0}"" foi encontrada." -"Message.SyncButtonLeft","There are {0} more emergency buttons left","緊急会議ボタンはあと{0}回使用できます","剩余紧急会议次数: {0}","全場拍桌數量還剩下{0}次可使用","Кнопку экстренной встречи можно использовать еще {0} раз","Existem {0} botões de emergência sobrando" -"Message.Executed","{0} was executed","{0}を処刑しました","{0}被房主处决了","{0}被處刑了","{0} Был казнен","{0} foi executado" -"Message.HideGameSettings","Game settings have been hidden by the host.","ゲーム設定はホストによって秘匿されています。","游戏设置被房主隐藏。","遊戲設定被房主隱藏。","Настройки игры скрыты хостом","As configurações do jogo foram escondidas pelo anfitrião." -"Message.NoDescription","No description.","説明はありません。","无描述","無說明。","Описание отсутствует","Sem descrição." -"Message.KickedByDenyName","{0} was kicked because its name matched ""{1}"".","{0}は名前が「{1}」に一致したためキックされました。","{0} 被踢出,因其昵称违规 ""{1}"".","因為{0}的名字和不良名字清單中的「{1}」相符,所以踢出了他。","{0} был кикнут, так как его имя соответствует ''{1}''","{0} foi expulso porque seu nome coincide com ""{1}""." -"Message.BanedByBanList","{0} has been banned because it has been banned in the past.","{0}は過去にBAN済みのためBANされました。","{0} 已被封禁,因其之前就被封禁过","因為{0}在被記錄在黑名單中,所以禁止了{0}再次進入此房間。","{0} был заблокирован, потому что он был заблокирован в прошлый раз","{0} foi banido por já ter sido banido antes." -"Message.KickedByNoFriendCode","{0} was kicked because the friend code does not exist.","{0}はフレンドコードが存在しないためキックされました。","{0}被踢出,因其好友编号无效。","{0}因為沒有好友代碼,所以踢出了他。","{0} был кикнут, так как у него нет кода друга","{0} foi expulso, pois o código de amigo não existe." -"Message.AddedPlayerToBanList","Added {0} to the ban list.","{0}をBANリストに追記しました。","{0}已被添加到封禁名单","已將 {0} 加入到黑名單中,此玩家將無法再進入你的房間。(需安裝TOH模組)","{0} был добавлен в список забаненых игроков","{0} foi adicionado a lista de banidos." -"Message.FailedToLoadOptions","Failed to load options","オプションの読み込みに失敗しました","","","Не удалось загрузить настройки","Erro ao carregar as opções" -"Message.CopiedOptions","Copied options","オプションデータをコピーしました","","","Скопированные настройки","Opções copiadas" -"Message.ExportedOptions","Exported options","オプションデータを出力しました","","","Экспортированные настройки","Opções exportadas" -"Message.LoadedOptions","Loaded options","オプションデータを読み込みました","","","Загруженные настройки","Opções carregadas" -"Message.OnlyHostCanLoadOptions","Only the host can load options","ホストのみがオプションを読み込めます","","","Только хост может загружать настройки","Apenas o anfitrião pode carregar opções" +"Message.SetToSeconds","Set to {0} seconds.","{0}秒に設定されました","信息发送冷却时间为{0}秒","訊息發送冷卻已設定為{0}秒","Установлен на {0} секунды","Definido para {0} segundos.","" +"Message.MessageWaitHelp","Specify the first argument in seconds.","第一引数を秒数で指定します。","指定一个数设定秒数","在指令後方指定一個數字來設定秒數","Укажите первый аргумент в секундах.","Especifique o primeiro argumento em segundos.","" +"Message.TemplateNotFoundHost","No messages matching ""{0}"" were found.\nPlease add to template.txt like so:\n{0}:contents\n\nDefined Tags:\n{1}","「{0}」に該当するメッセージが見つかりませんでした。\n{0}:内容\nのようにtemplate.txtに追記してください。\n\n定義されているタグ:\n{1}","未找到与“{0}”对应的消息。 \n{0}: 内容\n 请添加到 template.txt。 \n\n标签定义:\n{1}","沒有找到與「{0}」相關的訊息,\n請將內容加入template.txt作為{0}的定義內容。\n\n定義標籤:\n{1}","Сообщения соответствующие ''{0}'' не найдены. \n{0}:Содержание\nПожалуйста добавьте в template.txt \n\nОпределенные теги:\n{1}","Nenhuma mensagem correspondente a ""{0}"" foi encontrada.\nPor favor, adicione ao ""template.txt"" da seguinte forma:\n{0}:conteúdo \n\nTags Definidas:\n{1}","" +"Message.TemplateNotFoundClient","No messages matching ""{0}"" were found.","「{0}」に該当するメッセージが見つかりませんでした。","未找到与“{0}”相关的消息。","沒有找到與「{0}」相關的訊息","Сообщения соответствующие ''{0}'' не найдены.","Nenhuma mensagem correspondente a ""{0}"" foi encontrada.","" +"Message.SyncButtonLeft","There are {0} more emergency buttons left","緊急会議ボタンはあと{0}回使用できます","剩余紧急会议次数: {0}","全場拍桌數量還剩下{0}次可使用","Кнопку экстренной встречи можно использовать еще {0} раз","Existem {0} botões de emergência sobrando","" +"Message.Executed","{0} was executed","{0}を処刑しました","{0}被房主处决了","{0}被處刑了","{0} Был казнен","{0} foi executado","" +"Message.HideGameSettings","Game settings have been hidden by the host.","ゲーム設定はホストによって秘匿されています。","游戏设置被房主隐藏。","遊戲設定被房主隱藏。","Настройки игры скрыты хостом","As configurações do jogo foram escondidas pelo anfitrião.","" +"Message.NoDescription","No description.","説明はありません。","无描述","無說明。","Описание отсутствует","Sem descrição.","" +"Message.KickedByDenyName","{0} was kicked because its name matched ""{1}"".","{0}は名前が「{1}」に一致したためキックされました。","{0} 被踢出,因其昵称违规 ""{1}"".","因為{0}的名字和不良名字清單中的「{1}」相符,所以踢出了他。","{0} был кикнут, так как его имя соответствует ''{1}''","{0} foi expulso porque seu nome coincide com ""{1}"".","" +"Message.BanedByBanList","{0} has been banned because it has been banned in the past.","{0}は過去にBAN済みのためBANされました。","{0} 已被封禁,因其之前就被封禁过","因為{0}在被記錄在黑名單中,所以禁止了{0}再次進入此房間。","{0} был заблокирован, потому что он был заблокирован в прошлый раз","{0} foi banido por já ter sido banido antes.","" +"Message.KickedByNoFriendCode","{0} was kicked because the friend code does not exist.","{0}はフレンドコードが存在しないためキックされました。","{0}被踢出,因其好友编号无效。","{0}因為沒有好友代碼,所以踢出了他。","{0} был кикнут, так как у него нет кода друга","{0} foi expulso, pois o código de amigo não existe.","" +"Message.AddedPlayerToBanList","Added {0} to the ban list.","{0}をBANリストに追記しました。","{0}已被添加到封禁名单","已將 {0} 加入到黑名單中,此玩家將無法再進入你的房間。(需安裝TOH模組)","{0} был добавлен в список забаненых игроков","{0} foi adicionado a lista de banidos.","" +"Message.FailedToLoadOptions","Failed to load options","オプションの読み込みに失敗しました","加载选项数据失败","","Не удалось загрузить настройки","Erro ao carregar as opções","" +"Message.CopiedOptions","Copied options","オプションデータをコピーしました","已将选项数据复制到剪辑版","","Скопированные настройки","Opções copiadas","" +"Message.ExportedOptions","Exported options","オプションデータを出力しました","输出选项数据到文件","","Экспортированные настройки","Opções exportadas","" +"Message.LoadedOptions","Loaded options","オプションデータを読み込みました","数据选项已加载","","Загруженные настройки","Opções carregadas","" +"Message.OnlyHostCanLoadOptions","Only the host can load options","ホストのみがオプションを読み込めます","只有房主可以加载数据选项","","Только хост может загружать настройки","Apenas o anfitrião pode carregar opções","" "## 警告" -"Warning.EgoistCannotWin","Egoist cannot win","エゴイストが勝利できません","野心家无法获胜","利己主義者無法獲勝","Эгоист не может победить!","Egoísta não pode vencer" -"Warning.OverrideExiledPlayer","All ejects will be displayed as a tie since the jackals are in effect.","ジャッカルがいるため、追放はすべて同数投票として表示されます。","","","Из-за того что в игре есть Шакал(ы) все изгнания будут видны как ничья (но только визуально, на результат голосования это не влияет)","" -"Warning.InvalidRpc","Kicked {0} because an invalid RPC was received.\nPlease check that no mods other than TOH installed.","不正なRPCを受信したため{0}をキックしました。\nTOH以外のMODが入っていないか確認してください。","{0} 被踢出,因其 RPC 无效。 \n请确保没有 TOH 以外的模组。","{0}因為收到無效的RPC,所以踢出了他,\n請確保遊戲除了TOH之外沒有其他模組系統被載入","{0} Был кикнут так как получен недопустимый RPC. \nУбедитесь что в системе нет других модов кроме TOH.","{0} Foi expulso, porque um RPC inválido foi recebido. \nPor favor verifique se nenhum outro mod além de TOH está instalado." -"Warning.NoModHost","No mod installed on the host","ホストにMODが導入されていません","该房主并未安装mod","該房房主沒有安裝Town Of Host模組","У Хоста Лобби не установлен Town of Host","O anfitrião não tem o mod instalado" -"Warning.MismatchedVersion","{0}\nhave a different version of {1}","{0} の\n{1} のバージョンがホストと合致しません。","{0} \n的游戏版本为{1},与主机版本不符。","","{0} Версия \nне соответствует версии Хоста лобби {1}","{0}\ntem uma versão diferente de {1}" -"Warning.AutoExitAtMismatchedVersion","The host has no or a different version of {0}\nYou will be kicked in {1}","{0} のバージョンがホストと合致しません。\nあと {1} 秒で切断されます。","{0} 的版本与主机不符。\n 将在 {1} 秒后被踢出。","","Версия {0} не соответствует Хосту лобби. \nВы будете исключены через {1} секунды","O anfitrião não tem ou tem uma versão diferente de {0}\nVocê será expulso em {1}" -"Warning.NotMatchImpostorCount","Max/min number of Impostor roles is greater than the number of Impostors.","インポスター役職の最小/最大割り当て人数がインポスターより多すぎます。","内鬼数量过小或过大","","Макс./Мин. количество Предательских ролей больше, чем общее количество Предателей.","O número max/min de classes de impostor é maior que o número de impostores." -"Warning.NotMatchRoleCount","The role is not cast correctly because the minimum number of roles is greater than the number of players.","ロールの最少人数合計がプレイヤーより多いため正常に配役されません。","职业数量设置大于玩家数量","","Роль подобрана неправильно, так как минимальное количество ролей больше, чем количество игроков.","A classe não foi lançada corretamente porque o número mínimo de funções é maior que o número de jogadores." +"Warning.EgoistCannotWin","Egoist cannot win","エゴイストが勝利できません","野心家无法获胜","利己主義者無法獲勝","Эгоист не может победить!","Egoísta não pode vencer","" +"Warning.OverrideExiledPlayer","All ejects will be displayed as a tie since the jackals are in effect.","ジャッカルがいるため、追放はすべて同数投票として表示されます。","由于开启了豺狼,所有驱逐动画将显示为平票(只影响视觉,不影响结果)","","Из-за того что в игре есть Шакал(ы) все изгнания будут видны как ничья (но только визуально, на результат голосования это не влияет)","Todas as ejeções serão exibidas como empate, já que os chacais estão em vigor.","" +"Warning.InvalidRpc","Kicked {0} because an invalid RPC was received.\nPlease check that no mods other than TOH installed.","不正なRPCを受信したため{0}をキックしました。\nTOH以外のMODが入っていないか確認してください。","{0} 被踢出,因其 RPC 无效。 \n请确保没有 TOH 以外的模组。","{0}因為收到無效的RPC,所以踢出了他,\n請確保遊戲除了TOH之外沒有其他模組系統被載入","{0} Был кикнут так как получен недопустимый RPC. \nУбедитесь что в системе нет других модов кроме TOH.","{0} Foi expulso, porque um RPC inválido foi recebido. \nPor favor verifique se nenhum outro mod além de TOH está instalado.","" +"Warning.NoModHost","No mod installed on the host","ホストにMODが導入されていません","该房主并未安装mod","該房房主沒有安裝Town Of Host模組","У Хоста Лобби не установлен Town of Host","O anfitrião não tem o mod instalado","" +"Warning.MismatchedVersion","{0}\nhave a different version of {1}","{0} の\n{1} のバージョンがホストと合致しません。","{0} \n的游戏版本为{1},与主机版本不符。","","{0} Версия \nне соответствует версии Хоста лобби {1}","{0}\ntem uma versão diferente de {1}","" +"Warning.AutoExitAtMismatchedVersion","The host has no or a different version of {0}\nYou will be kicked in {1}","{0} のバージョンがホストと合致しません。\nあと {1} 秒で切断されます。","{0} 的版本与主机不符。\n 将在 {1} 秒后被踢出。","","Версия {0} не соответствует Хосту лобби. \nВы будете исключены через {1} секунды","O anfitrião não tem ou tem uma versão diferente de {0}\nVocê será expulso em {1}","" +"Warning.NotMatchImpostorCount","Max/min number of Impostor roles is greater than the number of Impostors.","インポスター役職の最小/最大割り当て人数がインポスターより多すぎます。","内鬼数量过小或过大","","Макс./Мин. количество Предательских ролей больше, чем общее количество Предателей.","O número max/min de classes de impostor é maior que o número de impostores.","" +"Warning.NotMatchRoleCount","The role is not cast correctly because the minimum number of roles is greater than the number of players.","ロールの最少人数合計がプレイヤーより多いため正常に配役されません。","职业数量设置大于玩家数量","","Роль подобрана неправильно, так как минимальное количество ролей больше, чем количество игроков.","A classe não foi lançada corretamente porque o número mínimo de funções é maior que o número de jogadores.","" "## エラー" -"Error.MeetingException","Error: {0}\r\nPlease use SHIFT+M+ENTER to end the meeting","エラー: {0}\r\nSHIFT+M+ENTERで会議を強制終了してください","错误: {0}\r\n使用 SHIFT+M+ENTER 来中止会议","錯誤: {0}\r\n按下SHIFT+M+ENTER來結束會議","Ошибка: {0}\r\nПожалуйста используйте SHIFT+M+ENTER чтобы принудительно завершить собрание","Erro: {0}\r\nPor favor use SHIFT+M+ENTER para encerrar a reunião" -"Error.InvalidRoleAssignment","Error: Invalid role found for a player during role assignment({0})","エラー: 役職設定中に無効な役職のプレイヤーを発見しました({0})","错误:在分配职业时发现职业无效的玩家 ({0})","錯誤:在設定職業時發現{0}持有無效職業","Ошибка: Во время назначения роли для игрока обнаружена недопустимая роль({0})","Erro: Classe inválida encontrada para um jogador no momento de dar as classes ({0})" -"Error.SpeedBoosterNullException","Error: The speed boost destination is null.\nPlease save the log and create a bug report ticket.","エラー: スピードブースト先がnullです。\nログを保存し、バグ報告チケットを作成してください。","错误:增速者的增速目标为空。 \n请保存日志并创建错误报告票。","錯誤:加速器加速的對象無效,\n請將遊戲輸出記錄保存並在TOH官方DC群組開啟一張支援票","Ошибка: пункт назначения повышения скорости не указан. \nСохраните логи и создайте заявку на отчет об ошибке.","Erro: O destino do Impulso de Velocidade é nulo.\nPor favor, salve o log e crie um ticket de erro (bug report)." -"Error.InvalidColor","Error: Only default colors are available.","エラー:デフォルトカラー以外は使えません","错误: 仅默认颜色可用","錯誤: 無法使用遊戲自帶以外的顏色。","Ошибка: Нельзя использовать другие цвета, кроме цветов по умолчанию","Erro: Apenas as cores padrão estão disponíveis." +"Error.MeetingException","Error: {0}\r\nPlease use SHIFT+M+ENTER to end the meeting","エラー: {0}\r\nSHIFT+M+ENTERで会議を強制終了してください","错误: {0}\r\n使用 SHIFT+M+ENTER 来中止会议","錯誤: {0}\r\n按下SHIFT+M+ENTER來結束會議","Ошибка: {0}\r\nПожалуйста используйте SHIFT+M+ENTER чтобы принудительно завершить собрание","Erro: {0}\r\nPor favor use SHIFT+M+ENTER para encerrar a reunião","" +"Error.InvalidRoleAssignment","Error: Invalid role found for a player during role assignment({0})","エラー: 役職設定中に無効な役職のプレイヤーを発見しました({0})","错误:在分配职业时发现职业无效的玩家 ({0})","錯誤:在設定職業時發現{0}持有無效職業","Ошибка: Во время назначения роли для игрока обнаружена недопустимая роль({0})","Erro: Classe inválida encontrada para um jogador no momento de dar as classes ({0})","" +"Error.SpeedBoosterNullException","Error: The speed boost destination is null.\nPlease save the log and create a bug report ticket.","エラー: スピードブースト先がnullです。\nログを保存し、バグ報告チケットを作成してください。","错误:增速者的增速目标为空。 \n请保存日志并创建错误报告票。","錯誤:加速器加速的對象無效,\n請將遊戲輸出記錄保存並在TOH官方DC群組開啟一張支援票","Ошибка: пункт назначения повышения скорости не указан. \nСохраните логи и создайте заявку на отчет об ошибке.","Erro: O destino do Impulso de Velocidade é nulo.\nPor favor, salve o log e crie um ticket de erro (bug report).","" +"Error.InvalidColor","Error: Only default colors are available.","エラー:デフォルトカラー以外は使えません","错误: 仅默认颜色可用","錯誤: 無法使用遊戲自帶以外的顏色。","Ошибка: Нельзя использовать другие цвета, кроме цветов по умолчанию","Erro: Apenas as cores padrão estão disponíveis.","" "### ErrorText関連" -"ErrorLevel1","Bugs may occur.","何らかのバグが発生する可能性があります。","可能同时产生多个bug","可能同時產生多個Bug","Могут возникнуть некоторые ошибки.","Bugs podem acontecer." -"ErrorLevel2","This may be a bug.","バグが発生している可能性があります。","可能出现bug","可能出現Bug","Возможно вы столкнулись с ошибкой.","Talvez isso seja um bug." -"ErrorLevel3","This version shouldn't have been released.","このバージョンはリリースされるべきではありません。","未发布版本","未發布版本","Эта версия никогда не должна быть выпущена.","Esta versão não deveria ter sido lançada." -"TerminateCommand","Abort Command","廃村コマンド","强制结束游戏命令","強制結束遊戲指令","Команда устарела","Abortar Comando" +"ErrorLevel1","Bugs may occur.","何らかのバグが発生する可能性があります。","可能同时产生多个bug","可能同時產生多個Bug","Могут возникнуть некоторые ошибки.","Bugs podem acontecer.","" +"ErrorLevel2","This may be a bug.","バグが発生している可能性があります。","可能出现bug","可能出現Bug","Возможно вы столкнулись с ошибкой.","Talvez isso seja um bug.","" +"ErrorLevel3","This version shouldn't have been released.","このバージョンはリリースされるべきではありません。","未发布版本","未發布版本","Эта версия никогда не должна быть выпущена.","Esta versão não deveria ter sido lançada.","" +"TerminateCommand","Abort Command","廃村コマンド","强制结束游戏命令","強制結束遊戲指令","Команда устарела","Abortar Comando","" "#### 000 Test" -"ERR-000-000-0","No Error","エラーなし","无错误","無錯誤","Ошибок не обнаружено","Nenhum Erro" -"ERR-000-900-0","Test Error Lv.0","テストエラーLv0","测试错误Lv0","測試錯誤Lv0","Ошибка теста - Lv0","Erro de Teste Nvl.0" -"ERR-000-910-1","Test Error Lv.1","テストエラーLv1","测试错误Lv1","測試錯誤Lv1","Ошибка теста - Lv1","Erro de Teste Nvl.1" -"ERR-000-920-2","Test Error Lv.2","テストエラーLv2","测试错误Lv2","測試錯誤Lv2","Ошибка теста - Lv2","Erro de Teste Nvl.2" -"ERR-000-930-3","Test Error Lv.3","テストエラーLv3","测试错误Lv3","測試錯誤Lv3","Ошибка теста - Lv3","Erro de Teste Nvl.3" -"ERR-000-804-1","TownOfHost does not support the Vanilla HnS, so unloaded.","現在バニラのHide And Seekはサポートされていません。modを無効化しました。","TOH不支持原版躲猫猫, mod因此失效","Town Of Host模組尚未支援原版的Hide And Seek模式,所以模組將暫時卸載。","Мод не поддерживает обычные Прятки. Мод был отключён","TownOfHost não suporta o Esconde-Esconde padrão, então foi descarregado." +"ERR-000-000-0","No Error","エラーなし","无错误","無錯誤","Ошибок не обнаружено","Nenhum Erro","" +"ERR-000-900-0","Test Error Lv.0","テストエラーLv0","测试错误Lv0","測試錯誤Lv0","Ошибка теста - Lv0","Erro de Teste Nvl.0","" +"ERR-000-910-1","Test Error Lv.1","テストエラーLv1","测试错误Lv1","測試錯誤Lv1","Ошибка теста - Lv1","Erro de Teste Nvl.1","" +"ERR-000-920-2","Test Error Lv.2","テストエラーLv2","测试错误Lv2","測試錯誤Lv2","Ошибка теста - Lv2","Erro de Teste Nvl.2","" +"ERR-000-930-3","Test Error Lv.3","テストエラーLv3","测试错误Lv3","測試錯誤Lv3","Ошибка теста - Lv3","Erro de Teste Nvl.3","" +"ERR-000-804-1","TownOfHost does not support the Vanilla HnS, so unloaded.","現在バニラのHide And Seekはサポートされていません。modを無効化しました。","TOH不支持原版躲猫猫, mod因此失效","Town Of Host模組尚未支援原版的Hide And Seek模式,所以模組將暫時卸載。","Мод не поддерживает обычные Прятки. Мод был отключён","TownOfHost não suporta o Esconde-Esconde padrão, então foi descarregado.","" "#### 001 Main" -"ERR-001-000-3","Main dictionary has duplicated keys.","MainのDictonaryでKeyの重複が発生しています。","在主目录出现重复密钥","在主目錄出現重複密鑰","Дублирование ключей происходит в основном словаре.","Dicionário principal tem chaves duplicadas." +"ERR-001-000-3","Main dictionary has duplicated keys.","MainのDictonaryでKeyの重複が発生しています。","在主目录出现重复密钥","在主目錄出現重複密鑰","Дублирование ключей происходит в основном словаре.","Dicionário principal tem chaves duplicadas.","" +"ERR-001-010-3","Duplicate Options ID","オプションIDが重複しています","选项ID重复","","Повторяющийся идентификатор настроек","","" "#### 002 Support" -"ERR-002-000-1","Unsupported AmongUs version. Please update.","サポートされていないAmongUsバージョンです。ゲームをアップデートしてください。","","","Неподдерживаемая версия AmongUs. Пожалуйста, обновите игру","Versão não suportada do Among Us. Por favor, atualize." +"ERR-002-000-1","Unsupported AmongUs version. Please update.","サポートされていないAmongUsバージョンです。ゲームをアップデートしてください。","不支持的 AmongUs 版本,请更新","","Неподдерживаемая версия AmongUs. Пожалуйста, обновите игру","Versão não suportada do Among Us. Por favor, atualize.","" "## その他" -"DefaultSystemMessageTitle","【===== System Message =====】","【===== システムメッセージ ======】","【===== 系统信息 ======】","【===== 系統訊息 ======】","【=== Системное сообщение ===】","【===== Mensagem do Sistema ======】" -"MessageFromTheHost","【Message From The Host】","【ホストからの伝言】","【房主消息】","【房主訊息】","【Сообщение от Хоста】","【Mensagem do Anfitrião】" -"TabGroup.MainSettings","Main Settings","メイン設定","主要设置","主要設定","Основная настройка","Configurações Principais" -"TabGroup.CrewmateRoles","Crewmate Roles","クルー役職","船员阵营职业","船員職業","Роли Членов Экипажа","Classes de Tripulante" -"TabGroup.NeutralRoles","Neutral Roles","ニュートラル役職","独立阵营职业","中立職業設定","Нейтральные Роли","Classes Neutras" -"TabGroup.ImpostorRoles","Impostor Roles","インポスター役職","内鬼阵营职业","偽裝者職業設定","Роли Предателей","Classes de Impostor" -"TabGroup.Addons","Add-Ons","属性","附加效果","副職業設定","Атрибуты","Atributos" -"ActiveRolesList","Active Roles List","有効な役職一覧","有效职业一览","已開啟職業列表","Список Активных Ролей","Lista de Classes Ativas" -"ForExample","Example Use","使用例","使用例子","使用例子","Например","Exemplo de Uso" -"ForceEnd","Aborted","廃村","强制结束游戏","遊戲已被強制結束","Игра Окончена","Abortado" -"EveryoneDied","No One Left","誰もいなくなった","没有人存活","沒有人存活","Все Мертвы","Não Sobrou Ninguém" -"ForceEndText","Host has aborted the game","ホストから強制終了コマンドが入力されました","房主强制结束了游戏","房主已強制結束遊戲","Хост завершил игру","Anfitrião abortou o jogo" -"HideGameCodes","Hide Game Code","ゲームコードを隠す","直播模式","隱藏遊戲代碼","Скрыть Игровой Код","Esconder Código do Jogo" -"updateButton","Update","アップデート","更新","更新","Обновление","Atualizar" -"updatePleaseWait","Please Wait...","しばらくお待ちください...","请稍候……","請稍後...","Пожалуйста, подождите...","Por favor aguarde..." -"updateManually","Update failed.\nPlease Update Manually.","アップデートに失敗しました。\n手動でアップデートをしてください。","更新失败。\n请尝试手动更新。","更新失敗,\n請嘗試手動更新","Ошибка обновления. \nПожалуйста обновите вручную.","Atualização falhou. \nPor favor, atualize manualmente." -"updateInProgress","Updating...","アップデート中...","更新中……","更新中...","Обновление...","Atualizando..." -"announcementUpdate","Update v{0}\n{1}","アップデート v{0}\n{1}","更新到v{0}\n{1}","更新v{0}\n{1}","Обновление v{0}\n{1}","Atualização v{0}\n{1}" -"announcementChangelog","ChangeLogs v{0}\n{1}","更新内容 v{0}\n{1}","更新日志 v{0}\n{1}","更新內容v{0}\n{1}","Журнал Изменений v{0}\n{1}","Alteração de Registros v{0}\n{1}" -"updateRestart","Update Finished!\nPlease restart the game.","アップデートが完了しました。\nゲームを再起動してください。","更新完毕。\n请重新启动游戏。","更新完成!\n請重新啟動遊戲","Обновление завершено! \nПожалуйста перезапустите игру.","Atualização terminou!\nPor favor reinicie o jogo." -"updateFailed","Update failed.","アップデートに失敗しました。","更新失败。","更新失敗","Не удалось обновить.","Atualização falhou." -"onSetPublicNoLatest","Public rooms are only available in the latest version.\nPlease update.","最新版以外で公開ルームにはできません。\nアップデートをしてください。","不可以在未安装最新模组的前提下创建公开房间。\n请更新本模组。","無法建立一個公共房間,因為使用的不是最新版本的模組\n請更新本模組。","Публичные лобби запрещены за исключением последней версии.\nПожалуйста обновите.","Salas públicas estão disponíveis apenas na última versão.\nPor favor atualize." -"CanNotJoinPublicRoomNoLatest","You can't join public rooms without the latest version.\nPlease update.","最新版以外で公開ルームには参加できません。\nアップデートをしてください。","不可以在未安装最新模组的前提下进入公开房间。\n请更新本模组。","無法在未安裝最新版本的前提下進入公開房間\n請更新本模組","Вы не можете присоединяться к публичным лобби за исключением последней версии.\nПожалуйста обновите.","Você não pode entrar em salas públicas sem a última versão.\nPor favor atualize." -"ModBrokenMessage","The MOD file is damaged.\nPlease reinstall.","MODを構成するファイルが破損しています。\nもう一度導入しなおしてください。","模组文件损坏。\n请重新安装本模组。","模組檔案損壞\n請重新安裝模組","Мод был поврежден.\nПожалуйста переустановите его снова.","Os arquivos do MOD estão danificados.\nPor favor reinstale." -"UnsupportedVersion","Unsupported AmongUs version.\nPlease update.","サポートされていないAmongUsバージョンです。\nゲームをアップデートしてください。","","","Неподдерживаемая версия AmongUs.\nПожалуйста, обновите игру","Versão não suportada do Among Us. \nPor favor, atualize." -"DisabledByProgram","Public rooms have been disabled by the program","公開ルームはプログラムによって無効化されています","公开房间的操作已被程序禁用","公開房間的操作已經被程式禁用","Создание публичного лобби отключена программой","Salas públicas foram desativadas pelo programa" -"EnterVentToWin","Enter Vent to Win!!","ベントに入って勝利しろ!!","跳管道来获得胜利!","跳管道來獲得勝利!!","Запрыгните в вентиляцию чтобы победить!!","Entre no Duto Para Ganhar!!!" -"FireworksPutPhase","{0} Fireworks Left","あと{0}個置け","还要安放{0}枚烟花","還需要安裝{0}枚煙火","Осталось {0} Фейерверков","Restam {0} Fogos de Artifício" -"FireworksWaitPhase","Wait for it...","時を待て...","耐心等待....","等待時機...","Подожди время...","Espere o momento certo..." -"FireworksReadyFirePhase","Fire!","打ち上げろ!","烟花来咯,大型烟花秀开始!","準備就緒,煙火秀開始!","Огонь!","Lançar!" -"StandardHAS","Hide And Seek with Roles","役職入りでかくれんぼ","在躲猫猫模式中加入职业","在躲貓貓中加入多職業模式中的職業","Прятки с Ролями","Esconde-Esconde com Classes" -"StandardHASWaitingTime","Standby","待機時間","躲猫猫猎人等待时间","躲貓貓偽裝者等待時間","Время ожидания","Tempo de Espera" -"InvalidArgs","Invalid Args","無効な引数","无效参数","無效的參數","Недопустимые Аргументы","Argumento Inválido" -"On","ON","オン","开启","開啟","ВКЛ","Ativado" -"Off","OFF","オフ","关闭","關閉","ВЫКЛ","Desativado" -"ColoredOn","ON","オン","开启","開啟","ВКЛ","Ativado" -"ColoredOff","OFF","オフ","关闭","關閉","ВЫКЛ","Desativado" -"CurrentActiveSettingsHelp","Current Active Settings Help","現在有効な設定の説明","当前启用设置及帮助","現在設定的模式和職業說明","Справка по текущим активным настройкам","Ajuda com Configurações Ativas" -"WitchCurrentMode","Current Mode:","現在のモード:","当前模式:","現在模式:","Текущий Режим: ","Modo Atual:" -"WitchModeKill","Kill","キル","击杀","殺人","Убить","Matar" -"WitchModeSpell","Spell","スペル","诅咒","下咒","Заклясть","Feitiço" -"WitchModeDouble","Double Enable","ダブル有効","双重有效","兩個模式同時啟用","Двойной","Duplo Ativado" -"BountyCurrentTarget","Current Target","現在のターゲット","当前目标","目標","Текущая цель","Alvo Atual" -"StealthDarkened","Darkened: {0}","暗転中: {0}","","","Затемнено: {0}","" -"Roles","Roles","役職","职业","職業","Роли","Classes" -"Settings","Settings","設定","设定","設定","Настройки","Configurações" -"Addons","Add-Ons","属性","附加效果","屬性","Атрибут","Atributos" -"LastResult","Match Results","試合結果","游戏结果","上一局的遊戲結果","Результат матча","Resultados da Partida" -"KillLog","Kill Log","キル履歴","击杀日志","擊殺紀錄","История убийств","Log de Mortes" -"Maximum","Max","最大数","最多人数","最大數量","Максимум","Máximo" -"Rate0","0%","0%","0%","0%","0%","0%" -"Rate5","5%","5%","5%","5%","5%","5%" -"Rate10","10%","10%","10%","10%","10%","10%" -"Rate20","20%","20%","20%","20%","20%","20%" -"Rate30","30%","30%","30%","30%","30%","30%" -"Rate40","40%","40%","40%","40%","40%","40%" -"Rate50","50%","50%","50%","50%","50%","50%" -"Rate60","60%","60%","60%","60%","60%","60%" -"Rate70","70%","70%","70%","70%","70%","70%" -"Rate80","80%","80%","80%","80%","80%","80%" -"Rate90","90%","90%","90%","90%","90%","90%" -"Rate100","100%","100%","100%","100%","100%","100%" -"Preset","Preset","プリセット","预设","設定檔","Сохранения","Predefinição" -"Preset_1","Preset 1","プリセット1","预设1","設定檔 1","Сохранение 1","Predefinição 1" -"Preset_2","Preset 2","プリセット2","预设2","設定檔 2","Сохранение 2","Predefinição 2" -"Preset_3","Preset 3","プリセット3","预设3","設定檔 3","Сохранение 3","Predefinição 3" -"Preset_4","Preset 4","プリセット4","预设4","設定檔 4","Сохранение 4","Predefinição 4" -"Preset_5","Preset 5","プリセット5","预设5","設定檔 5","Сохранение 5","Predefinição 5" -"Standard","Standard","スタンダード","标准","普通","Стандартный","Padrão" -"GameMode","Game Mode","ゲームモード","游戏模式","遊戲模式","Игровой Режим","Modo de Jogo" -"PressTabToNextPage","Press Tab or Number for Next Page...","Tabキーまたは数字キーを押して次のページへ...","按下tab键查看更多...","按下Tab以顯示下一頁...","Нажмите Tab или Цифру чтобы перейти на следующую страницу...","Aperte Tab ou Números para Passar a Página..." -"RoleSummaryText","Game End Player-Roles List:","ゲーム終了時の役職一覧:","游戏结束时玩家的职业:","遊戲結束時玩家的職業:","Игроки и роли в конце игры:","Lista de Classes dos Jogadores ao Final do Jogo:" -"doOverride","Override %role%'s Tasks(At Least 1)","%role%のタスクを上書きする(1以上)","特别设置%role%的任务数","特別設定%role%的任務","Изменить задания у %role%","Ignorar Tarefas de %role% (Mínimo1)" -"assignCommonTasks","Give %role% Common Tasks","%role%に通常タスクを割り当てる","%role%的普通任务数","%role%有普通任務","%role% имеет общие задания","Dar Tarefas Comuns a %role%" -"roleLongTasksNum","%role% Long Tasks","%role%のロングタスクの数","%role%的长任务数","%role%的長任務數量","%role% долгие задания","Dar Tarefas Longas a %role%" -"roleShortTasksNum","%role% Short Tasks","%role%のショートタスクの数","%role%的短任务数","%role%的短任務數量","%role% короткие задания","Dar Tarefas Curtas a %role%" -"Format.Players","{0}","{0}人","{0}人","{0}人","{0}","{0}" -"Format.Seconds","{0}s","{0}秒","{0}秒","{0}秒","{0} с","{0}seg" -"Format.Percent","{0}%","{0}%","{0}%","{0}%","{0}%","{0}%" -"Format.Times","{0}","{0}回","{0}次","{0}次","{0}","{0}" -"Format.Multiplier","{0}x","{0}x","{0}x","{0}x","{0}x","{0}x" -"Format.Votes","{0}","{0}票","{0}票","{0}票","{0}","{0}" -"Format.Pieces","{0}","{0}個","{0}个","{0}個","{0}","{0}" -"KillButtonText","Kill","キル","击杀","殺死","Убить","Matar" -"SniperSnipeButtonText","Snipe","狙撃","狙击","狙擊","Застрелить","Atirar" -"FireWorksExplosionButtonText","Fire","爆破","引爆","引爆","Зажечь","Explodir" -"FireWorksInstallAtionButtonText","Set","設置","设置","安裝","Установить","Colocar" -"SerialKillerSuicideButtonText","Suicide Timer","自殺まで","自杀倒计时","自殺倒數","До Суицида","Suicídio Em" -"WarlockCurseButtonText","Curse","呪う","下咒","詛咒","Проклинать","Amaldiçoar" -"WitchSpellButtonText","Spell","スペル","诅咒","下咒","Заклинать","Feitiço" -"VampireBiteButtonText","Bite","噛む","吸血","吸血","Укусить","Morder" -"ArsonistDouseButtonText","Douse","塗る","涂油","澆油","Облить","Encharcar" -"PuppeteerOperateButtonText","Manipulate","操る","操控","操控","Управлять","Manipular" -"BountyHunterChangeButtonText","Swap","変更","变更","變更","Смена цели","Trocar" -"EvilTrackerChangeButtonText","Track","追跡","追踪","追蹤","Отслеживать","Rastrear" -"DefaultShapeshiftText","Shift","変身","变形","變身","Превращение","Mutar" -"DisabledBySettings","Disabled by Settings","設定で無効化されています","已在设置被中禁用","在設定中被禁用","Отключено настройкой","Desativado Pelas Configurações" -"Disabled","Disabled","無効","禁用","已被禁用","Отключено","Desativado" -"FailToTrack","Failed To Track","追跡失敗","追踪失败","無法追蹤","Не удалось отследить","Falha ao Rastrear" -"LastAdminInfo","Last-minute admin information","直前のアドミン情報","","","Актуальная информация администратора","" -"MurderNotify","Murder","キル発生","","","Убийство","" -"Deadbody","DEAD","死体","","","Труп","" -"PenguinKillButtonText","Drag","拉致","","","ПЕРЕТАСКИВАТЬ","" -"PenguinTimerText","Drag Timer","残り時間","","","Время перетаскивания","" -"Infected","Infected","感染","","","Заражён","" +"DefaultSystemMessageTitle","【===== System Message =====】","【===== システムメッセージ ======】","【===== 系统信息 ======】","【===== 系統訊息 ======】","【=== Системное сообщение ===】","【===== Mensagem do Sistema ======】","" +"MessageFromTheHost","【Message From The Host】","【ホストからの伝言】","【房主消息】","【房主訊息】","【Сообщение от Хоста】","【Mensagem do Anfitrião】","" +"TabGroup.MainSettings","Main Settings","メイン設定","主要设置","主要設定","Основная настройка","Configurações Principais","" +"TabGroup.CrewmateRoles","Crewmate Roles","クルー役職","船员阵营职业","船員職業","Роли Членов Экипажа","Classes de Tripulante","" +"TabGroup.NeutralRoles","Neutral Roles","ニュートラル役職","独立阵营职业","中立職業設定","Нейтральные Роли","Classes Neutras","" +"TabGroup.ImpostorRoles","Impostor Roles","インポスター役職","内鬼阵营职业","偽裝者職業設定","Роли Предателей","Classes de Impostor","" +"TabGroup.Addons","Add-Ons","属性","附加效果","屬性","Атрибут","Atributos","" +"ActiveRolesList","Active Roles List","有効な役職一覧","有效职业一览","已開啟職業列表","Список Активных Ролей","Lista de Classes Ativas","" +"ForExample","Example Use","使用例","使用例子","使用例子","Например","Exemplo de Uso","" +"ForceEnd","Aborted","廃村","强制结束游戏","遊戲已被強制結束","Игра Окончена","Abortado","" +"EveryoneDied","No One Left","誰もいなくなった","没有人存活","沒有人存活","Все Мертвы","Não Sobrou Ninguém","" +"ForceEndText","Host has aborted the game","ホストから強制終了コマンドが入力されました","房主强制结束了游戏","房主已強制結束遊戲","Хост завершил игру","Anfitrião abortou o jogo","" +"ShowResults","Show Results","結果を表示","显示复盘信息","","Показать результаты","Mostrar Resultados","" +"HideResults","Hide Results","結果を隠す","隐藏复盘信息","","Скрыть результаты","Esconder Resultados","" +"HideGameCodes","Hide Game Code","ゲームコードを隠す","直播模式","隱藏遊戲代碼","Скрыть Игровой Код","Esconder Código do Jogo","" +"updateButton","Update","アップデート","更新","更新","Обновление","Atualizar","" +"updatePleaseWait","Please Wait...","しばらくお待ちください...","请稍候……","請稍後...","Пожалуйста, подождите...","Por favor aguarde...","" +"updateManually","Update failed.\nPlease Update Manually.","アップデートに失敗しました。\n手動でアップデートをしてください。","更新失败。\n请尝试手动更新。","更新失敗,\n請嘗試手動更新","Ошибка обновления. \nПожалуйста обновите вручную.","Atualização falhou. \nPor favor, atualize manualmente.","" +"updateInProgress","Updating...","アップデート中...","更新中……","更新中...","Обновление...","Atualizando...","" +"announcementUpdate","Update v{0}\n{1}","アップデート v{0}\n{1}","更新到v{0}\n{1}","更新v{0}\n{1}","Обновление v{0}\n{1}","Atualização v{0}\n{1}","" +"announcementChangelog","ChangeLogs v{0}\n{1}","更新内容 v{0}\n{1}","更新日志 v{0}\n{1}","更新內容v{0}\n{1}","Журнал Изменений v{0}\n{1}","Alteração de Registros v{0}\n{1}","" +"updateRestart","Update Finished!\nPlease restart the game.","アップデートが完了しました。\nゲームを再起動してください。","更新完毕。\n请重新启动游戏。","更新完成!\n請重新啟動遊戲","Обновление завершено! \nПожалуйста перезапустите игру.","Atualização terminou!\nPor favor reinicie o jogo.","" +"updateFailed","Update failed.","アップデートに失敗しました。","更新失败。","更新失敗","Не удалось обновить.","Atualização falhou.","" +"onSetPublicNoLatest","Public rooms are only available in the latest version.\nPlease update.","最新版以外で公開ルームにはできません。\nアップデートをしてください。","不可以在未安装最新模组的前提下创建公开房间。\n请更新本模组。","無法建立一個公共房間,因為使用的不是最新版本的模組\n請更新本模組。","Публичные лобби запрещены за исключением последней версии.\nПожалуйста обновите.","Salas públicas estão disponíveis apenas na última versão.\nPor favor atualize.","" +"CanNotJoinPublicRoomNoLatest","You can't join public rooms without the latest version.\nPlease update.","最新版以外で公開ルームには参加できません。\nアップデートをしてください。","不可以在未安装最新模组的前提下进入公开房间。\n请更新本模组。","無法在未安裝最新版本的前提下進入公開房間\n請更新本模組","Вы не можете присоединяться к публичным лобби за исключением последней версии.\nПожалуйста обновите.","Você não pode entrar em salas públicas sem a última versão.\nPor favor atualize.","" +"ModBrokenMessage","The MOD file is damaged.\nPlease reinstall.","MODを構成するファイルが破損しています。\nもう一度導入しなおしてください。","模组文件损坏。\n请重新安装本模组。","模組檔案損壞\n請重新安裝模組","Мод был поврежден.\nПожалуйста переустановите его снова.","Os arquivos do MOD estão danificados.\nPor favor reinstale.","" +"UnsupportedVersion","Unsupported AmongUs version.\nPlease update.","サポートされていないAmongUsバージョンです。\nゲームをアップデートしてください。","不支持的 AmongUs 版本。\n请更新","","Неподдерживаемая версия AmongUs.\nПожалуйста, обновите игру","Versão não suportada do Among Us. \nPor favor, atualize.","" +"DisabledByProgram","Public rooms have been disabled by the program","公開ルームはプログラムによって無効化されています","公开房间的操作已被程序禁用","公開房間的操作已經被程式禁用","Создание публичного лобби отключена программой","Salas públicas foram desativadas pelo programa","" +"PublicNotAvailableOnThisVersion","Public lobbies are not available on this version.\nPlease visit our Discord 'modアナウンス' channel for details.","このバージョンでは公開ルームは使用できません。\n詳しくはDiscordの「modアナウンス」チャンネルを参照してください。","此版本不支持公开。\n请访问TownOfHost的Discord的「modアナウンス」频道获取更多信息","","Публичные лобби недоступны в этой версии.\nПожалуйста, обратитесь к каналу Discord «「modアナウンス」» для получения дополнительной информации","","" +"EnterVentToWin","Enter Vent to Win!!","ベントに入って勝利しろ!!","跳管道来获得胜利!","跳管道來獲得勝利!!","Запрыгните в вентиляцию чтобы победить!!","Entre no Duto Para Ganhar!!!","" +"FireworksPutPhase","{0} Fireworks Left","あと{0}個置け","还要安放{0}枚烟花","還需要安裝{0}枚煙火","Осталось {0} Фейерверков","Restam {0} Fogos de Artifício","" +"FireworksWaitPhase","Wait for it...","時を待て...","耐心等待....","等待時機...","Подожди время...","Espere o momento certo...","" +"FireworksReadyFirePhase","Fire!","打ち上げろ!","烟花来咯,大型烟花秀开始!","準備就緒,煙火秀開始!","Огонь!","Lançar!","" +"StandardHAS","Hide And Seek with Roles","役職入りでかくれんぼ","在躲猫猫模式中加入职业","在躲貓貓中加入多職業模式中的職業","Прятки с Ролями","Esconde-Esconde com Classes","" +"StandardHASWaitingTime","Standby","待機時間","躲猫猫猎人等待时间","躲貓貓偽裝者等待時間","Время ожидания","Tempo de Espera","" +"InvalidArgs","Invalid Args","無効な引数","无效参数","無效的參數","Недопустимые Аргументы","Argumento Inválido","" +"On","ON","オン","开启","開啟","ВКЛ","Ativado","" +"Off","OFF","オフ","关闭","關閉","ВЫКЛ","Desativado","" +"ColoredOn","ON","オン","开启","開啟","ВКЛ","Ativado","" +"ColoredOff","OFF","オフ","关闭","關閉","ВЫКЛ","Desativado","" +"CurrentActiveSettingsHelp","Current Active Settings Help","現在有効な設定の説明","当前启用设置及帮助","現在設定的模式和職業說明","Справка по текущим активным настройкам","Ajuda com Configurações Ativas","" +"WitchCurrentMode","Current Mode:","現在のモード:","当前模式:","現在模式:","Текущий Режим: ","Modo Atual:","" +"WitchModeKill","","キル","击杀","殺人","Убит","Matar","" +"WitchModeSpell","Spell","スペル","诅咒","下咒","Заклинать","Feitiço","" +"WitchModeDouble","Double Enable","ダブル有効","双重有效","兩個模式同時啟用","Двойной","Duplo Ativado","" +"BountyCurrentTarget","Current Target","現在のターゲット","当前目标","目標","Текущая цель","Alvo Atual","" +"StealthDarkened","Darkened: {0}","暗転中: {0}","失明:{0}","","Затемнено: {0}","Escurecidos: {0}","" +"Roles","Roles","役職","职业","職業","Роль","Classes","" +"Settings","Settings","設定","设定","設定","Настройки","Configurações","" +"Addons","Add-Ons","属性","附加职业","屬性","Атрибут","","" +"LastResult","Match Results","試合結果","游戏结果","上一局的遊戲結果","Результат матча","Resultados da Partida","" +"KillLog","Kill Log","キル履歴","击杀日志","擊殺紀錄","История убийств","Log de Mortes","" +"Maximum","Max","最大数","最多人数","最大數量","Максимум","Máximo","" +"Rate0","0%","0%","0%","0%","0%","0%","" +"Rate5","5%","5%","5%","5%","5%","5%","" +"Rate10","10%","10%","10%","10%","10%","10%","" +"Rate20","20%","20%","20%","20%","20%","20%","" +"Rate30","30%","30%","30%","30%","30%","30%","" +"Rate40","40%","40%","40%","40%","40%","40%","" +"Rate50","50%","50%","50%","50%","50%","50%","" +"Rate60","60%","60%","60%","60%","60%","60%","" +"Rate70","70%","70%","70%","70%","70%","70%","" +"Rate80","80%","80%","80%","80%","80%","80%","" +"Rate90","90%","90%","90%","90%","90%","90%","" +"Rate100","100%","100%","100%","100%","100%","100%","" +"Preset","Preset","プリセット","预设","設定檔","Сохранения","Predefinição","" +"Preset_1","Preset 1","プリセット1","预设1","設定檔 1","Сохранение 1","Predefinição 1","" +"Preset_2","Preset 2","プリセット2","预设2","設定檔 2","Сохранение 2","Predefinição 2","" +"Preset_3","Preset 3","プリセット3","预设3","設定檔 3","Сохранение 3","Predefinição 3","" +"Preset_4","Preset 4","プリセット4","预设4","設定檔 4","Сохранение 4","Predefinição 4","" +"Preset_5","Preset 5","プリセット5","预设5","設定檔 5","Сохранение 5","Predefinição 5","" +"Standard","Standard","スタンダード","标准","普通","Стандартный","Padrão","" +"GameMode","Game Mode","ゲームモード","游戏模式","遊戲模式","Игровой Режим","Modo de Jogo","" +"PressTabToNextPage","Press Tab or Number for Next Page...","Tabキーまたは数字キーを押して次のページへ...","按下tab键查看更多...","按下Tab以顯示下一頁...","Нажмите Tab или Цифру чтобы перейти на следующую страницу...","Aperte Tab ou Números para Passar a Página...","" +"RoleSummaryText","Game End Player-Roles List:","ゲーム終了時の役職一覧:","游戏结束时玩家的职业:","遊戲結束時玩家的職業:","Игроки и роли в конце игры:","Lista de Classes dos Jogadores ao Final do Jogo:","" +"doOverride","Override %role%'s Tasks(At Least 1)","%role%のタスクを上書きする(1以上)","特别设置%role%的任务数","特別設定%role%的任務","Изменить задания у %role%","Ignorar Tarefas de %role% (Mínimo1)","" +"assignCommonTasks","Give %role% Common Tasks","%role%に通常タスクを割り当てる","%role%的普通任务数","%role%有普通任務","%role% имеет общие задания","Dar Tarefas Comuns a %role%","" +"roleLongTasksNum","%role% Long Tasks","%role%のロングタスクの数","%role%的长任务数","%role%的長任務數量","%role% долгие задания","Dar Tarefas Longas a %role%","" +"roleShortTasksNum","%role% Short Tasks","%role%のショートタスクの数","%role%的短任务数","%role%的短任務數量","%role% короткие задания","Dar Tarefas Curtas a %role%","" +"Format.Players","{0}","{0}人","{0}人","{0}人","{0}","{0}","" +"Format.Seconds","{0}s","{0}秒","{0}秒","{0}秒","{0} с","{0}seg","" +"Format.Percent","{0}%","{0}%","{0}%","{0}%","{0}%","{0}%","" +"Format.Times","{0}","{0}回","{0}次","{0}次","{0}","{0}","" +"Format.Multiplier","{0}x","{0}x","{0}x","{0}x","{0}x","{0}x","" +"Format.Votes","{0}","{0}票","{0}票","{0}票","{0}","{0}","" +"Format.Pieces","{0}","{0}個","{0}个","{0}個","{0}","{0}","" +"KillButtonText","Kill","キル","击杀","殺死","Убить","Matar","" +"SniperSnipeButtonText","Snipe","狙撃","狙击","狙擊","Застрелить","Atirar","" +"FireWorksExplosionButtonText","Fire","爆破","引爆","引爆","Зажечь","Explodir","" +"FireWorksInstallAtionButtonText","Set","設置","设置","安裝","Установить","Colocar","" +"SerialKillerSuicideButtonText","Suicide Timer","自殺まで","自杀倒计时","自殺倒數","До Суицида","Suicídio Em","" +"WarlockCurseButtonText","Curse","呪う","下咒","詛咒","Проклинать","Amaldiçoar","" +"WitchSpellButtonText","Spell","スペル","下咒","下咒","Заклясть","Feitiço","" +"VampireBiteButtonText","Bite","噛む","吸血","吸血","Укусить","Morder","" +"ArsonistDouseButtonText","Douse","塗る","涂油","澆油","Облить","Encharcar","" +"PuppeteerOperateButtonText","Manipulate","操る","操控","操控","Управлять","Manipular","" +"BountyHunterChangeButtonText","Swap","変更","变更","變更","Смена цели","Trocar","" +"EvilTrackerChangeButtonText","Track","追跡","追踪","追蹤","Отслеживать","Rastrear","" +"DefaultShapeshiftText","Shift","変身","变形","變身","Превращение","Mutar","" +"DisabledBySettings","Disabled by Settings","設定で無効化されています","已在设置被中禁用","在設定中被禁用","Отключено настройкой","Desativado Pelas Configurações","" +"Disabled","Disabled","無効","禁用","已被禁用","Отключено","Desativado","" +"FailToTrack","Failed To Track","追跡失敗","追踪失败","無法追蹤","Не удалось отследить","Falha ao Rastrear","" +"LastAdminInfo","Last-minute admin information","直前のアドミン情報","一分钟内的管理信息","","Актуальная информация администратора","Informações administrativas recentes","" +"MurderNotify","Murder","キル発生","击杀发生","","Убийство","Assassinato","" +"Deadbody","DEAD","死体","尸体","","Труп","MORTO","" +"PenguinKillButtonText","Drag","拉致","拖拽","","ПЕРЕТАСКИВАТЬ","Arrastar","" +"PenguinTimerText","Drag Timer","残り時間","剩余时间","","Время перетаскивания","Tempo para Arrastar","" +"Infected","","感染","感染","","Заражён","Infectado","" \ No newline at end of file diff --git a/Roles/AddOns/Common/AddOnsAssignData.cs b/Roles/AddOns/Common/AddOnsAssignData.cs index 1ea75b13d..4ccaa567a 100644 --- a/Roles/AddOns/Common/AddOnsAssignData.cs +++ b/Roles/AddOns/Common/AddOnsAssignData.cs @@ -29,10 +29,7 @@ public class AddOnsAssignData CustomRoles.HASTroll, CustomRoles.GM, }; - static readonly IEnumerable ValidRoles = CustomRolesHelper.AllRoles.Where(role - => role < CustomRoles.NotAssigned - && !InvalidRoles.Contains(role) - ); + static readonly IEnumerable ValidRoles = CustomRolesHelper.AllRoles.Where(role => !InvalidRoles.Contains(role)); static CustomRoles[] CrewmateRoles = ValidRoles.Where(role => role.IsCrewmate()).ToArray(); static CustomRoles[] ImpostorRoles = ValidRoles.Where(role => role.IsImpostor()).ToArray(); static CustomRoles[] NeutralRoles = ValidRoles.Where(role => role.IsNeutral()).ToArray(); diff --git a/Roles/Core/CustomRoleManager.cs b/Roles/Core/CustomRoleManager.cs index 02baaa528..ddfd77335 100644 --- a/Roles/Core/CustomRoleManager.cs +++ b/Roles/Core/CustomRoleManager.cs @@ -29,7 +29,7 @@ public static class CustomRoleManager /// /// 実際にキルを行ったプレイヤー 不変 /// >Killerが実際にキルを行おうとしたプレイヤー 不変 - public static void OnCheckMurder(PlayerControl attemptKiller, PlayerControl attemptTarget) + public static bool OnCheckMurder(PlayerControl attemptKiller, PlayerControl attemptTarget) => OnCheckMurder(attemptKiller, attemptTarget, attemptKiller, attemptTarget); /// /// @@ -38,7 +38,7 @@ public static void OnCheckMurder(PlayerControl attemptKiller, PlayerControl atte /// >Killerが実際にキルを行おうとしたプレイヤー 不変 /// 見た目上でキルを行うプレイヤー 可変 /// 見た目上でキルされるプレイヤー 可変 - public static void OnCheckMurder(PlayerControl attemptKiller, PlayerControl attemptTarget, PlayerControl appearanceKiller, PlayerControl appearanceTarget) + public static bool OnCheckMurder(PlayerControl attemptKiller, PlayerControl attemptTarget, PlayerControl appearanceKiller, PlayerControl appearanceTarget) { Logger.Info($"Attempt :{attemptKiller.GetNameWithRole()} => {attemptTarget.GetNameWithRole()}", "CheckMurder"); if (appearanceKiller != attemptKiller || appearanceTarget != attemptTarget) @@ -49,7 +49,10 @@ public static void OnCheckMurder(PlayerControl attemptKiller, PlayerControl atte appearanceKiller.ResetKillCooldown(); // 無効なキルをブロックする処理 必ず最初に実行する - if (!CheckMurderPatch.CheckForInvalidMurdering(info)) return; + if (!CheckMurderPatch.CheckForInvalidMurdering(info)) + { + return false; + } var killerRole = attemptKiller.GetRoleClass(); var targetRole = attemptTarget.GetRoleClass(); @@ -62,7 +65,10 @@ public static void OnCheckMurder(PlayerControl attemptKiller, PlayerControl atte // ターゲットのキルチェック処理実行 if (targetRole != null) { - if (!targetRole.OnCheckMurderAsTarget(info)) return; + if (!targetRole.OnCheckMurderAsTarget(info)) + { + return false; + } } } // キラーのキルチェック処理実行 @@ -75,11 +81,13 @@ public static void OnCheckMurder(PlayerControl attemptKiller, PlayerControl atte //MurderPlayer用にinfoを保存 CheckMurderInfos[appearanceKiller.PlayerId] = info; appearanceKiller.RpcMurderPlayer(appearanceTarget); + return true; } else { if (!info.CanKill) Logger.Info($"{appearanceTarget.GetNameWithRole()}をキル出来ない。", "CheckMurder"); if (!info.DoKill) Logger.Info($"{appearanceKiller.GetNameWithRole()}はキルしない。", "CheckMurder"); + return false; } } /// @@ -166,20 +174,16 @@ public static void OnFixedUpdate(PlayerControl player) /// public static HashSet> OnFixedUpdateOthers = new(); - public static bool OnSabotage(PlayerControl player, SystemTypes systemType, byte amount) + public static bool OnSabotage(PlayerControl player, SystemTypes systemType) { bool cancel = false; foreach (var roleClass in AllActiveRoles.Values) { - if (!roleClass.OnSabotage(player, systemType, amount)) + if (!roleClass.OnSabotage(player, systemType)) { cancel = true; } } - if (!RepairSystemPatch.OnSabotage(player, systemType, amount)) - { - cancel = true; - } return !cancel; } // ==初期化関連処理 == diff --git a/Roles/Core/Descriptions/RoleDescription.cs b/Roles/Core/Descriptions/RoleDescription.cs new file mode 100644 index 000000000..969935389 --- /dev/null +++ b/Roles/Core/Descriptions/RoleDescription.cs @@ -0,0 +1,47 @@ +using System.Text; + +namespace TownOfHost.Roles.Core.Descriptions; + +public abstract class RoleDescription +{ + public RoleDescription(SimpleRoleInfo roleInfo) + { + RoleInfo = roleInfo; + } + + public SimpleRoleInfo RoleInfo { get; } + /// イントロなどで表示される短い文 + public abstract string Blurb { get; } + /// + /// ヘルプコマンドで使用される長い説明文
+ /// AmongUs2023.7.12時点で,Impostor, Crewmateに関してはバニラ側でロング説明文が未実装のため「タスクを行う」と表示される + ///
+ public abstract string Description { get; } + public string FullFormatHelp + { + get + { + var builder = new StringBuilder(256); + // 役職名と説明文 + builder.AppendFormat("\n", BlankLineSize); + builder.AppendFormat("{1}\n", FirstHeaderSize, Translator.GetRoleString(RoleInfo.RoleName.ToString()).Color(RoleInfo.RoleColor.ToReadableColor())); + builder.AppendFormat("{1}\n", BodySize, Description); + // 陣営 + builder.AppendFormat("\n", BlankLineSize); + builder.AppendFormat("{1}\n", SecondHeaderSize, Translator.GetString("Team")); + // マッドメイトはインポスター陣営 + var roleTeam = RoleInfo.CustomRoleType == CustomRoleTypes.Madmate ? CustomRoleTypes.Impostor : RoleInfo.CustomRoleType; + builder.AppendFormat("{1}\n", BodySize, Translator.GetString($"CustomRoleTypes.{roleTeam}")); + // バニラ役職判定 + builder.AppendFormat("\n", BlankLineSize); + builder.AppendFormat("{1}\n", SecondHeaderSize, Translator.GetString("Basis")); + builder.AppendFormat("{1}\n", BodySize, Translator.GetString(RoleInfo.BaseRoleType.Invoke().ToString())); + return builder.ToString(); + } + } + + public const string FirstHeaderSize = "130%"; + public const string SecondHeaderSize = "100%"; + public const string BodySize = "70%"; + public const string BlankLineSize = "30%"; +} diff --git a/Roles/Core/Descriptions/SingleRoleDescription.cs b/Roles/Core/Descriptions/SingleRoleDescription.cs new file mode 100644 index 000000000..a56903381 --- /dev/null +++ b/Roles/Core/Descriptions/SingleRoleDescription.cs @@ -0,0 +1,23 @@ +namespace TownOfHost.Roles.Core.Descriptions; + +/// +/// Mod役職の説明文 +/// +public class SingleRoleDescription : RoleDescription +{ + public SingleRoleDescription(SimpleRoleInfo roleInfo) : base(roleInfo) + { + BlurbKey = $"{roleInfo.RoleName}{BlurbSuffix}"; + DescriptionKey = $"{RoleInfo.RoleName}{DescriptionSuffix}"; + } + + /// 短いひとこと説明文の翻訳キー + public string BlurbKey { get; } + /// 長い説明文の翻訳キー + public string DescriptionKey { get; } + public override string Blurb => Translator.GetString(BlurbKey); + public override string Description => Translator.GetString(DescriptionKey); + + public const string BlurbSuffix = "Info"; + public const string DescriptionSuffix = "InfoLong"; +} diff --git a/Roles/Core/Descriptions/VanillaRoleDescription.cs b/Roles/Core/Descriptions/VanillaRoleDescription.cs new file mode 100644 index 000000000..dc944bfba --- /dev/null +++ b/Roles/Core/Descriptions/VanillaRoleDescription.cs @@ -0,0 +1,18 @@ +using AmongUs.GameOptions; + +namespace TownOfHost.Roles.Core.Descriptions; + +/// +/// バニラ役職の説明文 +/// +public class VanillaRoleDescription : RoleDescription +{ + public VanillaRoleDescription(SimpleRoleInfo roleInfo, RoleTypes vanillaRoleType) : base(roleInfo) + { + this.vanillaRoleType = vanillaRoleType; + } + private readonly RoleTypes vanillaRoleType; + + public override string Blurb => DestroyableSingleton.Instance.GetRole(vanillaRoleType).Blurb; + public override string Description => DestroyableSingleton.Instance.GetRole(vanillaRoleType).BlurbLong; +} diff --git a/Roles/Core/Interfaces/IAdditionalWinner.cs b/Roles/Core/Interfaces/IAdditionalWinner.cs index ff27ab553..9b4824769 100644 --- a/Roles/Core/Interfaces/IAdditionalWinner.cs +++ b/Roles/Core/Interfaces/IAdditionalWinner.cs @@ -2,5 +2,5 @@ namespace TownOfHost.Roles.Core.Interfaces; public interface IAdditionalWinner { - public bool CheckWin(out AdditionalWinners winnerType); + public bool CheckWin(ref CustomRoles winnerRole); } diff --git a/Roles/Core/Interfaces/IImpostor.cs b/Roles/Core/Interfaces/IImpostor.cs index 221738281..b8c79f157 100644 --- a/Roles/Core/Interfaces/IImpostor.cs +++ b/Roles/Core/Interfaces/IImpostor.cs @@ -9,6 +9,8 @@ namespace TownOfHost.Roles.Core.Interfaces; ///
public interface IImpostor : IKiller, ISchrodingerCatOwner { + /// インポスターは基本サボタージュボタンを使える + bool IKiller.CanUseSabotageButton() => true; /// /// ラストインポスターになれるかどうか デフォルトtrue /// diff --git a/Roles/Core/Interfaces/IKiller.cs b/Roles/Core/Interfaces/IKiller.cs index b5ca28d16..2a0e9f55c 100644 --- a/Roles/Core/Interfaces/IKiller.cs +++ b/Roles/Core/Interfaces/IKiller.cs @@ -27,6 +27,17 @@ public interface IKiller /// /// キルクールダウン(秒) public float CalculateKillCooldown() => Options.DefaultKillCooldown; + /// + /// サボタージュボタンを使えるかどうか + /// + /// trueを返した場合,サボタージュボタンを使える + public bool CanUseSabotageButton(); + /// + /// ベントボタンを使えるかどうか + /// デフォルトでは使用可能 + /// + /// trueを返した場合,ベントボタンを使える + public bool CanUseImpostorVentButton() => true; /// /// キラーとしてのCheckMurder処理
diff --git a/Roles/Core/Interfaces/ISystemTypeUpdateHook.cs b/Roles/Core/Interfaces/ISystemTypeUpdateHook.cs new file mode 100644 index 000000000..64b1ef24a --- /dev/null +++ b/Roles/Core/Interfaces/ISystemTypeUpdateHook.cs @@ -0,0 +1,37 @@ +namespace TownOfHost.Roles.Core.Interfaces; + +/// +/// サボタージュに対する関与へのパッチ +/// +public interface ISystemTypeUpdateHook +{ + /// + /// Skeld,MIRAHQ, Polus, Fungleのリアクター + /// + public bool UpdateReactorSystem(ReactorSystemType reactorSystem, byte amount) => true; + /// + /// Airshipのリアクター + /// + public bool UpdateHeliSabotageSystem(HeliSabotageSystem heliSabotageSystem, byte amount) => true; + /// + /// Skeld, MIRAHQのO2 + /// + public bool UpdateLifeSuppSystem(LifeSuppSystemType lifeSuppSystem, byte amount) => true; + /// + /// Skeld, Polus, Airshipのコミュサボ + /// + public bool UpdateHudOverrideSystem(HudOverrideSystemType hudOverrideSystem, byte amount) => true; + /// + /// MIRAHQ, Fungleのコミュサボ + /// + public bool UpdateHqHudSystem(HqHudSystemType hqHudSystemType, byte amount) => true; + /// + /// Skeld, MIRAHQ, Polus, Airshipの停電と配電盤 + /// + public bool UpdateSwitchSystem(SwitchSystem switchSystem, byte amount) => true; + /// + /// Polus, Airship, Fungleのドア開け + /// + public bool UpdateDoorsSystem(DoorsSystemType doorsSystem, byte amount) => true; +} + diff --git a/Roles/Core/RoleBase.cs b/Roles/Core/RoleBase.cs index 0539d9b80..69d0695ed 100644 --- a/Roles/Core/RoleBase.cs +++ b/Roles/Core/RoleBase.cs @@ -240,15 +240,12 @@ public virtual void AfterMeetingTasks() public virtual bool OnInvokeSabotage(SystemTypes systemType) => true; /// - /// 誰かがサボタージュが発生させたときに呼ばれる - /// amount&0x80!=0がサボタージュ開始タイミング - /// その他の値は修理情報など + /// 誰かがサボタージュを発生させたときに呼ばれる /// /// アクションを起こしたプレイヤー /// サボタージュの種類 - /// 現在の状態など - /// falseで修理活動等のキャンセル - public virtual bool OnSabotage(PlayerControl player, SystemTypes systemType, byte amount) => true; + /// falseでサボタージュのキャンセル + public virtual bool OnSabotage(PlayerControl player, SystemTypes systemType) => true; // NameSystem // 名前は下記の構成で表示される diff --git a/Roles/Core/SimpleRoleInfo.cs b/Roles/Core/SimpleRoleInfo.cs index 90ff47a1d..81b97a67f 100644 --- a/Roles/Core/SimpleRoleInfo.cs +++ b/Roles/Core/SimpleRoleInfo.cs @@ -1,7 +1,7 @@ using System; -using System.Linq; using UnityEngine; using AmongUs.GameOptions; +using TownOfHost.Roles.Core.Descriptions; using static TownOfHost.Options; @@ -23,25 +23,16 @@ public class SimpleRoleInfo public bool IsEnable = false; public OptionCreatorDelegate OptionCreator; public string ChatCommand; - public bool RequireResetCam; + /// 本人視点のみインポスターに見える役職 + public bool IsDesyncImpostor; private Func introSound; public AudioClip IntroSound => introSound?.Invoke(); private Func canMakeMadmate; public bool CanMakeMadmate => canMakeMadmate?.Invoke() == true; public readonly string WikiPage = null; - /// - /// 人数設定の最小人数, 最大人数, 一単位数 - /// - public IntegerValueRule AssignCountRule; - /// - /// 人数設定に対し何人単位でアサインするか - /// 役職の抽選回数 = 設定人数 / AssignUnitCount - /// - public int AssignUnitCount => AssignCountRule?.Step ?? 1; - /// - /// 実際にアサインされる役職の内訳 - /// - public CustomRoles[] AssignUnitRoles; + public RoleAssignInfo AssignInfo { get; } + /// 役職の説明関係 + public RoleDescription Description { get; private set; } private SimpleRoleInfo( Type classType, @@ -54,13 +45,12 @@ private SimpleRoleInfo( OptionCreatorDelegate optionCreator, string chatCommand, string colorCode, - bool requireResetCam, + bool isDesyncImpostor, TabGroup tab, Func introSound, Func canMakeMadmate, string wikiPage, - IntegerValueRule assignCountRule, - CustomRoles[] assignUnitRoles + RoleAssignInfo assignInfo ) { ClassType = classType; @@ -71,13 +61,12 @@ CustomRoles[] assignUnitRoles CountType = countType; ConfigId = configId; OptionCreator = optionCreator; - RequireResetCam = requireResetCam; + IsDesyncImpostor = isDesyncImpostor; this.introSound = introSound; this.canMakeMadmate = canMakeMadmate; ChatCommand = chatCommand; WikiPage = wikiPage; - AssignCountRule = assignCountRule; - AssignUnitRoles = assignUnitRoles; + AssignInfo = assignInfo; if (colorCode == "") colorCode = customRoleType switch @@ -113,51 +102,47 @@ public static SimpleRoleInfo Create( OptionCreatorDelegate optionCreator, string chatCommand, string colorCode = "", - bool requireResetCam = false, + bool isDesyncImpostor = false, TabGroup tab = TabGroup.MainSettings, Func introSound = null, Func canMakeMadmate = null, CountTypes? countType = null, string wikiPage = null, - IntegerValueRule assignCountRule = null, - CustomRoles[] assignUnitRoles = null + RoleAssignInfo assignInfo = null ) { countType ??= customRoleType == CustomRoleTypes.Impostor ? CountTypes.Impostor : CountTypes.Crew; - assignCountRule ??= customRoleType == CustomRoleTypes.Impostor ? - new(1, 3, 1) : - new(1, 15, 1); - assignUnitRoles ??= Enumerable.Repeat(roleName, assignCountRule.Step).ToArray(); + assignInfo ??= new RoleAssignInfo(roleName, customRoleType); - return - new( - classType, - createInstance, - roleName, - baseRoleType, - customRoleType, - countType.Value, - configId, - optionCreator, - chatCommand, - colorCode, - requireResetCam, - tab, - introSound, - canMakeMadmate, - wikiPage, - assignCountRule, - assignUnitRoles - ); + var roleInfo = new SimpleRoleInfo( + classType, + createInstance, + roleName, + baseRoleType, + customRoleType, + countType.Value, + configId, + optionCreator, + chatCommand, + colorCode, + isDesyncImpostor, + tab, + introSound, + canMakeMadmate, + wikiPage, + assignInfo); + roleInfo.Description = new SingleRoleDescription(roleInfo); + return roleInfo; } public static SimpleRoleInfo CreateForVanilla( Type classType, Func createInstance, RoleTypes baseRoleType, string colorCode = "", - bool canMakeMadmate = false + bool canMakeMadmate = false, + RoleAssignInfo assignInfo = null ) { CustomRoles roleName; @@ -193,26 +178,25 @@ public static SimpleRoleInfo CreateForVanilla( customRoleType = CustomRoleTypes.Crewmate; break; } - return - new( - classType, - createInstance, - roleName, - () => baseRoleType, - customRoleType, - countType, - -1, - null, - null, - colorCode, - false, - TabGroup.MainSettings, - null, - () => canMakeMadmate, - null, - new(1, 15, 1), - new CustomRoles[1] { roleName } - ); + var roleInfo = new SimpleRoleInfo( + classType, + createInstance, + roleName, + () => baseRoleType, + customRoleType, + countType, + -1, + null, + null, + colorCode, + false, + TabGroup.MainSettings, + null, + () => canMakeMadmate, + null, + assignInfo ?? new(roleName, customRoleType)); + roleInfo.Description = new VanillaRoleDescription(roleInfo, baseRoleType); + return roleInfo; } public delegate void OptionCreatorDelegate(); } \ No newline at end of file diff --git a/Roles/Crewmate/SabotageMaster.cs b/Roles/Crewmate/SabotageMaster.cs index 6bd851452..ecbd65344 100644 --- a/Roles/Crewmate/SabotageMaster.cs +++ b/Roles/Crewmate/SabotageMaster.cs @@ -1,10 +1,12 @@ +using System.Linq; using AmongUs.GameOptions; using TownOfHost.Roles.Core; +using TownOfHost.Roles.Core.Interfaces; namespace TownOfHost.Roles.Crewmate; -public sealed class SabotageMaster : RoleBase +public sealed class SabotageMaster : RoleBase, ISystemTypeUpdateHook { public static readonly SimpleRoleInfo RoleInfo = SimpleRoleInfo.Create( @@ -59,7 +61,7 @@ enum OptionName public int UsedSkillCount; private bool DoorsProgressing = false; - private bool fixedComms = false; + private bool fixedSabotage; public static void SetupOptionItem() { @@ -71,110 +73,142 @@ public static void SetupOptionItem() OptionFixesComms = BooleanOptionItem.Create(RoleInfo, 14, OptionName.SabotageMasterFixesCommunications, false, false); OptionFixesElectrical = BooleanOptionItem.Create(RoleInfo, 15, OptionName.SabotageMasterFixesElectrical, false, false); } - public override bool OnSabotage(PlayerControl player, SystemTypes systemType, byte amount) + bool ISystemTypeUpdateHook.UpdateReactorSystem(ReactorSystemType reactorSystem, byte amount) { - if (!Is(player)) return true; - var shipStatus = ShipStatus.Instance; - if (SkillLimit > 0 && UsedSkillCount >= SkillLimit) return true; - switch (systemType) + if (!IsSkillAvailable()) return true; + if (!FixesReactors) return true; + if (amount.HasAnyBit(ReactorSystemType.AddUserOp)) { - case SystemTypes.Reactor: - if (!FixesReactors) break; - if (amount.HasAnyBit(64)) - { - //片方の入力が正解したタイミング - - //Skeld、Miraは16だけでOK。Airshipは16,17とも必要 - shipStatus.RepairSystem(SystemTypes.Reactor, Player, 16); - shipStatus.RepairSystem(SystemTypes.Reactor, Player, 17); - UsedSkillCount++; - } - break; - case SystemTypes.Laboratory: - if (!FixesReactors) break; - if (amount.HasAnyBit(64)) - { - //片方の入力がされたタイミング + //片方を直したタイミング + ShipStatus.Instance.UpdateSystem((MapNames)Main.NormalOptions.MapId == MapNames.Polus ? SystemTypes.Laboratory : SystemTypes.Reactor, Player, ReactorSystemType.ClearCountdown); + UsedSkillCount++; + } + return true; + } + bool ISystemTypeUpdateHook.UpdateHeliSabotageSystem(HeliSabotageSystem heliSabotageSystem, byte amount) + { + if (!IsSkillAvailable()) return true; + if (!FixesReactors) return true; + var tags = (HeliSabotageSystem.Tags)(amount & HeliSabotageSystem.TagMask); + if (tags == HeliSabotageSystem.Tags.ActiveBit) + { + //パネル開いたタイミング + fixedSabotage = false; + } + if (!fixedSabotage && tags == HeliSabotageSystem.Tags.FixBit) + { + //片方の入力が正解したタイミング + fixedSabotage = true; + //ヘリサボは16,17がそろったとき完了。 + var consoleId = amount & HeliSabotageSystem.IdMask; + var otherConsoleId = (consoleId + 1) % 2; + //もう一方のパネルの完了報告 + ShipStatus.Instance.UpdateSystem(SystemTypes.HeliSabotage, Player, (byte)(otherConsoleId | (int)HeliSabotageSystem.Tags.FixBit)); + UsedSkillCount++; + } + return true; + } + bool ISystemTypeUpdateHook.UpdateLifeSuppSystem(LifeSuppSystemType lifeSuppSystem, byte amount) + { + if (!IsSkillAvailable()) return true; + if (!FixesOxygens) return true; + if (amount.HasAnyBit(LifeSuppSystemType.AddUserOp)) + { + //片方の入力が正解したタイミング + ShipStatus.Instance.UpdateSystem(SystemTypes.LifeSupp, Player, LifeSuppSystemType.ClearCountdown); + UsedSkillCount++; + } + return true; + } + bool ISystemTypeUpdateHook.UpdateHqHudSystem(HqHudSystemType hqHudSystemType, byte amount) + { + if (!IsSkillAvailable()) return true; + if (!FixesComms) return true; + var tags = (HqHudSystemType.Tags)(amount & HqHudSystemType.TagMask); + if (tags == HqHudSystemType.Tags.ActiveBit) + { + //パネル開いたタイミング + fixedSabotage = false; + } + if (!fixedSabotage && tags == HqHudSystemType.Tags.FixBit) + { + //片方の入力が正解したタイミング + fixedSabotage = true; + //MiraHQのコミュは16,17がそろったとき完了。 + var consoleId = amount & HqHudSystemType.IdMask; + var otherConsoleId = (consoleId + 1) % 2; + //もう一方のパネルの完了報告 + ShipStatus.Instance.UpdateSystem(SystemTypes.Comms, Player, (byte)(otherConsoleId | (int)HqHudSystemType.Tags.FixBit)); + UsedSkillCount++; + } + return true; + } + bool ISystemTypeUpdateHook.UpdateSwitchSystem(SwitchSystem switchSystem, byte amount) + { + if (!IsSkillAvailable()) return true; + if (!FixesElectrical) return true; + if (amount.HasBit(SwitchSystem.DamageSystem)) return true; + //いずれかのスイッチが変更されたタイミング + //現在のスイッチ状態を今から動かすスイッチ以外を正解にする - //Polusラボは16だけで完了 - shipStatus.RepairSystem(SystemTypes.Laboratory, Player, 16); - UsedSkillCount++; - } - break; - case SystemTypes.LifeSupp: - if (!FixesOxygens) break; - if (amount.HasAnyBit(64)) - { - //片方の入力が正解したタイミング + var fixbit = 1 << amount; + switchSystem.ActualSwitches = (byte)(switchSystem.ExpectedSwitches ^ fixbit); + UsedSkillCount++; + return true; + } + bool ISystemTypeUpdateHook.UpdateDoorsSystem(DoorsSystemType doorsSystem, byte amount) + { + if (!IsSkillAvailable()) return true; + if (!FixesDoors) return true; + if (DoorsProgressing) return true; - //Skeld,MiraのO2は16だけで完了 - shipStatus.RepairSystem(SystemTypes.LifeSupp, Player, 16); - UsedSkillCount++; - } - break; - case SystemTypes.Comms: - if (!FixesComms) break; - if (amount.HasAnyBit(64)) - { - //パネル開いたタイミング - fixedComms = false; - } - if (!fixedComms && amount.HasAnyBit(16)) - { - //片方の入力が正解したタイミング + int mapId = Main.NormalOptions.MapId; + if (AmongUsClient.Instance.NetworkMode == NetworkModes.FreePlay) mapId = AmongUsClient.Instance.TutorialMapId; + var shipStatus = ShipStatus.Instance; - fixedComms = true; - //MiraHQのコミュは16,17がそろったとき完了。 - //もう一方のパネルの完了報告 - shipStatus.RepairSystem(SystemTypes.Comms, Player, (byte)(16 | (~amount & 1))); - UsedSkillCount++; - } - break; - case SystemTypes.Electrical: - if (!FixesElectrical) break; - if (!amount.HasAnyBit(128)) + DoorsProgressing = true; + if (mapId == 2) + { + //Polus + ShipStatusUpdateSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 71, 72); + ShipStatusUpdateSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 67, 68); + ShipStatusUpdateSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 64, 66); + ShipStatusUpdateSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 73, 74); + } + else if (mapId == 4) + { + //Airship + ShipStatusUpdateSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 64, 67); + ShipStatusUpdateSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 71, 73); + ShipStatusUpdateSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 74, 75); + ShipStatusUpdateSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 76, 78); + ShipStatusUpdateSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 68, 70); + ShipStatusUpdateSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 83, 84); + } + else if (mapId == 5) + { + // Fungle + var openedDoorId = amount & DoorsSystemType.IdMask; + var openedDoor = shipStatus.AllDoors.FirstOrDefault(door => door.Id == openedDoorId); + if (openedDoor == null) + { + Logger.Warn($"不明なドアが開けられました: {openedDoorId}", nameof(SabotageMaster)); + } + else + { + // 同じ部屋のドアで,今から開けるドアではないものを全部開ける + var room = openedDoor.Room; + foreach (var door in shipStatus.AllDoors) { - //いずれかのスイッチが変更されたタイミング - - var sw = shipStatus.Systems[SystemTypes.Electrical].TryCast(); - if (sw != null) + if (door.Id != openedDoorId && door.Room == room) { - //現在のスイッチ状態を今から動かすスイッチ以外を正解にする - var fixbit = 1 << amount; - sw.ActualSwitches = (byte)(sw.ExpectedSwitches ^ fixbit); - UsedSkillCount++; + door.SetDoorway(true); } } - break; - case SystemTypes.Doors: - if (!FixesDoors) break; - if (DoorsProgressing) break; - - int mapId = Main.NormalOptions.MapId; - if (AmongUsClient.Instance.NetworkMode == NetworkModes.FreePlay) mapId = AmongUsClient.Instance.TutorialMapId; - - DoorsProgressing = true; - if (mapId == 2) - { - //Polus - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 71, 72); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 67, 68); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 64, 66); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 73, 74); - } - else if (mapId == 4) - { - //Airship - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 64, 67); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 71, 73); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 74, 75); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 76, 78); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 68, 70); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 83, 84); - } - DoorsProgressing = false; - break; + } } + DoorsProgressing = false; return true; } + private bool IsSkillAvailable() => SkillLimit <= 0 || UsedSkillCount < SkillLimit; } \ No newline at end of file diff --git a/Roles/Crewmate/Sheriff.cs b/Roles/Crewmate/Sheriff.cs index 16f04346b..2cd5df720 100644 --- a/Roles/Crewmate/Sheriff.cs +++ b/Roles/Crewmate/Sheriff.cs @@ -75,11 +75,9 @@ private static void SetupOptionItem() } public static void SetUpNeutralOptions(int idOffset) { - foreach (var neutral in CustomRolesHelper.AllRoles.Where(x => x.IsNeutral()).ToArray()) + foreach (var neutral in CustomRolesHelper.AllStandardRoles.Where(x => x.IsNeutral()).ToArray()) { - if (neutral is CustomRoles.SchrodingerCat - or CustomRoles.HASFox - or CustomRoles.HASTroll) continue; + if (neutral is CustomRoles.SchrodingerCat) continue; SetUpKillTargetOption(neutral, idOffset, true, CanKillNeutrals); idOffset++; } @@ -119,9 +117,6 @@ public override void Add() var playerId = Player.PlayerId; CurrentKillCooldown = KillCooldown.GetFloat(); - if (!Main.ResetCamPlayerList.Contains(playerId)) - Main.ResetCamPlayerList.Add(playerId); - ShotLimit = ShotLimitOpt.GetInt(); Logger.Info($"{Utils.GetPlayerById(playerId)?.GetNameWithRole()} : 残り{ShotLimit}発", "Sheriff"); } @@ -141,7 +136,8 @@ public bool CanUseKillButton() => Player.IsAlive() && (CanKillAllAlive.GetBool() || GameStates.AlreadyDied) && ShotLimit > 0; - public override bool OnInvokeSabotage(SystemTypes systemType) => false; + public bool CanUseImpostorVentButton() => false; + public bool CanUseSabotageButton() => false; public override void ApplyGameOptions(IGameOptions opt) { opt.SetVision(false); diff --git a/Roles/Impostor/Mare.cs b/Roles/Impostor/Mare.cs index eb1d576d5..b028bb5b3 100644 --- a/Roles/Impostor/Mare.cs +++ b/Roles/Impostor/Mare.cs @@ -18,7 +18,11 @@ public sealed class Mare : RoleBase, IImpostor CustomRoleTypes.Impostor, 2300, SetupCustomOption, - "ma" + "ma", + assignInfo: new(CustomRoles.Mare, CustomRoleTypes.Impostor) + { + IsInitiallyAssignableCallBack = () => ShipStatus.Instance.Systems.TryGetValue(SystemTypes.Electrical, out var systemType) && systemType.TryCast(out _), // 停電が存在する + } ); public Mare(PlayerControl player) : base( @@ -100,21 +104,18 @@ public override void OnFixedUpdate(PlayerControl player) } } } - public override bool OnSabotage(PlayerControl player, SystemTypes systemType, byte amount) + public override bool OnSabotage(PlayerControl player, SystemTypes systemType) { if (systemType == SystemTypes.Electrical) { - if (amount.HasAnyBit(128)) + _ = new LateTask(() => { - _ = new LateTask(() => + //まだ停電が直っていなければキル可能モードに + if (Utils.IsActive(SystemTypes.Electrical)) { - //まだ停電が直っていなければキル可能モードに - if (Utils.IsActive(SystemTypes.Electrical)) - { - ActivateKill(true); - } - }, 4.0f, "Mare Activate Kill"); - } + ActivateKill(true); + } + }, 4.0f, "Mare Activate Kill"); } return true; } diff --git a/Roles/Impostor/Penguin.cs b/Roles/Impostor/Penguin.cs index 3c0c8fe3e..5980ede79 100644 --- a/Roles/Impostor/Penguin.cs +++ b/Roles/Impostor/Penguin.cs @@ -223,6 +223,7 @@ public override void OnFixedUpdate(PlayerControl player) sender.AutoStartRpc(Player.NetId, (byte)RpcCalls.MurderPlayer); { sender.WriteNetObject(abductVictim); + sender.Write((int)ExtendedPlayerControl.SucceededFlags); } sender.EndRpc(); } @@ -237,14 +238,14 @@ public override void OnFixedUpdate(PlayerControl player) var position = Player.transform.position; if (Player.PlayerId != 0) { - RandomSpawn.TP(AbductVictim.NetTransform, position); + AbductVictim.RpcSnapTo(position); } else { _ = new LateTask(() => { if (AbductVictim != null) - RandomSpawn.TP(AbductVictim.NetTransform, position); + AbductVictim.RpcSnapTo(position); } , 0.25f, ""); } diff --git a/Roles/Impostor/Sniper.cs b/Roles/Impostor/Sniper.cs index c313b1c8d..f4b0bfe96 100644 --- a/Roles/Impostor/Sniper.cs +++ b/Roles/Impostor/Sniper.cs @@ -235,7 +235,7 @@ public override void OnShapeshift(PlayerControl target) ); //あたった通知 - Player.RpcGuardAndKill(); + Player.RpcProtectedMurderPlayer(); //スナイプが起きたことを聞こえそうな対象に通知したい targets.Remove(snipedTarget); diff --git a/Roles/Impostor/Warlock.cs b/Roles/Impostor/Warlock.cs index c4aae4d1e..0989add6f 100644 --- a/Roles/Impostor/Warlock.cs +++ b/Roles/Impostor/Warlock.cs @@ -74,6 +74,7 @@ public void OnCheckMurderAsKiller(MurderInfo info) CursedPlayer = target; //呪える相手は一人だけなのでキルボタン無効化 killer.SetKillCooldown(255f); + killer.RpcResetAbilityCooldown(); } //どちらにしてもキルは無効 info.DoKill = false; diff --git a/Roles/Neutral/Arsonist.cs b/Roles/Neutral/Arsonist.cs index 6b4038fc3..827d922df 100644 --- a/Roles/Neutral/Arsonist.cs +++ b/Roles/Neutral/Arsonist.cs @@ -74,8 +74,9 @@ public override void Add() IsDoused.Add(ar.PlayerId, false); } public bool CanUseKillButton() => !IsDouseDone(Player); + public bool CanUseImpostorVentButton() => IsDouseDone(Player) && !Player.inVent; public float CalculateKillCooldown() => DouseCooldown; - public override bool OnInvokeSabotage(SystemTypes systemType) => false; + public bool CanUseSabotageButton() => false; public override string GetProgressText(bool comms = false) { var doused = GetDousedPlayerCount(); diff --git a/Roles/Neutral/Egoist.cs b/Roles/Neutral/Egoist.cs index 57708620b..f4a4a89f3 100644 --- a/Roles/Neutral/Egoist.cs +++ b/Roles/Neutral/Egoist.cs @@ -20,7 +20,14 @@ public sealed class Egoist : RoleBase, ISidekickable, IKiller, ISchrodingerCatOw "eg", "#5600ff", canMakeMadmate: () => OptionCanCreateMadmate.GetBool(), - countType: CountTypes.Impostor + countType: CountTypes.Impostor, + assignInfo: new RoleAssignInfo(CustomRoles.Egoist, CustomRoleTypes.Neutral) + { + AssignRoleType = CustomRoleTypes.Impostor, + IsInitiallyAssignableCallBack = + () => Main.RealOptionsData.GetInt(Int32OptionNames.NumImpostors) > 1, + AssignCountRule = new(1, 1, 1) + } ); public Egoist(PlayerControl player) : base( @@ -37,8 +44,7 @@ public Egoist(PlayerControl player) private static float KillCooldown; public static bool CanCreateMadmate; - - public static List Egoists = new(3); + private static PlayerControl egoist; public SchrodingerCat.TeamType SchrodingerCatChangeTo => SchrodingerCat.TeamType.Egoist; @@ -54,20 +60,18 @@ public override void Add() { NameColorManager.Add(impostor.PlayerId, Player.PlayerId); } - Egoists.Add(Player); + egoist = Player; } public override void OnDestroy() { - Egoists.Clear(); + egoist = null; } public float CalculateKillCooldown() => KillCooldown; - + public bool CanUseSabotageButton() => true; public static bool CheckWin() { - var impostorsDead = !Main.AllAlivePlayerControls.Any(p => p.Is(RoleTypes.Impostor)); - var isAnyEgoistAlive = Egoists.Any(p => p.IsAlive()); - - if (impostorsDead && isAnyEgoistAlive) //インポスター全滅でエゴイストが生存 + if (Main.AllAlivePlayerControls.All(p => !p.Is(RoleTypes.Impostor)) && + egoist.IsAlive()) //インポスター全滅でエゴイストが生存 { Win(); return true; diff --git a/Roles/Neutral/Executioner.cs b/Roles/Neutral/Executioner.cs index bd1576db5..152586cb0 100644 --- a/Roles/Neutral/Executioner.cs +++ b/Roles/Neutral/Executioner.cs @@ -153,9 +153,8 @@ public override void OnExileWrapUp(GameData.PlayerInfo exiled, ref bool DecidedW } CustomWinnerHolder.WinnerIds.Add(Player.PlayerId); } - public bool CheckWin(out AdditionalWinners winnerType) + public bool CheckWin(ref CustomRoles winnerRole) { - winnerType = AdditionalWinners.Executioner; return TargetExiled && CustomWinnerHolder.WinnerTeam != CustomWinner.Default; } public void ChangeRole() diff --git a/Roles/Neutral/Jackal.cs b/Roles/Neutral/Jackal.cs index f91a24238..2a95fce47 100644 --- a/Roles/Neutral/Jackal.cs +++ b/Roles/Neutral/Jackal.cs @@ -20,7 +20,10 @@ public sealed class Jackal : RoleBase, IKiller, ISchrodingerCatOwner "#00b4eb", true, countType: CountTypes.Jackal, - assignCountRule: new(1, 1, 1) + assignInfo: new RoleAssignInfo(CustomRoles.Jackal, CustomRoleTypes.Neutral) + { + AssignCountRule = new(1, 1, 1) + } ); public Jackal(PlayerControl player) : base( @@ -55,12 +58,9 @@ private static void SetupOptionItem() OptionHasImpostorVision = BooleanOptionItem.Create(RoleInfo, 13, GeneralOption.ImpostorVision, true, false); } public float CalculateKillCooldown() => KillCooldown; + public bool CanUseSabotageButton() => CanUseSabotage; + public bool CanUseImpostorVentButton() => CanVent; public override void ApplyGameOptions(IGameOptions opt) => opt.SetVision(HasImpostorVision); - public static void SetHudActive(HudManager __instance, bool isActive) - { - __instance.SabotageButton.ToggleVisible(isActive && CanUseSabotage); - } - public override bool OnInvokeSabotage(SystemTypes systemType) => CanUseSabotage; public void ApplySchrodingerCatOptions(IGameOptions option) => ApplyGameOptions(option); } } \ No newline at end of file diff --git a/Roles/Neutral/Opportunist.cs b/Roles/Neutral/Opportunist.cs index 70bedac96..7335daed4 100644 --- a/Roles/Neutral/Opportunist.cs +++ b/Roles/Neutral/Opportunist.cs @@ -26,9 +26,8 @@ public Opportunist(PlayerControl player) ) { } - public bool CheckWin(out AdditionalWinners winnerType) + public bool CheckWin(ref CustomRoles winnerRole) { - winnerType = AdditionalWinners.Opportunist; return Player.IsAlive(); } } diff --git a/Roles/Neutral/PlagueDoctor.cs b/Roles/Neutral/PlagueDoctor.cs index 6ae6f2bb3..a4fa4aacc 100644 --- a/Roles/Neutral/PlagueDoctor.cs +++ b/Roles/Neutral/PlagueDoctor.cs @@ -115,12 +115,13 @@ public override void Add() InfectInactiveTime += 5f; } public bool CanUseKillButton() => InfectCount != 0; + public bool CanUseImpostorVentButton() => false; public bool OverrideKillButtonText(out string text) { text = GetString("Infected"); return true; } - public override bool OnInvokeSabotage(SystemTypes systemType) => false; + public bool CanUseSabotageButton() => false; public override string GetProgressText(bool comms = false) { return Utils.ColorString(RoleInfo.RoleColor.ShadeColor(0.25f), $"({InfectCount})"); @@ -155,7 +156,7 @@ public void OnCheckMurderAsKiller(MurderInfo info) if (InfectCount > 0) { InfectCount--; - killer.RpcGuardAndKill(target); + killer.RpcProtectedMurderPlayer(target); DirectInfect(target); } info.DoKill = false; diff --git a/Roles/Neutral/SchrodingerCat.cs b/Roles/Neutral/SchrodingerCat.cs index 17fdc815c..823868ef7 100644 --- a/Roles/Neutral/SchrodingerCat.cs +++ b/Roles/Neutral/SchrodingerCat.cs @@ -118,7 +118,7 @@ public override bool OnCheckMurderAsTarget(MurderInfo info) ///
private void ChangeTeamOnKill(PlayerControl killer) { - killer.RpcGuardAndKill(Player); + killer.RpcProtectedMurderPlayer(Player); if (killer.GetRoleClass() is ISchrodingerCatOwner catOwner) { catOwner.OnSchrodingerCatKill(this); @@ -195,9 +195,8 @@ private void ChangeTeamRandomly() var team = candidates[rand.Next(candidates.Count)]; RpcSetTeam(team); } - public bool CheckWin(out AdditionalWinners winnerType) + public bool CheckWin(ref CustomRoles winnerRole) { - winnerType = AdditionalWinners.SchrodingerCat; bool? won = Team switch { TeamType.None => CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate && CanWinTheCrewmateBeforeChange, diff --git a/Roles/RoleAssignManager.cs b/Roles/RoleAssignManager.cs index db919b775..8d26f8c4b 100644 --- a/Roles/RoleAssignManager.cs +++ b/Roles/RoleAssignManager.cs @@ -53,7 +53,7 @@ private enum AssignAlgorithm "AssignAlgorithm.Fixed", "AssignAlgorithm.Random" }; - private static readonly CustomRoles[] AllMainRoles = CustomRolesHelper.AllRoles.Where(role => role < CustomRoles.NotAssigned).ToArray(); + private static CustomRoles[] AllMainRoles => Options.CurrentGameMode == CustomGameMode.Standard ? CustomRolesHelper.AllStandardRoles : CustomRolesHelper.AllHASRoles; public static OptionItem OptionAssignMode; private static Dictionary RandomAssignOptionsCollection = new(CustomRolesHelper.AllRoleTypes.Length); private static Dictionary AssignCount = new(CustomRolesHelper.AllRoleTypes.Length); @@ -154,7 +154,7 @@ private static void SetFixedAssignRole() foreach (var roleType in CustomRolesHelper.AllRoleTypes) { - var count = AssignRoleList.Count(role => role.GetCustomRoleTypes() == roleType); + var count = AssignRoleList.Count(role => role.GetAssignRoleType() == roleType); AssignCount.Add(roleType, count); } } @@ -230,13 +230,13 @@ private static void SetRandomAssignRoleList() //アサイン枠が足りてない場合 if (CustomRolesHelper.AllRoleTypes.Any( type => assignCount.TryGetValue(type, out var count) && - targetRoles.Count(role => role.GetCustomRoleTypes() == type) > count + targetRoles.Count(role => role.GetAssignRoleType() == type) > count )) continue; foreach (var targetRole in targetRoles) { AssignRoleList.Add(targetRole); - var targetRoleType = targetRole.GetCustomRoleTypes(); + var targetRoleType = targetRole.GetAssignRoleType(); if (assignCount.ContainsKey(targetRoleType)) assignCount[targetRoleType]--; } @@ -264,12 +264,12 @@ private static void SetRandomAssignRoleList() var selectedTicket = randomRoleTicketPool[rand.Next(randomRoleTicketPool.Count)]; var targetRoles = selectedTicket.Item1.GetAssignUnitRolesArray(); //アサイン枠が足りていれば追加 - if (CustomRolesHelper.AllRoleTypes.All(type => targetRoles.Count(role => role.GetCustomRoleTypes() == type) <= assignCount[type])) + if (CustomRolesHelper.AllRoleTypes.All(type => targetRoles.Count(role => role.GetAssignRoleType() == type) <= assignCount[type])) { foreach (var targetRole in targetRoles) { AssignRoleList.Add(targetRole); - assignCount[targetRole.GetCustomRoleTypes()]--; + assignCount[targetRole.GetAssignRoleType()]--; } } //1-9個ある同じチケットを削除 @@ -282,7 +282,7 @@ private static void SetRandomAssignRoleList() /// private static void SetAddOnsList(bool isFixedAssign) { - foreach (var subRole in CustomRolesHelper.AllRoles.Where(x => x > CustomRoles.NotAssigned)) + foreach (var subRole in CustomRolesHelper.AllAddOns) { var chance = subRole.GetChance(); var count = subRole.GetAssignCount(); @@ -307,20 +307,19 @@ private static List GetCandidateRoleList(int availableRate) } return candidateRoleList; } + private static RoleAssignInfo GetRoleAssignInfo(this CustomRoles role) => + CustomRoleManager.GetRoleInfo(role)?.AssignInfo; + private static CustomRoleTypes GetAssignRoleType(this CustomRoles role) => + role.GetRoleAssignInfo()?.AssignRoleType ?? role.GetCustomRoleTypes(); private static bool IsAssignable(this CustomRoles role) - => role switch - { - CustomRoles.Crewmate => false, - CustomRoles.Egoist => Main.RealOptionsData.GetInt(Int32OptionNames.NumImpostors) > 1, - _ => true, - }; + => role.GetRoleAssignInfo()?.IsInitiallyAssignable ?? true; /// /// アサインの抽選回数 /// private static int GetAssignCount(this CustomRoles role) { int maximumCount = role.GetCount(); - int assignUnitCount = CustomRoleManager.GetRoleInfo(role)?.AssignUnitCount ?? + int assignUnitCount = role.GetRoleAssignInfo()?.AssignUnitCount ?? role switch { CustomRoles.Lovers => 2, @@ -333,7 +332,7 @@ private static int GetAssignCount(this CustomRoles role) ///両陣営役職、コンビ役職向け /// private static CustomRoles[] GetAssignUnitRolesArray(this CustomRoles role) - => CustomRoleManager.GetRoleInfo(role)?.AssignUnitRoles ?? + => role.GetRoleAssignInfo()?.AssignUnitRoles ?? role switch { CustomRoles.Lovers => new CustomRoles[2] { CustomRoles.Lovers, CustomRoles.Lovers }, @@ -342,4 +341,38 @@ private static CustomRoles[] GetAssignUnitRolesArray(this CustomRoles role) public static bool IsPresent(this CustomRoles role) => AssignRoleList.Any(x => x == role); public static int GetRealCount(this CustomRoles role) => AssignRoleList.Count(x => x == role); } + public class RoleAssignInfo + { + public RoleAssignInfo(CustomRoles role, CustomRoleTypes roleType) + { + AssignRoleType = roleType; + IsInitiallyAssignableCallBack = () => true; + AssignCountRule = + roleType == CustomRoleTypes.Impostor ? new(1, 3, 1) : new(1, 15, 1); + AssignUnitRoles = + Enumerable.Repeat(role, AssignCountRule.Step).ToArray(); + } + /// + /// どのアサイン枠を消費するか + /// + public CustomRoleTypes AssignRoleType { get; init; } + /// + /// 試合開始時にアサインされるかどうかのデリゲート + /// + public Func IsInitiallyAssignableCallBack { get; init; } + public bool IsInitiallyAssignable => IsInitiallyAssignableCallBack.Invoke(); + /// + /// 人数設定の最小人数, 最大人数, 一単位数 + /// + public IntegerValueRule AssignCountRule { get; init; } + /// + /// 人数設定に対し何人単位でアサインするか + /// 役職の抽選回数 = 設定人数 / AssignUnitCount + /// + public int AssignUnitCount => AssignCountRule.Step; + /// + /// 実際にアサインされる役職の内訳 + /// + public CustomRoles[] AssignUnitRoles { get; init; } + } } \ No newline at end of file diff --git a/Roles/Vanilla/Crewmate.cs b/Roles/Vanilla/Crewmate.cs index 895c05c76..16b4b5b34 100644 --- a/Roles/Vanilla/Crewmate.cs +++ b/Roles/Vanilla/Crewmate.cs @@ -11,7 +11,12 @@ public sealed class Crewmate : RoleBase typeof(Crewmate), player => new Crewmate(player), RoleTypes.Crewmate, - "#8cffff" + "#8cffff", + assignInfo: new RoleAssignInfo(CustomRoles.Crewmate, CustomRoleTypes.Crewmate) + { + IsInitiallyAssignableCallBack = + () => false + } ); public Crewmate(PlayerControl player) : base( diff --git a/Roles/Vanilla/Engineer.cs b/Roles/Vanilla/Engineer.cs index 9fc5e6147..04c2a0e69 100644 --- a/Roles/Vanilla/Engineer.cs +++ b/Roles/Vanilla/Engineer.cs @@ -19,6 +19,4 @@ public Engineer(PlayerControl player) player ) { } - - public override string GetAbilityButtonText() => StringNames.VentAbility.ToString(); -} \ No newline at end of file +} diff --git a/Roles/Vanilla/GuardianAngel.cs b/Roles/Vanilla/GuardianAngel.cs index 756d8449e..c9e5b3903 100644 --- a/Roles/Vanilla/GuardianAngel.cs +++ b/Roles/Vanilla/GuardianAngel.cs @@ -10,7 +10,12 @@ public sealed class GuardianAngel : RoleBase SimpleRoleInfo.CreateForVanilla( typeof(GuardianAngel), player => new GuardianAngel(player), - RoleTypes.GuardianAngel + RoleTypes.GuardianAngel, + assignInfo: new RoleAssignInfo(CustomRoles.GuardianAngel, CustomRoleTypes.Crewmate) + { + IsInitiallyAssignableCallBack = + () => false + } ); public GuardianAngel(PlayerControl player) : base( @@ -18,6 +23,4 @@ public GuardianAngel(PlayerControl player) player ) { } - - public override string GetAbilityButtonText() => StringNames.ProtectAbility.ToString(); -} \ No newline at end of file +} diff --git a/Roles/Vanilla/Scientist.cs b/Roles/Vanilla/Scientist.cs index 59fc8ab6d..5c026454e 100644 --- a/Roles/Vanilla/Scientist.cs +++ b/Roles/Vanilla/Scientist.cs @@ -18,6 +18,4 @@ public Scientist(PlayerControl player) player ) { } - - public override string GetAbilityButtonText() => StringNames.VitalsAbility.ToString(); -} \ No newline at end of file +} diff --git a/Roles/Vanilla/Shapeshifter.cs b/Roles/Vanilla/Shapeshifter.cs index f7d7c7dae..4b96e4f81 100644 --- a/Roles/Vanilla/Shapeshifter.cs +++ b/Roles/Vanilla/Shapeshifter.cs @@ -20,6 +20,4 @@ public Shapeshifter(PlayerControl player) player ) { } - - public override string GetAbilityButtonText() => StringNames.ShapeshiftAbility.ToString(); -} \ No newline at end of file +} diff --git a/Templates/SimpleButton.cs b/Templates/SimpleButton.cs new file mode 100644 index 000000000..05d31243e --- /dev/null +++ b/Templates/SimpleButton.cs @@ -0,0 +1,99 @@ +using System; +using TMPro; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace TownOfHost.Templates; + +public class SimpleButton +{ + /// 新しいボタンを作成する + /// 親オブジェクト + /// オブジェクト名 + /// 通常時の背景色 + /// マウスホバー時の背景色 + /// クリック時に発火するアクション + /// ボタンのラベル + /// ボタンの大きさ + /// 初期状態でアクティブにするかどうか(デフォルトtrue) + public SimpleButton( + Transform parent, + string name, + Vector3 localPosition, + Color32 normalColor, + Color32 hoverColor, + Action action, + string label, + bool isActive = true) + { + if (baseButton == null) + { + throw new InvalidOperationException("baseButtonが未設定"); + } + + Button = Object.Instantiate(baseButton, parent); + Label = Button.transform.Find("FontPlacer/Text_TMP").GetComponent(); + NormalSprite = Button.inactiveSprites.GetComponent(); + HoverSprite = Button.activeSprites.GetComponent(); + buttonCollider = Button.GetComponent(); + + // ラベルをセンタリング + var container = Label.transform.parent; + Object.Destroy(Label.GetComponent()); + container.SetLocalX(0f); + Label.transform.SetLocalX(0f); + Label.horizontalAlignment = HorizontalAlignmentOptions.Center; + + Button.name = name; + Button.transform.localPosition = localPosition; + NormalSprite.color = normalColor; + HoverSprite.color = hoverColor; + Button.OnClick.AddListener(action); + Label.text = label; + Button.gameObject.SetActive(isActive); + } + public PassiveButton Button { get; } + public TextMeshPro Label { get; } + public SpriteRenderer NormalSprite { get; } + public SpriteRenderer HoverSprite { get; } + private readonly BoxCollider2D buttonCollider; + private Vector2 _scale; + public Vector2 Scale + { + get => _scale; + set => _scale = NormalSprite.size = HoverSprite.size = buttonCollider.size = value; + } + private float _fontSize; + public float FontSize + { + get => _fontSize; + set => _fontSize = Label.fontSize = Label.fontSizeMin = Label.fontSizeMax = value; + } + + private static PassiveButton baseButton; + public static void SetBase(PassiveButton passiveButton) + { + if (baseButton != null || passiveButton == null) + { + return; + } + // 複製 + baseButton = Object.Instantiate(passiveButton); + var label = baseButton.transform.Find("FontPlacer/Text_TMP").GetComponent(); + baseButton.gameObject.SetActive(false); + // シーン切替時に破棄されないようにする + Object.DontDestroyOnLoad(baseButton); + baseButton.name = "TOH_SimpleButtonBase"; + // 不要なコンポーネントを無効化 + Object.Destroy(baseButton.GetComponent()); + label.DestroyTranslator(); + label.fontSize = label.fontSizeMax = label.fontSizeMin = 3.5f; + label.enableWordWrapping = false; + label.text = "TOH SIMPLE BUTTON BASE"; + // 当たり判定がズレてるのを直す + var buttonCollider = baseButton.GetComponent(); + buttonCollider.offset = new(0f, 0f); + baseButton.OnClick = new(); + } + public static bool IsNullOrDestroyed(SimpleButton button) => button == null || button.Button == null; +} diff --git a/main.cs b/main.cs index 6e2da1b5f..2edb1c475 100644 --- a/main.cs +++ b/main.cs @@ -58,9 +58,11 @@ public class Main : BasePlugin public static readonly Version ParsedForkVersion = Version.Parse(ForkVersion); public const string PluginGuid = "com.emptybottle.townofhost"; - public const string PluginVersion = "5.1.1"; + public const string PluginVersion = "5.1.4"; // サポートされている最低のAmongUsバージョン - public static readonly string LowestSupportedVersion = "2023.7.11"; + public static readonly string LowestSupportedVersion = "2023.10.24"; + // このバージョンのみで公開ルームを無効にする場合 + public static readonly bool IsPublicAvailableOnThisVersion = false; public Harmony Harmony { get; } = new Harmony(PluginGuid); public static Version version = Version.Parse(PluginVersion); public static BepInEx.Logging.ManualLogSource Logger; @@ -79,8 +81,8 @@ public class Main : BasePlugin public static ConfigEntry ShowLobbySummary { get; private set; } public static ConfigEntry CopyGameCodeOnCreateLobby { get; private set; } public static ConfigEntry HauntMenuFocusCrewmate { get; private set; } - public static ConfigEntry PutBackWinTextPosition { get; private set; } public static ConfigEntry MessageWait { get; private set; } + public static ConfigEntry ShowResults { get; private set; } public static Dictionary playerVersion = new(); //Preset Name Options @@ -100,7 +102,6 @@ public class Main : BasePlugin public static Dictionary PlayerColors = new(); public static Dictionary AfterMeetingDeathPlayers = new(); public static Dictionary roleColors; - public static List ResetCamPlayerList; public static List winnerList; public static List clientIdList; public static List<(string, byte, string)> MessagesToSend; @@ -145,8 +146,8 @@ public override void Load() ShowLobbySummary = Config.Bind("Client Options", "Show Lobby Summary", true); CopyGameCodeOnCreateLobby = Config.Bind("Client Options", "Copy Game Code On Create Lobby", true); HauntMenuFocusCrewmate = Config.Bind("Client Options", "Haunt Menu Focuses Crewmate", true); - PutBackWinTextPosition = Config.Bind("Client Options", "Put Back WinText Position", true); DebugKeyInput = Config.Bind("Authentication", "Debug Key", ""); + ShowResults = Config.Bind("Result", "Show Results", true); Logger = BepInEx.Logging.Logger.CreateLogSource("TownOfHost"); TownOfHost.Logger.Enable(); @@ -281,14 +282,6 @@ public enum CustomWinner PlagueDoctor = CustomRoles.PlagueDoctor, HASTroll = CustomRoles.HASTroll, } - public enum AdditionalWinners - { - None = -1, - Opportunist = CustomRoles.Opportunist, - SchrodingerCat = CustomRoles.SchrodingerCat, - Executioner = CustomRoles.Executioner, - HASFox = CustomRoles.HASFox, - } /*public enum CustomRoles : byte { Default = 0,