mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-21 05:37:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51ad006158 | ||
|
|
36e3d6b5ae | ||
|
|
5454f08e3c | ||
|
|
b0ea127dc9 | ||
|
|
2c0154fb33 | ||
|
|
c30086f8a5 | ||
|
|
24d2f3b47e | ||
|
|
6eb3ec6eb7 | ||
|
|
8df9e65398 | ||
|
|
08abded8af | ||
|
|
cff05c5619 | ||
|
|
566f2ff591 | ||
|
|
bd59234d61 |
No files matched your search
@@ -354,7 +354,7 @@ internal static class FFAManager
|
||||
class FixedUpdatePatch
|
||||
{
|
||||
private static long LastFixedUpdate;
|
||||
public static void Postfix(PlayerControl __instance)
|
||||
public static void Postfix(/*PlayerControl __instance*/)
|
||||
{
|
||||
if (!GameStates.IsInTask || Options.CurrentGameMode != CustomGameMode.FFA) return;
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
using HarmonyLib;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TOHE
|
||||
{
|
||||
internal class HotPotatoManager
|
||||
{
|
||||
private static OptionItem Time;
|
||||
private static OptionItem HolderSpeed;
|
||||
private static OptionItem Chat;
|
||||
private static OptionItem Range;
|
||||
|
||||
private static (byte HolderID, byte LastHolderID, int TimeLeft, int RoundNum) HotPotatoState;
|
||||
private static Dictionary<byte, int> SurvivalTimes;
|
||||
private static float DefaultSpeed;
|
||||
|
||||
public static (byte HolderID, byte LastHolderID, int TimeLeft, int RoundNum) GetState() => HotPotatoState;
|
||||
|
||||
public static void SetupCustomOption()
|
||||
{
|
||||
Time = IntegerOptionItem.Create(69_213_001, "HotPotato_Time", new(1, 90, 1), 20, TabGroup.GameSettings, false)
|
||||
.SetHeader(true)
|
||||
.SetGameMode(CustomGameMode.HotPotato)
|
||||
.SetValueFormat(OptionFormat.Seconds)
|
||||
.SetColor(new Color32(232, 205, 70, byte.MaxValue));
|
||||
HolderSpeed = FloatOptionItem.Create(69_213_002, "HotPotato_HolderSpeed", new(0.1f, 5f, 0.1f), 1.5f, TabGroup.GameSettings, false)
|
||||
.SetGameMode(CustomGameMode.HotPotato)
|
||||
.SetValueFormat(OptionFormat.Multiplier)
|
||||
.SetColor(new Color32(232, 205, 70, byte.MaxValue));
|
||||
Chat = BooleanOptionItem.Create(69_213_003, "FFA_ChatDuringGame", false, TabGroup.GameSettings, false)
|
||||
.SetGameMode(CustomGameMode.HotPotato)
|
||||
.SetColor(new Color32(232, 205, 70, byte.MaxValue));
|
||||
Range = FloatOptionItem.Create(69_213_004, "HotPotato_Range", new(0.25f, 5f, 0.25f), 1f, TabGroup.GameSettings, false)
|
||||
.SetGameMode(CustomGameMode.HotPotato)
|
||||
.SetValueFormat(OptionFormat.Multiplier)
|
||||
.SetColor(new Color32(232, 205, 70, byte.MaxValue));
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
FixedUpdatePatch.Return = true;
|
||||
|
||||
HotPotatoState = (byte.MaxValue, byte.MaxValue, Time.GetInt() + 20, 1);
|
||||
SurvivalTimes = [];
|
||||
foreach (var pc in Main.AllPlayerControls) SurvivalTimes[pc.PlayerId] = 0;
|
||||
|
||||
DefaultSpeed = Main.AllPlayerSpeed[0];
|
||||
|
||||
if (Chat.GetBool()) _ = new LateTask(Utils.SetChatVisible, 10f, "Set Chat Visible for Everyone");
|
||||
}
|
||||
|
||||
public static void OnGameStart()
|
||||
{
|
||||
_ = new LateTask(() => { FixedUpdatePatch.Return = false; }, 7f, log: false);
|
||||
HotPotatoState = (byte.MaxValue, byte.MaxValue, Time.GetInt() + 5, 1);
|
||||
}
|
||||
|
||||
public static int GetSurvivalTime(byte id) => SurvivalTimes.TryGetValue(id, out var time) ? time : 0;
|
||||
public static string GetIndicator(byte id) => HotPotatoState.HolderID == id ? " ★ " : string.Empty;
|
||||
public static string GetSuffixText(byte id) => $"{(HotPotatoState.HolderID == id ? $"{Translator.GetString("HotPotato_HoldingNotify")}\n" : string.Empty)}{Translator.GetString("HotPotato_TimeLeftSuffix")}{HotPotatoState.TimeLeft}s";
|
||||
|
||||
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))]
|
||||
class FixedUpdatePatch
|
||||
{
|
||||
private static float UpdateDelay = 0;
|
||||
private static long LastFixedUpdate = 0;
|
||||
public static bool Return = false;
|
||||
public static void Postfix(PlayerControl __instance)
|
||||
{
|
||||
if (Options.CurrentGameMode != CustomGameMode.HotPotato || Return || !AmongUsClient.Instance.AmHost || !GameStates.IsInTask) return;
|
||||
|
||||
PlayerControl Holder = Utils.GetPlayerById(HotPotatoState.HolderID);
|
||||
if (Holder == null || Holder.Data.Disconnected || !Holder.IsAlive())
|
||||
{
|
||||
PassHotPotato();
|
||||
return;
|
||||
}
|
||||
|
||||
long now = Utils.GetTimeStamp();
|
||||
if (now > LastFixedUpdate)
|
||||
{
|
||||
HotPotatoState.TimeLeft--;
|
||||
LastFixedUpdate = now;
|
||||
Utils.NotifyRoles();
|
||||
}
|
||||
|
||||
if (HotPotatoState.TimeLeft <= 0)
|
||||
{
|
||||
Holder.Suicide();
|
||||
SurvivalTimes[HotPotatoState.HolderID] = Time.GetInt() * (HotPotatoState.RoundNum - 1);
|
||||
PassHotPotato();
|
||||
return;
|
||||
}
|
||||
|
||||
if (HotPotatoState.HolderID != __instance.PlayerId || !Main.AllAlivePlayerControls.Any(x => x.PlayerId != HotPotatoState.HolderID && (x.PlayerId != HotPotatoState.LastHolderID || Main.AllAlivePlayerControls.Length == 2) && Vector2.Distance(x.Pos(), Holder.Pos()) <= Range.GetFloat())) return;
|
||||
|
||||
UpdateDelay += UnityEngine.Time.fixedDeltaTime;
|
||||
if (UpdateDelay < 0.3f) return;
|
||||
UpdateDelay = 0;
|
||||
|
||||
var Target = Main.AllAlivePlayerControls.OrderBy(x => Vector2.Distance(x.Pos(), Holder.Pos())).FirstOrDefault(x => x.PlayerId != HotPotatoState.HolderID && (x.PlayerId != HotPotatoState.LastHolderID || Main.AllAlivePlayerControls.Length == 2));
|
||||
if (Target == null) return;
|
||||
|
||||
PassHotPotato(Target, resetTime: false);
|
||||
}
|
||||
private static void PassHotPotato(PlayerControl target = null, bool resetTime = true)
|
||||
{
|
||||
if (Return || Main.AllAlivePlayerControls.Length < 2) return;
|
||||
|
||||
if (resetTime)
|
||||
{
|
||||
HotPotatoState.TimeLeft = Time.GetInt();
|
||||
HotPotatoState.RoundNum++;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
target ??= Main.AllAlivePlayerControls[IRandom.Instance.Next(0, Main.AllAlivePlayerControls.Length)];
|
||||
|
||||
HotPotatoState.LastHolderID = HotPotatoState.HolderID;
|
||||
HotPotatoState.HolderID = target.PlayerId;
|
||||
|
||||
Main.AllPlayerSpeed[target.PlayerId] = HolderSpeed.GetFloat();
|
||||
target.MarkDirtySettings();
|
||||
|
||||
PlayerControl LastHolder = Utils.GetPlayerById(HotPotatoState.LastHolderID);
|
||||
if (LastHolder != null)
|
||||
{
|
||||
Main.AllPlayerSpeed[HotPotatoState.LastHolderID] = DefaultSpeed;
|
||||
LastHolder.MarkDirtySettings();
|
||||
Utils.NotifyRoles(SpecifyTarget: LastHolder);
|
||||
}
|
||||
|
||||
Logger.Info($"Hot Potato Passed: {LastHolder.GetRealName()} => {target.GetRealName()}", "HotPotato");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Logger.Exception(ex, "HotPotatoManager.FixedUpdatePatch.PassHotPotato");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Utils.NotifyRoles(SpecifyTarget: target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public class Counter(int totalGreenTime, int totalRedTime, long startTimeStamp,
|
||||
public bool MoveAndStop { get => moveAndStop; set => moveAndStop = value; }
|
||||
|
||||
private static int TotalYellowTime => 3;
|
||||
private static Color Orange => new(255, 165, 0, 255);
|
||||
private static Color32 Orange => new(255, 165, 0, 255);
|
||||
|
||||
public int Timer { get => (IsRed ? TotalRedTime : IsYellow ? TotalYellowTime : TotalGreenTime) - (int)Math.Round((double)(Utils.GetTimeStamp() - StartTimeStamp)); }
|
||||
public string ColoredTimerString { get => IsYellow ? Utils.ColorString(Color.black, "00") : Utils.ColorString(IsRed ? Color.red : Color.green, Timer < 10 ? $"0{Timer}" : Timer.ToString()); }
|
||||
@@ -202,7 +202,7 @@ internal class MoveAndStopManager
|
||||
limit = 2f;
|
||||
}
|
||||
|
||||
FixedUpdatePatch.Limit.TryAdd(pc.PlayerId, limit);
|
||||
FixedUpdatePatch.Limit[pc.PlayerId] = limit;
|
||||
}
|
||||
}
|
||||
public static int GetRankOfScore(byte playerId)
|
||||
|
||||
@@ -334,7 +334,7 @@ internal static class SoloKombatManager
|
||||
class FixedUpdatePatch
|
||||
{
|
||||
private static long LastFixedUpdate;
|
||||
public static void Postfix(PlayerControl __instance)
|
||||
public static void Postfix(/*PlayerControl __instance*/)
|
||||
{
|
||||
if (!GameStates.IsInTask || Options.CurrentGameMode != CustomGameMode.SoloKombat) return;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public static class ShuffleListExtension
|
||||
/// <typeparam name="T">The type of the collection</typeparam>
|
||||
/// <param name="collection">The collection to be shuffled</param>
|
||||
/// <param name="random">An instance of a randomizer algorithm</param>
|
||||
/// <returns></returns>
|
||||
/// <returns>The shuffled collection</returns>
|
||||
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> collection, IRandom random)
|
||||
{
|
||||
var list = collection.ToList();
|
||||
@@ -91,6 +91,13 @@ internal class CustomRoleSelector
|
||||
else RoleResult.Add(pc, CustomRoles.Tasker);
|
||||
}
|
||||
return;
|
||||
case CustomGameMode.HotPotato:
|
||||
RoleResult = [];
|
||||
foreach (PlayerControl pc in Main.AllAlivePlayerControls)
|
||||
{
|
||||
RoleResult.Add(pc, CustomRoles.Potato);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
RoleResult = [];
|
||||
@@ -170,16 +177,21 @@ internal class CustomRoleSelector
|
||||
// Shuffle - Shuffles all roles in the list into a randomized order
|
||||
// Take - Takes the first x roles of the list ... x is the maximum number of roles we could need of that team
|
||||
|
||||
AllRoles[RoleAssignType.Impostor] = AllRoles[RoleAssignType.Impostor].DistinctBy(x => x.Role).Shuffle(rd).Take(optImpNum).ToList();
|
||||
AllRoles[RoleAssignType.NeutralKilling] = AllRoles[RoleAssignType.NeutralKilling].DistinctBy(x => x.Role).Shuffle(rd).Take(optNeutralKillingNum).ToList();
|
||||
AllRoles[RoleAssignType.NonKillingNeutral] = AllRoles[RoleAssignType.NonKillingNeutral].DistinctBy(x => x.Role).Shuffle(rd).Take(optNonNeutralKillingNum).ToList();
|
||||
AllRoles[RoleAssignType.Crewmate] = AllRoles[RoleAssignType.Crewmate].DistinctBy(x => x.Role).Shuffle(rd).Take(playerCount).ToList();
|
||||
AllRoles[RoleAssignType.Impostor] = AllRoles[RoleAssignType.Impostor].Shuffle(rd).Take(optImpNum).ToList();
|
||||
AllRoles[RoleAssignType.NeutralKilling] = AllRoles[RoleAssignType.NeutralKilling].Shuffle(rd).Take(optNeutralKillingNum).ToList();
|
||||
AllRoles[RoleAssignType.NonKillingNeutral] = AllRoles[RoleAssignType.NonKillingNeutral].Shuffle(rd).Take(optNonNeutralKillingNum).ToList();
|
||||
AllRoles[RoleAssignType.Crewmate] = AllRoles[RoleAssignType.Crewmate].Shuffle(rd).Take(playerCount).ToList();
|
||||
|
||||
AllRoles[RoleAssignType.Impostor].AddRange(TempAlwaysImpRoles);
|
||||
AllRoles[RoleAssignType.NeutralKilling].AddRange(TempAlwaysNKRoles);
|
||||
AllRoles[RoleAssignType.NonKillingNeutral].AddRange(TempAlwaysNNKRoles);
|
||||
AllRoles[RoleAssignType.Crewmate].AddRange(TempAlwaysCrewRoles);
|
||||
|
||||
AllRoles[RoleAssignType.Impostor] = AllRoles[RoleAssignType.Impostor].DistinctBy(x => x.Role).ToList();
|
||||
AllRoles[RoleAssignType.NeutralKilling] = AllRoles[RoleAssignType.NeutralKilling].DistinctBy(x => x.Role).ToList();
|
||||
AllRoles[RoleAssignType.NonKillingNeutral] = AllRoles[RoleAssignType.NonKillingNeutral].DistinctBy(x => x.Role).ToList();
|
||||
AllRoles[RoleAssignType.Crewmate] = AllRoles[RoleAssignType.Crewmate].DistinctBy(x => x.Role).ToList();
|
||||
|
||||
Logger.Msg("======================================================", "SelectedRoles");
|
||||
Logger.Info(string.Join(", ", AllRoles[RoleAssignType.Impostor].Select(x => x.Role.ToString())), "SelectedImpostorRoles");
|
||||
Logger.Info(string.Join(", ", AllRoles[RoleAssignType.NeutralKilling].Select(x => x.Role.ToString())), "SelectedNKRoles");
|
||||
|
||||
@@ -99,6 +99,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Express => CustomRoles.Crewmate,
|
||||
CustomRoles.NiceEraser => CustomRoles.Crewmate,
|
||||
CustomRoles.TaskManager => CustomRoles.Crewmate,
|
||||
CustomRoles.Beacon => CustomRoles.Crewmate,
|
||||
CustomRoles.Rabbit => CustomRoles.Crewmate,
|
||||
CustomRoles.Shiftguard => CustomRoles.Crewmate,
|
||||
CustomRoles.Mole => CustomRoles.Engineer,
|
||||
@@ -239,6 +240,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Express => CustomRoles.CrewmateTOHE,
|
||||
CustomRoles.NiceEraser => CustomRoles.CrewmateTOHE,
|
||||
CustomRoles.TaskManager => CustomRoles.CrewmateTOHE,
|
||||
CustomRoles.Beacon => CustomRoles.CrewmateTOHE,
|
||||
CustomRoles.Rabbit => CustomRoles.CrewmateTOHE,
|
||||
CustomRoles.Shiftguard => CustomRoles.CrewmateTOHE,
|
||||
CustomRoles.Mole => CustomRoles.EngineerTOHE,
|
||||
@@ -306,6 +308,8 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Killer => RoleTypes.Impostor,
|
||||
//Move And Stop
|
||||
CustomRoles.Tasker => RoleTypes.Crewmate,
|
||||
//Hot Potato
|
||||
CustomRoles.Potato => RoleTypes.Crewmate,
|
||||
//Standard
|
||||
CustomRoles.Sheriff => UsePets && Sheriff.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor,
|
||||
CustomRoles.Crusader => UsePets && Crusader.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor,
|
||||
@@ -386,69 +390,7 @@ internal static class CustomRolesHelper
|
||||
_ => RoleTypes.GuardianAngel
|
||||
};
|
||||
}
|
||||
public static bool IsAdditionRole(this CustomRoles role) => role is
|
||||
CustomRoles.Lovers or
|
||||
CustomRoles.LastImpostor or
|
||||
CustomRoles.Ntr or
|
||||
CustomRoles.Madmate or
|
||||
CustomRoles.Watcher or
|
||||
CustomRoles.Sleuth or
|
||||
CustomRoles.Flashman or
|
||||
CustomRoles.Undead or
|
||||
CustomRoles.Busy or
|
||||
CustomRoles.Truant or
|
||||
CustomRoles.Disco or
|
||||
CustomRoles.Asthmatic or
|
||||
CustomRoles.Giant or
|
||||
CustomRoles.Physicist or
|
||||
CustomRoles.Nimble or
|
||||
CustomRoles.Torch or
|
||||
CustomRoles.Seer or
|
||||
CustomRoles.Bait or
|
||||
CustomRoles.Diseased or
|
||||
CustomRoles.Antidote or
|
||||
CustomRoles.Swift or
|
||||
CustomRoles.Gravestone or
|
||||
CustomRoles.Trapper or
|
||||
CustomRoles.Mare or
|
||||
CustomRoles.Brakar or
|
||||
CustomRoles.Oblivious or
|
||||
CustomRoles.Bewilder or
|
||||
CustomRoles.Sunglasses or
|
||||
CustomRoles.Knighted or
|
||||
CustomRoles.Workhorse or
|
||||
CustomRoles.Fool or
|
||||
CustomRoles.Autopsy or
|
||||
CustomRoles.Necroview or
|
||||
CustomRoles.Avanger or
|
||||
CustomRoles.Youtuber or
|
||||
CustomRoles.Loyal or
|
||||
CustomRoles.Egoist or
|
||||
CustomRoles.Recruit or
|
||||
CustomRoles.Glow or
|
||||
CustomRoles.TicketsStealer or
|
||||
CustomRoles.DualPersonality or
|
||||
CustomRoles.Mimic or
|
||||
CustomRoles.Reach or
|
||||
CustomRoles.Magnet or
|
||||
CustomRoles.DeadlyQuota or
|
||||
CustomRoles.Damocles or
|
||||
CustomRoles.Stressed or
|
||||
CustomRoles.Cleansed or
|
||||
CustomRoles.Charmed or
|
||||
CustomRoles.Onbound or
|
||||
CustomRoles.Lazy or
|
||||
// CustomRoles.Reflective or
|
||||
CustomRoles.Rascal or
|
||||
CustomRoles.Contagious or
|
||||
CustomRoles.Guesser or
|
||||
CustomRoles.Rogue or
|
||||
CustomRoles.Unreportable or
|
||||
CustomRoles.Lucky or
|
||||
CustomRoles.Unlucky or
|
||||
CustomRoles.DoubleShot or
|
||||
CustomRoles.Ghoul or
|
||||
CustomRoles.EvilSpirit;
|
||||
public static bool IsAdditionRole(this CustomRoles role) => role > CustomRoles.NotAssigned;
|
||||
public static bool IsNonNK(this CustomRoles role) // ROLE ASSIGNING, NOT NEUTRAL TYPE
|
||||
=> role is
|
||||
CustomRoles.Jester or
|
||||
@@ -838,6 +780,8 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Killer or
|
||||
//Move And Stop
|
||||
CustomRoles.Tasker or
|
||||
//Hot Potato
|
||||
CustomRoles.Potato or
|
||||
//Standard
|
||||
CustomRoles.Jester or
|
||||
CustomRoles.Doppelganger or
|
||||
@@ -1257,7 +1201,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Oblivious when mainRole is CustomRoles.Detective or CustomRoles.Cleaner or CustomRoles.Medusa or CustomRoles.Mortician or CustomRoles.Mediumshiper or CustomRoles.GuardianAngelTOHE => false,
|
||||
CustomRoles.Youtuber when !mainRole.IsCrewmate() || mainRole is CustomRoles.Madmate or CustomRoles.Sheriff or CustomRoles.GuardianAngelTOHE => false,
|
||||
CustomRoles.Egoist when !mainRole.IsImpostor() => false,
|
||||
CustomRoles.Damocles when mainRole is CustomRoles.Bomber or CustomRoles.Nuker or CustomRoles.SerialKiller or CustomRoles.Cantankerous => false,
|
||||
CustomRoles.Damocles when mainRole is CustomRoles.Bomber or CustomRoles.Nuker or CustomRoles.SerialKiller or CustomRoles.Cantankerous or CustomRoles.Sapper => false,
|
||||
CustomRoles.Necroview when mainRole is CustomRoles.Visionary => false,
|
||||
CustomRoles.Flashman when mainRole is CustomRoles.Swiftclaw => false,
|
||||
CustomRoles.Ghoul when mainRole is CustomRoles.Lazy => false,
|
||||
@@ -1460,6 +1404,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Trapper when pc.Is(CustomRoles.GuardianAngelTOHE) => false,
|
||||
CustomRoles.Reach when !pc.CanUseKillButton() => false,
|
||||
CustomRoles.Magnet when !pc.CanUseKillButton() => false,
|
||||
CustomRoles.Haste when !pc.CanUseKillButton() => false,
|
||||
CustomRoles.Watcher when (pc.GetCustomRole().IsCrewmate() && !Options.CrewCanBeWatcher.GetBool()) || (pc.GetCustomRole().IsNeutral() && !Options.NeutralCanBeWatcher.GetBool()) || (pc.GetCustomRole().IsImpostor() && !Options.ImpCanBeWatcher.GetBool()) => false,
|
||||
CustomRoles.Diseased when (pc.GetCustomRole().IsCrewmate() && !Options.CrewCanBeDiseased.GetBool()) || (pc.GetCustomRole().IsNeutral() && !Options.NeutralCanBeDiseased.GetBool()) || (pc.GetCustomRole().IsImpostor() && !Options.ImpCanBeDiseased.GetBool()) => false,
|
||||
CustomRoles.Antidote when (pc.GetCustomRole().IsCrewmate() && !Options.CrewCanBeAntidote.GetBool()) || (pc.GetCustomRole().IsNeutral() && !Options.NeutralCanBeAntidote.GetBool()) || (pc.GetCustomRole().IsImpostor() && !Options.ImpCanBeAntidote.GetBool()) => false,
|
||||
|
||||
+3
-2
@@ -43,15 +43,16 @@ class Logger
|
||||
}
|
||||
public static void Disable(string tag) { if (!disableList.Contains(tag)) disableList.Add(tag); }
|
||||
|
||||
public static void SendInGame(string text, bool isAlways = false)
|
||||
public static void SendInGame(string text/*, bool isAlways = false*/)
|
||||
{
|
||||
if (!isEnable) return;
|
||||
if (DestroyableSingleton<HudManager>._instance)
|
||||
{
|
||||
DestroyableSingleton<HudManager>.Instance.Notifier.AddItem(text);
|
||||
Info(text, "SendInGame");
|
||||
Warn(text, "SendInGame");
|
||||
}
|
||||
}
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "<Pending>")]
|
||||
private static void SendToFile(string text, LogLevel level = LogLevel.Info, string tag = "", bool escapeCRLF = true, int lineNumber = 0, string fileName = "")
|
||||
{
|
||||
if (!isEnable || disableList.Contains(tag)) return;
|
||||
|
||||
@@ -81,6 +81,11 @@ public static class DevManager
|
||||
DevUserList.Add(new(code: "akinlaptop#2206", color: "null", tag: "null", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "MR Carr"));
|
||||
DevUserList.Add(new(code: "fursilty#4676", color: "#0000ff", tag: "arthurzin", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "arthurzin"));
|
||||
DevUserList.Add(new(code: "stonefuzzy#8673", color: "#00ffe5", tag: "HyperAtillNotMe", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "HyperAtill"));
|
||||
DevUserList.Add(new(code: "frizzytram#2508", color: "#1C87FF", tag: "RafaelBIT50", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "RafaelBIT50"));
|
||||
DevUserList.Add(new(code: "foggyzing#6238", color: "null", tag: "null", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "LdZinnn"));
|
||||
DevUserList.Add(new(code: "onsideblur#3929", color: "#fc3a51", tag: "YouTuber", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "Manelzin"));
|
||||
DevUserList.Add(new(code: "modestspan#7071", color: "null", tag: "null", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "Shark"));
|
||||
DevUserList.Add(new(code: "divotbusy#0624", color: "null", tag: "null", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "PreCeptorBR"));
|
||||
|
||||
// Sponsor
|
||||
DevUserList.Add(new(code: "recentduct#6068", color: "#FF00FF", tag: "高冷男模法师", isUp: false, isDev: false, colorCmd: false, deBug: true, upName: null));
|
||||
|
||||
+4
-58
@@ -4,6 +4,7 @@ using InnerNet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TOHE.Modules;
|
||||
using static TOHE.Translator;
|
||||
|
||||
namespace TOHE;
|
||||
@@ -28,6 +29,7 @@ internal class EAC
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost) return false;
|
||||
if (pc == null || reader == null) return false;
|
||||
if (RoleBasisChanger.IsChangeInProgress) return false;
|
||||
try
|
||||
{
|
||||
MessageReader sr = MessageReader.Get(reader);
|
||||
@@ -99,17 +101,6 @@ internal class EAC
|
||||
Logger.Fatal($"Player [{pc.GetClientId()}:{pc.GetRealName()}] has a non-in-game meeting and has been rejected", "EAC");
|
||||
return true;
|
||||
}
|
||||
if (ReportTimes.TryGetValue(pc.PlayerId, out int rtimes))
|
||||
{
|
||||
if (rtimes > 14)
|
||||
{
|
||||
WarnHost();
|
||||
Report(pc, "Spam report bodies A");
|
||||
HandleCheat(pc, "Spam report bodies A");
|
||||
Logger.Fatal($"Player [{pc.GetClientId()}:{pc.GetRealName()}] reported corpses 14 times and has been rejected", "EAC");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RpcCalls.SetColor:
|
||||
case RpcCalls.CheckColor:
|
||||
@@ -258,9 +249,9 @@ internal class EAC
|
||||
}
|
||||
catch (System.IO.InvalidDataException) { }
|
||||
catch (NullReferenceException) { }
|
||||
catch (Exception e)
|
||||
catch /*(Exception e)*/
|
||||
{
|
||||
Logger.Exception(e, "EAC");
|
||||
//Logger.Exception(e, "EAC");
|
||||
//throw e;
|
||||
}
|
||||
WarnHost(-1);
|
||||
@@ -358,51 +349,6 @@ internal class EAC
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<byte, int> ReportTimes = [];
|
||||
public static bool RpcReportDeadBodyCheck(PlayerControl player, GameData.PlayerInfo target)
|
||||
{
|
||||
if (!ReportTimes.ContainsKey(player.PlayerId))
|
||||
{
|
||||
ReportTimes.Add(player.PlayerId, 0);
|
||||
}
|
||||
//target == null , button event
|
||||
if (target == null || !Main.OverDeadPlayerList.Contains(target.PlayerId))
|
||||
{
|
||||
ReportTimes[player.PlayerId]++;
|
||||
}
|
||||
|
||||
if (!GameStates.IsInGame)
|
||||
{
|
||||
WarnHost();
|
||||
Report(player, "Report body out of game");
|
||||
HandleCheat(player, "Report body out of game");
|
||||
Logger.Fatal($"Player [{player.GetClientId()}:{player.GetRealName()}] is not meeting in the game and has been rejected", "EAC");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ReportTimes[player.PlayerId] >= 14)
|
||||
{
|
||||
//I believe nobody can report 14 different bodies in a single round if host players normally
|
||||
//This check is still not enough to stop spam meeting hacks or crazy hosts that spam kill command
|
||||
WarnHost();
|
||||
Report(player, "Spam report bodies");
|
||||
HandleCheat(player, "Spam report bodies");
|
||||
Logger.Fatal($"Player [{player.GetClientId()}:{player.GetRealName()}] reported corpses 14 times and has been rejected", "EAC");
|
||||
return true;
|
||||
}
|
||||
if (GameStates.IsMeeting)
|
||||
{
|
||||
//Cancel rpc report body if a meeting is already held
|
||||
Logger.Info($"Player [{player.GetClientId()}:{player.GetRealName()}] held a meeting during the meeting and it has been rejected", "EAC");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// Niko intended to do report living player check,
|
||||
// but concerning roles like bait, hacker somehow never use report dead body,
|
||||
// Niko gave up
|
||||
}
|
||||
public static void Report(PlayerControl pc, string reason)
|
||||
{
|
||||
string msg = $"{pc.GetClientId()}|{pc.FriendCode}|{pc.Data.PlayerName}|{pc.GetClient().GetHashedPuid()}|{reason}";
|
||||
|
||||
@@ -413,7 +413,7 @@ static class ExtendedPlayerControl
|
||||
}
|
||||
public static string GetNameWithRole(this PlayerControl player, bool forUser = false)
|
||||
{
|
||||
return $"{player?.Data?.PlayerName}" + (GameStates.IsInGame && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop ? $" ({player?.GetAllRoleName(forUser).RemoveHtmlTags().Replace('\n', ' ')})" : string.Empty);
|
||||
return $"{player?.Data?.PlayerName}" + (GameStates.IsInGame && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato ? $" ({player?.GetAllRoleName(forUser).RemoveHtmlTags().Replace('\n', ' ')})" : string.Empty);
|
||||
}
|
||||
public static string GetRoleColorCode(this PlayerControl player)
|
||||
{
|
||||
@@ -483,9 +483,10 @@ static class ExtendedPlayerControl
|
||||
}
|
||||
public static bool HasKillButton(this PlayerControl pc)
|
||||
{
|
||||
CustomRoles role = pc.GetCustomRole();
|
||||
if (!pc.IsAlive() || pc.Data.Role.Role == RoleTypes.GuardianAngel || Pelican.IsEaten(pc.PlayerId)) return false;
|
||||
else if (pc.GetCustomRole().GetDYRole() == RoleTypes.Impostor || pc.GetCustomRole().GetVNRole() is CustomRoles.Impostor or CustomRoles.Shapeshifter) return true;
|
||||
else return pc.Is(CustomRoleTypes.Impostor) || pc.GetCustomRole().IsNK() || pc.GetCustomRole().IsTasklessCrewmate();
|
||||
else if (role.GetDYRole() == RoleTypes.Impostor || role.GetVNRole() is CustomRoles.Impostor or CustomRoles.Shapeshifter) return true;
|
||||
else return pc.Is(CustomRoleTypes.Impostor) || role.IsNK() || role.IsTasklessCrewmate();
|
||||
}
|
||||
public static bool CanUseKillButton(this PlayerControl pc)
|
||||
{
|
||||
@@ -502,6 +503,8 @@ static class ExtendedPlayerControl
|
||||
CustomRoles.Killer => pc.IsAlive(),
|
||||
//Move And Stop
|
||||
CustomRoles.Tasker => false,
|
||||
//Hot Potato
|
||||
CustomRoles.Potato => false,
|
||||
//Standard
|
||||
CustomRoles.FireWorks => FireWorks.CanUseKillButton(pc),
|
||||
CustomRoles.Mafia => CanMafiaKill(),
|
||||
@@ -707,6 +710,8 @@ static class ExtendedPlayerControl
|
||||
CustomRoles.Killer => true,
|
||||
//Move And Stop
|
||||
CustomRoles.Tasker => false,
|
||||
//Hot Potato
|
||||
CustomRoles.Potato => false,
|
||||
|
||||
_ => pc.Is(CustomRoleTypes.Impostor),
|
||||
};
|
||||
@@ -1178,7 +1183,7 @@ static class ExtendedPlayerControl
|
||||
Totocalcio.SetKillCooldown(player.PlayerId);
|
||||
break;
|
||||
case CustomRoles.Romantic:
|
||||
Romantic.SetKillCooldown(player.PlayerId);
|
||||
Romantic.SetKillCooldown();
|
||||
break;
|
||||
case CustomRoles.VengefulRomantic:
|
||||
Main.AllPlayerKillCooldown[player.PlayerId] = Romantic.VengefulKCD.GetFloat();
|
||||
@@ -1359,9 +1364,11 @@ static class ExtendedPlayerControl
|
||||
break;
|
||||
}
|
||||
|
||||
target.SetRealKiller(killer, NotOverRide: true);
|
||||
|
||||
if (killer.PlayerId == target.PlayerId && killer.shapeshifting)
|
||||
{
|
||||
_ = new LateTask(() => { killer.RpcMurderPlayerV2(target); }, 1.5f, "Shapeshifting Suicide Delay");
|
||||
_ = new LateTask(() => { killer.RpcMurderPlayer(target, true); }, 1.5f, "Shapeshifting Suicide Delay");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1466,11 +1473,11 @@ static class ExtendedPlayerControl
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
Logger.Info("target=null", "SetRealKiller");
|
||||
Logger.Info("target is null", "SetRealKiller");
|
||||
return;
|
||||
}
|
||||
var State = Main.PlayerStates[target.PlayerId];
|
||||
if (State.RealKiller.TIMESTAMP != DateTime.MinValue && NotOverRide) return; //既に値がある場合上書きしない
|
||||
if (State.RealKiller.TIMESTAMP != DateTime.MinValue && NotOverRide) return; // Do not overwrite if value already exists
|
||||
byte killerId = killer == null ? byte.MaxValue : killer.PlayerId;
|
||||
RPC.SetRealKiller(target.PlayerId, killerId);
|
||||
}
|
||||
|
||||
@@ -132,6 +132,7 @@ public class PlayerGameOptionsSender(PlayerControl player) : GameOptionsSender
|
||||
opt.SetFloat(FloatOptionNames.ImpostorLightMod, 1.25f);
|
||||
}
|
||||
break;
|
||||
case CustomGameMode.HotPotato:
|
||||
case CustomGameMode.MoveAndStop:
|
||||
opt.SetVision(true);
|
||||
opt.SetFloat(FloatOptionNames.CrewLightMod, 1.25f);
|
||||
@@ -729,6 +730,12 @@ public class PlayerGameOptionsSender(PlayerControl player) : GameOptionsSender
|
||||
opt.SetFloat(FloatOptionNames.ImpostorLightMod, Sentinel.LoweredVision.GetFloat());
|
||||
}
|
||||
|
||||
if (Beacon.IsAffectedPlayer(player.PlayerId))
|
||||
{
|
||||
opt.SetFloat(FloatOptionNames.CrewLightMod, Beacon.IncreasedVision);
|
||||
opt.SetFloat(FloatOptionNames.ImpostorLightMod, Beacon.IncreasedVision);
|
||||
}
|
||||
|
||||
/* if ((Main.FlashbangInProtect.Count > 0 && Main.ForFlashbang.Contains(player.PlayerId) && (!player.GetCustomRole().IsCrewmate())))
|
||||
{
|
||||
opt.SetVision(false);
|
||||
|
||||
@@ -503,15 +503,18 @@ public class TaskState
|
||||
if (!Options.CrewpostorLungeKill.GetBool())
|
||||
{
|
||||
target.SetRealKiller(player);
|
||||
target.RpcCheckAndMurder(target);
|
||||
player.RpcGuardAndKill();
|
||||
if (player.RpcCheckAndMurder(target, true))
|
||||
{
|
||||
target.Suicide(PlayerState.DeathReason.Kill, player);
|
||||
player.RpcGuardAndKill();
|
||||
}
|
||||
Logger.Info("No lunge mode kill", "Crewpostor");
|
||||
}
|
||||
else
|
||||
{
|
||||
player.SetRealKiller(target);
|
||||
player.Kill(target);
|
||||
player.RpcGuardAndKill();
|
||||
player.RpcCheckAndMurder(target);
|
||||
//player.RpcGuardAndKill();
|
||||
Logger.Info("lunge mode kill", "Crewpostor");
|
||||
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public static class MafiaRevengeManager
|
||||
MafiaMsgCheck(pc, $"/rv {PlayerId}", true);
|
||||
}
|
||||
|
||||
private static void MafiaOnClick(byte playerId, MeetingHud __instance)
|
||||
private static void MafiaOnClick(byte playerId/*, MeetingHud __instance*/)
|
||||
{
|
||||
Logger.Msg($"Click: ID {playerId}", "Mafia UI");
|
||||
var pc = Utils.GetPlayerById(playerId);
|
||||
@@ -161,7 +161,7 @@ public static class MafiaRevengeManager
|
||||
renderer.sprite = CustomButton.Get("TargetIcon");
|
||||
PassiveButton button = targetBox.GetComponent<PassiveButton>();
|
||||
button.OnClick.RemoveAllListeners();
|
||||
button.OnClick.AddListener((Action)(() => MafiaOnClick(pva.TargetPlayerId, __instance)));
|
||||
button.OnClick.AddListener((Action)(() => MafiaOnClick(pva.TargetPlayerId/*, __instance*/)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ public class ModUpdater
|
||||
latestVersion = new(data["tag_name"]?.ToString().TrimStart('v'));
|
||||
latestTitle = $"Ver. {latestVersion}";
|
||||
JArray assets = data["assets"].Cast<JArray>();
|
||||
Logger.Info(assets.ToString(), "ModUpdater");
|
||||
//Logger.Info(assets.ToString(), "ModUpdater");
|
||||
for (int i = 0; i < assets.Count; i++)
|
||||
{
|
||||
if (assets[i]["name"].ToString() == $"TOHE-PLUS.v{latestVersion}.zip")
|
||||
|
||||
@@ -30,10 +30,17 @@ public static class NameColorManager
|
||||
{
|
||||
color = "";
|
||||
|
||||
if (Options.CurrentGameMode == CustomGameMode.MoveAndStop)
|
||||
switch (Options.CurrentGameMode)
|
||||
{
|
||||
color = "#ffffff";
|
||||
return true;
|
||||
case CustomGameMode.MoveAndStop:
|
||||
color = "#ffffff";
|
||||
return true;
|
||||
case CustomGameMode.HotPotato:
|
||||
var (HolderID, LastHolderID, _, _) = HotPotatoManager.GetState();
|
||||
if (target.PlayerId == HolderID) color = "#000000";
|
||||
else if (target.PlayerId == LastHolderID) color = "#00ffff";
|
||||
else color = "#ffffff";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (seer.Is(CustomRoleTypes.Impostor) && target.Is(CustomRoleTypes.Impostor)) color = (target.Is(CustomRoles.Egoist) && Options.ImpEgoistVisibalToAllies.GetBool() && seer != target) ? Main.roleColors[CustomRoles.Egoist] : Main.roleColors[CustomRoles.Impostor];
|
||||
|
||||
+18
-1
@@ -1,4 +1,5 @@
|
||||
using HarmonyLib;
|
||||
using Steamworks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -20,6 +21,7 @@ public enum CustomGameMode
|
||||
SoloKombat = 0x02,
|
||||
FFA = 0x03,
|
||||
MoveAndStop = 0x04,
|
||||
HotPotato = 0x05,
|
||||
All = int.MaxValue
|
||||
}
|
||||
|
||||
@@ -60,6 +62,7 @@ public static class Options
|
||||
1 => CustomGameMode.SoloKombat,
|
||||
2 => CustomGameMode.FFA,
|
||||
3 => CustomGameMode.MoveAndStop,
|
||||
4 => CustomGameMode.HotPotato,
|
||||
_ => CustomGameMode.Standard
|
||||
};
|
||||
|
||||
@@ -68,7 +71,8 @@ public static class Options
|
||||
"Standard",
|
||||
"SoloKombat",
|
||||
"FFA",
|
||||
"MoveAndStop"
|
||||
"MoveAndStop",
|
||||
"HotPotato"
|
||||
];
|
||||
|
||||
// 役職数・確率
|
||||
@@ -795,6 +799,8 @@ public static class Options
|
||||
public static OptionItem OverrideScientistBasedRoles;
|
||||
public static OptionItem WhackAMole;
|
||||
|
||||
public static OptionItem SpawnAdditionalRefugeeOnImpsDead;
|
||||
|
||||
|
||||
// 投票モード
|
||||
public static OptionItem VoteMode;
|
||||
@@ -1555,6 +1561,8 @@ public static class Options
|
||||
.SetParent(CustomRoleSpawnChances[CustomRoles.Demolitionist]);
|
||||
RoleLoadingText = "Crewmate roles\nTask Manager";
|
||||
SetupRoleOptions(5575, TabGroup.CrewmateRoles, CustomRoles.TaskManager);
|
||||
RoleLoadingText = "Crewmate roles\nBeacon";
|
||||
Beacon.SetupCustomOption();
|
||||
RoleLoadingText = "Crewmate roles\nRabbit";
|
||||
Rabbit.SetupCustomOption();
|
||||
RoleLoadingText = "Crewmate roles\nShiftguard";
|
||||
@@ -2287,6 +2295,8 @@ public static class Options
|
||||
.SetParent(CustomRoleSpawnChances[CustomRoles.Necroview]);
|
||||
NeutralCanBeNecroview = BooleanOptionItem.Create(14412, "NeutralCanBeNecroview", true, TabGroup.Addons, false)
|
||||
.SetParent(CustomRoleSpawnChances[CustomRoles.Necroview]);
|
||||
RoleLoadingText = "Add-ons\nHaste";
|
||||
SetupAdtRoleOptions(14550, CustomRoles.Haste, canSetNum: true);
|
||||
RoleLoadingText = "Add-ons\nReach";
|
||||
SetupAdtRoleOptions(14600, CustomRoles.Reach, canSetNum: true);
|
||||
|
||||
@@ -2896,6 +2906,8 @@ public static class Options
|
||||
FFAManager.SetupCustomOption();
|
||||
//Move And Stop
|
||||
MoveAndStopManager.SetupCustomOption();
|
||||
//Hot Potato
|
||||
HotPotatoManager.SetupCustomOption();
|
||||
|
||||
|
||||
LoadingPercentage = 65;
|
||||
@@ -3294,6 +3306,11 @@ public static class Options
|
||||
.SetGameMode(CustomGameMode.Standard)
|
||||
.SetParent(EveryoneCanVent);
|
||||
|
||||
SpawnAdditionalRefugeeOnImpsDead = BooleanOptionItem.Create(23857, "SpawnAdditionalRefugeeOnImpsDead", false, TabGroup.TaskSettings, false)
|
||||
.SetGameMode(CustomGameMode.Standard)
|
||||
.SetColor(Color.magenta)
|
||||
.SetHeader(true);
|
||||
|
||||
|
||||
//Disable Short Tasks
|
||||
DisableShortTasks = BooleanOptionItem.Create(23000, "DisableShortTasks", false, TabGroup.TaskSettings, false)
|
||||
|
||||
+2
-2
@@ -271,7 +271,7 @@ internal class RPCHandlerPatch
|
||||
Main.OverrideWelcomeMsg = string.Format(GetString("RpcAntiBlackOutNotifyInLobby"), __instance?.Data?.PlayerName, GetString("EndWhenPlayerBug"));
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
Logger.SendInGame(string.Format(GetString("RpcAntiBlackOutEndGame"), __instance?.Data?.PlayerName), true);
|
||||
Logger.SendInGame(string.Format(GetString("RpcAntiBlackOutEndGame"), __instance?.Data?.PlayerName)/*, true*/);
|
||||
}, 3f, "Anti-Black Msg SendInGame");
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
@@ -285,7 +285,7 @@ internal class RPCHandlerPatch
|
||||
Logger.Fatal($"{__instance?.Data?.PlayerName}({__instance.PlayerId}): Change Role Setting Postfix - Error, continue the game according to settings", "Anti-blackout");
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
Logger.SendInGame(string.Format(GetString("RpcAntiBlackOutIgnored"), __instance?.Data?.PlayerName), true);
|
||||
Logger.SendInGame(string.Format(GetString("RpcAntiBlackOutIgnored"), __instance?.Data?.PlayerName)/*, true*/);
|
||||
}, 3f, "Anti-Black Msg SendInGame");
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using Hazel;
|
||||
using InnerNet;
|
||||
using TOHE.Roles.Neutral;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TOHE.Modules
|
||||
{
|
||||
// https://github.com/Rabek009/MoreGamemodes
|
||||
internal static class RoleBasisChanger
|
||||
{
|
||||
public static bool IsChangeInProgress = false;
|
||||
public static void ChangeRoleBasis(this PlayerControl player, RoleTypes targetVNRole)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost) return;
|
||||
|
||||
if (player.PlayerId == PlayerControl.LocalPlayer.PlayerId)
|
||||
{
|
||||
PlayerControl.LocalPlayer.RpcSetRole(targetVNRole);
|
||||
PlayerControl.LocalPlayer.SyncSettings();
|
||||
|
||||
Utils.NotifyRoles(SpecifySeer: PlayerControl.LocalPlayer);
|
||||
Utils.NotifyRoles(SpecifyTarget: PlayerControl.LocalPlayer);
|
||||
|
||||
HudManager.Instance.SetHudActive(PlayerControl.LocalPlayer, PlayerControl.LocalPlayer.Data.Role, !GameStates.IsMeeting);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
IsChangeInProgress = true;
|
||||
|
||||
Vector2 position = player.Pos();
|
||||
PlayerControl PlayerPrefab = AmongUsClient.Instance.PlayerPrefab;
|
||||
PlayerControl newplayer = Object.Instantiate(PlayerPrefab, position, Quaternion.identity);
|
||||
|
||||
newplayer.PlayerId = player.PlayerId;
|
||||
newplayer.FriendCode = player.FriendCode;
|
||||
newplayer.Puid = player.Puid;
|
||||
|
||||
ClientData pclient = player.GetClient();
|
||||
|
||||
player.TP(Pelican.GetBlackRoomPS());
|
||||
AmongUsClient.Instance.Despawn(player);
|
||||
AmongUsClient.Instance.Spawn(newplayer, player.OwnerId, SpawnFlags.None);
|
||||
pclient.Character = newplayer;
|
||||
|
||||
newplayer.OwnerId = player.OwnerId;
|
||||
|
||||
pclient.InScene = true;
|
||||
pclient.IsReady = true;
|
||||
|
||||
newplayer.MyPhysics.ResetMoveState();
|
||||
|
||||
GameData.Instance.RemovePlayer(player.PlayerId);
|
||||
GameData.Instance.AddPlayer(newplayer);
|
||||
|
||||
newplayer.RpcSetRole(targetVNRole);
|
||||
|
||||
GameData.Instance.SetDirty();
|
||||
newplayer.ReactorFlash(0.2f);
|
||||
newplayer.TP(position);
|
||||
|
||||
_ = new LateTask(() => { IsChangeInProgress = false; }, 5f, log: false);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.Spawn))]
|
||||
public static class Client_SpawnDisable
|
||||
{
|
||||
public static bool Prefix(InnerNetClient __instance, [HarmonyArgument(0)] InnerNetObject netObjParent, [HarmonyArgument(1)] int ownerId, [HarmonyArgument(2)] SpawnFlags flags)
|
||||
{
|
||||
if (!IsChangeInProgress) return true;
|
||||
|
||||
ownerId = ((ownerId == -3) ? __instance.ClientId : ownerId);
|
||||
MessageWriter messageWriter = __instance.Streams[0];
|
||||
__instance.WriteSpawnMessage(netObjParent, ownerId, flags, messageWriter);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.Despawn))]
|
||||
public static class Client_DespawnDisable
|
||||
{
|
||||
public static bool Prefix(InnerNetClient __instance, [HarmonyArgument(0)] InnerNetObject objToDespawn)
|
||||
{
|
||||
if (!IsChangeInProgress) return true;
|
||||
|
||||
MessageWriter messageWriter = __instance.Streams[0];
|
||||
messageWriter.StartMessage(5);
|
||||
messageWriter.WritePacked(objToDespawn.NetId);
|
||||
messageWriter.EndMessage();
|
||||
__instance.RemoveNetObject(objToDespawn);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
-65
@@ -36,7 +36,7 @@ public static class Utils
|
||||
Main.OverrideWelcomeMsg = GetString("AntiBlackOutNotifyInLobby");
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
Logger.SendInGame(GetString("AntiBlackOutLoggerSendInGame"), true);
|
||||
Logger.SendInGame(GetString("AntiBlackOutLoggerSendInGame")/*, true*/);
|
||||
}, 3f, "Anti-Black Msg SendInGame");
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
@@ -54,14 +54,14 @@ public static class Utils
|
||||
{
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
Logger.SendInGame(GetString("AntiBlackOutRequestHostToForceEnd"), true);
|
||||
Logger.SendInGame(GetString("AntiBlackOutRequestHostToForceEnd")/*, true*/);
|
||||
}, 3f, "Anti-Black Msg SendInGame");
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
Logger.SendInGame(GetString("AntiBlackOutHostRejectForceEnd"), true);
|
||||
Logger.SendInGame(GetString("AntiBlackOutHostRejectForceEnd")/*, true*/);
|
||||
}, 3f, "Anti-Black Msg SendInGame");
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
@@ -75,7 +75,7 @@ public static class Utils
|
||||
{
|
||||
foreach (PlayerControl pc in Main.AllAlivePlayerControls)
|
||||
{
|
||||
pc.TP(location, log);
|
||||
TP(pc.NetTransform, location, log);
|
||||
}
|
||||
}
|
||||
public static bool TP(CustomNetworkTransform nt, Vector2 location, bool log = true)
|
||||
@@ -472,6 +472,7 @@ public static class Utils
|
||||
case CustomGameMode.SoloKombat: return false;
|
||||
case CustomGameMode.FFA: return false;
|
||||
case CustomGameMode.MoveAndStop: return true;
|
||||
case CustomGameMode.HotPotato: return false;
|
||||
}
|
||||
//if (p.IsDead && Options.GhostIgnoreTasks.GetBool()) hasTasks = false;
|
||||
var role = States.MainRole;
|
||||
@@ -644,7 +645,7 @@ public static class Utils
|
||||
public static bool IsRoleTextEnabled(PlayerControl __instance)
|
||||
{
|
||||
bool result = false;
|
||||
if (__instance.AmOwner || Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.SoloKombat or CustomGameMode.MoveAndStop) result = true; //自分ならロールを表示
|
||||
if (__instance.AmOwner || Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.SoloKombat or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato) result = true; //自分ならロールを表示
|
||||
if (Main.VisibleTasksCount && PlayerControl.LocalPlayer.Data.IsDead && Options.GhostCanSeeOtherRoles.GetBool()) result = true; //他プレイヤーでVisibleTasksCountが有効なおかつ自分が死んでいるならロールを表示
|
||||
if (PlayerControl.LocalPlayer.Is(CustomRoles.Mimic) && Main.VisibleTasksCount && __instance.Data.IsDead && Options.MimicCanSeeDeadRoles.GetBool()) result = true; //他プレイヤーでVisibleTasksCountが有効なおかつ自分が死んでいるならロールを表示
|
||||
//if (__instance.GetCustomRole() == (CustomRoles.Ntr) && Options.LoverKnowRoles.GetBool()) result = true;
|
||||
@@ -1463,6 +1464,17 @@ public static class Utils
|
||||
sb.Append($"\n ").Append(EndGamePatch.SummaryText[id.Item2]);
|
||||
}
|
||||
break;
|
||||
case CustomGameMode.HotPotato:
|
||||
List<byte> list4 = [];
|
||||
foreach (byte id in cloneRoles.ToArray())
|
||||
{
|
||||
list4.Add(id);
|
||||
}
|
||||
foreach (byte id in list4.ToArray())
|
||||
{
|
||||
sb.Append($"\n ").Append(EndGamePatch.SummaryText[id]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
foreach (byte id in cloneRoles.ToArray())
|
||||
{
|
||||
@@ -1495,7 +1507,7 @@ public static class Utils
|
||||
var sb = new StringBuilder();
|
||||
if (SetEverythingUpPatch.LastWinsText != string.Empty) sb.Append($"<size=90%>{GetString("LastResult")} {SetEverythingUpPatch.LastWinsText}</size>");
|
||||
if (SetEverythingUpPatch.LastWinsReason != string.Empty) sb.Append($"\n<size=90%>{GetString("LastEndReason")} {SetEverythingUpPatch.LastWinsReason}</size>");
|
||||
if (sb.Length > 0 && Options.CurrentGameMode is not CustomGameMode.SoloKombat and not CustomGameMode.FFA and not CustomGameMode.MoveAndStop) SendMessage("\n", PlayerId, sb.ToString());
|
||||
if (sb.Length > 0 && Options.CurrentGameMode is not CustomGameMode.SoloKombat and not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato) SendMessage("\n", PlayerId, sb.ToString());
|
||||
}
|
||||
public static string EmptyMessage() => "<size=0>.</size>";
|
||||
public static string GetSubRolesText(byte id, bool disableColor = false, bool intro = false, bool summary = false)
|
||||
@@ -1870,6 +1882,9 @@ public static class Utils
|
||||
case CustomGameMode.MoveAndStop:
|
||||
name = $"<color=#00ffa5><size=1.7>{GetString("ModeMoveAndStop")}</size></color>\r\n" + name;
|
||||
break;
|
||||
case CustomGameMode.HotPotato:
|
||||
name = $"<color=#e8cd46><size=1.7>{GetString("ModeHotPotato")}</size></color>\r\n" + name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1942,11 +1957,11 @@ public static class Utils
|
||||
|
||||
public static Task DoNotifyRoles(bool isForMeeting = false, PlayerControl SpecifySeer = null, PlayerControl SpecifyTarget = null, bool NoCache = false, bool ForceLoop = false, bool CamouflageIsForMeeting = false, bool GuesserIsForMeeting = false, bool MushroomMixup = false)
|
||||
{
|
||||
var caller = new System.Diagnostics.StackFrame(1, false);
|
||||
var callerMethod = caller.GetMethod();
|
||||
string callerMethodName = callerMethod.Name;
|
||||
string callerClassName = callerMethod.DeclaringType.FullName;
|
||||
Logger.Info("NotifyRoles was called from " + callerClassName + "." + callerMethodName, "NotifyRoles");
|
||||
//var caller = new System.Diagnostics.StackFrame(1, false);
|
||||
//var callerMethod = caller.GetMethod();
|
||||
//string callerMethodName = callerMethod.Name;
|
||||
//string callerClassName = callerMethod.DeclaringType.FullName;
|
||||
//Logger.Info("NotifyRoles was called from " + callerClassName + "." + callerMethodName, "NotifyRoles");
|
||||
HudManagerPatch.NowCallNotifyRolesCount++;
|
||||
HudManagerPatch.LastSetNameDesyncCount = 0;
|
||||
|
||||
@@ -1971,7 +1986,7 @@ public static class Utils
|
||||
string SelfTaskText = GetProgressText(seer);
|
||||
SelfMark.Clear();
|
||||
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop) goto GameMode0;
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato) goto GameMode0;
|
||||
|
||||
if (Snitch.IsEnable) SelfMark.Append(Snitch.GetWarningArrow(seer));
|
||||
if (seer.Is(CustomRoles.Lovers)) SelfMark.Append(ColorString(GetRoleColor(CustomRoles.Lovers), "♥"));
|
||||
@@ -1989,7 +2004,7 @@ public static class Utils
|
||||
|
||||
SelfSuffix.Clear();
|
||||
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop) goto GameMode;
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato) goto GameMode;
|
||||
|
||||
if (!isForMeeting)
|
||||
{
|
||||
@@ -2114,11 +2129,14 @@ public static class Utils
|
||||
case CustomGameMode.MoveAndStop:
|
||||
SelfSuffix.Append(MoveAndStopManager.GetSuffixText(seer));
|
||||
break;
|
||||
case CustomGameMode.HotPotato when seer.IsAlive() && !seer.IsModClient():
|
||||
SelfSuffix.Append(HotPotatoManager.GetSuffixText(seer.PlayerId));
|
||||
break;
|
||||
}
|
||||
|
||||
string SeerRealName = seer.GetRealName(isForMeeting);
|
||||
|
||||
if (!isForMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool() && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop)
|
||||
if (!isForMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool() && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato)
|
||||
{
|
||||
if (seer.GetCustomRole().IsCrewmate() && !seer.Is(CustomRoles.Madmate))
|
||||
{
|
||||
@@ -2147,7 +2165,7 @@ public static class Utils
|
||||
string SelfDeathReason = seer.KnowDeathReason(seer) ? $"\n<size=1.7>({ColorString(GetRoleColor(CustomRoles.Doctor), GetVitalText(seer.PlayerId))})</size>" : string.Empty;
|
||||
string SelfName = $"{ColorString(seer.GetRoleColor(), SeerRealName)}{SelfDeathReason}{SelfMark}";
|
||||
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop) goto GameMode2;
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato) goto GameMode2;
|
||||
|
||||
switch (seer.GetCustomRole())
|
||||
{
|
||||
@@ -2220,7 +2238,7 @@ public static class Utils
|
||||
{
|
||||
TargetMark.Clear();
|
||||
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop) goto BeforeEnd2;
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato) goto BeforeEnd2;
|
||||
|
||||
if (Witch.IsEnable) TargetMark.Append(Witch.GetSpelledMark(target.PlayerId, isForMeeting));
|
||||
if (HexMaster.IsEnable) TargetMark.Append(HexMaster.GetHexedMark(target.PlayerId, isForMeeting));
|
||||
@@ -2321,7 +2339,7 @@ public static class Utils
|
||||
//CursedSoul.KnowRole(seer, target) ||
|
||||
Amnesiac.KnowRole(seer, target) ||
|
||||
Virus.KnowRole(seer, target) ||
|
||||
Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop ||
|
||||
Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato ||
|
||||
(seer.IsRevealedPlayer(target) && !target.Is(CustomRoles.Trickster)) ||
|
||||
seer.Is(CustomRoles.God) ||
|
||||
target.Is(CustomRoles.GM)
|
||||
@@ -2338,7 +2356,7 @@ public static class Utils
|
||||
|
||||
string TargetPlayerName = target.GetRealName(isForMeeting);
|
||||
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop) goto BeforeEnd;
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato) goto BeforeEnd;
|
||||
|
||||
switch (seer.GetCustomRole())
|
||||
{
|
||||
@@ -2442,7 +2460,7 @@ public static class Utils
|
||||
|
||||
TargetPlayerName = TargetPlayerName.ApplyNameColorData(seer, target, isForMeeting);
|
||||
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop) goto End;
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato) goto End;
|
||||
|
||||
if (seer.Is(CustomRoleTypes.Impostor) && target.Is(CustomRoles.Snitch) && target.Is(CustomRoles.Madmate) && target.GetTaskState().IsTaskFinished)
|
||||
TargetMark.Append(ColorString(GetRoleColor(CustomRoles.Impostor), "★"));
|
||||
@@ -3122,6 +3140,8 @@ public static class Utils
|
||||
}
|
||||
public static void AfterMeetingTasks()
|
||||
{
|
||||
bool needReactorFlash = GameManager.Instance.LogicFlow.IsGameOverDueToDeath();
|
||||
|
||||
foreach (var pc in Main.AllAlivePlayerControls)
|
||||
{
|
||||
pc.AddKillTimerToDict();
|
||||
@@ -3137,6 +3157,8 @@ public static class Utils
|
||||
pc.MarkDirtySettings();
|
||||
}, Options.TruantWaitingTime.GetFloat(), $"Truant Waiting: {pc.GetNameWithRole()}");
|
||||
}
|
||||
|
||||
if (needReactorFlash && !pc.IsModClient()) pc.ReactorFlash(0.2f);
|
||||
}
|
||||
|
||||
if (Options.DiseasedCDReset.GetBool())
|
||||
@@ -3173,7 +3195,6 @@ public static class Utils
|
||||
if (EvilTracker.IsEnable) EvilTracker.AfterMeetingTasks();
|
||||
if (SerialKiller.IsEnable()) SerialKiller.AfterMeetingTasks();
|
||||
if (Spiritualist.IsEnable) Spiritualist.AfterMeetingTasks();
|
||||
if (Jailor.IsEnable) Jailor.AfterMeetingTasks();
|
||||
if (Mycologist.IsEnable) Mycologist.AfterMeetingTasks();
|
||||
if (Sprayer.IsEnable) Sprayer.AfterMeetingTasks();
|
||||
if (PlagueDoctor.IsEnable) PlagueDoctor.AfterMeetingTasks();
|
||||
@@ -3254,16 +3275,13 @@ public static class Utils
|
||||
case CustomRoles.Pelican:
|
||||
Pelican.OnPelicanDied(target.PlayerId);
|
||||
break;
|
||||
case CustomRoles.Romantic:
|
||||
Romantic.isRomanticAlive = false;
|
||||
break;
|
||||
case CustomRoles.Devourer:
|
||||
Devourer.OnDevourerDied(target.PlayerId);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Romantic.BetPlayer.ContainsValue(target.PlayerId))
|
||||
Romantic.ChangeRole(target.PlayerId);
|
||||
if (Romantic.PartnerId == target.PlayerId)
|
||||
_ = new LateTask(Romantic.ChangeRole, 0.5f, "Romantic ChangeRole");
|
||||
|
||||
if (Executioner.Target.ContainsValue(target.PlayerId))
|
||||
Executioner.ChangeRoleByTarget(target);
|
||||
@@ -3274,7 +3292,6 @@ public static class Utils
|
||||
if (Hitman.targetId == target.PlayerId)
|
||||
Hitman.targetId = byte.MaxValue;
|
||||
|
||||
|
||||
if (Hacker.IsEnable) Hacker.AddDeadBody(target);
|
||||
if (Mortician.IsEnable) Mortician.OnPlayerDead(target);
|
||||
if (Bloodhound.IsEnable) Bloodhound.OnPlayerDead(target);
|
||||
@@ -3283,12 +3300,6 @@ public static class Utils
|
||||
|
||||
FixedUpdatePatch.LoversSuicide(target.PlayerId, onMeeting);
|
||||
}
|
||||
public static void ChangeInt(ref int ChangeTo, int input, int max)
|
||||
{
|
||||
var tmp = ChangeTo * 10;
|
||||
tmp += input;
|
||||
ChangeTo = Math.Clamp(tmp, 0, max);
|
||||
}
|
||||
public static void CountAlivePlayers(bool sendLog = false)
|
||||
{
|
||||
int AliveImpostorCount = Main.AllAlivePlayerControls.Count(pc => pc.Is(CustomRoleTypes.Impostor));
|
||||
@@ -3302,7 +3313,7 @@ public static class Utils
|
||||
if (sendLog)
|
||||
{
|
||||
var sb = new StringBuilder(100);
|
||||
if (Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop)
|
||||
if (Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato)
|
||||
{
|
||||
foreach (var countTypes in Enum.GetValues(typeof(CountTypes)).Cast<CountTypes>())
|
||||
{
|
||||
@@ -3341,14 +3352,14 @@ public static class Utils
|
||||
}
|
||||
public static void DumpLog()
|
||||
{
|
||||
string f = $"{Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)}/TOHE-logs/";
|
||||
string f = $"{Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)}/TOHE+_Logs/";
|
||||
string t = DateTime.Now.ToString("yyyy-MM-dd_HH.mm.ss");
|
||||
string filename = $"{f}TOHE-v{Main.PluginVersion}-{t}.log";
|
||||
if (!Directory.Exists(f)) Directory.CreateDirectory(f);
|
||||
FileInfo file = new(@$"{Environment.CurrentDirectory}/BepInEx/LogOutput.log");
|
||||
file.CopyTo(@filename);
|
||||
if (PlayerControl.LocalPlayer != null)
|
||||
HudManager.Instance?.Chat?.AddChat(PlayerControl.LocalPlayer, string.Format(GetString("Message.DumpfileSaved"), $"TOHE - v{Main.PluginVersion}-{t}.log"));
|
||||
HudManager.Instance?.Chat?.AddChat(PlayerControl.LocalPlayer, string.Format(GetString("Message.DumpfileSaved"), $"TOHE+ v{Main.PluginVersion} {t}.log"));
|
||||
System.Diagnostics.ProcessStartInfo psi = new("Explorer.exe")
|
||||
{ Arguments = "/e,/select," + @filename.Replace("/", "\\") };
|
||||
System.Diagnostics.Process.Start(psi);
|
||||
@@ -3426,6 +3437,10 @@ public static class Utils
|
||||
case CustomGameMode.MoveAndStop:
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} -{TaskCount.Replace("(", string.Empty).Replace(")", string.Empty)} ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.HotPotato:
|
||||
int time = HotPotatoManager.GetSurvivalTime(id);
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>Survived: <#ffffff>{(time == 0 ? "Until The End</color>" : $"{time}</color>s")}</color> ({GetVitalText(id, true)})";
|
||||
break;
|
||||
}
|
||||
return check && GetDisplayRoleName(id, true).RemoveHtmlTags().Contains("INVALID:NotAssigned")
|
||||
? "INVALID"
|
||||
@@ -3551,40 +3566,40 @@ public static class Utils
|
||||
return new Color(R, G, B, color.a);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 乱数の簡易的なヒストグラムを取得する関数
|
||||
/// <params name="nums">生成した乱数を格納したint配列</params>
|
||||
/// <params name="scale">ヒストグラムの倍率 大量の乱数を扱う場合、この値を下げることをお勧めします。</params>
|
||||
/// </summary>
|
||||
public static string WriteRandomHistgram(int[] nums, float scale = 1.0f)
|
||||
{
|
||||
int[] countData = new int[nums.Max() + 1];
|
||||
foreach (int num in nums)
|
||||
{
|
||||
if (0 <= num) countData[num]++;
|
||||
}
|
||||
StringBuilder sb = new();
|
||||
for (int i = 0; i < countData.Length; i++)
|
||||
{
|
||||
// 倍率適用
|
||||
countData[i] = (int)(countData[i] * scale);
|
||||
///// <summary>
|
||||
///// 乱数の簡易的なヒストグラムを取得する関数
|
||||
///// <params name="nums">生成した乱数を格納したint配列</params>
|
||||
///// <params name="scale">ヒストグラムの倍率 大量の乱数を扱う場合、この値を下げることをお勧めします。</params>
|
||||
///// </summary>
|
||||
//public static string WriteRandomHistgram(int[] nums, float scale = 1.0f)
|
||||
//{
|
||||
// int[] countData = new int[nums.Max() + 1];
|
||||
// foreach (int num in nums)
|
||||
// {
|
||||
// if (0 <= num) countData[num]++;
|
||||
// }
|
||||
// StringBuilder sb = new();
|
||||
// for (int i = 0; i < countData.Length; i++)
|
||||
// {
|
||||
// // 倍率適用
|
||||
// countData[i] = (int)(countData[i] * scale);
|
||||
|
||||
// 行タイトル
|
||||
sb.AppendFormat("{0:D2}", i).Append(" : ");
|
||||
// // 行タイトル
|
||||
// sb.AppendFormat("{0:D2}", i).Append(" : ");
|
||||
|
||||
// ヒストグラム部分
|
||||
for (int j = 0; j < countData[i]; j++)
|
||||
sb.Append('|');
|
||||
// // ヒストグラム部分
|
||||
// for (int j = 0; j < countData[i]; j++)
|
||||
// sb.Append('|');
|
||||
|
||||
// 改行
|
||||
sb.Append('\n');
|
||||
}
|
||||
// // 改行
|
||||
// sb.Append('\n');
|
||||
// }
|
||||
|
||||
// その他の情報
|
||||
sb.Append("最大数 - 最小数: ").Append(countData.Max() - countData.Min());
|
||||
// // その他の情報
|
||||
// sb.Append("最大数 - 最小数: ").Append(countData.Max() - countData.Min());
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
// return sb.ToString();
|
||||
//}
|
||||
|
||||
public static void SetChatVisible()
|
||||
{
|
||||
@@ -3606,6 +3621,6 @@ public static class Utils
|
||||
public static bool IsAllAlive => Main.PlayerStates.Values.All(state => state.countTypes == CountTypes.OutOfGame || !state.IsDead);
|
||||
public static int PlayersCount(CountTypes countTypes) => Main.PlayerStates.Values.Count(state => state.countTypes == countTypes);
|
||||
public static int AlivePlayersCount(CountTypes countTypes) => Main.AllAlivePlayerControls.Count(pc => pc.Is(countTypes));
|
||||
public static int IsOneAlive(CountTypes countTypes) => Main.AllAlivePlayerControls.Any(pc => pc.Is(countTypes)) ? 1 : 0;
|
||||
//public static int IsOneAlive(CountTypes countTypes) => Main.AllAlivePlayerControls.Any(pc => pc.Is(countTypes)) ? 1 : 0;
|
||||
public static bool IsPlayerModClient(this byte id) => Main.playerVersion.ContainsKey(id);
|
||||
}
|
||||
+17
-17
@@ -498,15 +498,7 @@ internal class ChatCommands
|
||||
|
||||
Utils.SendMessage(msgText, localPlayerId);
|
||||
break;
|
||||
|
||||
/*
|
||||
case "/qq":
|
||||
canceled = true;
|
||||
if (Main.newLobby) Cloud.ShareLobby(true);
|
||||
else Utils.SendMessage("很抱歉,每个房间车队姬只会发一次", localPlayerId);
|
||||
break;
|
||||
*/
|
||||
|
||||
|
||||
case "/changerole":
|
||||
//if (!DebugModeManager.AmDebugger) break;
|
||||
if (GameStates.IsLobby || !PlayerControl.LocalPlayer.FriendCode.GetDevUser().IsUp) break;
|
||||
@@ -519,15 +511,20 @@ internal class ChatCommands
|
||||
var roleName = GetString(rl.ToString()).ToLower().Trim();
|
||||
if (setRole.Contains(roleName))
|
||||
{
|
||||
PlayerControl.LocalPlayer.RpcSetRole(rl.GetRoleTypes());
|
||||
PlayerControl.LocalPlayer.RpcSetCustomRole(rl);
|
||||
PlayerControl.LocalPlayer.SyncSettings();
|
||||
Utils.NotifyRoles(SpecifySeer: PlayerControl.LocalPlayer);
|
||||
Utils.NotifyRoles(SpecifyTarget: PlayerControl.LocalPlayer);
|
||||
HudManager.Instance.SetHudActive(PlayerControl.LocalPlayer, PlayerControl.LocalPlayer.Data.Role, !GameStates.IsMeeting);
|
||||
Utils.AddRoles(PlayerControl.LocalPlayer.PlayerId, rl);
|
||||
Main.PlayerStates[PlayerControl.LocalPlayer.PlayerId].RemoveSubRole(CustomRoles.NotAssigned);
|
||||
PlayerControl pc = PlayerControl.LocalPlayer;
|
||||
if (!rl.IsAdditionRole()) pc.RpcSetRole(rl.GetRoleTypes());
|
||||
pc.RpcSetCustomRole(rl);
|
||||
pc.SyncSettings();
|
||||
Utils.NotifyRoles(SpecifySeer: pc);
|
||||
Utils.NotifyRoles(SpecifyTarget: pc);
|
||||
if (!rl.IsAdditionRole())
|
||||
{
|
||||
HudManager.Instance.SetHudActive(pc, pc.Data.Role, !GameStates.IsMeeting);
|
||||
Utils.AddRoles(pc.PlayerId, rl);
|
||||
}
|
||||
Main.PlayerStates[pc.PlayerId].RemoveSubRole(CustomRoles.NotAssigned);
|
||||
Main.ChangedRole = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -815,6 +812,9 @@ internal class ChatCommands
|
||||
case CustomGameMode.MoveAndStop:
|
||||
Utils.SendMessage(GetString("ModeDescribe.MoveAndStop"), playerId);
|
||||
return;
|
||||
case CustomGameMode.HotPotato:
|
||||
Utils.SendMessage(GetString("ModeDescribe.HotPotato"), playerId);
|
||||
return;
|
||||
}
|
||||
|
||||
role = role.Trim().ToLower();
|
||||
|
||||
@@ -155,7 +155,7 @@ public static class ChatManager
|
||||
if (!AmongUsClient.Instance.AmHost || !GameStates.IsModHost) return;
|
||||
ChatUpdatePatch.DoBlockChat = true;
|
||||
string msg = Utils.EmptyMessage();
|
||||
List<CustomRoles> roles = Enum.GetValues(typeof(CustomRoles)).Cast<CustomRoles>().Where(x => x is not CustomRoles.KB_Normal and not CustomRoles.Killer and not CustomRoles.Tasker).ToList();
|
||||
List<CustomRoles> roles = Enum.GetValues(typeof(CustomRoles)).Cast<CustomRoles>().Where(x => x is not CustomRoles.KB_Normal and not CustomRoles.Killer and not CustomRoles.Tasker and not CustomRoles.Potato).ToList();
|
||||
string[] specialTexts = ["bet", "bt", "guess", "gs", "shoot", "st", "赌", "猜", "审判", "tl", "判", "审", "trial"];
|
||||
var totalAlive = Main.AllAlivePlayerControls.Length;
|
||||
var x = Main.AllAlivePlayerControls;
|
||||
|
||||
+128
-131
@@ -8,6 +8,7 @@ using System.Linq;
|
||||
using TOHE.Roles.Impostor;
|
||||
using TOHE.Roles.Neutral;
|
||||
using UnityEngine;
|
||||
using static TOHE.CustomWinnerHolder;
|
||||
using static TOHE.Translator;
|
||||
using static TOHE.Utils;
|
||||
|
||||
@@ -23,15 +24,15 @@ class GameEndChecker
|
||||
|
||||
if (predicate == null) return false;
|
||||
|
||||
if (Options.NoGameEnd.GetBool() && CustomWinnerHolder.WinnerTeam is not CustomWinner.Draw and not CustomWinner.Error) return false;
|
||||
if (Options.NoGameEnd.GetBool() && WinnerTeam is not CustomWinner.Draw and not CustomWinner.Error) return false;
|
||||
|
||||
var reason = GameOverReason.ImpostorByKill;
|
||||
|
||||
predicate.CheckForEndGame(out reason);
|
||||
|
||||
if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop)
|
||||
if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato)
|
||||
{
|
||||
if (CustomWinnerHolder.WinnerIds.Count > 0 || CustomWinnerHolder.WinnerTeam != CustomWinner.Default)
|
||||
if (WinnerIds.Count > 0 || WinnerTeam != CustomWinner.Default)
|
||||
{
|
||||
ShipStatus.Instance.enabled = false;
|
||||
StartEndGame(reason);
|
||||
@@ -41,7 +42,7 @@ class GameEndChecker
|
||||
}
|
||||
|
||||
//ゲーム終了時
|
||||
if (CustomWinnerHolder.WinnerTeam != CustomWinner.Default)
|
||||
if (WinnerTeam != CustomWinner.Default)
|
||||
{
|
||||
NameNotifyManager.Reset();
|
||||
NotifyRoles(ForceLoop: true);
|
||||
@@ -51,214 +52,177 @@ class GameEndChecker
|
||||
if (reason == GameOverReason.ImpostorBySabotage && (CustomRoles.Jackal.RoleExist() || CustomRoles.Sidekick.RoleExist()) && Jackal.CanWinBySabotageWhenNoImpAlive.GetBool() && !Main.AllAlivePlayerControls.Any(x => x.GetCustomRole().IsImpostorTeam()))
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
CustomWinnerHolder.WinnerIds.Clear();
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Jackal);
|
||||
CustomWinnerHolder.WinnerRoles.Add(CustomRoles.Jackal);
|
||||
WinnerIds.Clear();
|
||||
ResetAndSetWinner(CustomWinner.Jackal);
|
||||
WinnerRoles.Add(CustomRoles.Jackal);
|
||||
}
|
||||
|
||||
switch (CustomWinnerHolder.WinnerTeam)
|
||||
switch (WinnerTeam)
|
||||
{
|
||||
case CustomWinner.Crewmate:
|
||||
Main.AllPlayerControls
|
||||
.Where(pc => (pc.Is(CustomRoleTypes.Crewmate) && !pc.Is(CustomRoles.Lovers) && !pc.Is(CustomRoles.Madmate) && !pc.Is(CustomRoles.Rogue) && !pc.Is(CustomRoles.Charmed) && !pc.Is(CustomRoles.Recruit) && !pc.Is(CustomRoles.Contagious) && !pc.Is(CustomRoles.EvilSpirit) && !pc.Is(CustomRoles.Recruit)))
|
||||
.Do(pc => CustomWinnerHolder.WinnerIds.Add(pc.PlayerId));
|
||||
.Do(pc => WinnerIds.Add(pc.PlayerId));
|
||||
break;
|
||||
case CustomWinner.Impostor:
|
||||
Main.AllPlayerControls
|
||||
.Where(pc => ((pc.Is(CustomRoleTypes.Impostor) && (!pc.Is(CustomRoles.DeadlyQuota) || Main.PlayerStates.Count(x => x.Value.GetRealKiller() == pc.PlayerId) >= Options.DQNumOfKillsNeeded.GetInt())) || pc.Is(CustomRoles.Madmate)) && !pc.Is(CustomRoles.Lovers) && !pc.Is(CustomRoles.Rogue) && !pc.Is(CustomRoles.Charmed) && !pc.Is(CustomRoles.Recruit) && !pc.Is(CustomRoles.Contagious) && !pc.Is(CustomRoles.EvilSpirit) && !pc.Is(CustomRoles.Recruit))
|
||||
.Do(pc => CustomWinnerHolder.WinnerIds.Add(pc.PlayerId));
|
||||
.Where(pc => ((pc.Is(CustomRoleTypes.Impostor) && (!pc.Is(CustomRoles.DeadlyQuota) || Main.PlayerStates.Count(x => x.Value.GetRealKiller() == pc.PlayerId) >= Options.DQNumOfKillsNeeded.GetInt())) || pc.Is(CustomRoles.Madmate) || pc.Is(CustomRoles.Refugee)) && !pc.Is(CustomRoles.Lovers) && !pc.Is(CustomRoles.Rogue) && !pc.Is(CustomRoles.Charmed) && !pc.Is(CustomRoles.Recruit) && !pc.Is(CustomRoles.Contagious) && !pc.Is(CustomRoles.EvilSpirit) && !pc.Is(CustomRoles.Recruit))
|
||||
.Do(pc => WinnerIds.Add(pc.PlayerId));
|
||||
break;
|
||||
case CustomWinner.Succubus:
|
||||
Main.AllPlayerControls
|
||||
.Where(pc => pc.Is(CustomRoles.Succubus) || (pc.Is(CustomRoles.Charmed) && !pc.Is(CustomRoles.Rogue)))
|
||||
.Do(pc => CustomWinnerHolder.WinnerIds.Add(pc.PlayerId));
|
||||
.Do(pc => WinnerIds.Add(pc.PlayerId));
|
||||
break;
|
||||
case CustomWinner.Necromancer:
|
||||
Main.AllPlayerControls
|
||||
.Where(pc => pc.Is(CustomRoles.Necromancer) || pc.Is(CustomRoles.Deathknight) || pc.Is(CustomRoles.Undead) && !pc.Is(CustomRoles.Rogue))
|
||||
.Do(pc => CustomWinnerHolder.WinnerIds.Add(pc.PlayerId));
|
||||
.Do(pc => WinnerIds.Add(pc.PlayerId));
|
||||
break;
|
||||
case CustomWinner.Virus:
|
||||
Main.AllPlayerControls
|
||||
.Where(pc => pc.Is(CustomRoles.Virus) || (pc.Is(CustomRoles.Contagious) && !pc.Is(CustomRoles.Rogue)))
|
||||
.Do(pc => CustomWinnerHolder.WinnerIds.Add(pc.PlayerId));
|
||||
.Do(pc => WinnerIds.Add(pc.PlayerId));
|
||||
break;
|
||||
case CustomWinner.Jackal:
|
||||
Main.AllPlayerControls
|
||||
.Where(pc => (pc.Is(CustomRoles.Jackal) || pc.Is(CustomRoles.Sidekick) || pc.Is(CustomRoles.Recruit)) && !pc.Is(CustomRoles.Lovers) && !pc.Is(CustomRoles.Rogue))
|
||||
.Do(pc => CustomWinnerHolder.WinnerIds.Add(pc.PlayerId));
|
||||
.Do(pc => WinnerIds.Add(pc.PlayerId));
|
||||
break;
|
||||
case CustomWinner.Spiritcaller:
|
||||
Main.AllPlayerControls
|
||||
.Where(pc => pc.Is(CustomRoles.Spiritcaller) || pc.Is(CustomRoles.EvilSpirit))
|
||||
.Do(pc => CustomWinnerHolder.WinnerIds.Add(pc.PlayerId));
|
||||
.Do(pc => WinnerIds.Add(pc.PlayerId));
|
||||
break;
|
||||
case CustomWinner.RuthlessRomantic:
|
||||
foreach (var pc in Main.AllPlayerControls.Where(pc => pc.Is(CustomRoles.RuthlessRomantic)).ToArray())
|
||||
{
|
||||
CustomWinnerHolder.WinnerIds.Add(Romantic.BetPlayer[pc.PlayerId]);
|
||||
}
|
||||
//Main.AllPlayerControls
|
||||
// .Where(pc => (pc.Is(CustomRoles.RuthlessRomantic) || (Romantic.BetPlayer.TryGetValue(pc.PlayerId, out var RomanticPartner)) && pc.PlayerId == RomanticPartner))
|
||||
// .Do(pc => CustomWinnerHolder.WinnerIds.Add(pc.PlayerId));
|
||||
WinnerIds.Add(Romantic.PartnerId);
|
||||
break;
|
||||
}
|
||||
if (CustomWinnerHolder.WinnerTeam is not CustomWinner.Draw and not CustomWinner.None and not CustomWinner.Error)
|
||||
if (WinnerTeam is not CustomWinner.Draw and not CustomWinner.None and not CustomWinner.Error)
|
||||
{
|
||||
foreach (PlayerControl pc in Main.AllPlayerControls)
|
||||
{
|
||||
switch (pc.GetCustomRole())
|
||||
{
|
||||
case CustomRoles.DarkHide when !pc.Data.IsDead && ((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor && !reason.Equals(GameOverReason.ImpostorBySabotage)) || CustomWinnerHolder.WinnerTeam == CustomWinner.DarkHide || (CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate && !reason.Equals(GameOverReason.HumansByTask) && DarkHide.IsWinKill[pc.PlayerId] == true && DarkHide.SnatchesWin.GetBool())):
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.DarkHide);
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
case CustomRoles.DarkHide when !pc.Data.IsDead && ((WinnerTeam == CustomWinner.Impostor && !reason.Equals(GameOverReason.ImpostorBySabotage)) || WinnerTeam == CustomWinner.DarkHide || (WinnerTeam == CustomWinner.Crewmate && !reason.Equals(GameOverReason.HumansByTask) && DarkHide.IsWinKill[pc.PlayerId] == true && DarkHide.SnatchesWin.GetBool())):
|
||||
ResetAndSetWinner(CustomWinner.DarkHide);
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
break;
|
||||
case CustomRoles.Phantom when pc.GetTaskState().IsTaskFinished && pc.Data.IsDead && (CustomWinnerHolder.WinnerTeam is CustomWinner.Impostor or CustomWinner.Crewmate or CustomWinner.Jackal or CustomWinner.BloodKnight or CustomWinner.SerialKiller or CustomWinner.Juggernaut or CustomWinner.Ritualist or CustomWinner.Poisoner or CustomWinner.Succubus or CustomWinner.Jinx or CustomWinner.Virus or CustomWinner.Arsonist or CustomWinner.Pelican or CustomWinner.HexMaster or CustomWinner.Wraith or CustomWinner.Pestilence or CustomWinner.Rogue or CustomWinner.Doppelganger or CustomWinner.Spiritcaller) && Options.PhantomSnatchesWin.GetBool():
|
||||
case CustomRoles.Phantom when pc.GetTaskState().IsTaskFinished && pc.Data.IsDead && (WinnerTeam is CustomWinner.Impostor or CustomWinner.Crewmate or CustomWinner.Jackal or CustomWinner.BloodKnight or CustomWinner.SerialKiller or CustomWinner.Juggernaut or CustomWinner.Ritualist or CustomWinner.Poisoner or CustomWinner.Succubus or CustomWinner.Jinx or CustomWinner.Virus or CustomWinner.Arsonist or CustomWinner.Pelican or CustomWinner.HexMaster or CustomWinner.Wraith or CustomWinner.Pestilence or CustomWinner.Rogue or CustomWinner.Doppelganger or CustomWinner.Spiritcaller) && Options.PhantomSnatchesWin.GetBool():
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Phantom);
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
ResetAndSetWinner(CustomWinner.Phantom);
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
break;
|
||||
case CustomRoles.Phantom when !Options.PhantomSnatchesWin.GetBool() && !pc.IsAlive() && pc.GetTaskState().IsTaskFinished:
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Phantom);
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Phantom);
|
||||
break;
|
||||
case CustomRoles.Opportunist when pc.IsAlive():
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Opportunist);
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Opportunist);
|
||||
break;
|
||||
case CustomRoles.Pursuer when pc.IsAlive() && CustomWinnerHolder.WinnerTeam != CustomWinner.Jester && CustomWinnerHolder.WinnerTeam != CustomWinner.Lovers && CustomWinnerHolder.WinnerTeam != CustomWinner.Terrorist && CustomWinnerHolder.WinnerTeam != CustomWinner.Executioner && CustomWinnerHolder.WinnerTeam != CustomWinner.Collector && CustomWinnerHolder.WinnerTeam != CustomWinner.Innocent && CustomWinnerHolder.WinnerTeam != CustomWinner.Youtuber:
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Pursuer);
|
||||
case CustomRoles.Pursuer when pc.IsAlive() && WinnerTeam != CustomWinner.Jester && WinnerTeam != CustomWinner.Lovers && WinnerTeam != CustomWinner.Terrorist && WinnerTeam != CustomWinner.Executioner && WinnerTeam != CustomWinner.Collector && WinnerTeam != CustomWinner.Innocent && WinnerTeam != CustomWinner.Youtuber:
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Pursuer);
|
||||
break;
|
||||
case CustomRoles.Sunnyboy when !pc.IsAlive():
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Sunnyboy);
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Sunnyboy);
|
||||
break;
|
||||
case CustomRoles.Maverick when pc.IsAlive() && Maverick.NumOfKills >= Maverick.MinKillsToWin.GetInt():
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Maverick);
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Maverick);
|
||||
break;
|
||||
case CustomRoles.Provocateur when Main.Provoked.TryGetValue(pc.PlayerId, out var tar) && !CustomWinnerHolder.WinnerIds.Contains(tar):
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Provocateur);
|
||||
case CustomRoles.Provocateur when Main.Provoked.TryGetValue(pc.PlayerId, out var tar) && !WinnerIds.Contains(tar):
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Provocateur);
|
||||
break;
|
||||
case CustomRoles.FFF when FFF.isWon:
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.FFF);
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.FFF);
|
||||
break;
|
||||
case CustomRoles.Totocalcio when Totocalcio.BetPlayer.TryGetValue(pc.PlayerId, out var betTarget) && (CustomWinnerHolder.WinnerIds.Contains(betTarget) || (Main.PlayerStates.TryGetValue(betTarget, out var ps) && CustomWinnerHolder.WinnerRoles.Contains(ps.MainRole))):
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Totocalcio);
|
||||
case CustomRoles.Totocalcio when Totocalcio.BetPlayer.TryGetValue(pc.PlayerId, out var betTarget) && (WinnerIds.Contains(betTarget) || (Main.PlayerStates.TryGetValue(betTarget, out var ps) && WinnerRoles.Contains(ps.MainRole))):
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Totocalcio);
|
||||
break;
|
||||
case CustomRoles.Romantic when Romantic.BetPlayer.TryGetValue(pc.PlayerId, out var betTarget) && (CustomWinnerHolder.WinnerIds.Contains(betTarget) || (Main.PlayerStates.TryGetValue(betTarget, out var ps) && CustomWinnerHolder.WinnerRoles.Contains(ps.MainRole))):
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Romantic);
|
||||
case CustomRoles.Romantic when WinnerIds.Contains(Romantic.PartnerId) || (Main.PlayerStates.TryGetValue(Romantic.PartnerId, out var ps) && WinnerRoles.Contains(ps.MainRole)):
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Romantic);
|
||||
break;
|
||||
case CustomRoles.VengefulRomantic when VengefulRomantic.hasKilledKiller:
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.WinnerIds.Add(Romantic.BetPlayer[pc.PlayerId]);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.VengefulRomantic);
|
||||
case CustomRoles.VengefulRomantic when VengefulRomantic.HasKilledKiller:
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
WinnerIds.Add(Romantic.PartnerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.VengefulRomantic);
|
||||
break;
|
||||
case CustomRoles.Lawyer when Lawyer.Target.TryGetValue(pc.PlayerId, out var lawyertarget) && (CustomWinnerHolder.WinnerIds.Contains(lawyertarget) || (Main.PlayerStates.TryGetValue(lawyertarget, out var ps) && CustomWinnerHolder.WinnerRoles.Contains(ps.MainRole))):
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Lawyer);
|
||||
case CustomRoles.Lawyer when Lawyer.Target.TryGetValue(pc.PlayerId, out var lawyertarget) && (WinnerIds.Contains(lawyertarget) || (Main.PlayerStates.TryGetValue(lawyertarget, out var ps) && WinnerRoles.Contains(ps.MainRole))):
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Lawyer);
|
||||
break;
|
||||
case CustomRoles.Postman when Postman.IsFinished:
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Postman);
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Postman);
|
||||
break;
|
||||
case CustomRoles.SoulHunter when SoulHunter.Souls >= SoulHunter.NumOfSoulsToWin.GetInt():
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.SoulHunter);
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.SoulHunter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor)
|
||||
if (WinnerTeam == CustomWinner.Impostor)
|
||||
{
|
||||
var aliveImps = Main.AllAlivePlayerControls.Where(x => x.Is(CustomRoleTypes.Impostor));
|
||||
var aliveImpCount = aliveImps.Count();
|
||||
|
||||
// If there's an Egoist, and there is at least 1 non-Egoist impostor alive, Egoist loses
|
||||
if (aliveImpCount > 1 && CustomWinnerHolder.WinnerIds.Any(x => GetPlayerById(x).Is(CustomRoles.Egoist)))
|
||||
if (aliveImpCount > 1 && WinnerIds.Any(x => GetPlayerById(x).Is(CustomRoles.Egoist)))
|
||||
{
|
||||
CustomWinnerHolder.WinnerIds.RemoveWhere(x => GetPlayerById(x).Is(CustomRoles.Egoist));
|
||||
WinnerIds.RemoveWhere(x => GetPlayerById(x).Is(CustomRoles.Egoist));
|
||||
}
|
||||
// If there's only 1 impostor alive, and all alive impostors are Egoists, the Egoist wins alone
|
||||
else if (aliveImpCount == 1 && aliveImps.All(x => x.Is(CustomRoles.Egoist)))
|
||||
{
|
||||
var pc = aliveImps.FirstOrDefault();
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Egoist);
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
ResetAndSetWinner(CustomWinner.Egoist);
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
}
|
||||
}
|
||||
|
||||
if (CustomRoles.God.RoleExist())
|
||||
{
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.God);
|
||||
ResetAndSetWinner(CustomWinner.God);
|
||||
Main.AllPlayerControls
|
||||
.Where(p => p.Is(CustomRoles.God) && p.IsAlive())
|
||||
.Do(p => CustomWinnerHolder.WinnerIds.Add(p.PlayerId));
|
||||
.Do(p => WinnerIds.Add(p.PlayerId));
|
||||
}
|
||||
|
||||
if (CustomRoles.Lovers.RoleExist() && !reason.Equals(GameOverReason.HumansByTask) && Main.AllPlayerControls.Length >= 2 && !(!Main.LoversPlayers.All(p => p.IsAlive()) && Options.LoverSuicide.GetBool()) && CustomWinnerHolder.WinnerTeam is CustomWinner.Crewmate or CustomWinner.Impostor or CustomWinner.Jackal or CustomWinner.Pelican)
|
||||
if (WinnerIds.Any(x => GetPlayerById(x).Is(CustomRoles.Lovers)))
|
||||
{
|
||||
CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Lovers);
|
||||
AdditionalWinnerTeams.Add(AdditionalWinners.Lovers);
|
||||
Main.AllPlayerControls
|
||||
.Where(p => p.Is(CustomRoles.Lovers))
|
||||
.Do(p => CustomWinnerHolder.WinnerIds.Add(p.PlayerId));
|
||||
.Do(p => WinnerIds.Add(p.PlayerId));
|
||||
}
|
||||
|
||||
//Lovers follow winner
|
||||
if (CustomWinnerHolder.WinnerTeam is not CustomWinner.Lovers and not CustomWinner.Crewmate and not CustomWinner.Impostor)
|
||||
{
|
||||
foreach (var pc in Main.AllPlayerControls.Where(x => x.Is(CustomRoles.Lovers)).ToArray())
|
||||
{
|
||||
if (CustomWinnerHolder.WinnerIds.Any(x => GetPlayerById(x).Is(CustomRoles.Lovers)))
|
||||
{
|
||||
if (!CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId)) CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
if (!CustomWinnerHolder.AdditionalWinnerTeams.Contains(AdditionalWinners.Lovers)) CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Lovers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Ruthless Romantic partner win condition
|
||||
if (RuthlessRomantic.IsEnable && (CustomWinnerHolder.WinnerIds.Contains(RuthlessRomantic.playerIdList[0]) || (Main.PlayerStates.TryGetValue(RuthlessRomantic.playerIdList[0], out var pz) && CustomWinnerHolder.WinnerRoles.Contains(pz.MainRole))))
|
||||
if (Options.NeutralWinTogether.GetBool() && WinnerIds.Any(x => GetPlayerById(x) != null && GetPlayerById(x).GetCustomRole().IsNeutral()))
|
||||
{
|
||||
foreach (PlayerControl pc in Main.AllPlayerControls)
|
||||
{
|
||||
if (Romantic.BetPlayer.TryGetValue(pc.PlayerId, out var betTarget))
|
||||
{
|
||||
CustomWinnerHolder.WinnerIds.Add(betTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CustomWinnerHolder.WinnerTeam == CustomWinner.Lovers || CustomWinnerHolder.AdditionalWinnerTeams.Contains(AdditionalWinners.Lovers))
|
||||
{
|
||||
Main.AllPlayerControls.Where(p => p.Is(CustomRoles.Lovers) && !CustomWinnerHolder.WinnerIds.Contains(p.PlayerId))
|
||||
.Do(p => CustomWinnerHolder.WinnerIds.Add(p.PlayerId));
|
||||
}
|
||||
|
||||
if (Options.NeutralWinTogether.GetBool() && CustomWinnerHolder.WinnerIds.Any(x => GetPlayerById(x) != null && GetPlayerById(x).GetCustomRole().IsNeutral()))
|
||||
{
|
||||
foreach (PlayerControl pc in Main.AllPlayerControls)
|
||||
{
|
||||
if (pc.GetCustomRole().IsNeutral() && !CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId))
|
||||
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
|
||||
if (pc.GetCustomRole().IsNeutral() && !WinnerIds.Contains(pc.PlayerId))
|
||||
WinnerIds.Add(pc.PlayerId);
|
||||
}
|
||||
}
|
||||
else if (Options.NeutralRoleWinTogether.GetBool())
|
||||
{
|
||||
foreach (var id in CustomWinnerHolder.WinnerIds)
|
||||
foreach (var id in WinnerIds)
|
||||
{
|
||||
var pc = GetPlayerById(id);
|
||||
if (pc == null || !pc.GetCustomRole().IsNeutral()) continue;
|
||||
foreach (PlayerControl tar in Main.AllPlayerControls)
|
||||
{
|
||||
if (!CustomWinnerHolder.WinnerIds.Contains(tar.PlayerId) && tar.GetCustomRole() == pc.GetCustomRole())
|
||||
CustomWinnerHolder.WinnerIds.Add(tar.PlayerId);
|
||||
if (!WinnerIds.Contains(tar.PlayerId) && tar.GetCustomRole() == pc.GetCustomRole())
|
||||
WinnerIds.Add(tar.PlayerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,7 +243,7 @@ class GameEndChecker
|
||||
|
||||
// Set ghost role
|
||||
List<byte> ReviveRequiredPlayerIds = [];
|
||||
var winner = CustomWinnerHolder.WinnerTeam;
|
||||
var winner = WinnerTeam;
|
||||
foreach (var pc in Main.AllPlayerControls)
|
||||
{
|
||||
if (winner == CustomWinner.Draw)
|
||||
@@ -287,7 +251,7 @@ class GameEndChecker
|
||||
SetGhostRole(ToGhostImpostor: true);
|
||||
continue;
|
||||
}
|
||||
bool canWin = CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId) || CustomWinnerHolder.WinnerRoles.Contains(pc.GetCustomRole());
|
||||
bool canWin = WinnerIds.Contains(pc.PlayerId) || WinnerRoles.Contains(pc.GetCustomRole());
|
||||
bool isCrewmateWin = reason.Equals(GameOverReason.HumansByVote) || reason.Equals(GameOverReason.HumansByTask);
|
||||
SetGhostRole(ToGhostImpostor: canWin ^ isCrewmateWin); // XOR
|
||||
|
||||
@@ -310,7 +274,7 @@ class GameEndChecker
|
||||
|
||||
// Sync of CustomWinnerHolder info
|
||||
var winnerWriter = self.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.EndGame, SendOption.Reliable);
|
||||
CustomWinnerHolder.WriteTo(winnerWriter);
|
||||
WriteTo(winnerWriter);
|
||||
self.FinishRpcImmediately(winnerWriter);
|
||||
|
||||
// Delay to ensure that resuscitation is delivered after the ghost roll setting
|
||||
@@ -342,13 +306,14 @@ class GameEndChecker
|
||||
public static void SetPredicateToSoloKombat() => predicate = new SoloKombatGameEndPredicate();
|
||||
public static void SetPredicateToFFA() => predicate = new FFAGameEndPredicate();
|
||||
public static void SetPredicateToMoveAndStop() => predicate = new MoveAndStopGameEndPredicate();
|
||||
public static void SetPredicateToHotPotato() => predicate = new HotPotatoGameEndPredicate();
|
||||
|
||||
class NormalGameEndPredicate : GameEndPredicate
|
||||
{
|
||||
public override bool CheckForEndGame(out GameOverReason reason)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
if (CustomWinnerHolder.WinnerTeam != CustomWinner.Default) return false;
|
||||
if (WinnerTeam != CustomWinner.Default) return false;
|
||||
if (CheckGameEndBySabotage(out reason) || CheckGameEndByTask(out reason) || CheckGameEndByLivingPlayers(out reason)) return true;
|
||||
return false;
|
||||
}
|
||||
@@ -430,7 +395,7 @@ class GameEndChecker
|
||||
if (Main.AllAlivePlayerControls.All(p => p.Is(CustomRoles.Lovers)))
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Lovers);
|
||||
ResetAndSetWinner(CustomWinner.Lovers);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -452,7 +417,7 @@ class GameEndChecker
|
||||
winner = CustomWinner.Crewmate;
|
||||
}
|
||||
else return false;
|
||||
if (winner != null) CustomWinnerHolder.ResetAndSetWinner((CustomWinner)winner);
|
||||
if (winner != null) ResetAndSetWinner((CustomWinner)winner);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -479,12 +444,12 @@ class GameEndChecker
|
||||
{
|
||||
Logger.Fatal("Error while selecting NK winner", "CheckGameEndPatch.CheckGameEndByLivingPlayers");
|
||||
Logger.SendInGame("There was an error while selecting the winner. Please report this bug to the developer! (Do /dump to get logs)");
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Error);
|
||||
ResetAndSetWinner(CustomWinner.Error);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (winner != null) CustomWinnerHolder.ResetAndSetWinner((CustomWinner)winner);
|
||||
if (rl != null) CustomWinnerHolder.WinnerRoles.Add((CustomRoles)rl);
|
||||
if (winner != null) ResetAndSetWinner((CustomWinner)winner);
|
||||
if (rl != null) WinnerRoles.Add((CustomRoles)rl);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -497,7 +462,7 @@ class GameEndChecker
|
||||
public override bool CheckForEndGame(out GameOverReason reason)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
if (CustomWinnerHolder.WinnerIds.Count > 0) return false;
|
||||
if (WinnerIds.Count > 0) return false;
|
||||
if (CheckGameEndByLivingPlayers(out reason)) return true;
|
||||
return false;
|
||||
}
|
||||
@@ -510,7 +475,7 @@ class GameEndChecker
|
||||
|
||||
var winner = Main.AllPlayerControls.FirstOrDefault(x => !x.Is(CustomRoles.GM) && SoloKombatManager.GetRankOfScore(x.PlayerId) == 1);
|
||||
|
||||
CustomWinnerHolder.WinnerIds =
|
||||
WinnerIds =
|
||||
[
|
||||
winner.PlayerId
|
||||
];
|
||||
@@ -525,7 +490,7 @@ class GameEndChecker
|
||||
public override bool CheckForEndGame(out GameOverReason reason)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
if (CustomWinnerHolder.WinnerIds.Count > 0) return false;
|
||||
if (WinnerIds.Count > 0) return false;
|
||||
if (CheckGameEndByLivingPlayers(out reason)) return true;
|
||||
return false;
|
||||
}
|
||||
@@ -544,7 +509,7 @@ class GameEndChecker
|
||||
|
||||
Logger.Warn($"Winner: {GetPlayerById(winnerId).GetRealName().RemoveHtmlTags()}", "FFA");
|
||||
|
||||
CustomWinnerHolder.WinnerIds =
|
||||
WinnerIds =
|
||||
[
|
||||
winnerId
|
||||
];
|
||||
@@ -559,7 +524,7 @@ class GameEndChecker
|
||||
|
||||
Logger.Info($"Winner: {winner.GetRealName().RemoveHtmlTags()}", "FFA");
|
||||
|
||||
CustomWinnerHolder.WinnerIds =
|
||||
WinnerIds =
|
||||
[
|
||||
winner.PlayerId
|
||||
];
|
||||
@@ -582,7 +547,7 @@ class GameEndChecker
|
||||
public override bool CheckForEndGame(out GameOverReason reason)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
if (CustomWinnerHolder.WinnerIds.Count > 0) return false;
|
||||
if (WinnerIds.Count > 0) return false;
|
||||
if (CheckGameEndByLivingPlayers(out reason)) return true;
|
||||
return false;
|
||||
}
|
||||
@@ -593,7 +558,7 @@ class GameEndChecker
|
||||
|
||||
if (MoveAndStopManager.RoundTime <= 0)
|
||||
{
|
||||
var winner = Main.AllPlayerControls.Where(x => !x.Is(CustomRoles.GM) && x != null).OrderBy(x => MoveAndStopManager.GetRankOfScore(x.PlayerId)).First();
|
||||
var winner = Main.AllPlayerControls.Where(x => !x.Is(CustomRoles.GM) && x != null).OrderBy(x => MoveAndStopManager.GetRankOfScore(x.PlayerId)).OrderBy(x => x.IsAlive()).First();
|
||||
|
||||
byte winnerId;
|
||||
if (winner == null) winnerId = 0;
|
||||
@@ -601,7 +566,7 @@ class GameEndChecker
|
||||
|
||||
Logger.Warn($"Winner: {GetPlayerById(winnerId).GetRealName().RemoveHtmlTags()}", "MoveAndStop");
|
||||
|
||||
CustomWinnerHolder.WinnerIds =
|
||||
WinnerIds =
|
||||
[
|
||||
winnerId
|
||||
];
|
||||
@@ -616,7 +581,7 @@ class GameEndChecker
|
||||
|
||||
Logger.Info($"Winner: {winner.GetRealName().RemoveHtmlTags()}", "MoveAndStop");
|
||||
|
||||
CustomWinnerHolder.WinnerIds =
|
||||
WinnerIds =
|
||||
[
|
||||
winner.PlayerId
|
||||
];
|
||||
@@ -635,6 +600,38 @@ class GameEndChecker
|
||||
}
|
||||
}
|
||||
|
||||
class HotPotatoGameEndPredicate : GameEndPredicate
|
||||
{
|
||||
public override bool CheckForEndGame(out GameOverReason reason)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
if (WinnerIds.Count > 0) return false;
|
||||
if (CheckGameEndByLivingPlayers(out reason)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool CheckGameEndByLivingPlayers(out GameOverReason reason)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
|
||||
switch (Main.AllAlivePlayerControls.Length)
|
||||
{
|
||||
case 1:
|
||||
var winner = Main.AllAlivePlayerControls[0];
|
||||
Logger.Info($"Winner: {winner.GetRealName().RemoveHtmlTags()}", "HotPotato");
|
||||
WinnerIds = [winner.PlayerId];
|
||||
Main.DoBlockNameChange = true;
|
||||
return true;
|
||||
case 0:
|
||||
ResetAndSetWinner(CustomWinner.Error);
|
||||
Logger.Warn("No players alive. Force ending the game", "HotPotato");
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class GameEndPredicate
|
||||
{
|
||||
/// <summary>Checks the game ending condition and stores the value in CustomWinnerHolder. </summary>
|
||||
@@ -651,7 +648,7 @@ class GameEndChecker
|
||||
if (GameData.Instance.TotalTasks <= GameData.Instance.CompletedTasks)
|
||||
{
|
||||
reason = GameOverReason.HumansByTask;
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Crewmate);
|
||||
ResetAndSetWinner(CustomWinner.Crewmate);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -670,7 +667,7 @@ class GameEndChecker
|
||||
LifeSupp.Countdown < 0f) // Time up confirmation
|
||||
{
|
||||
// oxygen sabotage
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Impostor);
|
||||
ResetAndSetWinner(CustomWinner.Impostor);
|
||||
reason = GameOverReason.ImpostorBySabotage;
|
||||
LifeSupp.Countdown = 10000f;
|
||||
return true;
|
||||
@@ -687,7 +684,7 @@ class GameEndChecker
|
||||
critical.Countdown < 0f) // Time up confirmation
|
||||
{
|
||||
// reactor sabotage
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Impostor);
|
||||
ResetAndSetWinner(CustomWinner.Impostor);
|
||||
reason = GameOverReason.ImpostorBySabotage;
|
||||
critical.ClearSabotage();
|
||||
return true;
|
||||
|
||||
+11
-1
@@ -1,6 +1,8 @@
|
||||
using AmongUs.Data;
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using System.Linq;
|
||||
using TOHE.Modules;
|
||||
using TOHE.Roles.AddOns.Crewmate;
|
||||
using TOHE.Roles.AddOns.Impostor;
|
||||
using TOHE.Roles.Crewmate;
|
||||
@@ -144,7 +146,7 @@ class ExileControllerWrapUpPatch
|
||||
pc.RpcResetAbilityCooldown();
|
||||
PetsPatch.RpcRemovePet(pc);
|
||||
}
|
||||
if (Options.RandomSpawn.GetBool() || Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop)
|
||||
if (Options.RandomSpawn.GetBool() || Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato)
|
||||
{
|
||||
RandomSpawn.SpawnMap map = Main.NormalOptions.MapId switch
|
||||
{
|
||||
@@ -158,6 +160,14 @@ class ExileControllerWrapUpPatch
|
||||
if (map != null) Main.AllAlivePlayerControls.Do(map.RandomTeleport);
|
||||
}
|
||||
|
||||
//if (Options.SpawnAdditionalRefugeeOnImpsDead.GetBool() && !Main.AllAlivePlayerControls.Any(x => x.PlayerId != exiled.PlayerId && x.Is(CustomRoleTypes.Impostor)))
|
||||
//{
|
||||
// var pc = Main.AllAlivePlayerControls.Shuffle(IRandom.Instance).FirstOrDefault(x => x.PlayerId != exiled.PlayerId && x.Is(CustomRoleTypes.Crewmate));
|
||||
// pc?.ChangeRoleBasis(RoleTypes.Impostor);
|
||||
// pc?.RpcSetCustomRole(CustomRoles.Refugee);
|
||||
// pc?.SetKillCooldown();
|
||||
//}
|
||||
|
||||
FallFromLadder.Reset();
|
||||
Utils.CountAlivePlayers(true);
|
||||
Utils.AfterMeetingTasks();
|
||||
|
||||
@@ -379,6 +379,7 @@ public static class SetRecommendationsPatch
|
||||
{
|
||||
case CustomGameMode.SoloKombat:
|
||||
case CustomGameMode.FFA:
|
||||
case CustomGameMode.HotPotato:
|
||||
__instance.CrewLightMod = __instance.ImpostorLightMod = 1.25f;
|
||||
__instance.NumImpostors = 3;
|
||||
__instance.NumCommonTasks = 0;
|
||||
|
||||
+21
-2
@@ -144,7 +144,7 @@ class HudManagerPatch
|
||||
|
||||
if (SetHudActivePatch.IsActive)
|
||||
{
|
||||
if (player.IsAlive() || Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop)
|
||||
if (player.IsAlive() || Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato)
|
||||
{
|
||||
//MOD入り用のボタン下テキスト変更
|
||||
switch (player.GetCustomRole())
|
||||
@@ -448,7 +448,7 @@ class HudManagerPatch
|
||||
__instance.KillButton?.OverrideText(GetString("TotocalcioKillButtonText"));
|
||||
break;
|
||||
case CustomRoles.Romantic:
|
||||
if (Romantic.BetTimes.TryGetValue(player.PlayerId, out var timesV1) && timesV1 >= 1) __instance.KillButton?.OverrideText(GetString("RomanticKillButtonText"));
|
||||
if (!Romantic.HasPickedPartner) __instance.KillButton?.OverrideText(GetString("RomanticKillButtonText"));
|
||||
else __instance.KillButton?.OverrideText(GetString("MedicalerButtonText"));
|
||||
break;
|
||||
case CustomRoles.VengefulRomantic:
|
||||
@@ -531,6 +531,7 @@ class HudManagerPatch
|
||||
CustomGameMode.SoloKombat => SoloKombatManager.GetHudText(),
|
||||
CustomGameMode.FFA when player.PlayerId == 0 => FFAManager.GetHudText(),
|
||||
CustomGameMode.MoveAndStop when player.PlayerId == 0 => MoveAndStopManager.HUDText,
|
||||
CustomGameMode.HotPotato when player.PlayerId == 0 => HotPotatoManager.GetSuffixText(player.PlayerId),
|
||||
CustomGameMode.Standard => player.GetCustomRole() switch
|
||||
{
|
||||
CustomRoles.BountyHunter => BountyHunter.GetTargetText(player, true),
|
||||
@@ -688,6 +689,7 @@ class SetHudActivePatch
|
||||
switch (Options.CurrentGameMode)
|
||||
{
|
||||
case CustomGameMode.MoveAndStop:
|
||||
case CustomGameMode.HotPotato:
|
||||
__instance.ReportButton?.ToggleVisible(false);
|
||||
__instance.KillButton?.ToggleVisible(false);
|
||||
__instance.SabotageButton?.ToggleVisible(false);
|
||||
@@ -924,12 +926,29 @@ class TaskPanelBehaviourPatch
|
||||
List<(int, byte)> list3 = [];
|
||||
foreach (var id in Main.PlayerStates.Keys) list3.Add((MoveAndStopManager.GetRankOfScore(id), id));
|
||||
list3.Sort();
|
||||
list3 = [.. list3.OrderBy(x => (Utils.GetPlayerById(x.Item2).IsAlive()))];
|
||||
foreach (var id in list3.Where(x => SummaryText3.ContainsKey(x.Item2)).ToArray())
|
||||
{
|
||||
bool alive = Utils.GetPlayerById(id.Item2).IsAlive();
|
||||
AllText += $"{(!alive ? "<#777777>" : string.Empty)}<size=1.6>\r\n{(alive ? SummaryText3[id.Item2] : SummaryText3[id.Item2].RemoveHtmlTags())}{(!alive ? " <#ff0000>DEAD</color>" : string.Empty)}</size>";
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CustomGameMode.HotPotato:
|
||||
|
||||
List<string> SummaryText4 = [];
|
||||
foreach (var id in Main.PlayerStates.Keys.ToArray())
|
||||
{
|
||||
PlayerControl pc = Utils.GetPlayerById(id);
|
||||
string name = pc.GetRealName().RemoveHtmlTags().Replace("\r\n", string.Empty);
|
||||
bool alive = pc.IsAlive();
|
||||
string summary = $"{(!alive ? "<size=70%><#777777>" : "<size=80%>")}{HotPotatoManager.GetIndicator(id)}{Utils.ColorString(Main.PlayerColors[id], name)}{(!alive ? "</color> <#ff0000>DEAD</color></size>" : "</size>")}";
|
||||
SummaryText4.Add(summary);
|
||||
}
|
||||
|
||||
AllText += $"\r\n\r\n{string.Join('\n', SummaryText4)}";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ public static class HudSpritePatch
|
||||
newAbilityButton = CustomButton.Get("Block");
|
||||
break;
|
||||
case CustomRoles.Romantic:
|
||||
newKillButton = CustomButton.Get(Romantic.BetTimes.TryGetValue(player.PlayerId, out var times) && times >= 1 ? "Romance" : "RomanticProtect");
|
||||
newKillButton = CustomButton.Get(!Romantic.HasPickedPartner ? "Romance" : "RomanticProtect");
|
||||
break;
|
||||
case CustomRoles.VengefulRomantic:
|
||||
newKillButton = CustomButton.Get("RomanticKill");
|
||||
|
||||
+20
-2
@@ -49,6 +49,16 @@ class SetUpRoleTextPatch
|
||||
__instance.RoleBlurbText.text = GetString("TaskerInfo");
|
||||
break;
|
||||
}
|
||||
case CustomGameMode.HotPotato:
|
||||
{
|
||||
var color = ColorUtility.TryParseHtmlString("#e8cd46", out var c) ? c : new(255, 255, 255, 255);
|
||||
__instance.YouAreText.transform.gameObject.SetActive(false);
|
||||
__instance.RoleText.text = GetString("HotPotato");
|
||||
__instance.RoleText.color = color;
|
||||
__instance.RoleBlurbText.color = color;
|
||||
__instance.RoleBlurbText.text = GetString("PotatoInfo");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CustomRoles role = PlayerControl.LocalPlayer.GetCustomRole();
|
||||
@@ -241,6 +251,7 @@ class BeginCrewmatePatch
|
||||
CustomRoles.DovesOfNeace or
|
||||
CustomRoles.Spiritualist or
|
||||
CustomRoles.Spiritcaller or
|
||||
CustomRoles.Beacon or
|
||||
CustomRoles.Farseer
|
||||
=> GetIntroSound(RoleTypes.GuardianAngel),
|
||||
|
||||
@@ -398,6 +409,13 @@ class BeginCrewmatePatch
|
||||
__instance.ImpostorText.gameObject.SetActive(true);
|
||||
__instance.ImpostorText.text = GetString("TaskerInfo");
|
||||
break;
|
||||
case CustomGameMode.HotPotato:
|
||||
__instance.TeamTitle.text = GetString("HotPotato");
|
||||
__instance.TeamTitle.color = __instance.BackgroundBar.material.color = new Color32(232, 205, 70, byte.MaxValue);
|
||||
PlayerControl.LocalPlayer.Data.Role.IntroSound = GetIntroSound(RoleTypes.Shapeshifter);
|
||||
__instance.ImpostorText.gameObject.SetActive(true);
|
||||
__instance.ImpostorText.text = GetString("PotatoInfo");
|
||||
break;
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.RightShift))
|
||||
@@ -525,7 +543,7 @@ class IntroCutsceneDestroyPatch
|
||||
Main.AllPlayerControls.Where(x => Main.AllPlayerKillCooldown[x.PlayerId] != 7f && Main.AllPlayerKillCooldown[x.PlayerId] != 7.5f).Do(pc => pc.SetKillCooldown(Options.StartingKillCooldown.GetInt() - 2));
|
||||
}, 2f, "FixKillCooldownTask");
|
||||
}
|
||||
else if (Options.FixFirstKillCooldown.GetBool() && Options.CurrentGameMode is not CustomGameMode.SoloKombat and not CustomGameMode.FFA and not CustomGameMode.MoveAndStop)
|
||||
else if (Options.FixFirstKillCooldown.GetBool() && Options.CurrentGameMode is not CustomGameMode.SoloKombat and not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato)
|
||||
{
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
@@ -587,7 +605,7 @@ class IntroCutsceneDestroyPatch
|
||||
PlayerControl.LocalPlayer.RpcExile();
|
||||
Main.PlayerStates[PlayerControl.LocalPlayer.PlayerId].SetDead();
|
||||
}
|
||||
if (Options.RandomSpawn.GetBool() || Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop)
|
||||
if (Options.RandomSpawn.GetBool() || Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato)
|
||||
{
|
||||
RandomSpawn.SpawnMap map = Main.NormalOptions.MapId switch
|
||||
{
|
||||
|
||||
@@ -201,7 +201,7 @@ class CheckForEndVotingPatch
|
||||
VotedForId = ps.VotedFor
|
||||
});
|
||||
|
||||
statesList = NiceSwapper.OnCheckForEndVoting(statesList);
|
||||
NiceSwapper.OnCheckForEndVoting();
|
||||
|
||||
if (CheckRole(ps.TargetPlayerId, CustomRoles.Mayor) && !Options.MayorHideVote.GetBool()) //Mayorの投票数
|
||||
{
|
||||
@@ -326,7 +326,7 @@ class CheckForEndVotingPatch
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.SendInGame(string.Format(GetString("Error.MeetingException"), ex.Message), true);
|
||||
Logger.SendInGame(string.Format(GetString("Error.MeetingException"), ex.Message)/*, true*/);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-2
@@ -58,9 +58,9 @@ class EndGamePatch
|
||||
if (date == DateTime.MinValue) continue;
|
||||
var killerId = kvp.Value.GetRealKiller();
|
||||
var targetId = kvp.Key;
|
||||
sb.Append($"\n{date:T} {Main.AllPlayerNames[targetId]} ({(Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop ? string.Empty : Utils.GetDisplayRoleName(targetId, true))}{(Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop ? string.Empty : Utils.GetSubRolesText(targetId, summary: true))}) [{Utils.GetVitalText(kvp.Key)}]");
|
||||
sb.Append($"\n{date:T} {Main.AllPlayerNames[targetId]} ({(Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato ? string.Empty : Utils.GetDisplayRoleName(targetId, true))}{(Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop ? string.Empty : Utils.GetSubRolesText(targetId, summary: true))}) [{Utils.GetVitalText(kvp.Key)}]");
|
||||
if (killerId != byte.MaxValue && killerId != targetId)
|
||||
sb.Append($"\n\t⇐ {Main.AllPlayerNames[killerId]} ({(Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop ? string.Empty : Utils.GetDisplayRoleName(killerId, true))}{(Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop ? string.Empty : Utils.GetSubRolesText(killerId, summary: true))})");
|
||||
sb.Append($"\n\t⇐ {Main.AllPlayerNames[killerId]} ({(Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato ? string.Empty : Utils.GetDisplayRoleName(killerId, true))}{(Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop ? string.Empty : Utils.GetSubRolesText(killerId, summary: true))})");
|
||||
}
|
||||
KillLog = sb.ToString();
|
||||
if (!KillLog.Contains('\n')) KillLog = string.Empty;
|
||||
@@ -221,6 +221,14 @@ class SetEverythingUpPatch
|
||||
WinnerText.color = Main.PlayerColors[winnerId];
|
||||
goto EndOfText;
|
||||
}
|
||||
case CustomGameMode.HotPotato:
|
||||
{
|
||||
var winnerId = CustomWinnerHolder.WinnerIds.FirstOrDefault();
|
||||
__instance.BackgroundBar.material.color = new Color32(232, 205, 70, 255);
|
||||
WinnerText.text = Main.AllPlayerNames[winnerId] + " wins!";
|
||||
WinnerText.color = Main.PlayerColors[winnerId];
|
||||
goto EndOfText;
|
||||
}
|
||||
}
|
||||
|
||||
var winnerRole = (CustomRoles)CustomWinnerHolder.WinnerTeam;
|
||||
@@ -388,6 +396,13 @@ class SetEverythingUpPatch
|
||||
sb.Append($"\n ").Append(EndGamePatch.SummaryText[id.Item2]);
|
||||
break;
|
||||
}
|
||||
case CustomGameMode.HotPotato:
|
||||
{
|
||||
var list = cloneRoles.OrderByDescending(x => HotPotatoManager.GetSurvivalTime(x));
|
||||
foreach (var id in cloneRoles.Where(EndGamePatch.SummaryText.ContainsKey))
|
||||
sb.Append($"\n ").Append(EndGamePatch.SummaryText[id]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
sb.Append($"</b>\n");
|
||||
|
||||
@@ -405,7 +405,7 @@ class ExternalRpcPetPatch
|
||||
if (!Main.CursedPlayers[pc.PlayerId].Data.IsDead)
|
||||
{
|
||||
var cp = Main.CursedPlayers[pc.PlayerId];
|
||||
UnityEngine.Vector2 cppos = cp.Pos();
|
||||
Vector2 cppos = cp.Pos();
|
||||
Dictionary<PlayerControl, float> cpdistance = [];
|
||||
float dis;
|
||||
foreach (PlayerControl p in AllAlivePlayers)
|
||||
@@ -453,7 +453,7 @@ class ExternalRpcPetPatch
|
||||
{
|
||||
var position = Main.LastEnteredVentLocation[pc.PlayerId];
|
||||
Logger.Msg($"{pc.GetNameWithRole().RemoveHtmlTags()}:{position}", "MinerTeleport");
|
||||
pc.TP(new UnityEngine.Vector2(position.x, position.y));
|
||||
pc.TP(new Vector2(position.x, position.y));
|
||||
}
|
||||
break;
|
||||
case CustomRoles.Escapee:
|
||||
|
||||
@@ -121,7 +121,7 @@ class CheckMurderPatch
|
||||
return false;
|
||||
}
|
||||
|
||||
var divice = Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop ? 3000f : 2000f;
|
||||
var divice = Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato ? 3000f : 2000f;
|
||||
float minTime = Mathf.Max(0.02f, AmongUsClient.Instance.Ping / divice * 6f); // The value of AmongUsClient.Instance.Ping is in milliseconds (ms), so ÷1000
|
||||
// No value is stored in TimeSinceLastKill || Stored time is greater than or equal to minTime => Allow kill
|
||||
// ↓ If not allowed
|
||||
@@ -177,6 +177,7 @@ class CheckMurderPatch
|
||||
FFAManager.OnPlayerAttack(killer, target);
|
||||
return true;
|
||||
case CustomGameMode.MoveAndStop:
|
||||
case CustomGameMode.HotPotato:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -780,7 +781,7 @@ class CheckMurderPatch
|
||||
// return false;
|
||||
|
||||
// Romantic partner is protected
|
||||
if (Romantic.BetPlayer.ContainsValue(target.PlayerId) && Romantic.isPartnerProtected) return false;
|
||||
if (Romantic.PartnerId == target.PlayerId && Romantic.IsPartnerProtected) return false;
|
||||
|
||||
if (Options.OppoImmuneToAttacksWhenTasksDone.GetBool())
|
||||
{
|
||||
@@ -943,7 +944,7 @@ class CheckMurderPatch
|
||||
Logger.Info($"{target.GetRealName()} reverse killed:{killer.GetRealName()}", "Veteran Kill");
|
||||
return false;
|
||||
}
|
||||
if (killer.Is(CustomRoles.Pestilence))
|
||||
else
|
||||
{
|
||||
target.SetRealKiller(killer);
|
||||
killer.Kill(target);
|
||||
@@ -1584,7 +1585,7 @@ class ReportDeadBodyPatch
|
||||
{
|
||||
if (GameStates.IsMeeting) return false;
|
||||
if (Options.DisableMeeting.GetBool()) return false;
|
||||
if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop) return false;
|
||||
if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato) return false;
|
||||
if (!CanReport[__instance.PlayerId])
|
||||
{
|
||||
WaitReport[__instance.PlayerId].Add(target);
|
||||
@@ -1873,6 +1874,7 @@ class ReportDeadBodyPatch
|
||||
if (Reckless.IsEnable) Reckless.OnReportDeadBody();
|
||||
Swiftclaw.OnReportDeadBody();
|
||||
Mathematician.OnReportDeadBody();
|
||||
Beacon.OnReportDeadBody();
|
||||
|
||||
if (Mortician.IsEnable) Mortician.OnReportDeadBody(player, target);
|
||||
if (Tracefinder.IsEnable) Tracefinder.OnReportDeadBody(/*player, target*/);
|
||||
@@ -2062,9 +2064,11 @@ class FixedUpdatePatch
|
||||
|
||||
if (!Main.KillTimers.ContainsKey(playerId))
|
||||
Main.KillTimers.Add(playerId, 10f);
|
||||
else if (!player.inVent && !player.MyPhysics.Animations.IsPlayingEnterVentAnimation() && Main.KillTimers[playerId] > 0)
|
||||
else if (((!player.inVent && !player.MyPhysics.Animations.IsPlayingEnterVentAnimation()) || player.Is(CustomRoles.Haste)) && Main.KillTimers[playerId] > 0)
|
||||
Main.KillTimers[playerId] -= Time.fixedDeltaTime;
|
||||
|
||||
if (!lowLoad && player.IsModClient() && player.Is(CustomRoles.Haste)) player.ForceKillTimerContinue = true;
|
||||
|
||||
if (DoubleTrigger.FirstTriggerTimer.Count > 0) DoubleTrigger.OnFixedUpdate(player);
|
||||
switch (player.GetCustomRole())
|
||||
{
|
||||
@@ -2164,6 +2168,7 @@ class FixedUpdatePatch
|
||||
Sprayer.OnCheckPlayerPosition(player);
|
||||
Asthmatic.OnCheckPlayerPosition(player);
|
||||
PlagueDoctor.OnCheckPlayerPosition(player);
|
||||
Beacon.OnCheckPlayerPosition(player);
|
||||
}
|
||||
|
||||
if (!lowLoad && Main.PlayerStates.TryGetValue(playerId, out var playerState) && GameStates.IsInTask)
|
||||
@@ -2732,7 +2737,7 @@ class FixedUpdatePatch
|
||||
RoleText.text = RoleTextData.Item1;
|
||||
RoleText.color = RoleTextData.Item2;
|
||||
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.SoloKombat or CustomGameMode.MoveAndStop) RoleText.text = string.Empty;
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.SoloKombat or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato) RoleText.text = string.Empty;
|
||||
|
||||
RoleText.enabled = IsRoleTextEnabled(__instance);
|
||||
|
||||
@@ -3039,6 +3044,9 @@ class FixedUpdatePatch
|
||||
case CustomGameMode.MoveAndStop when seer.PlayerId == target.PlayerId:
|
||||
Suffix.Append(MoveAndStopManager.GetSuffixText(seer));
|
||||
break;
|
||||
case CustomGameMode.HotPotato when !seer.IsModClient() && seer.PlayerId == target.PlayerId && seer.IsAlive():
|
||||
Suffix.Append(HotPotatoManager.GetSuffixText(seer.PlayerId));
|
||||
break;
|
||||
}
|
||||
|
||||
if (Vulture.ArrowsPointingToDeadBody.GetBool() && Vulture.IsEnable)
|
||||
@@ -3181,6 +3189,11 @@ class ExitVentPatch
|
||||
pc.TPtoRndVent();
|
||||
}, 0.5f, "Whack-A-Mole TP");
|
||||
}
|
||||
|
||||
if (!pc.IsModClient() && pc.Is(CustomRoles.Haste))
|
||||
{
|
||||
pc.SetKillCooldown(Math.Max(Main.KillTimers[pc.PlayerId], 0.1f));
|
||||
}
|
||||
}
|
||||
}
|
||||
[HarmonyPatch(typeof(Vent), nameof(Vent.EnterVent))]
|
||||
@@ -3469,32 +3482,30 @@ class CoEnterVentPatch
|
||||
Main.KillTimers[__instance.myPlayer.PlayerId] = timer + 0.5f;
|
||||
}
|
||||
|
||||
if (Options.CurrentGameMode == CustomGameMode.FFA && FFAManager.FFA_DisableVentingWhenTwoPlayersAlive.GetBool() && Main.AllAlivePlayerControls.Length <= 2)
|
||||
switch (Options.CurrentGameMode)
|
||||
{
|
||||
var pc = __instance?.myPlayer;
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
pc?.Notify(GetString("FFA-NoVentingBecauseTwoPlayers"), 7f);
|
||||
pc?.MyPhysics?.RpcBootFromVent(id);
|
||||
}, 0.5f);
|
||||
return true;
|
||||
}
|
||||
if (Options.CurrentGameMode == CustomGameMode.FFA && FFAManager.FFA_DisableVentingWhenKCDIsUp.GetBool() && Main.KillTimers[__instance.myPlayer.PlayerId] <= 0)
|
||||
{
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
__instance.myPlayer?.Notify(GetString("FFA-NoVentingBecauseKCDIsUP"), 7f);
|
||||
__instance.myPlayer?.MyPhysics?.RpcBootFromVent(id);
|
||||
}, 0.5f);
|
||||
return true;
|
||||
}
|
||||
if (Options.CurrentGameMode == CustomGameMode.MoveAndStop)
|
||||
{
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
__instance.myPlayer?.MyPhysics?.RpcBootFromVent(id);
|
||||
}, 0.5f);
|
||||
return true;
|
||||
case CustomGameMode.FFA when FFAManager.FFA_DisableVentingWhenTwoPlayersAlive.GetBool() && Main.AllAlivePlayerControls.Length <= 2:
|
||||
var pc = __instance?.myPlayer;
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
pc?.Notify(GetString("FFA-NoVentingBecauseTwoPlayers"), 7f);
|
||||
pc?.MyPhysics?.RpcBootFromVent(id);
|
||||
}, 0.5f);
|
||||
return true;
|
||||
case CustomGameMode.FFA when FFAManager.FFA_DisableVentingWhenKCDIsUp.GetBool() && Main.KillTimers[__instance.myPlayer.PlayerId] <= 0:
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
__instance.myPlayer?.Notify(GetString("FFA-NoVentingBecauseKCDIsUP"), 7f);
|
||||
__instance.myPlayer?.MyPhysics?.RpcBootFromVent(id);
|
||||
}, 0.5f);
|
||||
return true;
|
||||
case CustomGameMode.MoveAndStop:
|
||||
case CustomGameMode.HotPotato:
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
__instance.myPlayer?.MyPhysics?.RpcBootFromVent(id);
|
||||
}, 0.5f);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Glitch.hackedIdList.ContainsKey(__instance.myPlayer.PlayerId))
|
||||
|
||||
@@ -59,7 +59,7 @@ class RepairSystemPatch
|
||||
|
||||
IsComms = PlayerControl.LocalPlayer.myTasks.ToArray().Any(x => x.TaskType == TaskTypes.FixComms);
|
||||
|
||||
if ((Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop) && systemType == SystemTypes.Sabotage) return false;
|
||||
if ((Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato) && systemType == SystemTypes.Sabotage) return false;
|
||||
|
||||
if (Options.DisableSabotage.GetBool() && systemType == SystemTypes.Sabotage) return false;
|
||||
|
||||
@@ -98,7 +98,7 @@ class RepairSystemPatch
|
||||
if (Options.DisableAirshipCargoLightsPanel.GetBool() && Vector2.Distance(player.transform.position, new(30.56f, 2.12f)) <= 2f) return false;
|
||||
break;
|
||||
case SystemTypes.Sabotage when AmongUsClient.Instance.NetworkMode != NetworkModes.FreePlay:
|
||||
if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop) return false;
|
||||
if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato) return false;
|
||||
if (Main.BlockSabo.Count > 0) return false;
|
||||
if (Glitch.hackedIdList.ContainsKey(player.PlayerId))
|
||||
{
|
||||
@@ -286,7 +286,7 @@ class CheckTaskCompletionPatch
|
||||
{
|
||||
public static bool Prefix(ref bool __result)
|
||||
{
|
||||
if (Options.DisableTaskWin.GetBool() || Options.NoGameEnd.GetBool() || TaskState.InitialTotalTasks == 0 || Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop)
|
||||
if (Options.DisableTaskWin.GetBool() || Options.NoGameEnd.GetBool() || TaskState.InitialTotalTasks == 0 || Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato)
|
||||
{
|
||||
__result = false;
|
||||
return false;
|
||||
|
||||
@@ -19,9 +19,8 @@ class EmergencyMinigamePatch
|
||||
{
|
||||
public static void Postfix(EmergencyMinigame __instance)
|
||||
{
|
||||
if (Options.DisableMeeting.GetBool() || Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop)
|
||||
if (Options.DisableMeeting.GetBool() || Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato)
|
||||
__instance.Close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
[HarmonyPatch(typeof(Vent), nameof(Vent.CanUse))]
|
||||
|
||||
@@ -296,6 +296,7 @@ internal class ChangeRoleSettings
|
||||
Mastermind.Init();
|
||||
Asthmatic.Init();
|
||||
Perceiver.Init();
|
||||
Beacon.Init();
|
||||
NSerialKiller.Init();
|
||||
SoulHunter.Init();
|
||||
Enderman.Init();
|
||||
@@ -371,6 +372,8 @@ internal class ChangeRoleSettings
|
||||
SoloKombatManager.Init();
|
||||
FFAManager.Init();
|
||||
MoveAndStopManager.Init();
|
||||
HotPotatoManager.Init();
|
||||
|
||||
CustomWinnerHolder.Reset();
|
||||
AntiBlackout.Reset();
|
||||
NameNotifyManager.Reset();
|
||||
@@ -588,7 +591,7 @@ internal class SelectRolesPatch
|
||||
}
|
||||
|
||||
// For other gamemodes:
|
||||
if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop)
|
||||
if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato)
|
||||
{
|
||||
foreach (var pair in Main.PlayerStates)
|
||||
ExtendedPlayerControl.RpcSetCustomRole(pair.Key, pair.Value.MainRole);
|
||||
@@ -654,6 +657,8 @@ internal class SelectRolesPatch
|
||||
|
||||
EndOfSelectRolePatch:
|
||||
|
||||
if (Options.CurrentGameMode == CustomGameMode.HotPotato) HotPotatoManager.OnGameStart();
|
||||
|
||||
HudManager.Instance.SetHudActive(true);
|
||||
List<PlayerControl> AllPlayers = [];
|
||||
CustomRpcSender sender = CustomRpcSender.Create("SelectRoles Sender", SendOption.Reliable);
|
||||
@@ -688,6 +693,9 @@ internal class SelectRolesPatch
|
||||
case CustomGameMode.MoveAndStop:
|
||||
GameEndChecker.SetPredicateToMoveAndStop();
|
||||
break;
|
||||
case CustomGameMode.HotPotato:
|
||||
GameEndChecker.SetPredicateToHotPotato();
|
||||
break;
|
||||
}
|
||||
|
||||
GameOptionsSender.AllSenders.Clear();
|
||||
@@ -697,7 +705,7 @@ internal class SelectRolesPatch
|
||||
}
|
||||
|
||||
// Added players with unclassified roles to the list of players who require ResetCam.
|
||||
Main.ResetCamPlayerList.AddRange(Main.AllPlayerControls.Where(p => p.GetCustomRole() is CustomRoles.Arsonist or CustomRoles.Revolutionist or CustomRoles.Sidekick or CustomRoles.KB_Normal or CustomRoles.Killer or CustomRoles.Tasker or CustomRoles.Innocent || (p.Is(CustomRoles.Witness) && (!Options.UsePets.GetBool() || Options.WitnessUsePet.GetBool()))).Select(p => p.PlayerId));
|
||||
Main.ResetCamPlayerList.AddRange(Main.AllPlayerControls.Where(p => p.GetCustomRole() is CustomRoles.Arsonist or CustomRoles.Revolutionist or CustomRoles.Sidekick or CustomRoles.KB_Normal or CustomRoles.Killer or CustomRoles.Tasker or CustomRoles.Potato or CustomRoles.Innocent || (p.Is(CustomRoles.Witness) && (!Options.UsePets.GetBool() || Options.WitnessUsePet.GetBool()))).Select(p => p.PlayerId));
|
||||
Utils.CountAlivePlayers(true);
|
||||
Utils.SyncAllSettings();
|
||||
SetColorPatch.IsAntiGlitchDisabled = false;
|
||||
|
||||
+33
-13
@@ -272,6 +272,7 @@
|
||||
"Guesser": "Guesser",
|
||||
"Necroview": "Necroview",
|
||||
"Reach": "Reach",
|
||||
"Haste": "Haste",
|
||||
"Magnet": "Magnet",
|
||||
"Charmed": "Charmed",
|
||||
"Bait": "Bait",
|
||||
@@ -328,6 +329,7 @@
|
||||
"Prefix.Necroview": "Necroview",
|
||||
"Prefix.Recruit": "Recruit",
|
||||
"Prefix.Reach": "Reach",
|
||||
"Prefix.Haste": "Haste",
|
||||
"Prefix.Magnet": "Magnet",
|
||||
"Prefix.Onbound": "Onbound",
|
||||
"Prefix.Knighted": "Knighted",
|
||||
@@ -538,6 +540,7 @@
|
||||
"FoolInfo": "You can't fix sabotages",
|
||||
"AvangerInfo": "You take someone with you upon death",
|
||||
"YoutuberInfo": "Get killed first to win",
|
||||
"HasteInfo": "Your kill cooldown progresses while in vents",
|
||||
"EgoistInfo": "Win on your own",
|
||||
"TicketsStealerInfo": "Gain votes with kills",
|
||||
"DualPersonalityInfo": "You count as two players",
|
||||
@@ -749,7 +752,7 @@
|
||||
"MasochistInfoLong": "(Neutrals):\\nAs the Masochist, your goal is to get attacked a few times to win.\\n\\nYou cannot be guessed, as that adds to your attack count.",
|
||||
"DoomsayerInfoLong": "(Neutrals):\\nThe Doomsayer can guess the role of a certain player during the meeting.\\nIf the Doomsayer guesses a certain number of roles (the number depends on the host settings) then he wins.\\nThe guessing command is: /bt [player id] [role]\\nYou can see the player's id before the player's name, or use the /id command to view the id of all players.",
|
||||
"LastImpostorInfoLong": "(Add-ons):\\nLast Impostor gets reduced kill cooldown.",
|
||||
"LoversInfoLong": "(Add-ons),\\nLovers win together and die together if one is exiled or killed. They can see the ♥ mark next to each other's name.",
|
||||
"LoversInfoLong": "(Add-ons),\\nLovers win together and may die together if one is exiled or killed. They can see the ♥ mark next to each other's name. If one of the lovers win at the end, the other wins too.",
|
||||
"MadmateInfoLong": "(Add-ons):\\nMadmates assist the Impostors in winning the game. They are only available to Crewmates but can identify Impostors depending on the host's settings. Certain roles like Sheriff, Snitch, and Mayor can become Madmates. The abilities of some roles change when they become Madmates. For example, the Grenadier's flash bomb works on Crewmates and Neutrals instead of Impostors. The Judge can judge anyone.",
|
||||
"NtrInfoLong": "(Add-ons):\\nWhen there is Neptune, all players will see that they are Lovers with Neptune, and they will not die in love together and will not change the win conditions. Note: Lovers won't become Neptune, and Neptune won't become Lovers.",
|
||||
"WatcherInfoLong": "(Add-ons):\\nDuring the meeting, Watcher can see everyone's votes.",
|
||||
@@ -784,6 +787,7 @@
|
||||
"KnightedInfoLong": "(Add-ons):\\nWhen a Monarch knights someone, they get an extra vote.",
|
||||
"AsthmaticInfoLong": "(Add-ons):\\nYou always have a symbol below your name with a timer that is red or green. You cannot move when the symbol/timer is red. The timer indicates when the color will change.",
|
||||
"UnreportableInfoLong": "(Add-ons):\\nWith the Disregarded add-on, your corpse cannot be reported.",
|
||||
"HasteInfoLong": "(Add-ons):\\nWith the Disregarded add-on, your kill cooldown continues to go down at all times.",
|
||||
"ContagiousInfoLong": "(Betrayal Add-ons):\\nWhen the Virus infects you, you become contagious.\\nContagious players are on the Virus team.\\n\\nWhether or not you die after a meeting depends on the settings for the Virus.",
|
||||
"RogueInfoLong": "(Betrayal Add-ons):\\nAs the Rogue, you work against your team and work with other Rogues.\\n\\nYou cannot win with your original team.",
|
||||
"LuckyInfoLong": "(Add-ons):\\nWith the Lucky add-on there is a probability for you to evade the kill, the specific probability is set by the host. When the evasion takes effect, the killer will see the shield-animation, but you not know anything.",
|
||||
@@ -1659,7 +1663,7 @@
|
||||
"UndertakerInfoLong": "(Impostors):\\nAs the Undertaker, you can mark your target using your kill button, then shapeshift/pet to kill them. However, the person you assassinate will teleport to your location when you shapeshift, so their body will be wherever you are! Use this well to hide bodies!",
|
||||
"Romantic": "Romantic",
|
||||
"RomanticInfo": "Select your partner and win with them",
|
||||
"RomanticInfoLong": "(Neutral):\\nAs the Romantic, you can first use your kill button on someone to select them as your partner.\\nAfter that, as long as both you and your partner are alive, you can use your kill button on anyone to give your partner a temporarily shield. If your partner wins, you win too.\\n\\nIf your partner dies, your role will change according to the following conditions:\\nIf your partner was a Neutral Killer, you will turn into the Ruthless Romantic, which is a basic neutral killer: Be the last one standing to win. If you win, your partner wins too.\\nIf your target was an Impostor, you become a Refugee. Your job is now to help the impostors (and thus your partner) win.\\nIn every other case, you become the Vengeful Romantic. Your goal is to kill the killer of your partner. If you do so, you and your partner win with the winning team. If you try to kill anyone else, you will die.",
|
||||
"RomanticInfoLong": "(Neutral):\\nAs the Romantic, you can first use your kill button on someone to select them as your partner.\\nAfter that, as long as both you and your partner are alive, you can use your kill button on anyone to give your partner a temporarily shield. If your partner wins, you win too.\\n\\nIf your partner dies, your role will change according to the following conditions:\\nIf your partner had a kill button of any type, you will get the same role as they were.\\nIf your partner was a Non-Killing Neutral or your partner died by themselves (suicided or has no real killer), you will turn into the Ruthless Romantic, which is a basic neutral killer: Be the last one standing to win. If you win, your partner wins too.\\nIf your target was an Impostor, you become a Refugee. Your job is now to help the impostors (and thus your partner) win.\\nIn every other case, you become the Vengeful Romantic. Your goal is to kill the killer of your partner. If you do so, you and your partner win with the winning team. If you try to kill anyone else, you will die.",
|
||||
"VengefulRomantic": "Vengeful Romantic",
|
||||
"VengefulRomanticInfo": "Kill the killer of your Partner",
|
||||
"VengefulRomanticInfoLong": "(Neutral):\\nAs Vengeful Romantic you need to kill the killer of your partner. You only get one shot. If you kill wrong player, you misfire. Upon killing the right target you win with the winning team along with your Romantic Partner",
|
||||
@@ -2793,6 +2797,7 @@
|
||||
"EveryoneCanVent": "Everyone Can Vent",
|
||||
"OverrideScientistBasedRoles": "Also Override Scientist Based Roles to Engineer",
|
||||
"WhackAMole": "Whack-A-Mole",
|
||||
"SpawnAdditionalRefugeeOnImpsDead": "Random <#8cffff>Crewmate</color> Turns To <#ff1919>Refugee</color> When All <#ff1919>Impostors</color> Die",
|
||||
"NoPetActionMsg1": "You just ate a cookie",
|
||||
"NoPetActionMsg2": "You look around",
|
||||
"NoPetActionMsg3": "You start wondering whether you are sus or not",
|
||||
@@ -2852,17 +2857,17 @@
|
||||
"GamblerIgnoreCursedWolfAndJinx": "Kill <color=#ff1919>Cursed Wolf</color> and <color=#ed2f91>Jinx</color>",
|
||||
"GamblerIgnorePestilence": "Kill <color=#343136>Pestilence</color>",
|
||||
"GamblerPositiveEffectChance": "Positive Effect Chance",
|
||||
"GamblerGet.DelayedKill": "<color=#00ffa5>Effect:</color> Delayed Kill (<color=#00ffa5>{0}s</color>)",
|
||||
"GamblerGet.Shield": "<color=#00ffa5>Effect:</color> Temporary Shield (<color=#00ffa5>{0}s</color>)",
|
||||
"GamblerGet.NoLunge": "<color=#00ffa5>Effect:</color> No Lunge",
|
||||
"GamblerGet.Swap": "<color=#00ffa5>Effect:</color> Swap Places with Someone",
|
||||
"GamblerGet.IgnoreDefense": "<color=#00ffa5>Effect:</color> Break Through Defense",
|
||||
"GamblerGet.LowKCD": "<color=#00ffa5>Effect:</color> Lower Kill Cooldown (<color=#00ffa5>{0}s</color>)",
|
||||
"GamblerGet.Speedup": "<color=#00ffa5>Effect:</color> Higher Speed (<color=#00ffa5>{1}x</color>, for <color=#00ffa5>{0}s</color>)",
|
||||
"GamblerGet.BSR": "<color=#ffa500>Effect:</color> Self-Report (in <color=#ffa500>{0}s</color>)",
|
||||
"GamblerGet.Freeze": "<color=#ffa500>Effect:</color> Freeze (for <color=#ffa500>{0}s</color>)",
|
||||
"GamblerGet.LowVision": "<color=#ffa500>Effect:</color> Lower Vision (<color=#ffa500>{1}x</color>, for <color=#ffa500>{0}s</color>)",
|
||||
"GamblerGet.HighKCD": "<color=#ffa500>Effect:</color> Higher Kill Cooldown (<color=#ffa500>{0}s</color>)",
|
||||
"GamblerGet.DelayedKill": "<color=#ffffff><color=#00ffa5>Effect:</color> Delayed Kill (<color=#00ffa5>{0}s</color>)</color>",
|
||||
"GamblerGet.Shield": "<color=#ffffff><color=#00ffa5>Effect:</color> Temporary Shield (<color=#00ffa5>{0}s</color>)</color>",
|
||||
"GamblerGet.NoLunge": "<color=#ffffff><color=#00ffa5>Effect:</color> No Lunge</color>",
|
||||
"GamblerGet.Swap": "<color=#ffffff><color=#00ffa5>Effect:</color> Swap Places with Someone</color>",
|
||||
"GamblerGet.IgnoreDefense": "<color=#ffffff><color=#00ffa5>Effect:</color> Break Through Defense</color>",
|
||||
"GamblerGet.LowKCD": "<color=#ffffff><color=#00ffa5>Effect:</color> Lower Kill Cooldown (<color=#00ffa5>{0}s</color>)</color>",
|
||||
"GamblerGet.Speedup": "<color=#ffffff><color=#00ffa5>Effect:</color> Higher Speed (<color=#00ffa5>{1}x</color>, for <color=#00ffa5>{0}s</color>)</color>",
|
||||
"GamblerGet.BSR": "<color=#ffffff><color=#ffa500>Effect:</color> Self-Report (in <color=#ffa500>{0}s</color>)</color>",
|
||||
"GamblerGet.Freeze": "<color=#ffffff><color=#ffa500>Effect:</color> Freeze (for <color=#ffa500>{0}s</color>)</color>",
|
||||
"GamblerGet.LowVision": "<color=#ffffff><color=#ffa500>Effect:</color> Lower Vision (<color=#ffa500>{1}x</color>, for <color=#ffa500>{0}s</color>)</color>",
|
||||
"GamblerGet.HighKCD": "<color=#ffffff><color=#ffa500>Effect:</color> Higher Kill Cooldown (<color=#ffa500>{0}s</color>)</color>",
|
||||
"Hitman": "Hitman",
|
||||
"HitmanInfo": "Select your target",
|
||||
"HitmanInfoLong": "(Impostors):\\nAs the Hitman, you may shapeshift into someone to mark them as your target. If you kill your target, your next kill cooldown will be lower.\\n\\nNote: Once you chose a target, you cannot change it. You'll get a chance to mark a target again after killing your current target.",
|
||||
@@ -2950,6 +2955,10 @@
|
||||
"SoulHunterNotify": "You have <color=#00ffa5>{0}s</color> to kill <color=#3f2c61>{1}</color>",
|
||||
"SoulHunterNotifySuccess": "<color=#00ff00>Success!</color>",
|
||||
"SoulHunterNotifyFreeze": "You can begin hunting <color=#3f2c61>{0}</color> in <color=#00ffa5>{1}s</color>",
|
||||
"Beacon": "Beacon",
|
||||
"BeaconInfo": "Increase Nearby Players' Vision During Lights",
|
||||
"BeaconInfoLong": "(Crewmates):\\nAs the Beacon, during the lights sabotage, you increase the vision of players around you.",
|
||||
"BeaconVisionIncrease": "Increased Vision for Affected Players During Lights Sabotage",
|
||||
"Librarian": "Librarian",
|
||||
"LibrarianInfo": "Silence the Reporters Nearby",
|
||||
"LibrarianInfoLong": "(Impostors):\\nAs the Librarian, when you're in silencing mode, if someone reports a body next to you, you'll kill them instead. After you kill someone like this, everyone will see 'SSSH!' in red under your name.",
|
||||
@@ -3106,6 +3115,17 @@
|
||||
"Tasker": "Tasker",
|
||||
"TaskerInfo": "FINISH TASKS FIRST TO WIN",
|
||||
"TaskerInfoLong": "FINISH TASKS FIRST TO WIN",
|
||||
"HotPotato": "HOT POTATO",
|
||||
"ModeHotPotato": "Gamemode: Hot Potato",
|
||||
"ModeDescribe.HotPotato": "In the Hot Potato gamemode, one player always has the hot potato, and they can pass it to anyone by going near someone. Each X seconds (X is set by the host), the player who holds the hot potato dies. The last player alive wins!",
|
||||
"HotPotato_Time": "The Holder of the Hot Potato is Killed Every",
|
||||
"HotPotato_HolderSpeed": "Speed of the Player Holding the Hot Potato",
|
||||
"HotPotato_Range": "Pass Range",
|
||||
"HotPotato_TimeLeftSuffix": "<#00ffa5>Kill in </color>",
|
||||
"HotPotato_HoldingNotify": "<#ffff00><size=2>You hold the Hot Potato! Pass it or die!</size></color>",
|
||||
"Potato": "Player",
|
||||
"PotatoInfo": "AVOID HAVING THE HOT POTATO",
|
||||
"PotatoInfoLong": "AVOID HAVING THE HOT POTATO",
|
||||
"GNoCommandInfo": "<b>This Command can only be used when in lobby or after you die.</b>\\n\\ntype <b>/gno X</b> to play guess a number. X can be a number between 0 and 99 (both included). \\n\\nYou get maximum of <color=#ff0000>7 tries</color> to guess the number.\\n\\n Example:- <b>/gno 10</b>",
|
||||
"GNoLost": "Oh, you were so close! Just one more guess, and you might have deciphered the Da Vinci code! By the way, <color=#ff0000>the secret number was... {0}!</color> But hey, you were only off by a few billion possibilities. Better luck next time, Sherlock! ",
|
||||
"GNoLow": "Oh, you're really nailing this! It's so low, I almost need a shovel to dig it up!\\nYou have <color=#ff0000>{0} guesses</color> left!",
|
||||
|
||||
+75
-55
@@ -17,14 +17,14 @@
|
||||
"TeamImpostor": "Impostor",
|
||||
"TeamNeutral": "Neutro",
|
||||
"TeamCrewmate": "Tripulante",
|
||||
"TeamMadmate": "Louco",
|
||||
"TeamMadmate": "Cúmplice",
|
||||
"YouAreCrewmate": "Você é um Tripulante",
|
||||
"YouAreImpostor": "Você é um Impostor",
|
||||
"YouAreNeutral": "Você é um Neutro",
|
||||
"YouAreMadmate": "Você é um Louco",
|
||||
"YouAreMadmate": "Você é um Cúmplice",
|
||||
"CrewmateSub": "Encontre e expulse os <color=#ff1919>Impostores</color>",
|
||||
"ImpostorSub": "Sabote e mate todos",
|
||||
"NeutralSub": "Vença por sua conta!",
|
||||
"NeutralSub": "Trabalhe sozinho e alcance a vitória",
|
||||
"MadmateSub": "Ajude os <color=#ff1919>Impostores</color>",
|
||||
"KB_Normal": "Desafiante",
|
||||
"KB_NormalInfo": "Mate todos!",
|
||||
@@ -165,7 +165,7 @@
|
||||
"Bodyguard": "Guarda-Costa",
|
||||
"Counterfeiter": "Enganador",
|
||||
"Grenadier": "Atordoador",
|
||||
"Lighter": "Visor",
|
||||
"Lighter": "Tocha",
|
||||
"Medic": "Guardião",
|
||||
"Divinator": "Vidente",
|
||||
"Glitch": "Glitch",
|
||||
@@ -272,6 +272,7 @@
|
||||
"Guesser": "Adivinhador",
|
||||
"Necroview": "Necropista",
|
||||
"Reach": "Alcançador",
|
||||
"Haste": "Pressa",
|
||||
"Magnet": "Magnético",
|
||||
"Charmed": "Encantado",
|
||||
"Bait": "Armador",
|
||||
@@ -284,7 +285,7 @@
|
||||
"Rogue": "Vigarista",
|
||||
"Lucky": "Sortudo",
|
||||
"Unlucky": "Azarado",
|
||||
"DoubleShot": "Segunda Chance",
|
||||
"DoubleShot": "Tiro Duplo",
|
||||
"Rascal": "Verificador",
|
||||
"Soulless": "Sem Alma",
|
||||
"Gravestone": "Lápide",
|
||||
@@ -328,13 +329,14 @@
|
||||
"Prefix.Necroview": "Necropista",
|
||||
"Prefix.Recruit": "Recruta",
|
||||
"Prefix.Reach": "Alcançador",
|
||||
"Prefix.Haste": "Pressa",
|
||||
"Prefix.Magnet": "Magnético",
|
||||
"Prefix.Onbound": "Inadivinhável",
|
||||
"Prefix.Knighted": "Nomeado",
|
||||
"Prefix.Unreportable": "Irreportável",
|
||||
"Prefix.Rogue": "Vigarista",
|
||||
"Prefix.Lucky": "Sortudo",
|
||||
"Prefix.DoubleShot": "Segunda Chance",
|
||||
"Prefix.DoubleShot": "Tiro Duplo",
|
||||
"Prefix.Rascal": "Verificador",
|
||||
"Prefix.Gravestone": "Lápide",
|
||||
"Prefix.Lazy": "Preguiçoso",
|
||||
@@ -386,7 +388,7 @@
|
||||
"EvilGuesserInfo": "Adivinhe as funções da Tripulação",
|
||||
"CapitalismInfo": "Dê mais tarefas para a Tripulação",
|
||||
"GangsterInfo": "Converta jogadores para o seu lado",
|
||||
"CleanerInfo": "Reporte cadáveres para fazê-los serem irreportáveis",
|
||||
"CleanerInfo": "Reporte corpos para fazê-los serem irreportáveis",
|
||||
"BallLightningInfo": "Converta jogadores em Fantasmas Quânticos",
|
||||
"GreedierInfo": "Seu tempo de abate muda",
|
||||
"CursedWolfInfo": "Você sobrevive a algumas tentativas de morte",
|
||||
@@ -434,7 +436,7 @@
|
||||
"MarshallInfo": "Termine suas tarefas para provar sua inocência",
|
||||
"SpeedBoosterInfo": "Aumente sua velocidade",
|
||||
"DoctorInfo": "Saiba como cada jogador morreu",
|
||||
"DictatorInfo": "Ejete um jogador com base no seu próprio julgamento",
|
||||
"DictatorInfo": "Expulse um jogador com base no seu próprio julgamento",
|
||||
"DetectiveInfo": "Ganhe informações ao reportar",
|
||||
"UndercoverInfo": "Os Impostores veem você como seu parceiro",
|
||||
"SwordsManInfo": "Você pode matar apenas 1 jogador",
|
||||
@@ -450,7 +452,7 @@
|
||||
"DivinatorInfo": "Revele funções nas reuniões",
|
||||
"GlitchInfo": "Hackeie e mate todos",
|
||||
"JudgeInfo": "Silêncio no tribunal!",
|
||||
"MorticianInfo": "Localize cadáveres",
|
||||
"MorticianInfo": "Localize corpos",
|
||||
"MediumshiperInfo": "Converse com fantasmas",
|
||||
"ObserverInfo": "Você pode ver todas as animações de escudo",
|
||||
"DovesOfNeaceInfo": "Use dutos para resetar todas as recargas de abate",
|
||||
@@ -481,7 +483,7 @@
|
||||
"MarioInfo": "Pule para dentro! Pule para fora!",
|
||||
"JackalInfo": "Assassine todos",
|
||||
"GodInfo": "Tudo está sob seu controle",
|
||||
"InnocentInfo": "Faça com que alguém seja ejetado fazendo com que ele te mate",
|
||||
"InnocentInfo": "Faça com que alguém seja expulso fazendo com que ele te mate",
|
||||
"PelicanInfo": "Devore todos os jogadores",
|
||||
"RevolutionistInfo": "Recrute jogadores para vencer com você",
|
||||
"FFFInfo": "Mate <color=#ff9ace>Amantes</color> e <color=#00a4ff>Netunos</color>",
|
||||
@@ -538,6 +540,7 @@
|
||||
"FoolInfo": "Você não pode consertar sabotagens",
|
||||
"AvangerInfo": "Você leva alguém com você após a morte",
|
||||
"YoutuberInfo": "Seja o primeiro a ser morto para ganhar",
|
||||
"HasteInfo": "Seu tempo de recarga para matar continua nos dutos",
|
||||
"EgoistInfo": "Ganhe por conta própria",
|
||||
"TicketsStealerInfo": "Ganhe votos com abates",
|
||||
"DualPersonalityInfo": "Você está morto e vivo simultaneamente",
|
||||
@@ -590,15 +593,15 @@
|
||||
"SerialKillerInfoLong": "(Impostores):\\n★Você possui um tempo de matar menor em comparação com os outros Impostores, mas há uma condição: se você não matar ninguém até o final do seu tempo de metamorfose, você morrerá.",
|
||||
"ShapeMasterInfoLong": "(Impostores):\\n★O Metamestre tem pouco tempo de duração da metamorfose.",
|
||||
"MareInfoLong": "(Atributos):\\nO Pesadelo tem um tempo de recarga baixo mas pode apenas matar durante a sabotagem das luzes. \\nAdicionalmente, seu nome aparecerá vermelho durante o apagão. \\nApenas atribuida para Impostores e não pode ser adivinhada.",
|
||||
"VampireInfoLong": "(Impostores):\\n★O alvo mordido pelo vampiro não morrerá imediatamente, mas falecerá repentinamente após algum tempo. \\n★Se a reunião for chamada durante esse período, o alvo morrerá imediatamente. Se você morder um Armador, realizará um assassinato normal e será forçado a relatar o corpo.",
|
||||
"VampireInfoLong": "(Impostores):\\n★O alvo mordido pelo vampiro não morrerá imediatamente, mas morrerá repentinamente após algum tempo. \\n★Se a reunião for chamada durante esse período, o alvo morrerá imediatamente. Se você morder um Armador, realizará um morte normal e será forçado a reportar o corpo.",
|
||||
"WarlockInfoLong": "(Impostores):\\n★O Controlador de Mentes pode controlar outros jogadores enquanto não estiver usando a metamorfose. \\n★Quando ele se metamorfa depois de controlar, o jogador que foi controlado, e o jogador mais próximo do jogador controlado, são mortos, não importa a distância entre eles. \\n★Enquanto metamorfado, você pode realizar assassinatos normais. \\n★Apenas 1 jogador pode ser controlado por vez.",
|
||||
"ZombieInfoLong": "(Impostores):\\nO Zumbi tem um tempo de espera curto para matar, mas se move muito lentamente e tem pouquíssima visão. O Zumbi não poderá ser eliminado por ninguém além do Ditador, e a velocidade de movimento do Zumbi diminuirá gradualmente à medida que ela mata ou o tempo passa.",
|
||||
"AssassinInfoLong": "(Impostores):\\n★O Ninja pode usar o botão de matar para marcar um alvo, e se a marcação for concluida, uma animação de escudo será mostrada como lembrete. \\n★O Ninja pode usar a metamorfose para se teleportar a pessoa marcada e matá-la. \\n★O Ninja não pode matar regularmente, mas pode ser capaz de matar regularmente depois de um assassinato de acordo com as configurações do anfitrião.",
|
||||
"HackerInfoLong": "(Impostores):\\n★O Ilusionista pode se metamorfar para confundir o seu alvo de metamorfose. \\n★O jogador confundido vai imediatamente reportar o cadáver morto pelo Ilusionista nessa rodada. \\n★Se não existir nenhum cadáver, o alvo reportará seu próprio cadáver (o efeito é similar ao Paranóico). \\n★Nota: Confundir o Preguiçoso não fará ele reportar o corpo, apenas perde seu tempo.",
|
||||
"HackerInfoLong": "(Impostores):\\n★O Ilusionista pode se metamorfar para confundir o seu alvo de metamorfose. \\n★O jogador confundido vai imediatamente reportar o corpo morto pelo Ilusionista nessa rodada. \\n★Se não existir nenhum cadáver, o alvo reportará seu próprio cadáver (o efeito é similar ao Paranóico). \\n★Nota: Confundir o Preguiçoso não fará ele reportar o corpo, apenas perde seu tempo.",
|
||||
"MinerInfoLong": "(Impostores):\\n★O Mineiro pode se metamorfar para se teleportar de volta à última ventilação que usou.",
|
||||
"MinimalismInfoLong": "(Impostores):\\n★A Máquina Mortífera tem um tempo de abate muito curto, porém, não pode usar ventilações, tem uma visão menor, não pode sabotar, não pode reportar e não pode chamar a Reunião de Emergência. \\n★A Máquina Mortífera tem a mesma visão que os Tripulantes.",
|
||||
"EscapeeInfoLong": "(Impostores):\\n★O Escapista pode marcar um lugar se metamorfando, e ele será teleportado para o lugar marcado quando se metamorfar de novo (não quando se destransformar).",
|
||||
"WitchInfoLong": "(Impostores):\\n★O Feiticeiro possui 2 métodos de ataque: Matar diretamente e enfeitiçamento.\\n★Pressionar o botão de matar duas vezes rapidamente resultará em uma morte normal, enquanto pressionar o botão de matar apenas uma vez lançará um feitiço em um jogador.\\n★Durante a próxima reunião, o alvo enfeitiçado terá um símbolo 「†」 marcado próximo ao nome, o qual será visível para todos os jogadores.\\n★ Se o Feiticeiro não for ejetado ou morto até o final da reunião, todos os jogadores enfeitiçados morrerão assim que a reunião acabar.",
|
||||
"WitchInfoLong": "(Impostores):\\n★O Feiticeiro possui 2 métodos de ataque: Matar diretamente e enfeitiçamento.\\n★Pressionar o botão de matar duas vezes rapidamente resultará em uma morte normal, enquanto pressionar o botão de matar apenas uma vez lançará um feitiço em um jogador.\\n★Durante a próxima reunião, o alvo enfeitiçado terá um símbolo 「†」 marcado próximo ao nome, o qual será visível para todos os jogadores.\\n★ Se o Feiticeiro não for expulso ou morto até o final da reunião, todos os jogadores enfeitiçados morrerão assim que a reunião acabar.",
|
||||
"MafiaInfoLong": "MafiaInfoLong:(Impostores):\\n★O Nemesis não pode matar enquanto existir outros Impostores vivos no jogo, mas pode usar o comando /rv [ID do jogador] depois da morte para matar um jogador.",
|
||||
"GodfatherInfoLong": "(Impostores):\\n★Como o Rei do Crime, você pode votar em alguém para fazer dele seu alvo. \\n★Na próxima rodada, se alguém matar o alvo, o jogador se torna um Refugiado.",
|
||||
"PuppeteerInfoLong": "(Impostores):\\n★O jogador controlado pelo Marionetista matará o próximo Tripulante ou Neutro que encostar nele. \\n★Se o último a tocar morrer em contato, o primeiro também morrerá. \\n★Marionetistas não podem realizar assassinatos normais. \\n★Nota: O Preguiçoso não matará ninguém, mesmo que você o controle; é apenas perda de tempo.",
|
||||
@@ -609,11 +612,11 @@
|
||||
"AntiAdminerInfoLong": "(Impostores):\\n★O Anti-Administrador pode a qualquer momento descobrir se há tripulantes ou neutros próximos a dispositivos (câmeras, mesa de administração, etc).",
|
||||
"SansInfoLong": "(Impostores):\\n★O Arrogante reduz seu tempo de abate com cada abate bem sucedido dele.",
|
||||
"BomberInfoLong": "(Impostores):\\n★O Bombardeiro pode usar o botão de metamorfose para se auto-explodir, matando jogadores com uma certa distância. \\n★ Mas tem um preço, o Bombardeiro vai também morrer.",
|
||||
"ScavengerInfoLong": "(Impostores):\\n★O Necrófago mata sem deixar cadáveres. \\n★Além disso, se a vítima for um Armador, nenhum autorrelato será feito.",
|
||||
"BoobyTrapInfoLong": "(Impostores):\\n★O Caçador vai colocar uma armadilha em um cadáver de uma vítima depois de matá-la, e o jogador que reportar vai ser morto imediatamente; o jogador que foi morto vai também ter o efeito do Caçador, e assim por diante. \\n★Mas tem um preço, o Caçador vai morrer imediatamente depois de realizar o abate. \\n★Nota: O Caçador não tem o efeito em seu próprio cadáver.",
|
||||
"ScavengerInfoLong": "(Impostores):\\n★O Necrófago mata sem deixar corpos. \\n★Além disso, se a vítima for um Armador, nenhuma auto-denúncia será feito.",
|
||||
"BoobyTrapInfoLong": "(Impostores):\\n★O Caçador vai colocar uma armadilha em um corpo de uma vítima depois de matá-la, e o jogador que reportar vai ser morto imediatamente; o jogador que foi morto vai também ter o efeito do Caçador, e assim por diante. \\n★Mas tem um preço, o Caçador vai morrer imediatamente depois de realizar o abate. \\n★Nota: O Caçador não tem o efeito em seu próprio cadáver.",
|
||||
"CapitalismInfoLong": "(Impostores):\\n★O Capitalista pode adicionar uma tarefa curta a um jogador pressionando o botão de matar. \\n★ Nota: Usar essa habilidade em um jogador que não tem tarefas ou já terminou todas não terá efeito.",
|
||||
"GangsterInfoLong": "(Impostores):\\n★O Gangster pode tentar recrutar um jogador para ser um Cúmplice pressionando o botão de matar. \\n★Se a tentativa de recrutamento for bem-sucedida, tanto o Gangster quanto o alvo verão uma animação de escudo como lembrete (apenas visível para eles). \\n★O número restante de recrutamentos é mostrado ao lado do nome do Gangster (o máximo é definido pelo anfitrião). \\n★Se o Gangster tentar recrutar jogadores que não podem ser recrutados, como alguns Neutros ou Tripulantes especiais, eles matarão o alvo normalmente. \\n★Quando o Gangster não tiver mais recrutamentos restantes, eles poderão realizar abates normais a partir desse ponto em diante.",
|
||||
"CleanerInfoLong": "(Impostores):\\n★O Faxineiro pode pressionar o botão de relatório para limpar qualquer cadáver (incluindo aqueles que ele mesmo matou). \\n★Se a limpeza for bem-sucedida, o Faxineiro verá uma animação de escudo no cadáver como lembrete (visível apenas para ele mesmo). \\n★ O cadáver limpo não pode ser relatado (incluindo pelo Impostor).",
|
||||
"CleanerInfoLong": "(Impostores):\\n★O Faxineiro pode pressionar o botão de relatório para limpar qualquer corpo (incluindo aqueles que ele mesmo matou). \\n★Se a limpeza for bem-sucedida, o Faxineiro verá uma animação de escudo no corpo como lembrete (visível apenas para ele mesmo). \\n★ O cadáver limpo não pode ser reportado (incluindo pelo Impostor).",
|
||||
"BallLightningInfoLong": "(Impostores):\\n★O Quantificador não pode matar regularmente. \\n★Depois do Quantificador usar seu botão de abate em um alvo, o alvo vai evaporar em um Fantasma Quântico depois do tempo definido, e todos poderão ver o 「■」 próximo ao seu nome como um lembrete. \\n★Se um jogador que não for um Fantasma Quântico tocar um Fantasma Quântico, o Fantasma Quântico vai ser quantizado imediatamente e morrer. \\n★Se o Fantasma Quântico sobreviver à reunião, ele morrerá após o fim da reunião. \\n★Durante esse tempo, qualquer interação ativa (botão de abate) com o Fantasma Quântico não terá efeito. \\n★De acordo com as configurações do anfitrião, matar o Quantificador pode fazer o assassino evaporar em um Fantasma Quântico.",
|
||||
"GreedierInfoLong": "(Impostores):\\n★Ganancioso mata com abates pares e ímpares, ambos terão tempos de recarga diferentes. \\n★O tempo de recarga do Ganancioso é resetado toda reunião, e o primeiro abate do Ganancioso é sempre um abate par.",
|
||||
"CursedWolfInfoLong": "(Impostores):\\n★Quando o Lobo Maldito for morto, ele vai amaldiçoar o assassino para a morte (O máximo de vezes que você pode conter ataques é setado pelo anfitrião).",
|
||||
@@ -621,7 +624,7 @@
|
||||
"QuickShooterInfoLong": "(Impostores):\\n★Quando seu tempo de recarga acaba, o Atirador Rápido pode resetar seu tempo de recarga com a metamorfose e armazenar munição (quando o armazém é bem sucedido, uma animação de escudo é visível apenas para ele mesmo vai aparecer em seu próprio corpo como lembrete). \\n★Depois do Atirador Rápido completar um abate, ele pode compensar o tempo de recarga com munições até que se esgote. \\n★ No início da reunião, o Atirador Rápido pode apenas manter um certo número de munições (o número é setado pelo anfitrião).",
|
||||
"CamouflagerInfoLong": "(Impostores):\\n★Quando o Camuflador usa a metamorfose, todos os jogadores começam a parecer exatamente os mesmos. \\n★Esse estado dos jogadores acaba quando o Camuflador reverte a metamorfose. \\n★A habilidade será inválida se a reunião for chamada durante o tempo da metamorfose.",
|
||||
"EraserInfoLong": "(Impostores):\\n★O Apagador pode votar em qualquer Tripulante durante a reunião para apagar sua função, e o apagamente vai fazer efeito depois do término da reunião.",
|
||||
"OverKillerInfoLong": "(Impostores):\\n★Os abates do Açougueiro terão múltiplos cadáveres, fazendo assim impossível identificar com precisão outro corpo morto quando reportar.",
|
||||
"OverKillerInfoLong": "(Impostores):\\n★Os abates do Açougueiro terão múltiplos corpos, fazendo assim impossível identificar com precisão outro corpo morto quando reportar.",
|
||||
"HangmanInfoLong": "(Impostores):\\n★O metódo de matar do Enforcador durante a metamorfose é estrangular. \\n★ O estrangulamento ignora qualquer status do alvo, tal como o escudo do Guardião, etc. \\n★O jogador estrangulado não deixará cadáveres, nem ativará nenhuma de suas habilidades. \\n★Por exemplo, o Veterano não mata de volta, o Sortudo não tem chances, etc. (incluindo Atributos).",
|
||||
"SwooperInfoLong": "(Impostores):\\n★O Camaleão pode usar a ventilação para se tornar temporariamente invisível e, em seguida, usar a ventilação novamente para cancelar sua invisibilidade. \\n★Isso significa que o Camaleão nunca pode usar ventilações normalmente, mesmo quando estiver em tempo de recarga. \\n★(Nota: Mesmo que você não tenha entrado na ventilação novamente para ficar visível, o jogo vai forçá-lo a ficar visível após o tempo de recarga acabar).",
|
||||
"CrewpostorInfoLong": "(Time Impostor):\\n★Você mata o jogador mais próximo após finalizar uma tarefa.",
|
||||
@@ -661,7 +664,7 @@
|
||||
"SpeedBoosterInfoLong": "(Tripulantes):\\n★O Impulsionador de Velocidade aumenta seu movimento de velocidade toda vez que ele completar uma tarefa. \\n★ Nota: Ele aparece com velocidade normal para os outros.",
|
||||
"DoctorInfoLong": "(Tripulantes):\\n★O Médico pode ver a causa da morte de todos os jogadores. \\n★ Em adição, o Médico pode acessar os vitais enquanto ainda tiver bateria.",
|
||||
"DictatorInfoLong": "(Tripulantes)::\\n★Quando o Ditador vota em alguém, a reunião vai terminar enquanto o jogador votado pelo Ditador vai ser ejetado. \\n★ No momento em que o Ditador votar alguém pra fora, o Ditador também vai morrer.",
|
||||
"DetectiveInfoLong": "(Tripulantes):\\n★Depois que a Perícia reportar um cadáver, ele receberá uma mensagem, a qual falará a Perícia qual a função da vítima. \\n★ De acordo com as configurações do anfitrião, a Perícia saberá qual a função do assassino. \\n★ Nota: A Perícia não será o Alheio.",
|
||||
"DetectiveInfoLong": "(Tripulantes):\\n★Depois que a Perícia reportar um corpo, ele receberá uma mensagem, a qual falará a Perícia qual a função da vítima. \\n★ De acordo com as configurações do anfitrião, a Perícia saberá qual a função do assassino. \\n★ Nota: A Perícia não será o Alheio.",
|
||||
"UndercoverInfoLong": "(Tripulantes):\\n★Os Impostores sabem quem ele é, mas ele mesmo não sabe quem são os Impostores.",
|
||||
"NiceGuesserInfoLong": "(Tripulantes):\\n★O Justiceiro pode adivinhar a função de certo jogador durante a reunião. \\n★ Se estiver correto, o alvo morrerá, e se estiver errado, o Justiceiro vai se suicidar \\n★ O comando de advinhamento é: /bt [id do jogador] [função] \\n★ Você pode ver o id dos jogadores ao lado de seus nomes, ou usando o comando /id \\n★ O Justiceiro pode adivinhar tripulantes quando se tornam Cúmplices.",
|
||||
"SwordsManInfoLong": "(Tripulantes):\\n★O Vigilante não tem tarefas. \\n★ Ele pode matar qualquer pessoa mas ele só pode fazer isso uma vez o jogo inteiro.",
|
||||
@@ -677,22 +680,22 @@
|
||||
"DivinatorInfoLong": "(Tripulantes):\\n★O Vidente pode votar em um jogador na reunião para ter uma informação revelante sobre esse jogador, qual é associada com a função do jogador. \\n★ Se o Vidente completar todas as tarefas, ele pode diretamente saber a função especifica do jogador. \\n★ O Vidente pode usar apenas uma previsão por reunião, e o máximo de previsões é setado pelo anfitrião.",
|
||||
"GlitchInfoLong": "(Neutros):\\n★O Glitch é um erro da nave e tem que matar todo mundo \\n★Você pode hackear os jogadores, o que os impede de matar, usar dutos e reportar cadáveres por algum tempo. \\n★Você precisa matar todo mundo para vencer. \\n★Clique Único = Hackear \\n★Clique Duplo = Matar \\n★Você pode usar dutos.\\n★Você Pode se transformar usando o botão sabotagem, não as portas mas os botões clássicos de sabotagem, Elétrica, O2 e Reator. \\n★Devido a problemas técnicos não é possível se transformar quando a sabotagem está ativa.",
|
||||
"JudgeInfoLong": "(Tripulantes):\\n★O Juiz pode julgar um certo jogador durante a reunião. \\n★ Se o alvo for malvado, o alvo vai ser morto, e se estiver errado, o Juiz vai cometer suicidio.\\n★ O comando de Julgamente é: /tl [id do jogador] \\n★ Você pode ver o id do jogador antes de seu nome. \\n★ Juizes podem julgar todos os jogadores quando ele se torna Cúmplice.",
|
||||
"MorticianInfoLong": "(Tripulantes):\\n★O Funerário pode ver setas apontando para todos os cadáveres, e se o Funerário reportar o cadáver, ele vai saber o último jogador que teve contado com a vítima.",
|
||||
"MorticianInfoLong": "(Tripulantes):\\n★O Funerário pode ver setas apontando para todos os corpos, e se o Funerário reportar um corpo, ele vai saber o último jogador que teve contato com a vítima.",
|
||||
"MediumshiperInfoLong": "(Tripulantes):\\n★O Médium pode estabelecer contato com os mortos depois de seu corpo ser reportado. \\n★ O jogador que reportar não precisa ser o Médium. \\n★ O jogador morto pode responder apenas SIM ou NÃO para a pergunta do Médium qual apenas o Médium vai poder ver.",
|
||||
"ObserverInfoLong": "(Tripulantes):\\n★O Observador pode ver todas as animações de escudo causado por outros jogadores depois da primeira reunião.",
|
||||
"MonarchInfoLong": "(Tripulantes):\\n★O Monarca, você pode dar aos jogadores um voto extra \\n★ Você não pode dar voto para alguém que já tem votos extra. \\n★ Os jogadores que receberem os votos apareceram com o nome dourado.",
|
||||
"DovesOfNeaceInfoLong": "(Tripulantes):\\n★Quando Pacifista usa a ventilação, ele resetará o tempo de abate para todos os jogadores com botão de matar. \\n★ Quando ele se torna um Cúmplica, essa habilidade vai apenas funcionar em Tripulantes.",
|
||||
"FarseerInfoLong": "(Tripulantes):\\n★Como Profeta você tem uma visão muito limitada mas você pode usar seu botão de abate para revelar a função de um jogador. \\n★ Use o botão de matar para começar a revelar, uma marca 「○」 vai ser mostrada próxima ao alvo. \\n★ Mantenha-se perto do alvo por um tempo definido para revelar sua função, se você se mover para longe do alvo a revelação vai ser cancelada.",
|
||||
"BloodhoundInfoLong": "(Tripulantes):\\n★Como Detetive você não pode reportar cadáveres, assim que você tentar reportar você verá uma seta apontando para o assassino do cadáver. \\n★ Se a reunião for chamada, as setas somem.",
|
||||
"BloodhoundInfoLong": "(Tripulantes):\\n★Como Detetive você não pode reportar corpos, pois assim que você tentar reportar você verá uma seta apontando para o assassino do corpo. \\n★ Se a reunião for chamada, as setas somem.",
|
||||
"TrackerInfoLong": "(Tripulantes):\\n★Como Localizador, você pode votar em um jogador na reunião, o qual vai ser marcado a posição dele para você no jogo com setas. \\n★ Em adição, no começo da reunião será exibido para você qual a sala que o jogador esteve, se a opção for ativada.",
|
||||
"MerchantInfoLong": "(Tripulantes):\\n★Como Atribuidor, você vende um add-on aleatório para um jogador aleatório qual tiver todas as suas tarefas concluídas. \\n★Cada add-on vendido lhe rende dinheiro. \\n★ Se você tiver uma certa quantidade de dinheiro, você pode evitar a próxima tentativa de abate. \\n★ O jogador subornado não será capaz de matar você, mas você não sabe quem é.",
|
||||
"RetributionistInfoLong": "(Tripulantes):\\n★Como Fantasma Assassino, você pode matar uma quantidade limitada de jogadores depois de sua morte. \\n★ Use /ret [ID do jogador].",
|
||||
"DeputyInfoLong": "(Tripulantes):\\n★Como Deputado, use seu botão de abate em um jogador para resetar seu tempo de recarga. \\n★ Se o alvo não tiver um botão de abate, foi um desperdício.",
|
||||
"GuardianInfoLong": "(Tripulantes):\\n★Como Imortal, você se torna imortal completando suas tarefas. \\n★ Você não pode ser adivinhado nas reuniões.",
|
||||
"AddictInfoLong": "(Tripulantes):\\n★Como Invulnerável, você tem um tempo de suícidio. \\n★ Quando ele expirar, você vai matar você mesmo \\n★ O temporizador é indicado pelo tempo de recarga da ventilação. \\n★ Quando o tempo de recarga de ventilação é 0, você ainda tem um tempo curto para usar a ventilação. \\n★ Se você não fizer isso você morre, se você fizer isso o temporizador de suícidio vai resetar \\n★ Também, depois de você usar a ventilação ninguém pode interagir com você por um período definido de tempo. \\n★ Depois do período acabar, você vai estar imobilizado por um outro período de tempo e não pode reportar cadáveres.",
|
||||
"AddictInfoLong": "(Tripulantes):\\n★Como Invulnerável, você tem um tempo de suícidio. \\n★ Quando ele expirar, você vai matar você mesmo \\n★ O temporizador é indicado pelo tempo de recarga da ventilação. \\n★ Quando o tempo de recarga de ventilação é 0, você ainda tem um tempo curto para usar a ventilação. \\n★ Se você não fizer isso você morre, se você fizer isso o temporizador de suícidio vai resetar \\n★ Também, depois de você usar a ventilação ninguém pode interagir com você por um período definido de tempo. \\n★ Depois do período acabar, você vai estar imobilizado por um outro período de tempo e não poderá reportar corpos.",
|
||||
"TracefinderInfoLong": "(Tripulantes):\\n★ Como Biólogo, você acessa os vitais. \\n★ Em adição, você consegue setas apontando para cadáveres.",
|
||||
"OracleInfoLong": "(Tripulantes):\\n★O Oráculo, você pode votar em um jogador durante a reunião \\n★ Você verá se ele é um Tripulante, Neutro ou Impostor.",
|
||||
"SpiritualistInfoLong": "SpiritualistInfoLong:(Tripulantes):\\n★O Espiritualista irá ter uma seta apontando para o fantasma que morreu na última reunião.\\n★ Se for um jogador bom, ele talvez te leve para um Impostor ou um Neutro Assassino.\\n★ Se o jogador for mal, ele talvez te leve para um Tripulante.\\n",
|
||||
"SpiritualistInfoLong": "(Tripulantes):\\n★O Espiritualista irá ter uma seta apontando para o fantasma que morreu na última reunião.\\n★ Se for um jogador bom, ele talvez te leve para um Impostor ou um Neutro Assassino.\\n★ Se o jogador for mal, ele talvez te leve para um Tripulante.",
|
||||
"ChameleonInfoLong": "(Impostores):\\n★O Camaleão pode usar a ventilação para se tornar temporariamente invisível e, em seguida, usar a ventilação novamente para cancelar sua invisibilidade. \\n★Isso significa que o Camaleão nunca pode usar ventilações normalmente, mesmo quando estiver em tempo de recarga. \\n★(Nota: Mesmo que você não tenha entrado na ventilação novamente para ficar visível, o jogo vai forçá-lo a ficar visível após o tempo de recarga acabar).",
|
||||
"ParityCopInfoLong": "(Tripulantes):\\n★Cheque se 2 jogadores são do mesmo time ou não. Você receberá uma mensagem afirmativa se eles forem do mesmo time, ou uma mensagem negativa se eles não forem.\\n★ Os jogadores neutros e convertidos serão do mesmo time. O Trapaceiro será considerado Tripulante e o Verificador será considerado Impostor.\\n★ Comando para chegar : /cmp [id do jogador 1] [id do jogador 2]",
|
||||
"AdmirerInfoLong": "(Tripulantes):\\n★O Admirador pode admirar um jogador para fazer ele virar um tripulante. \\n★Ele agora vencerá com os tripulantes e não com seu time original. \\n★Você só pode fazer isso uma vez.",
|
||||
@@ -740,7 +743,7 @@
|
||||
"RitualistInfoLong": "(Neutros):\\nO Ritualista pode revelar a função de jogadores usando o botão matar.\\n\\nClique Único: Revelar Função\\nClique Duplo: Matar\\n\\nSe seus usos de habilidade se esgotarem, seu botão matar funciona normalmente.",
|
||||
"PickpocketInfoLong": "(Neutros):\\n★O Rouba-Votos rouba o voto de quem ele mata.\\n★ Esses votos são escondidos.\\n★ Mate todos para vencer.",
|
||||
"TraitorInfoLong": "(Neutros):\\n★O Traidor é um Impostor que traiu os Impostores.\\n★ O Traidor saberá quem são os impostores, mas os impostores não saberão quem é o traidor.\\n★ Os Impostores podem matar o Traidor, mas o Traidor não pode matar os Impostores.\\n★ O Traidor precisa encontrar outra forma de eliminar os Impostores, então matar todos e vencer!",
|
||||
"VultureInfoLong": "(Neutros):\\n★O Canibal não reporta corpos normalmente.\\n★ O Canibal come o corpo clicando em reportar, fazendo com que não seja mais possível reportar o corpo.\\n★ Coma a maioria dos corpos para vencer!",
|
||||
"VultureInfoLong": "(Neutros):\\n★O Canibal não reporta corpos normalmente.\\n★ O Canibal come os corpos clicando em reportar, fazendo com que não seja mais possível reportar o corpo.\\n★ Coma a maioria dos corpos para vencer!",
|
||||
"MedusaInfoLong": "(Neutros):\\n★A Medusa pode transformar os corpos em pedra, como se tivesse limpado eles.\\n★ Corpos transformados em pedras não podem ser reportados.\\n★ Mate todos para vencer.",
|
||||
"BakerInfoLong": "(Neutros):\\n★O Padeiro consegue distribuir pão. \\n★Ao começo de cada reunião, mensagens aparecerão. \\n★Se você terminar suas tarefas você se torna o Faminto quando a reunião é chamada.",
|
||||
"FamineInfoLong": "(Neutros):\\n★O Faminto pode distribuir pão envenenado. \\n★Jogadores com o pão envenenado morrerão ao fim da reunião se o Faminto não for ejetado. \\n★Se o Faminto ficar vivo até o fim do jogo, ele rouba a vitória.",
|
||||
@@ -749,7 +752,7 @@
|
||||
"MasochistInfoLong": "(Neutros):\\n★O Masoquista precisa ser atacado algumas vezes para ganhar. \\n★Você não pode ser adivinhado, se não isso é adicionado a sua contagem de ataques.",
|
||||
"DoomsayerInfoLong": "(Neutros):\\n★O Adivinhador Neutro pode adivinhar a função de um certo jogador durante a reunião. \\n★Se o Adivinhador Neutro acertar a adivinhação um certo número de funções (o número depende das configurações do Anfitrião) então ele ganha. \\n★O comando de adivinhação é: /bt [ID do jogador] [função] \\n★Você pode ver o ID dos jogadores antes do nome do jogador ou usando o comando /id.",
|
||||
"LastImpostorInfoLong": "(Atributos):\\n★Esse Atributo é dado para o Último Impostor vivo. Reduz o tempo de recarga.",
|
||||
"LoversInfoLong": "(Atributos):\\n★Amantes é uma combinação de 2 jogadores. Os amantes ganham quando apenas eles ficam vivos. Se um dos amantes ganhar, o outro também vence. Os Amantes podem ver 「♡」 no nome de cada Amante. Se nenhum dos Amantes morrerem, o outro vai morrer de amor (talvez não morra, depende da configuração do anfitrião). Quando um dos Amantes for morto na reunião, o outro vai morrer e virar um corpo morto que não poderá ser reportado.",
|
||||
"LoversInfoLong": "(Add-ons),\\nLovers win together and may die together if one is exiled or killed. They can see the ♥ mark next to each other's name. If one of the lovers win at the end, the other wins too.",
|
||||
"MadmateInfoLong": "(Atributos):\\n★Apenas o Trpipulante pode se tornar um Cúmplice. A Tarefa do Cúmplice é ajudar os Impostores a vencer, o Cúmplice irá perder se os Impostores forem mortos/ejetados. Talvez o Cúmplica saiba quem são os Impostores e talvez os Impostores saibam quem é o Cúmplice (configuração do Anfitrião).",
|
||||
"NtrInfoLong": "(Atributos):\\n★Quando tiver um Netuno na partida, todos os jogadores vão ver que são Amantes do Netuno, eles não vão morrer juntos e as condições de ganhar são as mesmas. Nota: Amantes não vão se tornar Netuno, e Netuno não vai se tornar Amante.",
|
||||
"WatcherInfoLong": "(Atributos):\\n★Durante a reunião, o Visão de Voto verá o voto de todos.",
|
||||
@@ -783,7 +786,8 @@
|
||||
"OnboundInfoLong": "(Atributos):\\n★Com o atributo Inadivinhável, você não pode ser adivinhado em reuniões.",
|
||||
"KnightedInfoLong": "(Atributos):\\n★Quando um Monarca nomeia alguém como cavaleiro, eles recebem um voto extra.",
|
||||
"AsthmaticInfoLong": "(Atributos):\\nVocê sempre tem um símbolo em baixo do seu nome com um temporizador que está verde ou vermelho. Você não pode se mover quando o símbolo/temporizador está vermelho. O temporizador indica quando a cor vai mudar.",
|
||||
"UnreportableInfoLong": "(Atributos):\\n★Com o atributo Irreportável, seu corpo não pode ser relatado.",
|
||||
"UnreportableInfoLong": "(Atributos):\\n★Com o atributo Irreportável, seu corpo não pode ser reportado.",
|
||||
"HasteInfoLong": "(Atributos):\\nCom o atributo Irreportável, seu tempo de recarga para matar continua diminuindo a todo momento.",
|
||||
"ContagiousInfoLong": "(Atributos de Traição):\\n★Quando o Vírus te infecta, você se torna Contagiado.\\nJogadores Contagiados fazem parte da equipe do Vírus.\\n\\nSe você morrer após uma reunião depende das configurações para o Vírus.",
|
||||
"RogueInfoLong": "(Atributos de Traição):\\n★Como um Vigarista, você trabalha contra a sua equipe e trabalha com outros Vigaristas.\\n\\nVocê não pode vencer com a sua equipe original.",
|
||||
"LuckyInfoLong": "(Atributos):\\n★Com o atributo Sortudo, há uma probabilidade de você escapar de um abate, a probabilidade específica é definida pelo anfitrião. Quando o efeito de esquivar ocorre, o assassino verá a animação de escudo, mas você não saberá de nada.",
|
||||
@@ -805,7 +809,7 @@
|
||||
"UnluckyInfoLong": "(Atributos):\\n★O Azarado tem como poder: fazer tarefas, matar ou usar ventilações tem chance de te matar.",
|
||||
"GhoulInfoLong": "(Atributos):\\n★Duas coisas podem acontecer com o Espírito quando ele terminar as tarefas. \\n★Se estiver vivo: Suicídio \\n★Se estiver morto: Mata o seu assassino. \\n★Apenas atribuidas para tripulantes que tenham tarefas.",
|
||||
"SunglassesInfoLong": "(Atributos):\\n★O Óculos de Sol tem sua visão reduzida.",
|
||||
"GMInfoLong": "(Nenhum):\\n★O Espectador é uma função observadora. \\n★Sua presença não faz diferença n jogo, e todos os jogadores sabem quem o Espectador é. \\n★A função do Espectador vai ser atribuida para o Anfitrião, o qual irá se tornar um fantasma no inicio da partida.",
|
||||
"GMInfoLong": "(Nenhum):\\n★O Espectador é uma função observadora. \\n★Sua presença não faz diferença no jogo, e todos os jogadores sabem quem o Espectador é. \\n★A função do Espectador vai ser atribuída para o Anfitrião, o qual irá se tornar um fantasma no início da partida.",
|
||||
"SunnyboyInfoLong": "(Neutros):\\n★O Sunnyboy checa o status de vida dos outros através do painel da vida. \\nEnquanto o Sunnyboy estiver vivo, o jogo não irá acabar em por causa de uma vantagem númerica. \\n★Se o Sunnyboy morrer no fim do jogo, o Sunnyboy ganhará com o time vencedor.",
|
||||
"BardInfoLong": "(Impostores):\\n★Enquanto o Bardo estiver vivo, a mensagem de confirmação de ejeção será composta pelo Bardo. \\n★Quando o Bardo completar a criação, o tempo de recarga do Bardo será permanentemente dividida pela metade.",
|
||||
"NukerInfoLong": "(Impostores):\\n★O Nuclear é uma bomba muito forte. \\n★Use sua metamorfose para explodir todos os jogadores.",
|
||||
@@ -969,7 +973,7 @@
|
||||
"AutoPlayAgain": "Jogar de novo automaticamente",
|
||||
"AutoPlayAgainCountdown": "Atraso antes de entrar na sala novamente",
|
||||
"RemovePetsAtDeadPlayers": "Remover pets de jogadores mortos",
|
||||
"SeeEjectedRolesInMeeting": "Ver Funções Ejetadas em Reuniões",
|
||||
"SeeEjectedRolesInMeeting": "Ver as funções de quem foi expulso em reuniões",
|
||||
"EnableKillerLeftCommand": "Habilitar uso do comando /kcount",
|
||||
"DoorsResetMode": "Redefinir o modo das portas",
|
||||
"AllClosed": "Tudo Fechado",
|
||||
@@ -997,7 +1001,7 @@
|
||||
"Skip": "Pular",
|
||||
"WhenTie": "Quando houver empate",
|
||||
"TieMode.Default": "Sem ejeções",
|
||||
"TieMode.All": "Ejetar todos",
|
||||
"TieMode.All": "Expulsar todos",
|
||||
"TieMode.Random": "Ejetar aleatoriamente",
|
||||
"DisableDevices": "Desabilitar Dispositivos",
|
||||
"DisableSkeldDevices": "Desabilitar Dispositivos no Skeld",
|
||||
@@ -1031,7 +1035,7 @@
|
||||
"AirshipAdditionalSpawn": "Locais de Surgimento Adicionais (<color=#ed0007>Airship</color>)",
|
||||
"CommsCamouflage": "Camuflagem durante a Sabotagem das Comunicações",
|
||||
"CommsCamouflageDisableOnFungle": "Desabilitar no Fungle",
|
||||
"DisableReportWhenCC": "Desabilitar relatório do corpo enquanto estiver camuflado",
|
||||
"DisableReportWhenCC": "Desabilitar reporte de corpos enquanto estiver camuflado",
|
||||
"EnableDebugMode": "Habilitar Modo de Depuração",
|
||||
"ChangeNameToRoleInfo": "Mostrar info. de função para clientes não modificados na 1º Rodada",
|
||||
"SendRoleDescriptionFirstMeeting": "Mostrar descrições de função para clientes não modificados na Primeira Reunião",
|
||||
@@ -1251,7 +1255,7 @@
|
||||
"CanTargetNeutralEvil": "O seu alvo pode ser um <color=#7f8c8d>Neutro</color> <color=#912900>Malvado</color>",
|
||||
"CanTargetNeutralChaos": "O seu alvo pode ser um <color=#7f8c8d>Neutro</color> do <color=#D4AF37>Caos</color>",
|
||||
"SidekickSheriffCanGoBerserk": "<color=#f8cd46>Xerife</color> <color=#00b4eb>Recrutado</color> pode enlouquecer",
|
||||
"LawyerCanTargetImpostor": "O alvo poderá ser <color=#ff1919>Impostor</color>",
|
||||
"LawyerCanTargetImpostor": "O seu cliente pode ser um <color=#ff1919>Impostor</color>",
|
||||
"LawyerCanTargetNeutralKiller": "O seu cliente pode ser um <color=#7f8c8d>Neutro Assassino</color>",
|
||||
"LawyerCanTargetCrewmate": "O seu cliente pode ser um <color=#8cffff>Tripulante</color>",
|
||||
"LawyerCanTargetJester": "O seu cliente pode ser um <color=#ec62a5>Palhaço</color>",
|
||||
@@ -1382,11 +1386,11 @@
|
||||
"ImpCanBeBait": "<color=#ff1919>Impostores</color> podem se tornar <color=#00f7ff>Armadores</color>",
|
||||
"CrewCanBeBait": "<color=#8cffff>Tripulantes</color> podem se tornar <color=#00f7ff>Armadores</color>",
|
||||
"NeutralCanBeBait": "<color=#7f8c8d>Neutros</color> podem se tornar <color=#00f7ff>Armadores</color>",
|
||||
"BaitDelayMin": "Tempo Mínimo de Atraso no Relatório",
|
||||
"BaitDelayMax": "Tempo Máximo de Atraso no Relatório",
|
||||
"BaitDelayNotify": "<color=#ffff00>Avisar ao assassino sobre o auto-relatório</color>",
|
||||
"BaitDelayMin": "Tempo Mínimo de Atraso no Reporte",
|
||||
"BaitDelayMax": "Tempo Máximo de Atraso no Reporte",
|
||||
"BaitDelayNotify": "<color=#ffff00>Avisar ao assassino sobre a auto-denúncia</color>",
|
||||
"BaitNotification": "Revelar <color=#00f7ff>Armador</color> na Primeira Reunião",
|
||||
"BaitAdviceAlive": "<b>{0}</b> é o Armador. O jogador que abater o Armador irá reportar o corpo automaticamente!",
|
||||
"BaitAdviceAlive": "<b>{0}</b> é o Armador. O jogador que matar o Armador irá reportar o corpo automaticamente!",
|
||||
"CounterfeiterSkillCooldown": "Recarga de Habilidae",
|
||||
"CounterfeiterSkillLimitTimes": "Número máximo de usos",
|
||||
"PursuerSkillCooldown": "Recarga de Habilidade",
|
||||
@@ -1578,7 +1582,7 @@
|
||||
"DovesOfNeaceCooldown": "Recarga de Habilidade",
|
||||
"DovesOfNeaceMaxOfUseage": "Número Máximo de Habilidades",
|
||||
"BloodhoundArrowsPointingToDeadBody": "Setas apontando para os corpos",
|
||||
"BloodhoundLeaveDeadBodyUnreportable": "Os corpos usados pelo <color=#8B0000>Detetive</color> não podem ser relatados",
|
||||
"BloodhoundLeaveDeadBodyUnreportable": "Os corpos usados pelo <color=#8B0000>Detetive</color> não podem ser reportados",
|
||||
"BloodhoundNotifyKiller": "Informar ao assassino que ele está sendo rastreado",
|
||||
"BloodhoundKillerNotify": "O <color=#8B0000>Detetive</color> está te rastreando!",
|
||||
"TrackerHideVote": "Ocultar votos do <color=#3CB371>Rastreador</color>",
|
||||
@@ -1659,7 +1663,7 @@
|
||||
"UndertakerInfoLong": "(Impostores):\\nComo Conjurador, você pode marcar seu alvo usando o seu botão de matar, e em seguida metamorfar/acariciar o pet para matá-los. No entanto, a pessoa que você matar será teleportada para sua localização quando você usar o metamorfo, e com isso o corpo irá se teletransportar para você! Use isto bem para esconder corpos!",
|
||||
"Romantic": "Romântico",
|
||||
"RomanticInfo": "Proteja seu parceiro para ganhar junto com ele",
|
||||
"RomanticInfoLong": "(Neutros):\\nO Romântico pode escolher seu parceiro usando seu botão de abate (isso pode ser feito a qualquer ponto do jogo). Uma vez que o parceiro for escolhido, o Romântico poderá usar seu botão de abate para dar ao seu parceiro um escudo temporário, o qual vai proteger ele de ataques. Se o parceiro dele morrer, a função do Romântico mudará de acordo com as seguintes condições:\\n1. Se o parceiro dele era um Impostor, o Romântico se torna um Refugiado.\\n2. Se o parceiro dele era um Neutro Assassino, então ele se torna um Romântico Impiedoso.\\n3. Se o parceiro dele era um Membro do Coventículo, então ele se torna uma Alma Penada.\\n4. Se o parceiro dele era um Tripulante ou um neutro não assassino, o Romântico se torna o Romântico Vingativo. \\n\\nO Romântico ganha com o time vencedor se o parceiro dele vencer.\\nNota: Se sua função mudar, sua condição de vitória será mudada de acordo",
|
||||
"RomanticInfoLong": "(Neutral):\\nAs the Romantic, you can first use your kill button on someone to select them as your partner.\\nAfter that, as long as both you and your partner are alive, you can use your kill button on anyone to give your partner a temporarily shield. If your partner wins, you win too.\\n\\nIf your partner dies, your role will change according to the following conditions:\\nIf your partner had a kill button of any type, you will get the same role as they were.\\nIf your partner was a Non-Killing Neutral or your partner died by themselves (suicided or has no real killer), you will turn into the Ruthless Romantic, which is a basic neutral killer: Be the last one standing to win. If you win, your partner wins too.\\nIf your target was an Impostor, you become a Refugee. Your job is now to help the impostors (and thus your partner) win.\\nIn every other case, you become the Vengeful Romantic. Your goal is to kill the killer of your partner. If you do so, you and your partner win with the winning team. If you try to kill anyone else, you will die.",
|
||||
"VengefulRomantic": "Romântico Vingativo",
|
||||
"VengefulRomanticInfo": "Vingue seu parceiro para ganhar",
|
||||
"VengefulRomanticInfoLong": "(Neutros):\\n★Você era um Romântico e teve seu parceiro morto (era um tripulante ou um neutro passivo). \\n★O Româncito Vingativo tem como o objetivo vingar seu parceiro, portanto significa que você tem um botão de abate para matar o assassino do seu parceiro.",
|
||||
@@ -1703,7 +1707,7 @@
|
||||
"Prefix.DeadlyQuota": "Cota Mortal",
|
||||
"Damocles": "Dâmocles",
|
||||
"DamoclesInfo": "Tudo muda seu Tempo De Vida",
|
||||
"DamoclesInfoLong": "(Atributos):\\nApenas Impostores pode obter este Atributo. O Jogador com este Atributo verá constantemente um Temporizador próximo ao nome. Caso o Temporizador se esgotar, o jogador morre. O Temporizador é pausado em Reuniões. Abaixo está uma lista do que aumenta ou dimnui seu temporizador de Suicídio:\\n\\n<size=80%>[<color=#00ff00><b>+</b></color>] Você ou outro impostor / cumplice mata\\n[<color=#00ff00><b>+</b></color>] Entrar em um Duto no qual você não entrou nesta rodada\\n[<color=#00ff00><b>+</b></color>] Sobreviver a uma Reunião\\n[<color=#00ff00><b>+</b></color>] Um Jogador que não for do seu time é ejetado\\n[<color=#ff0000><b>-</b></color>] Consertar uma Sabotagem\\n[<color=#ff0000><b>-</b></color>] Um Impostor ou Cumplice morre\\n[<color=#ff0000><b>-</b></color>] Reportar um corpo ou Chamar uma Reunião\\n[<color=#ff0000><b>-</b></color>] Um jogador do seu time é ejetado</size>",
|
||||
"DamoclesInfoLong": "(Atributos):\\nApenas Impostores pode obter este Atributo. O Jogador com este Atributo verá constantemente um Temporizador próximo ao nome. Caso o Temporizador se esgotar, o jogador morre. O Temporizador é pausado em Reuniões. Abaixo está uma lista do que aumenta ou diminui seu temporizador de Suicídio:\\n\\n<size=80%>[<color=#00ff00><b>+</b></color>] Você ou outro impostor/cúmplice mata\\n[<color=#00ff00><b>+</b></color>] Entrar em um Duto no qual você não entrou nesta rodada\\n[<color=#00ff00><b>+</b></color>] Sobreviver a uma Reunião\\n[<color=#00ff00><b>+</b></color>] Um Jogador que não for do seu time é ejetado\\n[<color=#ff0000><b>-</b></color>] Consertar uma Sabotagem\\n[<color=#ff0000><b>-</b></color>] Um Impostor ou Cúmplice morre\\n[<color=#ff0000><b>-</b></color>] Reportar um corpo ou Chamar uma Reunião\\n[<color=#ff0000><b>-</b></color>] Um jogador do seu time é ejetado</size>",
|
||||
"Prefix.Damocles": "Dâmocles",
|
||||
"DamoclesTimeLeft": "<color=#00ffa5>Tempo Restante:</color> <color=#ffffff><b>{0}</b>s</color>",
|
||||
"DamoclesExtraTimeAfterKill": "Tempo Obtido por Abate",
|
||||
@@ -1715,10 +1719,10 @@
|
||||
"Prefix.Stressed": "Estressado",
|
||||
"StressedExtraTimeAfterTask": "Tempo Obtido após Completar uma tarefa",
|
||||
"StressedExtraTimeAfterImpostorDeath": "Tempo obtido napós um Impostor ser Abatido",
|
||||
"StressedExtraTimeAfterImpostorEjected": "Tempo Obtido após um Impostor ser ejetado",
|
||||
"StressedExtraTimeAfterImpostorEjected": "Tempo Obtido após um Impostor ser Expulso",
|
||||
"StressedExtraTimeAfterFixingSabotage": "Tempo Obtido ao Consertar uma sabotagem",
|
||||
"StressedExtraTimeAfterReporting": "Tempo Obtido ao Reportar Corpos",
|
||||
"StressedTimePenaltyAfterCrewmateEjected": "Tempo Retirado Após Um Tripulante ser Ejetado",
|
||||
"StressedTimePenaltyAfterCrewmateEjected": "Tempo Retirado Após Um Tripulante ser Expulso",
|
||||
"SabotageCooldownControl": "Controle de Recarga da Sabotagem",
|
||||
"SabotageCooldown": "Recarga da Sabotagem",
|
||||
"SabotageTimeControl": "Controle de Duração de Sabotagem",
|
||||
@@ -1881,7 +1885,7 @@
|
||||
"SuccubusCharmedPlayer": "Você encantou com sucesso o jogador",
|
||||
"CharmedBySuccubus": "Você foi encantado com o Cultista",
|
||||
"SuccubusInvalidTarget": "O alvo não pode ser encantado",
|
||||
"KillBaitNotify": "<color=#ffff00>Você fará o auto-relato em <color=#ffab1b>{0}</color>s</color>",
|
||||
"KillBaitNotify": "<color=#ffff00>Você fará um auto-denúncia em <color=#ffab1b>{0}</color>s</color>",
|
||||
"InfectiousInvalidTarget": "O alvo não pode ser infectado",
|
||||
"BittenByInfectious": "Você foi infectado pelo Infeccioso!",
|
||||
"InfectiousBittenPlayer": "Você infectou com sucesso o jogador",
|
||||
@@ -1910,8 +1914,8 @@
|
||||
"KnightedByMonarch": "Você foi nomeado Cavaleiro por um Monarca!",
|
||||
"MonarchInvalidTarget": "O alvo não pode ser nomeado Cavaleiro",
|
||||
"VirusNoticeTitle": "[ Cadáver infectado! ]",
|
||||
"VirusNoticeMessage": "O corpo que você relatou foi infectado pelo Vírus! Você agora faz parte da Equipe Vírus. Ajude o Vírus a vencer o jogo.",
|
||||
"VirusNoticeMessage2": "O corpo que você relatou foi infectado pelo Vírus! Vote no Vírus nesta reunião ou você morrerá.",
|
||||
"VirusNoticeMessage": "O corpo que você reportou foi infectado pelo Vírus! Você agora faz parte da Equipe Vírus. Ajude o Vírus a vencer o jogo.",
|
||||
"VirusNoticeMessage2": "O corpo que você reportou foi infectado pelo Vírus! Expulse o Vírus nesta reunião ou você morrerá.",
|
||||
"SpiritcallerNoticeTitle": "VOCÊ SE TRANSFORMOU EM UM ESPÍRITO MALIGNO",
|
||||
"SpiritcallerNoticeMessage": "O Caçador de Almas matou você e o transformou em um Espírito Maligno. Sua tarefa agora é ajudar o Caçador de Almas a vencer usando seu botão assustador para atrapalhar outros jogadores ou para proteger o Caçador de Almas. Use /m para obter mais informações.",
|
||||
"FarseerRevealCooldown": "Recarga de <color=#BA55D3>Revelar</color>",
|
||||
@@ -2793,6 +2797,7 @@
|
||||
"EveryoneCanVent": "Todos podem usar os dutos",
|
||||
"OverrideScientistBasedRoles": "Também sobrescrever as funções baseadas no cientista para o engenheiro",
|
||||
"WhackAMole": "Whack-A-Mole",
|
||||
"SpawnAdditionalRefugeeOnImpsDead": "Random <#8cffff>Crewmate</color> Turns To <#ff1919>Refugee</color> When All <#ff1919>Impostors</color> Die",
|
||||
"NoPetActionMsg1": "Você acabou de comer um biscoito",
|
||||
"NoPetActionMsg2": "Você olha em volta",
|
||||
"NoPetActionMsg3": "Você começa a se perguntar se é sus ou não",
|
||||
@@ -2849,20 +2854,20 @@
|
||||
"GamblerWhatToIgnore": "O que Ignorar quando o Efeito IMPARAVEL estiver ativo",
|
||||
"GamblerIgnoreMedicShield": "ignorar <color=#8cffff>Escudo</color> do <color=#00ff97>Medico</color>",
|
||||
"GamblerIgnoreVeteranAlert": "Ignorar Alerta do <color=#a77738>Veterano</color>",
|
||||
"GamblerIgnoreCursedWolfAndJinx": "\"Matar <color=#ff1919>Lobo Maldito</color> e <color=#ed2f91>Jinx</color",
|
||||
"GamblerIgnoreCursedWolfAndJinx": "Matar <color=#ff1919>Lobo Maldito</color> e <color=#ed2f91>Jinx</color>",
|
||||
"GamblerIgnorePestilence": "Matar <color=#343136>Peste</color>",
|
||||
"GamblerPositiveEffectChance": "Chance de Efeito Positivo",
|
||||
"GamblerGet.DelayedKill": "<color=#00ffa5>Efeito:</color> Abate Atrasado (<color=#00ffa5>{0}s</color>)",
|
||||
"GamblerGet.Shield": "<color=#00ffa5>Efeito:</color> Escudo Temporário (<color=#00ffa5>{0}s</color>)",
|
||||
"GamblerGet.NoLunge": "<color=#00ffa5>Efeito:</color> Sem Investida",
|
||||
"GamblerGet.Swap": "<color=#00ffa5>Efeito:</color> Trocar de lugar com alguem",
|
||||
"GamblerGet.IgnoreDefense": "<color=#00ffa5>Efeito:</color> Quebrar Defesa",
|
||||
"GamblerGet.LowKCD": "<color=#00ffa5>Efeito:</color> Recarga de Abate Mais Baixa (<color=#00ffa5>{0}s</color>)",
|
||||
"GamblerGet.Speedup": "<color=#00ffa5>Efeito:</color> Impulso de Velocidade (<color=#00ffa5>{1}x</color>, por <color=#00ffa5>{0}s</color>)",
|
||||
"GamblerGet.BSR": "<color=#00ffa5>Efeito:</color> Auto-Denuncia (em <color=#ffa500>{0}s</color>)",
|
||||
"GamblerGet.Freeze": "<color=#00ffa5>Efeito:</color> Congelar (for <color=#ffa500>{0}s</color>)",
|
||||
"GamblerGet.LowVision": "<color=#00ffa5>Efeito:</color> Visão Baixa (<color=#ffa500>{1}x</color>, por <color=#ffa500>{0}s</color>)",
|
||||
"GamblerGet.HighKCD": "<color=#00ffa5>Efeito:</color> Recarga de abate Mais Alta (<color=#ffa500>{0}s</color>)",
|
||||
"GamblerGet.DelayedKill": "<color=#ffffff><color=#00ffa5>Efeito:</color> Abate Atrasado (<color=#00ffa5>{0}s</color>)</color>",
|
||||
"GamblerGet.Shield": "<color=#ffffff><color=#00ffa5>Efeito:</color> Escudo Temporário (<color=#00ffa5>{0}s</color>)</color>",
|
||||
"GamblerGet.NoLunge": "<color=#ffffff><color=#00ffa5>Efeito:</color> Sem Investida</color>",
|
||||
"GamblerGet.Swap": "<color=#ffffff><color=#00ffa5>Efeito:</color> Trocar de lugar com alguém</color>",
|
||||
"GamblerGet.IgnoreDefense": "<color=#ffffff><color=#00ffa5>Efeito:</color> Ignorar Escudos</color>",
|
||||
"GamblerGet.LowKCD": "<color=#ffffff><color=#00ffa5>Efeito:</color> Recarga para matar mais baixa (<color=#00ffa5>{0}s</color>)</color>",
|
||||
"GamblerGet.Speedup": "<color=#ffffff><color=#00ffa5>Efeito:</color> Impulso de Velocidade (<color=#00ffa5>{1}x</color>, por <color=#00ffa5>{0}s</color>)</color>",
|
||||
"GamblerGet.BSR": "<color=#ffffff><color=#ffa500>Efeito:</color> Auto Denunciar o corpo (em <color=#ffa500>{0}s</color>)</color>",
|
||||
"GamblerGet.Freeze": "<color=#ffffff><color=#ffa500>Efeito:</color> Congelar (por <color=#ffa500>{0}s</color>)</color>",
|
||||
"GamblerGet.LowVision": "<color=#ffffff><color=#ffa500>Efeito:</color> Visão Menor (<color=#ffa500>{1}x</color>, por <color=#ffa500>{0}s</color>)</color>",
|
||||
"GamblerGet.HighKCD": "<color=#ffffff><color=#ffa500>Efeito:</color> Recarga para matar mais alta (<color=#ffa500>{0}s</color>)</color>",
|
||||
"Hitman": "Hitman",
|
||||
"HitmanInfo": "Selecione seu Alvo",
|
||||
"HitmanInfoLong": "(Impostores):\\nComo Hitman, você pode metamorfar em alguem para marcar seu alvo. Caso mate seu alvo, sua próxima recarga de abate será menor.\\n\\nNota: Uma vez que você escolehr o seu alvo, você não pode muda-lo. Você vai obter outra chance para marcar um alvo assim que matar seu alvo atual.",
|
||||
@@ -2950,6 +2955,10 @@
|
||||
"SoulHunterNotify": "Você tem <color=#00ffa5>{0}s</color> para matar <color=#3f2c61>{1}</color>",
|
||||
"SoulHunterNotifySuccess": "<color=#00ff00>Sucesso!</color>",
|
||||
"SoulHunterNotifyFreeze": "Você pode começar a caçar <color=#3f2c61>{0}</color> em <color=#00ffa5>{1}s</color>",
|
||||
"Beacon": "Sinalizador",
|
||||
"BeaconInfo": "Aumente a visão dos jogadores próximos durante as luzes",
|
||||
"BeaconInfoLong": "(Tripulantes):\\nComo o Sinalizador, durante a sabotagem das luzes, você aumenta a visão dos jogadores ao seu redor.",
|
||||
"BeaconVisionIncrease": "Máximo de visão para os jogadores afetados durante sabotagem de luzes",
|
||||
"Librarian": "Bibliotecário",
|
||||
"LibrarianInfo": "Silencie os Reportadores próximos",
|
||||
"LibrarianInfoLong": "(Impostores):\\nComo Bibliotecário, quando você estiver em modo silencioso, se alguem proximo reportar um corpo, você matará o reportador ao inves disso. Após você matar alguem assim, todos verão um 'SSSH!' em vermelho abaixo do nome.",
|
||||
@@ -3106,6 +3115,17 @@
|
||||
"Tasker": "Tarefador",
|
||||
"TaskerInfo": "Seja o Primeiro a Terminar as Tarefas para vencer!",
|
||||
"TaskerInfoLong": "Seja o Primeiro a Terminar as Tarefas para vencer!",
|
||||
"HotPotato": "BATATA QUENTE",
|
||||
"ModeHotPotato": "Modo de Jogo: Batata Quente",
|
||||
"ModeDescribe.HotPotato": "No modo de jogo Batata Quente, um jogador sempre tem a batata quente e ele pode passá-la para qualquer um indo perto de alguém. A cada X segundos (X é definido pelo anfitrião), o jogador que segura a batata quente morre. O último jogador vivo vence!",
|
||||
"HotPotato_Time": "O jogador com a batata quente é morto a cada",
|
||||
"HotPotato_HolderSpeed": "Velocidade do jogador segurando a Batata Quente",
|
||||
"HotPotato_Range": "Alcance para passar a Batata Quente",
|
||||
"HotPotato_TimeLeftSuffix": "<#00ffa5>Matar em </color>",
|
||||
"HotPotato_HoldingNotify": "<#ffff00><size=2>Você está com a Batata Quente! Passe ou morra!</size></color>",
|
||||
"Potato": "Jogador",
|
||||
"PotatoInfo": "EVITE FICAR COM A BATATA QUENTE",
|
||||
"PotatoInfoLong": "EVITE FICAR COM A BATATA QUENTE",
|
||||
"GNoCommandInfo": "<b>Esse comando só pode ser usado no lobby ou depois que você morrer.</b>\\n\\nDigite <b>/gno X</b> para jogar e adivinhar um número. X pode ser um número entre 0 e 99 (ambos incluídos). \\n\\nVocê tem um máximo de <color=#ff0000>7 tentativas</color> para adivinhar o número.\\n\\n Exemplo:- <b>/gno 10</b>",
|
||||
"GNoLost": "Oh, você estava tão perto! Só mais um chute e você poderia ter decifrado o código Da Vinci! A propósito, <color=#ff0000>o número secreto era... {0}!</color> Mas, ei, você só errou por alguns bilhões de possibilidades. Mais sorte na próxima vez, Sherlock!",
|
||||
"GNoLow": "Oh, você está realmente acertando em cheio! É tão baixo que quase preciso de uma pá para desenterrá-lo!\\nVocê tem <color=#ff0000>{0} adivinhações</color> restantes!",
|
||||
@@ -3234,7 +3254,7 @@
|
||||
"OptionBeartrap": "Pode vender <color=#5a8fd0>Imobilizador</color>",
|
||||
"OptionTiebreaker": "Pode vender <color=#1447af>Desempatador</color>",
|
||||
"OptionOnbound": "Pode vender <color=#BAAAE9>Inadivinhável</color>",
|
||||
"OptionDisregarded": "Pode vender <color=#FF6347>Irreportável</color>",
|
||||
"OptionDisregarded": "Pode vender Irreportável",
|
||||
"OptionLucky": "Pode vender <color=#b8d7a3>Sortudo</color>",
|
||||
"OptionAutopsy": "Pode vender <color=#80ffdd>Autópsia</color>",
|
||||
"OptionLoyal": "Pode vender <color=#B71556>Leal</color>",
|
||||
|
||||
@@ -26,7 +26,7 @@ public static class LastImpostor
|
||||
{
|
||||
//ラストインポスターがすでにいれば処理不要
|
||||
if (currentId != byte.MaxValue || !AmongUsClient.Instance.AmHost) return;
|
||||
if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop
|
||||
if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato
|
||||
|| !CustomRoles.LastImpostor.IsEnable() || Main.AliveImpostorCount != 1)
|
||||
return;
|
||||
foreach (PlayerControl pc in Main.AllAlivePlayerControls)
|
||||
|
||||
+22
-22
@@ -320,29 +320,29 @@ namespace TOHE.Roles.Crewmate
|
||||
var str = new StringBuilder();
|
||||
switch (PotionID)
|
||||
{
|
||||
case 1: // Shield
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#00ff97>{GetString("ShieldPotion")}</color>");
|
||||
break;
|
||||
case 2: // Suicide
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#ff0000>{GetString("AwkwardPotion")}</color>");
|
||||
break;
|
||||
case 3: // TP to random player
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#42d1ff>{GetString("TeleportPotion")}</color>");
|
||||
break;
|
||||
case 4: // Increased speed
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#ff8400>{GetString("SpeedPotion")}</color>");
|
||||
break;
|
||||
case 5: // Quick fix next sabo
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#3333ff>{GetString("QuickFixPotion")}</color>");
|
||||
break;
|
||||
case 6: // Invisibility
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#01c834>{GetString("InvisibilityPotion")}</color>");
|
||||
break;
|
||||
case 7: // Increased vision
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#eee5be>{GetString("SightPotion")}</color>");
|
||||
break;
|
||||
case 1: // Shield
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#00ff97>{GetString("ShieldPotion")}</color>");
|
||||
break;
|
||||
case 2: // Suicide
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#ff0000>{GetString("AwkwardPotion")}</color>");
|
||||
break;
|
||||
case 3: // TP to random player
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#42d1ff>{GetString("TeleportPotion")}</color>");
|
||||
break;
|
||||
case 4: // Increased speed
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#ff8400>{GetString("SpeedPotion")}</color>");
|
||||
break;
|
||||
case 5: // Quick fix next sabo
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#3333ff>{GetString("QuickFixPotion")}</color>");
|
||||
break;
|
||||
case 6: // Invisibility
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#01c834>{GetString("InvisibilityPotion")}</color>");
|
||||
break;
|
||||
case 7: // Increased vision
|
||||
str.Append($" <color=#00ffa5>{GetString("Stored")}:</color> <color=#eee5be>{GetString("SightPotion")}</color>");
|
||||
break;
|
||||
}
|
||||
if (FixNextSabo) str.Append($" <color=#777777>({Translator.GetString("QuickFix")})</color>");
|
||||
if (FixNextSabo) str.Append($" <color=#777777>({GetString("QuickFix")})</color>");
|
||||
return str.ToString();
|
||||
}
|
||||
public static void RepairSystem(SystemTypes systemType, byte amount)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TOHE.Roles.Crewmate
|
||||
{
|
||||
internal class Beacon
|
||||
{
|
||||
private static int Id => 643480;
|
||||
private static OptionItem VisionIncrease;
|
||||
private static OptionItem Radius;
|
||||
private static List<byte> AffectedPlayers;
|
||||
private static Dictionary<byte, long> LastChange;
|
||||
public static void SetupCustomOption()
|
||||
{
|
||||
Options.SetupRoleOptions(Id, TabGroup.CrewmateRoles, CustomRoles.Beacon);
|
||||
VisionIncrease = FloatOptionItem.Create(Id + 2, "BeaconVisionIncrease", new(0.05f, 1.5f, 0.05f), 0.5f, TabGroup.CrewmateRoles, false)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Beacon])
|
||||
.SetValueFormat(OptionFormat.Multiplier);
|
||||
Radius = FloatOptionItem.Create(Id + 3, "PerceiverRadius", new(0.1f, 5f, 0.1f), 1.5f, TabGroup.CrewmateRoles, false)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Beacon])
|
||||
.SetValueFormat(OptionFormat.Multiplier);
|
||||
}
|
||||
public static void Init()
|
||||
{
|
||||
AffectedPlayers = [];
|
||||
LastChange = [];
|
||||
}
|
||||
public static bool IsAffectedPlayer(byte id) => Utils.IsActive(SystemTypes.Electrical) && AffectedPlayers.Contains(id);
|
||||
public static float IncreasedVision => VisionIncrease.GetFloat();
|
||||
public static void OnCheckPlayerPosition(PlayerControl pc)
|
||||
{
|
||||
if (!GameStates.IsInTask || pc == null) return;
|
||||
|
||||
long now = Utils.GetTimeStamp();
|
||||
if (LastChange.TryGetValue(pc.PlayerId, out var ts) && ts == now) return;
|
||||
|
||||
bool isBeaconNearby = Main.AllAlivePlayerControls.Any(x => x.Is(CustomRoles.Beacon) && UnityEngine.Vector2.Distance(x.Pos(), pc.Pos()) <= Radius.GetFloat());
|
||||
bool isAffectedPlayer = AffectedPlayers.Contains(pc.PlayerId);
|
||||
|
||||
if (isAffectedPlayer && !isBeaconNearby)
|
||||
{
|
||||
AffectedPlayers.Remove(pc.PlayerId);
|
||||
if (Utils.IsActive(SystemTypes.Electrical)) pc.MarkDirtySettings();
|
||||
LastChange[pc.PlayerId] = now;
|
||||
}
|
||||
else if (!isAffectedPlayer && isBeaconNearby)
|
||||
{
|
||||
AffectedPlayers.Add(pc.PlayerId);
|
||||
if (Utils.IsActive(SystemTypes.Electrical)) pc.MarkDirtySettings();
|
||||
LastChange[pc.PlayerId] = now;
|
||||
}
|
||||
}
|
||||
public static void OnReportDeadBody()
|
||||
{
|
||||
AffectedPlayers.Clear();
|
||||
LastChange.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,13 +212,4 @@ public static class Chameleon
|
||||
}
|
||||
return str.ToString();
|
||||
}
|
||||
|
||||
public static bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (!IsInvis(killer.PlayerId)) return true;
|
||||
killer.SetKillCooldown();
|
||||
target.RpcCheckAndMurder(target);
|
||||
target.SetRealKiller(killer);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public static class Divinator
|
||||
playerIdList.Add(playerId);
|
||||
CheckLimit.TryAdd(playerId, CheckLimitOpt.GetInt());
|
||||
|
||||
List<CustomRoles> AllRoles = [.. EnumHelper.GetAllValues<CustomRoles>().Where(x => !x.IsAdditionRole() && x is not CustomRoles.Killer and not CustomRoles.Tasker and not CustomRoles.KB_Normal)];
|
||||
List<CustomRoles> AllRoles = [.. EnumHelper.GetAllValues<CustomRoles>().Where(x => !x.IsAdditionRole() && x is not CustomRoles.Killer and not CustomRoles.Tasker and not CustomRoles.KB_Normal and not CustomRoles.Potato)];
|
||||
var r = IRandom.Instance;
|
||||
|
||||
foreach (var pc in Main.AllAlivePlayerControls)
|
||||
|
||||
@@ -114,66 +114,19 @@ public static class Jailor
|
||||
|
||||
public static void OnReportDeadBody()
|
||||
{
|
||||
if (!notifyJailedOnMeeting.GetBool()) return;
|
||||
foreach (var targetId in JailorTarget.Values)
|
||||
{
|
||||
if (targetId == byte.MaxValue) continue;
|
||||
var tpc = Utils.GetPlayerById(targetId);
|
||||
if (tpc == null) continue;
|
||||
if (notifyJailedOnMeeting.GetBool() && tpc.IsAlive())
|
||||
if (tpc.IsAlive())
|
||||
{
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
Utils.SendMessage(GetString("JailedNotifyMsg"), targetId, title: Utils.ColorString(Utils.GetRoleColor(CustomRoles.Jailor), GetString("JailorTitle")));
|
||||
}, 0.3f, "JailorNotifyJailed");
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnVote(PlayerControl voter, PlayerControl target)
|
||||
{
|
||||
if (voter == null || target == null) return;
|
||||
if (!voter.Is(CustomRoles.Jailor)) return;
|
||||
if (JailorDidVote[voter.PlayerId]) return;
|
||||
if (JailorTarget[voter.PlayerId] == byte.MaxValue) return;
|
||||
JailorDidVote[voter.PlayerId] = true;
|
||||
if (target.PlayerId == JailorTarget[voter.PlayerId])
|
||||
{
|
||||
if (JailorExeLimit[voter.PlayerId] > 0)
|
||||
{
|
||||
JailorExeLimit[voter.PlayerId] = JailorExeLimit[voter.PlayerId] - 1;
|
||||
JailorHasExe[voter.PlayerId] = true;
|
||||
}
|
||||
else JailorHasExe[voter.PlayerId] = false;
|
||||
}
|
||||
SendRPC(voter.PlayerId, setTarget: false);
|
||||
}
|
||||
|
||||
public static bool CanBeExecuted(this CustomRoles role)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void AfterMeetingTasks()
|
||||
{
|
||||
foreach (var pid in JailorHasExe.Keys)
|
||||
{
|
||||
var targetId = JailorTarget[pid];
|
||||
if (targetId != byte.MaxValue && JailorHasExe[pid])
|
||||
{
|
||||
var tpc = Utils.GetPlayerById(targetId);
|
||||
if (tpc.IsAlive())
|
||||
{
|
||||
CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Execution, targetId);
|
||||
tpc.SetRealKiller(Utils.GetPlayerById(pid));
|
||||
}
|
||||
if (!tpc.GetCustomRole().CanBeExecuted())
|
||||
{
|
||||
JailorExeLimit[pid] = 0;
|
||||
SendRPC(pid, setTarget: false);
|
||||
}
|
||||
}
|
||||
JailorHasExe[pid] = false;
|
||||
JailorTarget[pid] = byte.MaxValue;
|
||||
JailorDidVote[pid] = false;
|
||||
SendRPC(pid, byte.MaxValue, setTarget: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
{
|
||||
State.ProtectedPlayerId = pc.PlayerId;
|
||||
Utils.SendMessage(string.Format(Translator.GetString("MathematicianAnsweredString"), pc.GetRealName(), answer), title: Translator.GetString("Mathematician"));
|
||||
State.AskedQuestion = false;
|
||||
}
|
||||
}
|
||||
public static void OnReportDeadBody()
|
||||
|
||||
@@ -23,7 +23,6 @@ public static class NiceSwapper
|
||||
private static (byte, byte) SwapTargets;
|
||||
public static float UseLimit;
|
||||
private static byte NiceSwapperId = byte.MaxValue;
|
||||
private static PlayerControl NiceSwapper_ = null;
|
||||
|
||||
public static void SetupCustomOption()
|
||||
{
|
||||
@@ -46,13 +45,11 @@ public static class NiceSwapper
|
||||
SwapTargets = (byte.MaxValue, byte.MaxValue);
|
||||
UseLimit = 0;
|
||||
NiceSwapperId = byte.MaxValue;
|
||||
NiceSwapper_ = null;
|
||||
}
|
||||
|
||||
public static void Add(byte playerId)
|
||||
{
|
||||
NiceSwapperId = playerId;
|
||||
NiceSwapper_ = Utils.GetPlayerById(playerId);
|
||||
UseLimit = SwapMax.GetInt();
|
||||
}
|
||||
|
||||
@@ -168,9 +165,9 @@ public static class NiceSwapper
|
||||
}
|
||||
}
|
||||
|
||||
public static List<MeetingHud.VoterState> OnCheckForEndVoting(List<MeetingHud.VoterState> votes)
|
||||
public static void OnCheckForEndVoting()
|
||||
{
|
||||
if (!(SwapTargets != (byte.MaxValue, byte.MaxValue))) return votes;
|
||||
if (!(SwapTargets != (byte.MaxValue, byte.MaxValue))) return;
|
||||
|
||||
var playerStates = MeetingHud.Instance.playerStates;
|
||||
var votedFor2 = playerStates.Where(x => x.VotedFor == SwapTargets.Item2).ToList();
|
||||
@@ -192,11 +189,9 @@ public static class NiceSwapper
|
||||
|
||||
PlayerControl Target1 = Utils.GetPlayerById(SwapTargets.Item1);
|
||||
PlayerControl Target2 = Utils.GetPlayerById(SwapTargets.Item2);
|
||||
if (Target1 == null || Target2 == null) return votes;
|
||||
if (Target1 == null || Target2 == null) return;
|
||||
|
||||
Utils.SendMessage(string.Format(GetString("SwapVote"), Target1.GetRealName(), Target2.GetRealName()), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.NiceSwapper), GetString("SwapTitle")));
|
||||
|
||||
return votes;
|
||||
}
|
||||
|
||||
private static bool MsgToPlayerAndRole(string msg, out byte id, out string error)
|
||||
|
||||
@@ -324,7 +324,7 @@ public static class ParityCop
|
||||
// }
|
||||
// ChatUpdatePatch.DoBlockChat = false;
|
||||
//}
|
||||
private static void ParityCopOnClick(byte playerId, MeetingHud __instance)
|
||||
private static void ParityCopOnClick(byte playerId/*, MeetingHud __instance*/)
|
||||
{
|
||||
Logger.Msg($"Click: ID {playerId}", "Inspector UI");
|
||||
var pc = Utils.GetPlayerById(playerId);
|
||||
@@ -364,7 +364,7 @@ public static class ParityCop
|
||||
renderer.sprite = CustomButton.Get("ParityCopIcon");
|
||||
PassiveButton button = targetBox.GetComponent<PassiveButton>();
|
||||
button.OnClick.RemoveAllListeners();
|
||||
button.OnClick.AddListener((Action)(() => ParityCopOnClick(pva.TargetPlayerId, __instance)));
|
||||
button.OnClick.AddListener((Action)(() => ParityCopOnClick(pva.TargetPlayerId/*, __instance*/)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ public static class Sheriff
|
||||
}
|
||||
public static void SetUpNeutralOptions(int Id)
|
||||
{
|
||||
foreach (var neutral in Enum.GetValues(typeof(CustomRoles)).Cast<CustomRoles>().Where(x => x.IsNeutral() && x is not CustomRoles.KB_Normal and not CustomRoles.Konan and not CustomRoles.Pestilence and not CustomRoles.Killer and not CustomRoles.Tasker))
|
||||
foreach (var neutral in Enum.GetValues(typeof(CustomRoles)).Cast<CustomRoles>().Where(x => x.IsNeutral() && x is not CustomRoles.KB_Normal and not CustomRoles.Konan and not CustomRoles.Pestilence and not CustomRoles.Killer and not CustomRoles.Tasker and not CustomRoles.Potato))
|
||||
{
|
||||
SetUpKillTargetOption(neutral, Id, true, CanKillNeutralsMode);
|
||||
Id++;
|
||||
|
||||
+33
-19
@@ -23,6 +23,7 @@ namespace TOHE.Roles.Crewmate
|
||||
private static RandomSpawn.SpawnMap Map;
|
||||
private static readonly Dictionary<(Vector2 LOCATION, string ROOM_NAME), long> Tornados = [];
|
||||
private static long LastNotify = GetTimeStamp();
|
||||
private static bool CanUseMap = true;
|
||||
|
||||
public static void SetupCustomOption()
|
||||
{
|
||||
@@ -47,16 +48,26 @@ namespace TOHE.Roles.Crewmate
|
||||
Tornados.Clear();
|
||||
LastNotify = GetTimeStamp();
|
||||
|
||||
Map = (MapNames)Main.NormalOptions.MapId switch
|
||||
try
|
||||
{
|
||||
MapNames.Skeld => new RandomSpawn.SkeldSpawnMap(),
|
||||
MapNames.Mira => new RandomSpawn.MiraHQSpawnMap(),
|
||||
MapNames.Polus => new RandomSpawn.PolusSpawnMap(),
|
||||
MapNames.Dleks => new RandomSpawn.DleksSpawnMap(),
|
||||
MapNames.Airship => new RandomSpawn.AirshipSpawnMap(),
|
||||
MapNames.Fungle => new RandomSpawn.FungleSpawnMap(),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
Map = Main.CurrentMap switch
|
||||
{
|
||||
MapNames.Skeld => new RandomSpawn.SkeldSpawnMap(),
|
||||
MapNames.Mira => new RandomSpawn.MiraHQSpawnMap(),
|
||||
MapNames.Polus => new RandomSpawn.PolusSpawnMap(),
|
||||
MapNames.Dleks => new RandomSpawn.DleksSpawnMap(),
|
||||
MapNames.Airship => new RandomSpawn.AirshipSpawnMap(),
|
||||
MapNames.Fungle => new RandomSpawn.FungleSpawnMap(),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
CanUseMap = true;
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
Logger.CurrentMethod(lineNumber: 60);
|
||||
Logger.Error("Unsupported Map", "Torando");
|
||||
CanUseMap = false;
|
||||
}
|
||||
}
|
||||
public static void Add(byte playerId)
|
||||
{
|
||||
@@ -101,21 +112,22 @@ namespace TOHE.Roles.Crewmate
|
||||
}
|
||||
public static void OnCheckPlayerPosition(PlayerControl pc)
|
||||
{
|
||||
if (!IsEnable || !GameStates.IsInTask || Tornados.Count == 0) return;
|
||||
if (!IsEnable || !GameStates.IsInTask || Tornados.Count == 0 || pc == null) return;
|
||||
|
||||
var Random = IRandom.Instance;
|
||||
var NotifyString = GetString("TeleportedByTornado");
|
||||
var now = GetTimeStamp();
|
||||
var tornadoRange = TornadoRange.GetFloat();
|
||||
var tornadoDuration = TornadoDuration.GetInt();
|
||||
|
||||
foreach (var tornadoPc in playerIdList.Select(x => GetPlayerById(x)).Where(x => x.PlayerId != pc.PlayerId).ToArray())
|
||||
if (!pc.Is(CustomRoles.Tornado))
|
||||
{
|
||||
var Random = IRandom.Instance;
|
||||
var NotifyString = GetString("TeleportedByTornado");
|
||||
var tornadoRange = TornadoRange.GetFloat();
|
||||
var tornadoDuration = TornadoDuration.GetInt();
|
||||
|
||||
foreach (var tornado in Tornados)
|
||||
{
|
||||
if (Vector2.Distance(tornado.Key.LOCATION, pc.Pos()) <= tornadoRange)
|
||||
{
|
||||
if (Random.Next(0, 100) < 50)
|
||||
if (!CanUseMap || Random.Next(0, 100) < 50)
|
||||
{
|
||||
pc.TPtoRndVent();
|
||||
}
|
||||
@@ -132,9 +144,11 @@ namespace TOHE.Roles.Crewmate
|
||||
SendRPCAddTornado(false, tornado.Key.LOCATION, tornado.Key.ROOM_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
if (tornadoPc == null || LastNotify >= now || !tornadoPc.Is(CustomRoles.Tornado) || tornadoPc.HasAbilityCD()) return;
|
||||
NotifyRoles(SpecifySeer: tornadoPc, SpecifyTarget: tornadoPc);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LastNotify >= now || pc.HasAbilityCD()) return;
|
||||
NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
|
||||
LastNotify = now;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ public static class Councillor
|
||||
MurderMsg(pc, $"/tl {PlayerId}", true);
|
||||
}
|
||||
|
||||
private static void CouncillorOnClick(byte playerId, MeetingHud __instance)
|
||||
private static void CouncillorOnClick(byte playerId/*, MeetingHud __instance*/)
|
||||
{
|
||||
Logger.Msg($"Click: ID {playerId}", "Councillor UI");
|
||||
var pc = Utils.GetPlayerById(playerId);
|
||||
@@ -270,7 +270,7 @@ public static class Councillor
|
||||
renderer.sprite = CustomButton.Get("MeetingKillButton");
|
||||
PassiveButton button = targetBox.GetComponent<PassiveButton>();
|
||||
button.OnClick.RemoveAllListeners();
|
||||
button.OnClick.AddListener((Action)(() => CouncillorOnClick(pva.TargetPlayerId, __instance)));
|
||||
button.OnClick.AddListener((Action)(() => CouncillorOnClick(pva.TargetPlayerId/*, __instance*/)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ namespace TOHE.Roles.Impostor
|
||||
public static void SetupCustomOption()
|
||||
{
|
||||
SetupRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Librarian);
|
||||
Radius = FloatOptionItem.Create(Id + 5, "LibrarianRadius", new(0.5f, 5f, 0.5f), 2f, TabGroup.ImpostorRoles, false)
|
||||
Radius = FloatOptionItem.Create(Id + 5, "LibrarianRadius", new(0.5f, 5f, 0.5f), 3f, TabGroup.ImpostorRoles, false)
|
||||
.SetParent(CustomRoleSpawnChances[CustomRoles.Librarian])
|
||||
.SetValueFormat(OptionFormat.Multiplier);
|
||||
ShowSSAnimation = BooleanOptionItem.Create(Id + 6, "LibrarianShowSSAnimation", false, TabGroup.ImpostorRoles, false)
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace TOHE.Roles.Neutral
|
||||
|| ((target.Is(CustomRoles.Undead) || target.Is(CustomRoles.Necromancer) || target.Is(CustomRoles.Deathknight)) && CanKillUndead.GetBool())
|
||||
|| ((target.Is(CustomRoles.Lovers) || target.Is(CustomRoles.Ntr)) && CanKillLovers.GetBool())
|
||||
|| ((target.Is(CustomRoles.Romantic) || target.Is(CustomRoles.RuthlessRomantic) || target.Is(CustomRoles.VengefulRomantic)
|
||||
|| Romantic.BetPlayer.ContainsValue(target.PlayerId)) && CanKillLovers.GetBool())
|
||||
|| Romantic.PartnerId == target.PlayerId) && CanKillLovers.GetBool())
|
||||
|| ((target.Is(CustomRoles.Sidekick) || target.Is(CustomRoles.Jackal) || target.Is(CustomRoles.Recruit)) && CanKillSidekicks.GetBool())
|
||||
|| (target.Is(CustomRoles.Egoist) && CanKillEgoists.GetBool())
|
||||
|| ((target.Is(CustomRoles.Contagious) || target.Is(CustomRoles.Virus)) && CanKillContagious.GetBool())
|
||||
@@ -91,8 +91,8 @@ namespace TOHE.Roles.Neutral
|
||||
}
|
||||
if (MisFireKillTarget.GetBool() && killer.RpcCheckAndMurder(target, true))
|
||||
{
|
||||
killer.Kill(target);
|
||||
target.SetRealKiller(killer);
|
||||
killer.Kill(target);
|
||||
target.Data.IsDead = true;
|
||||
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Misfire;
|
||||
}
|
||||
|
||||
+133
-156
@@ -11,12 +11,14 @@ namespace TOHE.Roles.Neutral;
|
||||
public static class Romantic
|
||||
{
|
||||
private static readonly int Id = 9850;
|
||||
public static List<byte> playerIdList = [];
|
||||
|
||||
private static readonly int MaxBetTimes = 1;
|
||||
public static bool isProtect;
|
||||
public static bool isRomanticAlive = true;
|
||||
public static bool isPartnerProtected;
|
||||
public static byte RomanticId = byte.MaxValue;
|
||||
public static PlayerControl Romantic_ = null;
|
||||
public static bool HasPickedPartner => PartnerId != byte.MaxValue;
|
||||
public static byte PartnerId = byte.MaxValue;
|
||||
public static PlayerControl Partner = null;
|
||||
|
||||
public static bool IsPartnerProtected = false;
|
||||
|
||||
public static OptionItem BetCooldown;
|
||||
private static OptionItem ProtectCooldown;
|
||||
@@ -28,9 +30,6 @@ public static class Romantic
|
||||
public static OptionItem RuthlessKCD;
|
||||
public static OptionItem RuthlessCanVent;
|
||||
|
||||
public static Dictionary<byte, int> BetTimes = [];
|
||||
public static Dictionary<byte, byte> BetPlayer = [];
|
||||
|
||||
public static void SetupCustomOption()
|
||||
{
|
||||
SetupSingleRoleOptions(Id, TabGroup.NeutralRoles, CustomRoles.Romantic, 1, zeroOne: false);
|
||||
@@ -51,103 +50,92 @@ public static class Romantic
|
||||
}
|
||||
public static void Init()
|
||||
{
|
||||
playerIdList = [];
|
||||
BetTimes = [];
|
||||
BetPlayer = [];
|
||||
isProtect = false;
|
||||
isPartnerProtected = false;
|
||||
RomanticId = byte.MaxValue;
|
||||
Romantic_ = null;
|
||||
PartnerId = byte.MaxValue;
|
||||
Partner = null;
|
||||
IsPartnerProtected = false;
|
||||
}
|
||||
public static void Add(byte playerId)
|
||||
{
|
||||
playerIdList.Add(playerId);
|
||||
BetTimes.Add(playerId, MaxBetTimes);
|
||||
RomanticId = playerId;
|
||||
Romantic_ = Utils.GetPlayerById(playerId);
|
||||
|
||||
if (!AmongUsClient.Instance.AmHost) return;
|
||||
if (!Main.ResetCamPlayerList.Contains(playerId))
|
||||
Main.ResetCamPlayerList.Add(playerId);
|
||||
}
|
||||
public static bool IsEnable => playerIdList.Count > 0;
|
||||
private static void SendRPC(byte playerId)
|
||||
public static bool IsEnable => RomanticId != byte.MaxValue;
|
||||
private static void SendRPC()
|
||||
{
|
||||
if (!IsEnable || !Utils.DoRPC) return;
|
||||
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SyncRomanticTarget, SendOption.Reliable, -1);
|
||||
writer.Write(playerId);
|
||||
writer.Write(BetTimes.TryGetValue(playerId, out var times) ? times : MaxBetTimes);
|
||||
writer.Write(BetPlayer.TryGetValue(playerId, out var player) ? player : byte.MaxValue);
|
||||
writer.Write(PartnerId);
|
||||
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
||||
}
|
||||
public static void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
byte PlayerId = reader.ReadByte();
|
||||
int Times = reader.ReadInt32();
|
||||
byte Target = reader.ReadByte();
|
||||
BetTimes.Remove(PlayerId);
|
||||
BetPlayer.Remove(PlayerId);
|
||||
BetTimes.Add(PlayerId, Times);
|
||||
if (Target != byte.MaxValue)
|
||||
BetPlayer.Add(PlayerId, Target);
|
||||
PartnerId = reader.ReadByte();
|
||||
Partner = Utils.GetPlayerById(PartnerId);
|
||||
}
|
||||
public static void SetKillCooldown(byte id)
|
||||
public static void SetKillCooldown()
|
||||
{
|
||||
float beforeCD = Main.AllPlayerKillCooldown.TryGetValue(id, out var kcd) ? kcd : 0f;
|
||||
if (BetTimes.TryGetValue(id, out var times) && times < 1) Main.AllPlayerKillCooldown[id] = ProtectCooldown.GetFloat();
|
||||
else Main.AllPlayerKillCooldown[id] = BetCooldown.GetFloat();
|
||||
if (beforeCD != Main.AllPlayerKillCooldown[id]) Utils.GetPlayerById(id)?.SyncSettings();
|
||||
//float cd = BetCooldown.GetFloat();
|
||||
//cd += Main.AllPlayerControls.Count(x => !x.IsAlive()) * BetCooldownIncrese.GetFloat();
|
||||
//cd = Math.Min(cd, MaxBetCooldown.GetFloat());
|
||||
//Main.AllPlayerKillCooldown[id] = cd;
|
||||
float beforeCD = Main.AllPlayerKillCooldown.TryGetValue(RomanticId, out var kcd) ? kcd : 0f;
|
||||
|
||||
if (HasPickedPartner) Main.AllPlayerKillCooldown[RomanticId] = ProtectCooldown.GetFloat();
|
||||
else Main.AllPlayerKillCooldown[RomanticId] = BetCooldown.GetFloat();
|
||||
|
||||
if (beforeCD != Main.AllPlayerKillCooldown[RomanticId]) Romantic_?.SyncSettings();
|
||||
}
|
||||
public static bool KnowRole(PlayerControl player, PlayerControl target)
|
||||
{
|
||||
if (!KnowTargetRole.GetBool()) return false;
|
||||
return player.Is(CustomRoles.Romantic) && BetPlayer.TryGetValue(player.PlayerId, out var tar) && tar == target.PlayerId;
|
||||
return player.Is(CustomRoles.Romantic) && PartnerId == target.PlayerId;
|
||||
}
|
||||
public static bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (killer.PlayerId == target.PlayerId) return true;
|
||||
//if (BetPlayer.TryGetValue(killer.PlayerId, out var tar) && tar == target.PlayerId) return false;
|
||||
if (!BetTimes.TryGetValue(killer.PlayerId, out var times) || times < 1) isProtect = true;
|
||||
if (killer == null || target == null || killer.PlayerId == target.PlayerId || killer.PlayerId != RomanticId) return true;
|
||||
|
||||
if (!isProtect)
|
||||
if (!HasPickedPartner)
|
||||
{
|
||||
BetTimes[killer.PlayerId]--;
|
||||
PartnerId = target.PlayerId;
|
||||
Partner = Utils.GetPlayerById(PartnerId);
|
||||
|
||||
BetPlayer.Remove(killer.PlayerId);
|
||||
BetPlayer.Add(killer.PlayerId, target.PlayerId);
|
||||
SendRPC(killer.PlayerId);
|
||||
SendRPC();
|
||||
|
||||
killer.ResetKillCooldown();
|
||||
killer.SetKillCooldown();
|
||||
killer.RPCPlayCustomSound("Bet");
|
||||
Romantic_.ResetKillCooldown();
|
||||
Romantic_.SetKillCooldown();
|
||||
Romantic_.RPCPlayCustomSound("Bet");
|
||||
|
||||
killer.Notify(GetString("RomanticBetPlayer"));
|
||||
if (BetTargetKnowRomantic.GetBool())
|
||||
target.Notify(Utils.ColorString(Utils.GetRoleColor(CustomRoles.Romantic), GetString("RomanticBetOnYou")));
|
||||
Romantic_.Notify(GetString("RomanticBetPlayer"));
|
||||
if (BetTargetKnowRomantic.GetBool()) target.Notify(Utils.ColorString(Utils.GetRoleColor(CustomRoles.Romantic), GetString("RomanticBetOnYou")));
|
||||
|
||||
Logger.Info($"Partner picked:{killer.GetNameWithRole().RemoveHtmlTags()} => {target.GetNameWithRole().RemoveHtmlTags()}", "Romantic");
|
||||
Logger.Info($"Partner picked: {Romantic_.GetNameWithRole().RemoveHtmlTags()} => {target.GetNameWithRole().RemoveHtmlTags()}", "Romantic");
|
||||
}
|
||||
else
|
||||
else if (!IsPartnerProtected)
|
||||
{
|
||||
if (!isPartnerProtected && BetPlayer.TryGetValue(killer.PlayerId, out var originalTarget))
|
||||
IsPartnerProtected = true;
|
||||
|
||||
Romantic_.ResetKillCooldown();
|
||||
Romantic_.SetKillCooldown();
|
||||
Romantic_.RPCPlayCustomSound("Shield");
|
||||
|
||||
Romantic_.Notify(GetString("RomanticProtectPartner"));
|
||||
Partner.Notify(GetString("RomanticIsProtectingYou"));
|
||||
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
var tpc = Utils.GetPlayerById(originalTarget);
|
||||
isPartnerProtected = true;
|
||||
killer.ResetKillCooldown();
|
||||
killer.SetKillCooldown();
|
||||
killer.RPCPlayCustomSound("Shield");
|
||||
killer.Notify(GetString("RomanticProtectPartner"));
|
||||
tpc.Notify(GetString("RomanticIsProtectingYou"));
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
if (!tpc.IsAlive()) return;
|
||||
isPartnerProtected = false;
|
||||
if (!GameStates.IsInTask) return;
|
||||
killer.Notify(GetString("ProtectingOver"));
|
||||
tpc.Notify(GetString("ProtectingOver"));
|
||||
killer.SetKillCooldown();
|
||||
}, ProtectDuration.GetFloat());
|
||||
}
|
||||
if (!Partner.IsAlive()) return;
|
||||
|
||||
IsPartnerProtected = false;
|
||||
|
||||
if (!GameStates.IsInTask) return;
|
||||
|
||||
Romantic_.Notify(GetString("ProtectingOver"));
|
||||
Partner.Notify(GetString("ProtectingOver"));
|
||||
|
||||
Romantic_.SetKillCooldown();
|
||||
}, ProtectDuration.GetFloat());
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -157,123 +145,117 @@ public static class Romantic
|
||||
if (!seer.Is(CustomRoles.Romantic))
|
||||
{
|
||||
if (!BetTargetKnowRomantic.GetBool()) return string.Empty;
|
||||
return (BetPlayer.TryGetValue(target.PlayerId, out var x) && seer.PlayerId == x) ?
|
||||
Utils.ColorString(Utils.GetRoleColor(CustomRoles.Romantic), "♥") : string.Empty;
|
||||
|
||||
return PartnerId == seer.PlayerId
|
||||
? Utils.ColorString(Utils.GetRoleColor(CustomRoles.Romantic), "♥")
|
||||
: string.Empty;
|
||||
}
|
||||
var GetValue = BetPlayer.TryGetValue(seer.PlayerId, out var targetId);
|
||||
return GetValue && targetId == target.PlayerId ? Utils.ColorString(Utils.GetRoleColor(CustomRoles.Romantic), "♥") : string.Empty;
|
||||
}
|
||||
public static string GetTargetText(byte playerId)
|
||||
{
|
||||
var player = Utils.GetPlayerById(playerId);
|
||||
if (player == null) return null;
|
||||
return Utils.ColorString(BetTimes.TryGetValue(playerId, out var timesV1) && timesV1 >= 1 ? Color.white : Utils.GetRoleColor(CustomRoles.Romantic), $"{(BetTimes.TryGetValue(playerId, out var timesV2) && timesV2 >= 1 && timesV2 >= 1 ? "PICK PARTNER" : "♥")}");
|
||||
}
|
||||
public static void OnReportDeadBody()
|
||||
{
|
||||
isPartnerProtected = false;
|
||||
}
|
||||
public static void ChangeRole(byte playerId)
|
||||
{
|
||||
var partner = Utils.GetPlayerById(playerId);
|
||||
if (partner == null) return;
|
||||
var partnerRole = partner.GetCustomRole();
|
||||
|
||||
byte romanticId = BetPlayer.First(x => x.Value == playerId).Key;
|
||||
var romantic = Utils.GetPlayerById(romanticId);
|
||||
if (romantic == null) return;
|
||||
return PartnerId == target.PlayerId
|
||||
? Utils.ColorString(Utils.GetRoleColor(CustomRoles.Romantic), "♥")
|
||||
: string.Empty;
|
||||
}
|
||||
public static string GetTargetText(byte playerId) => playerId != RomanticId
|
||||
? null
|
||||
: Utils.ColorString(!HasPickedPartner ? Color.white : Utils.GetRoleColor(CustomRoles.Romantic), $"{(!HasPickedPartner ? "PICK PARTNER" : "♥")}");
|
||||
public static void OnReportDeadBody() => IsPartnerProtected = false;
|
||||
public static void ChangeRole()
|
||||
{
|
||||
if (Partner == null || Romantic_ == null) return;
|
||||
|
||||
var killer = partner.GetRealKiller();
|
||||
var partnerRole = Partner.GetCustomRole();
|
||||
var killer = Partner.GetRealKiller();
|
||||
|
||||
if (killer?.PlayerId == romanticId) // If the Romantic killed their partner, they also die
|
||||
if (killer?.PlayerId == RomanticId) // If the Romantic killed their Partner, they also die
|
||||
{
|
||||
if (!romantic.IsAlive()) return;
|
||||
Logger.Info("Romantic killed their own partner, Romantic suicides", "Romantic");
|
||||
romantic.Suicide(PlayerState.DeathReason.FollowingSuicide, partner);
|
||||
if (!Romantic_.IsAlive()) return;
|
||||
Logger.Info("Romantic killed their own Partner, Romantic suicides", "Romantic");
|
||||
Romantic_.Suicide(PlayerState.DeathReason.FollowingSuicide, Partner);
|
||||
return;
|
||||
}
|
||||
else if (partnerRole.IsNonNK() || killer == null) // If partner is NNK or died by themselves, Romantic becomes Ruthless Romantic
|
||||
else if ((partnerRole.IsNonNK() && partnerRole is not CustomRoles.Romantic and not CustomRoles.VengefulRomantic and not CustomRoles.RuthlessRomantic) || killer == null || Main.PlayerStates[PartnerId].IsSuicide) // If Partner is NNK or died by themselves, Romantic becomes Ruthless Romantic
|
||||
{
|
||||
Logger.Info($"NK Romantic Partner Died / Partner killer is null => changing {romantic.GetNameWithRole().RemoveHtmlTags()} to Ruthless Romantic", "Romantic");
|
||||
romantic.RpcSetCustomRole(CustomRoles.RuthlessRomantic);
|
||||
RuthlessRomantic.Add(romanticId);
|
||||
Logger.Info($"NNK Romantic Partner Died ({partnerRole.IsNonNK()}) / Partner killer is null ({killer == null}) / Partner commited Suicide ({Main.PlayerStates[PartnerId].IsSuicide}) => Changing {Romantic_.GetNameWithRole().RemoveHtmlTags()} to Ruthless Romantic", "Romantic");
|
||||
Romantic_.RpcSetCustomRole(CustomRoles.RuthlessRomantic);
|
||||
RuthlessRomantic.Add(RomanticId);
|
||||
}
|
||||
else if (partner.HasKillButton() || partnerRole.IsNK() || partnerRole.IsCK()) // If partner has a kill button (NK or CK), Romantic becomes the role they were
|
||||
else if (Partner.Is(Team.Impostor)) // If Partner is Imp, Romantic joins imp team as Refugee
|
||||
{
|
||||
Logger.Info($"Impostor Romantic Partner Died => Changing {Romantic_.GetNameWithRole()} to Refugee", "Romantic");
|
||||
Romantic_.RpcSetCustomRole(CustomRoles.Refugee);
|
||||
}
|
||||
else if (Partner.HasKillButton() || partnerRole.IsNK() || partnerRole.IsTasklessCrewmate()) // If Partner has a kill button (NK or CK), Romantic becomes the role they were
|
||||
{
|
||||
try
|
||||
{
|
||||
romantic.RpcSetCustomRole(partnerRole);
|
||||
Utils.AddRoles(romantic.PlayerId, partnerRole);
|
||||
Logger.Info($"Romantic Partner With Kill Button Died => changing {romantic.GetNameWithRole()} to {partner.GetAllRoleName().RemoveHtmlTags()}", "Romantic");
|
||||
Romantic_.RpcSetCustomRole(partnerRole);
|
||||
Utils.AddRoles(RomanticId, partnerRole);
|
||||
HudManager.Instance.SetHudActive(Romantic_, Romantic_.Data.Role, !GameStates.IsMeeting);
|
||||
Main.PlayerStates[RomanticId].RemoveSubRole(CustomRoles.NotAssigned);
|
||||
Logger.Info($"Romantic Partner With Kill Button Died => Changing {Romantic_.GetNameWithRole()} to {Partner.GetAllRoleName().RemoveHtmlTags()}", "Romantic");
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Error($"Romantic Partner With Kill Button Died => changing {romantic.GetNameWithRole()} to {partner.GetAllRoleName().RemoveHtmlTags()} : FAILED ----> changing to Ruthless Romantic instead", "Romantic");
|
||||
romantic.RpcSetCustomRole(CustomRoles.RuthlessRomantic);
|
||||
RuthlessRomantic.Add(romanticId);
|
||||
Logger.Error($"Romantic Partner With Kill Button Died => Changing {Romantic_.GetNameWithRole()} to {Partner.GetAllRoleName().RemoveHtmlTags()} : FAILED ----> changing to Ruthless Romantic instead", "Romantic");
|
||||
Romantic_.RpcSetCustomRole(CustomRoles.RuthlessRomantic);
|
||||
RuthlessRomantic.Add(RomanticId);
|
||||
}
|
||||
}
|
||||
else if (partner.Is(Team.Impostor)) // If partner is Imp, Romantic joins imp team as Refugee
|
||||
else // In every other scenario, Romantic becomes Vengeful Romantic and must kill the killer of their Partner
|
||||
{
|
||||
Logger.Info($"Impostor Romantic Partner Died => changing {romantic.GetNameWithRole()} to Refugee", "Romantic");
|
||||
romantic.RpcSetCustomRole(CustomRoles.Refugee);
|
||||
}
|
||||
else // In every other scenario, Romantic becomes Vengeful Romantic and must kill the killer of their partner
|
||||
{
|
||||
_ = new LateTask(() =>
|
||||
{
|
||||
Logger.Info($"Crew/NNK Romantic Partner Died => changing {romantic.GetNameWithRole()} to Vengeful Romantic", "Romantic");
|
||||
Logger.Info($"Non-Killing Crew Romantic Partner Died => Changing {Romantic_.GetNameWithRole()} to Vengeful Romantic", "Romantic");
|
||||
|
||||
VengefulRomantic.Add(romanticId, killer.PlayerId);
|
||||
VengefulRomantic.SendRPC(romanticId);
|
||||
romantic.RpcSetCustomRole(CustomRoles.VengefulRomantic);
|
||||
}, 0.2f, "Convert to Vengeful romanticId");
|
||||
VengefulRomantic.Add(RomanticId, killer.PlayerId);
|
||||
VengefulRomantic.SendRPC();
|
||||
Romantic_.RpcSetCustomRole(CustomRoles.VengefulRomantic);
|
||||
}
|
||||
|
||||
Utils.NotifyRoles(SpecifySeer: romantic);
|
||||
Utils.NotifyRoles(SpecifyTarget: romantic);
|
||||
Utils.NotifyRoles(SpecifySeer: Romantic_);
|
||||
Utils.NotifyRoles(SpecifyTarget: Romantic_);
|
||||
|
||||
romantic.ResetKillCooldown();
|
||||
romantic.SetKillCooldown();
|
||||
Romantic_.ResetKillCooldown();
|
||||
Romantic_.SetKillCooldown();
|
||||
|
||||
romantic.SyncSettings();
|
||||
Romantic_.SyncSettings();
|
||||
}
|
||||
}
|
||||
|
||||
public static class VengefulRomantic
|
||||
{
|
||||
public static List<byte> playerIdList = [];
|
||||
|
||||
public static bool hasKilledKiller;
|
||||
public static Dictionary<byte, byte> VengefulTarget = [];
|
||||
public static byte VengefulRomanticId = byte.MaxValue;
|
||||
public static PlayerControl VengefulRomantic_ = null;
|
||||
|
||||
public static bool HasKilledKiller;
|
||||
public static byte Target = byte.MaxValue;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
playerIdList = [];
|
||||
VengefulTarget = [];
|
||||
hasKilledKiller = false;
|
||||
VengefulRomanticId = byte.MaxValue;
|
||||
VengefulRomantic_ = null;
|
||||
Target = byte.MaxValue;
|
||||
HasKilledKiller = false;
|
||||
}
|
||||
public static void Add(byte playerId, byte killerId = byte.MaxValue)
|
||||
{
|
||||
playerIdList.Add(playerId);
|
||||
VengefulTarget.Add(playerId, killerId);
|
||||
VengefulRomanticId = playerId;
|
||||
VengefulRomantic_ = Utils.GetPlayerById(playerId);
|
||||
|
||||
Target = killerId;
|
||||
|
||||
if (!AmongUsClient.Instance.AmHost) return;
|
||||
if (!Main.ResetCamPlayerList.Contains(playerId))
|
||||
Main.ResetCamPlayerList.Add(playerId);
|
||||
}
|
||||
public static bool IsEnable => playerIdList.Count > 0;
|
||||
|
||||
public static bool CanUseKillButton(PlayerControl player) => !player.Data.IsDead && !hasKilledKiller;
|
||||
|
||||
public static bool IsEnable => VengefulRomanticId != byte.MaxValue;
|
||||
public static bool CanUseKillButton(PlayerControl player) => !player.Data.IsDead && !HasKilledKiller;
|
||||
public static bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (killer.PlayerId == target.PlayerId) return true;
|
||||
if (killer.PlayerId == target.PlayerId || killer.PlayerId != VengefulRomanticId) return true;
|
||||
|
||||
if (VengefulTarget.TryGetValue(killer.PlayerId, out var PartnerKiller) && target.PlayerId == PartnerKiller)
|
||||
if (target.PlayerId == Target)
|
||||
{
|
||||
hasKilledKiller = true;
|
||||
Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
|
||||
HasKilledKiller = true;
|
||||
Utils.NotifyRoles(SpecifySeer: VengefulRomantic_, SpecifyTarget: target);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -286,24 +268,19 @@ public static class VengefulRomantic
|
||||
{
|
||||
var player = Utils.GetPlayerById(playerId);
|
||||
if (player == null) return null;
|
||||
return Utils.ColorString(hasKilledKiller ? Color.green : Utils.GetRoleColor(CustomRoles.VengefulRomantic), $"{(hasKilledKiller ? "✓" : "☹️")}");
|
||||
return Utils.ColorString(HasKilledKiller ? Color.green : Utils.GetRoleColor(CustomRoles.VengefulRomantic), $"{(HasKilledKiller ? "✓" : "☹️")}");
|
||||
}
|
||||
public static void SendRPC(byte playerId)
|
||||
public static void SendRPC()
|
||||
{
|
||||
if (!IsEnable || !Utils.DoRPC) return;
|
||||
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SyncVengefulRomanticTarget, SendOption.Reliable, -1);
|
||||
writer.Write(playerId);
|
||||
//writer.Write(BetTimes.TryGetValue(playerId, out var times) ? times : MaxBetTimes);
|
||||
writer.Write(VengefulTarget.TryGetValue(playerId, out var player) ? player : byte.MaxValue);
|
||||
writer.Write(Target);
|
||||
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
||||
}
|
||||
public static void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
byte PlayerId = reader.ReadByte();
|
||||
byte Target = reader.ReadByte();
|
||||
VengefulTarget.Remove(PlayerId);
|
||||
if (Target != byte.MaxValue)
|
||||
VengefulTarget.Add(PlayerId, Target);
|
||||
byte target = reader.ReadByte();
|
||||
Target = target;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ public class Main : BasePlugin
|
||||
public static ConfigEntry<string> DebugKeyInput { get; private set; }
|
||||
public static readonly string MainMenuText = " ";
|
||||
public const string PluginGuid = "com.gurge44.toheplus";
|
||||
public const string PluginVersion = "2.3.1";
|
||||
public const string PluginDisplayVersion = "2.3.1";
|
||||
public const string PluginVersion = "2.4.0";
|
||||
public const string PluginDisplayVersion = "2.4.0";
|
||||
public static readonly string SupportedAUVersion = "2023.10.24";
|
||||
public const int PluginCreate = 3;
|
||||
public const bool Canary = false;
|
||||
@@ -346,6 +346,7 @@ public class Main : BasePlugin
|
||||
{CustomRoles.Express, "#00ffff"},
|
||||
{CustomRoles.NiceEraser, "#00a5ff"},
|
||||
{CustomRoles.TaskManager, "#00ffa5"},
|
||||
{CustomRoles.Beacon, "#a3fdff"},
|
||||
{CustomRoles.Rabbit, "#88d2ff"},
|
||||
{CustomRoles.Shiftguard, "#eb34cc"},
|
||||
{CustomRoles.Mole, "#00ff80"},
|
||||
@@ -519,6 +520,7 @@ public class Main : BasePlugin
|
||||
{CustomRoles.Watcher, "#800080"},
|
||||
{CustomRoles.Sleuth, "#30221c"},
|
||||
{CustomRoles.Flashman, "#ff8400"},
|
||||
{CustomRoles.Haste, "#f0ec22"},
|
||||
{CustomRoles.Busy, "#32a852"},
|
||||
{CustomRoles.Truant, "#eb3467"},
|
||||
{CustomRoles.Disco, "#eb34e8"},
|
||||
@@ -583,7 +585,9 @@ public class Main : BasePlugin
|
||||
//FFA
|
||||
{CustomRoles.Killer, "#00ffff"},
|
||||
//Move And Stop
|
||||
{CustomRoles.Tasker, "#00ffa5"}
|
||||
{CustomRoles.Tasker, "#00ffa5"},
|
||||
//Hot Potato
|
||||
{CustomRoles.Potato, "#e8cd46"}
|
||||
};
|
||||
Enum.GetValues(typeof(CustomRoles)).Cast<CustomRoles>().Where(x => x.GetCustomRoleTypes() == CustomRoleTypes.Impostor).Do(x => roleColors.TryAdd(x, "#ff1919"));
|
||||
}
|
||||
@@ -749,6 +753,7 @@ public enum CustomRoles
|
||||
Altruist,
|
||||
Analyzer,
|
||||
Autocrat,
|
||||
Beacon,
|
||||
Benefactor,
|
||||
Bodyguard,
|
||||
CameraMan,
|
||||
@@ -916,7 +921,10 @@ public enum CustomRoles
|
||||
KB_Normal,
|
||||
//FFA
|
||||
Killer,
|
||||
//MoveAndStop
|
||||
Tasker,
|
||||
//HotPotato
|
||||
Potato,
|
||||
|
||||
//GM
|
||||
GM,
|
||||
@@ -952,6 +960,7 @@ public enum CustomRoles
|
||||
Glow,
|
||||
Gravestone,
|
||||
Guesser,
|
||||
Haste,
|
||||
Knighted,
|
||||
LastImpostor,
|
||||
Lazy,
|
||||
|
||||
Reference in new issue
Block a user