diff --git a/GameMode/FFAManager.cs b/GameMode/FFAManager.cs new file mode 100644 index 000000000..ddaa523be --- /dev/null +++ b/GameMode/FFAManager.cs @@ -0,0 +1,377 @@ +using HarmonyLib; +using Hazel; +using System.Collections.Generic; +using System.Linq; +using TOHE.Modules; +using UnityEngine; +using static TOHE.Translator; + +namespace TOHE; + +internal static class FFAManager +{ + private static Dictionary FFAShieldedList = new(); + private static Dictionary FFAIncreasedSpeedList = new(); + private static Dictionary FFADecreasedSpeedList = new(); + public static Dictionary FFALowerVisionList = new(); + + private static Dictionary originalSpeed = new(); + public static Dictionary KBScore = new(); + public static int RoundTime = new(); + + //Options + public static OptionItem FFA_GameTime; + public static OptionItem FFA_KCD; + public static OptionItem FFA_LowerVision; + public static OptionItem FFA_IncreasedSpeed; + public static OptionItem FFA_DecreasedSpeed; + public static OptionItem FFA_ShieldDuration; + public static OptionItem FFA_ModifiedVisionDuration; + public static OptionItem FFA_ModifiedSpeedDuration; + public static OptionItem FFA_DisableVentingWhenTwoPlayersAlive; + public static OptionItem FFA_DisableVentingWhenKCDIsUp; + public static OptionItem FFA_EnableRandomAbilities; + public static OptionItem FFA_EnableRandomTwists; + + public static void SetupCustomOption() + { + FFA_GameTime = IntegerOptionItem.Create(67_223_001, "FFA_GameTime", new(30, 600, 5), 300, TabGroup.GameSettings, false) + .SetGameMode(CustomGameMode.FFA) + .SetColor(new Color32(0, 255, 165, byte.MaxValue)) + .SetValueFormat(OptionFormat.Seconds) + .SetHeader(true); + FFA_KCD = FloatOptionItem.Create(67_223_002, "FFA_KCD", new(1f, 60f, 1f), 10f, TabGroup.GameSettings, false) + .SetGameMode(CustomGameMode.FFA) + .SetColor(new Color32(0, 255, 165, byte.MaxValue)) + .SetValueFormat(OptionFormat.Seconds); + FFA_DisableVentingWhenTwoPlayersAlive = BooleanOptionItem.Create(67_223_003, "FFA_DisableVentingWhenTwoPlayersAlive", true, TabGroup.GameSettings, false) + .SetGameMode(CustomGameMode.FFA) + .SetColor(new Color32(0, 255, 165, byte.MaxValue)); + FFA_DisableVentingWhenKCDIsUp = BooleanOptionItem.Create(67_223_004, "FFA_DisableVentingWhenKCDIsUp", true, TabGroup.GameSettings, false) + .SetGameMode(CustomGameMode.FFA) + .SetColor(new Color32(0, 255, 165, byte.MaxValue)); + FFA_EnableRandomAbilities = BooleanOptionItem.Create(67_223_005, "FFA_EnableRandomAbilities", true, TabGroup.GameSettings, false) + .SetGameMode(CustomGameMode.FFA) + .SetColor(new Color32(0, 255, 165, byte.MaxValue)); + FFA_ShieldDuration = FloatOptionItem.Create(67_223_006, "FFA_ShieldDuration", new(1f, 70f, 1f), 7f, TabGroup.GameSettings, false) + .SetGameMode(CustomGameMode.FFA) + .SetColor(new Color32(0, 255, 165, byte.MaxValue)) + .SetValueFormat(OptionFormat.Seconds); + FFA_IncreasedSpeed = FloatOptionItem.Create(67_223_007, "FFA_IncreasedSpeed", new(0.1f, 5f, 0.1f), 1.5f, TabGroup.GameSettings, false) + .SetGameMode(CustomGameMode.FFA) + .SetColor(new Color32(0, 255, 165, byte.MaxValue)) + .SetValueFormat(OptionFormat.Multiplier); + FFA_DecreasedSpeed = FloatOptionItem.Create(67_223_008, "FFA_DecreasedSpeed", new(0.1f, 5f, 0.1f), 1f, TabGroup.GameSettings, false) + .SetGameMode(CustomGameMode.FFA) + .SetColor(new Color32(0, 255, 165, byte.MaxValue)) + .SetValueFormat(OptionFormat.Multiplier); + FFA_ModifiedSpeedDuration = FloatOptionItem.Create(67_223_009, "FFA_ModifiedSpeedDuration", new(1f, 60f, 1f), 10f, TabGroup.GameSettings, false).SetGameMode(CustomGameMode.FFA).SetColor(new Color32(0, 255, 165, byte.MaxValue)) + .SetValueFormat(OptionFormat.Seconds); + FFA_LowerVision = FloatOptionItem.Create(67_223_010, "FFA_LowerVision", new(0f, 1f, 0.05f), 0.5f, TabGroup.GameSettings, false).SetGameMode(CustomGameMode.FFA).SetColor(new Color32(0, 255, 165, byte.MaxValue)) + .SetValueFormat(OptionFormat.Multiplier); + FFA_ModifiedVisionDuration = FloatOptionItem.Create(67_223_011, "FFA_ModifiedVisionDuration", new(1f, 70f, 1f), 5f, TabGroup.GameSettings, false).SetGameMode(CustomGameMode.FFA).SetColor(new Color32(0, 255, 165, byte.MaxValue)) + .SetValueFormat(OptionFormat.Seconds); + FFA_EnableRandomTwists = BooleanOptionItem.Create(67_223_012, "FFA_EnableRandomTwists", true, TabGroup.GameSettings, false) + .SetGameMode(CustomGameMode.FFA) + .SetColor(new Color32(0, 255, 165, byte.MaxValue)); + } + + public static void Init() + { + if (Options.CurrentGameMode != CustomGameMode.FFA) return; + + FFADecreasedSpeedList = new(); + FFAIncreasedSpeedList = new(); + FFALowerVisionList = new(); + FFAShieldedList = new(); + + originalSpeed = new(); + KBScore = new(); + RoundTime = FFA_GameTime.GetInt() + 8; + + foreach (var pc in Main.AllAlivePlayerControls) + { + //PlayerHPMax.TryAdd(pc.PlayerId, KB_HPMax.GetFloat()); + //PlayerHP.TryAdd(pc.PlayerId, KB_HPMax.GetFloat()); + //PlayerHPReco.TryAdd(pc.PlayerId, KB_RecoverPerSecond.GetFloat()); + //PlayerATK.TryAdd(pc.PlayerId, KB_ATK.GetFloat()); + //PlayerDF.TryAdd(pc.PlayerId, 0f); + + KBScore.TryAdd(pc.PlayerId, 0); + } + } + private static void SendRPCSyncFFAPlayer(byte playerId) + { + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SyncFFAPlayer, SendOption.Reliable, -1); + writer.Write(playerId); + writer.Write(KBScore[playerId]); + AmongUsClient.Instance.FinishRpcImmediately(writer); + } + public static void ReceiveRPCSyncFFAPlayer(MessageReader reader) + { + byte PlayerId = reader.ReadByte(); + KBScore[PlayerId] = reader.ReadInt32(); + } + public static void SendRPCSyncNameNotify(PlayerControl pc) + { + if (pc.AmOwner || !pc.IsModClient()) return; + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SyncFFANameNotify, SendOption.Reliable, pc.GetClientId()); + if (NameNotify.ContainsKey(pc.PlayerId)) + writer.Write(NameNotify[pc.PlayerId].Item1); + else writer.Write(string.Empty); + AmongUsClient.Instance.FinishRpcImmediately(writer); + } + public static void ReceiveRPCSyncNameNotify(MessageReader reader) + { + var name = reader.ReadString(); + NameNotify.Remove(PlayerControl.LocalPlayer.PlayerId); + if (name != null && name != string.Empty) + NameNotify.Add(PlayerControl.LocalPlayer.PlayerId, (name, 0)); + } + public static Dictionary NameNotify = new(); + public static void GetNameNotify(PlayerControl player, ref string name) + { + if (Options.CurrentGameMode != CustomGameMode.FFA || player == null) return; + if (NameNotify.ContainsKey(player.PlayerId)) + { + name = NameNotify[player.PlayerId].Item1; + return; + } + } + public static string GetDisplayScore(byte playerId) + { + int rank = GetRankOfScore(playerId); + string score = KBScore.TryGetValue(playerId, out var s) ? $"{s}" : "Invalid"; + string text = string.Format(GetString("KBDisplayScore"), rank.ToString(), score); + Color color = Utils.GetRoleColor(CustomRoles.Killer); + return Utils.ColorString(color, text); + } + public static int GetRankOfScore(byte playerId) + { + try + { + int ms = KBScore[playerId]; + int rank = 1 + KBScore.Values.Where(x => x > ms).Count(); + rank += KBScore.Where(x => x.Value == ms).ToList().IndexOf(new(playerId, ms)); + return rank; + } + catch + { + return Main.AllPlayerControls.Count(); + } + } + public static string GetHudText() + { + return string.Format(GetString("KBTimeRemain"), RoundTime.ToString()); + } + public static void OnPlayerAttack(PlayerControl killer, PlayerControl target) + { + if (killer == null || target == null || Options.CurrentGameMode != CustomGameMode.FFA) return; + if (target.inVent) return; + if (FFAShieldedList.ContainsKey(target.PlayerId)) return; + + OnPlayerKill(killer); + + RPC.PlaySoundRPC(target.PlayerId, Sounds.KillSound); + if (!target.IsModClient() && !target.AmOwner) + target.RpcGuardAndKill(colorId: 5); + + SendRPCSyncFFAPlayer(target.PlayerId); + + if (Main.AllAlivePlayerControls.Count() <= 3) + { + foreach (var pc in Main.AllAlivePlayerControls.Where(a => a.PlayerId != killer.PlayerId && a.PlayerId != target.PlayerId && a.IsAlive())) + { + TargetArrow.Add(killer.PlayerId, pc.PlayerId); + } + } + + if (FFA_EnableRandomAbilities.GetBool()) + { + byte EffectType = (byte)HashRandom.Next(0, 10); + if (EffectType <= 7) // Buff + { + byte EffectID = (byte)HashRandom.Next(0, 3); + switch (EffectID) + { + case 0: + FFAShieldedList.TryAdd(killer.PlayerId, Utils.GetTimeStamp()); + killer.Notify(GetString("FFA-Event-GetShield"), FFA_ShieldDuration.GetFloat()); + Main.AllPlayerKillCooldown[killer.PlayerId] = FFA_KCD.GetFloat(); + if (Main.NormalOptions.MapId == 4) _ = new LateTask(() => { FFAShieldedList.Remove(killer.PlayerId); killer.SyncSettings(); }, FFA_ShieldDuration.GetFloat()); + break; + case 1: + if (FFAIncreasedSpeedList.ContainsKey(killer.PlayerId)) + { + FFAIncreasedSpeedList.Remove(killer.PlayerId); + FFAIncreasedSpeedList.Add(killer.PlayerId, Utils.GetTimeStamp()); + } + else + { + FFAIncreasedSpeedList.TryAdd(killer.PlayerId, Utils.GetTimeStamp()); + originalSpeed.TryAdd(killer.PlayerId, Main.AllPlayerSpeed[killer.PlayerId]); + Main.AllPlayerSpeed[killer.PlayerId] = FFA_IncreasedSpeed.GetFloat(); + } + killer.Notify(GetString("FFA-Event-GetIncreasedSpeed"), FFA_ModifiedSpeedDuration.GetFloat()); + Main.AllPlayerKillCooldown[killer.PlayerId] = FFA_KCD.GetFloat(); + if (Main.NormalOptions.MapId == 4) _ = new LateTask(() => { FFAIncreasedSpeedList.Remove(killer.PlayerId); killer.SyncSettings(); }, FFA_ModifiedSpeedDuration.GetFloat()); + break; + case 2: + Main.AllPlayerKillCooldown[killer.PlayerId] = System.Math.Clamp(FFA_KCD.GetFloat() - 3f, 1f, 60f); + killer.Notify(GetString("FFA-Event-GetLowKCD")); + break; + default: + Main.AllPlayerKillCooldown[killer.PlayerId] = FFA_KCD.GetFloat(); + break; + } + } + else if (EffectType == 8) // De-Buff + { + byte EffectID = (byte)HashRandom.Next(0, 3); + switch (EffectID) + { + case 0: + if (FFADecreasedSpeedList.ContainsKey(killer.PlayerId)) + { + FFADecreasedSpeedList.Remove(killer.PlayerId); + FFADecreasedSpeedList.Add(killer.PlayerId, Utils.GetTimeStamp()); + } + else + { + FFADecreasedSpeedList.TryAdd(killer.PlayerId, Utils.GetTimeStamp()); + originalSpeed.TryAdd(killer.PlayerId, Main.AllPlayerSpeed[killer.PlayerId]); + Main.AllPlayerSpeed[killer.PlayerId] = FFA_DecreasedSpeed.GetFloat(); + } + killer.Notify(GetString("FFA-Event-GetDecreasedSpeed"), FFA_ModifiedSpeedDuration.GetFloat()); + Main.AllPlayerKillCooldown[killer.PlayerId] = FFA_KCD.GetFloat(); + if (Main.NormalOptions.MapId == 4) _ = new LateTask(() => { FFADecreasedSpeedList.Remove(killer.PlayerId); killer.SyncSettings(); }, FFA_ModifiedSpeedDuration.GetFloat()); + break; + case 1: + Main.AllPlayerKillCooldown[killer.PlayerId] = System.Math.Clamp(FFA_KCD.GetFloat() + 3f, 1f, 60f); + killer.Notify(GetString("FFA-Event-GetHighKCD")); + break; + case 2: + FFALowerVisionList.TryAdd(killer.PlayerId, Utils.GetTimeStamp()); + Main.AllPlayerKillCooldown[killer.PlayerId] = FFA_KCD.GetFloat(); + killer.Notify(GetString("FFA-Event-GetLowVision")); + if (Main.NormalOptions.MapId == 4) _ = new LateTask(() => { FFALowerVisionList.Remove(killer.PlayerId); killer.SyncSettings(); }, FFA_ModifiedVisionDuration.GetFloat()); + break; + default: + Main.AllPlayerKillCooldown[killer.PlayerId] = FFA_KCD.GetFloat(); + break; + } + } + else // Mixed + { + var rd = IRandom.Instance; + var vents = Object.FindObjectsOfType(); + var vent = vents[rd.Next(0, vents.Count)]; + _ = new LateTask(() => { Utils.TP(killer.NetTransform, new Vector2(vent.transform.position.x, vent.transform.position.y)); }, 0.5f); + killer.Notify(GetString("FFA-Event-GetTP")); + Main.AllPlayerKillCooldown[killer.PlayerId] = FFA_KCD.GetFloat(); + } + + killer.SyncSettings(); + } + + killer.RpcMurderPlayerV3(target); + } + + public static void OnPlayerKill(PlayerControl killer) + { + if (PlayerControl.LocalPlayer.Is(CustomRoles.GM)) + PlayerControl.LocalPlayer.KillFlash(); + + KBScore[killer.PlayerId]++; + } + + [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))] + class FixedUpdatePatch + { + private static long LastFixedUpdate = new(); + public static void Postfix(PlayerControl __instance) + { + if (!GameStates.IsInTask || Options.CurrentGameMode != CustomGameMode.FFA) return; + + if (AmongUsClient.Instance.AmHost) + { + if (LastFixedUpdate == Utils.GetTimeStamp()) return; + LastFixedUpdate = Utils.GetTimeStamp(); + + RoundTime--; + + if (!AmongUsClient.Instance.AmHost) return; + + foreach (var pc in Main.AllPlayerControls) + { + if (NameNotify.ContainsKey(pc.PlayerId) && NameNotify[pc.PlayerId].Item2 < Utils.GetTimeStamp()) + { + NameNotify.Remove(pc.PlayerId); + SendRPCSyncNameNotify(pc); + Utils.NotifyRoles(SpecifySeer: pc); + } + } + + byte FFAdoTPdecider = (byte)HashRandom.Next(0, 100); + bool FFAdoTP = false; + if (FFAdoTPdecider == 0) FFAdoTP = true; + + if (FFA_EnableRandomTwists.GetBool() && FFAdoTP) + { + List changePositionPlayers = new(); + + var rd = IRandom.Instance; + foreach (var pc in Main.AllAlivePlayerControls) + { + if (changePositionPlayers.Contains(pc.PlayerId) || !pc.IsAlive() || pc.onLadder || pc.inVent) continue; + + var filtered = Main.AllAlivePlayerControls.Where(a => + pc.IsAlive() && !pc.inVent && a.PlayerId != pc.PlayerId && !changePositionPlayers.Contains(a.PlayerId)).ToList(); + if (!filtered.Any()) break; + + PlayerControl target = filtered[rd.Next(0, filtered.Count)]; + + if (pc.inVent || target.inVent) continue; + + changePositionPlayers.Add(target.PlayerId); + changePositionPlayers.Add(pc.PlayerId); + + pc.RPCPlayCustomSound("Teleport"); + + var originPs = target.GetTruePosition(); + Utils.TP(target.NetTransform, pc.GetTruePosition()); + Utils.TP(pc.NetTransform, originPs); + + target.Notify(Utils.ColorString(new Color32(0, 255, 165, byte.MaxValue), string.Format(GetString("FFA-Event-RandomTP"), pc.GetRealName()))); + pc.Notify(Utils.ColorString(new Color32(0, 255, 165, byte.MaxValue), string.Format(GetString("FFA-Event-RandomTP"), target.GetRealName()))); + } + + changePositionPlayers.Clear(); + } + if (FFADecreasedSpeedList.TryGetValue(__instance.PlayerId, out var dstime) && dstime + FFA_ModifiedSpeedDuration.GetInt() < Utils.GetTimeStamp()) + { + FFADecreasedSpeedList.Remove(__instance.PlayerId); + Main.AllPlayerSpeed[__instance.PlayerId] = originalSpeed[__instance.PlayerId]; + originalSpeed.Remove(__instance.PlayerId); + __instance.SyncSettings(); + } + if (FFAIncreasedSpeedList.TryGetValue(__instance.PlayerId, out var istime) && istime + FFA_ModifiedSpeedDuration.GetInt() < Utils.GetTimeStamp()) + { + FFAIncreasedSpeedList.Remove(__instance.PlayerId); + Main.AllPlayerSpeed[__instance.PlayerId] = originalSpeed[__instance.PlayerId]; + originalSpeed.Remove(__instance.PlayerId); + __instance.SyncSettings(); + } + if (FFALowerVisionList.TryGetValue(__instance.PlayerId, out var lvtime) && lvtime + FFA_ModifiedSpeedDuration.GetInt() < Utils.GetTimeStamp()) + { + FFALowerVisionList.Remove(__instance.PlayerId); + __instance.SyncSettings(); + } + if (FFAShieldedList.TryGetValue(__instance.PlayerId, out var stime) && stime + FFA_ShieldDuration.GetInt() < Utils.GetTimeStamp()) + { + FFAShieldedList.Remove(__instance.PlayerId); + } + } + } + } +} \ No newline at end of file diff --git a/Modules/Cloud.cs b/Modules/Cloud.cs index 82c54f412..ae2ffd4c1 100644 --- a/Modules/Cloud.cs +++ b/Modules/Cloud.cs @@ -76,7 +76,7 @@ internal class Cloud { if (connecting || EacClientSocket != null && EacClientSocket.Connected) return; connecting = true; - new LateTask(() => + _ = new LateTask(() => { if (!AmongUsClient.Instance.AmHost || !GameData.Instance || AmongUsClient.Instance.NetworkMode == NetworkModes.LocalGame) { diff --git a/Modules/CustomRoleSelector.cs b/Modules/CustomRoleSelector.cs index 4f7225f21..4eb492e9c 100644 --- a/Modules/CustomRoleSelector.cs +++ b/Modules/CustomRoleSelector.cs @@ -64,6 +64,13 @@ internal class CustomRoleSelector RoleResult.Add(pc, CustomRoles.KB_Normal); return; } + if (Options.CurrentGameMode == CustomGameMode.FFA) + { + RoleResult = new(); + foreach (var pc in Main.AllAlivePlayerControls) + RoleResult.Add(pc, CustomRoles.Killer); + return; + } System.Collections.IList list = Enum.GetValues(typeof(CustomRoles)); for (int i1 = 0; i1 < list.Count; i1++) @@ -379,7 +386,7 @@ internal class CustomRoleSelector public static List AddonRolesList = new(); public static void SelectAddonRoles() { - if (Options.CurrentGameMode == CustomGameMode.SoloKombat) return; + if (Options.CurrentGameMode == CustomGameMode.SoloKombat || Options.CurrentGameMode == CustomGameMode.FFA) return; AddonRolesList = new(); System.Collections.IList list = Enum.GetValues(typeof(CustomRoles)); diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 6f9abc05a..b490a0fcc 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -201,7 +201,7 @@ internal static class CustomRolesHelper CustomRoles.Guardian => CustomRoles.CrewmateTOHE, CustomRoles.Addict => CustomRoles.EngineerTOHE, CustomRoles.Alchemist => CustomRoles.EngineerTOHE, - CustomRoles.Oracle => CustomRoles.EngineerTOHE, + CustomRoles.Oracle => CustomRoles.CrewmateTOHE, CustomRoles.Chameleon => CustomRoles.EngineerTOHE, _ => role.IsImpostor() ? CustomRoles.ImpostorTOHE : CustomRoles.CrewmateTOHE, }; @@ -213,6 +213,8 @@ internal static class CustomRolesHelper { //SoloKombat CustomRoles.KB_Normal => RoleTypes.Impostor, + //FFA + CustomRoles.Killer => RoleTypes.Impostor, //Standard CustomRoles.Sheriff => RoleTypes.Impostor, CustomRoles.Crusader => RoleTypes.Impostor, @@ -313,6 +315,7 @@ internal static class CustomRolesHelper CustomRoles.Mimic or CustomRoles.Reach or CustomRoles.DeadlyQuota or + CustomRoles.Cleansed or CustomRoles.Charmed or CustomRoles.Infected or CustomRoles.Onbound or @@ -669,6 +672,8 @@ internal static class CustomRolesHelper return role is //SoloKombat CustomRoles.KB_Normal or + //FFA + CustomRoles.Killer or //Standard CustomRoles.Jester or CustomRoles.Opportunist or @@ -794,6 +799,8 @@ internal static class CustomRolesHelper return role is //SoloKombat CustomRoles.KB_Normal or + //FFA + CustomRoles.Killer or //Standard CustomRoles.Jester or CustomRoles.Opportunist or @@ -1120,7 +1127,7 @@ internal static class CustomRolesHelper if (role is CustomRoles.Fool && ((pc.GetCustomRole().IsCrewmate() && !Options.CrewCanBeFool.GetBool()) || (pc.GetCustomRole().IsNeutral() && !Options.NeutralCanBeFool.GetBool()) || (pc.GetCustomRole().IsImpostor() && !Options.ImpCanBeFool.GetBool()) || pc.Is(CustomRoles.SabotageMaster) || pc.Is(CustomRoles.GuardianAngelTOHE))) return false; if (role is CustomRoles.Bloodhound && pc.Is(CustomRoles.Oblivious)) return false; if (role is CustomRoles.DoubleShot && ((pc.Is(CustomRoleTypes.Impostor) && !Options.ImpCanBeDoubleShot.GetBool()) || (pc.Is(CustomRoleTypes.Crewmate) && !Options.CrewCanBeDoubleShot.GetBool()) || (pc.Is(CustomRoleTypes.Neutral) && !Options.NeutralCanBeDoubleShot.GetBool()))) return false; - if (role is CustomRoles.DoubleShot && !pc.Is(CustomRoles.EvilGuesser) && !pc.Is(CustomRoles.NiceGuesser) && !pc.Is(CustomRoles.Guesser) && !Options.GuesserMode.GetBool()) return false; + if (role is CustomRoles.DoubleShot && !pc.Is(CustomRoles.EvilGuesser) && !pc.Is(CustomRoles.NiceGuesser) && !Options.GuesserMode.GetBool()) return false; if (role is CustomRoles.DoubleShot && Options.ImpCanBeDoubleShot.GetBool() && !pc.Is(CustomRoles.Guesser) && !pc.Is(CustomRoles.EvilGuesser) && pc.Is(CustomRoleTypes.Impostor) && !Options.ImpostorsCanGuess.GetBool()) return false; if (role is CustomRoles.DoubleShot && Options.CrewCanBeDoubleShot.GetBool() && !pc.Is(CustomRoles.Guesser) && !pc.Is(CustomRoles.NiceGuesser) && pc.Is(CustomRoleTypes.Crewmate) && !Options.CrewmatesCanGuess.GetBool()) return false; if (role is CustomRoles.DoubleShot && Options.NeutralCanBeDoubleShot.GetBool() && !pc.Is(CustomRoles.Guesser) && ((pc.GetCustomRole().IsNonNK() && !Options.PassiveNeutralsCanGuess.GetBool()) || (pc.GetCustomRole().IsNK() && !Options.NeutralKillersCanGuess.GetBool()))) return false; diff --git a/Modules/CustomRpcSender.cs b/Modules/CustomRpcSender.cs index 126b32938..2f648f71b 100644 --- a/Modules/CustomRpcSender.cs +++ b/Modules/CustomRpcSender.cs @@ -1,267 +1,267 @@ -using AmongUs.GameOptions; -using Hazel; +using AmongUs.GameOptions; +using Hazel; using Il2CppInterop.Runtime.InteropTypes.Arrays; -using InnerNet; -using System; -namespace TOHE; - -public class CustomRpcSender -{ - public MessageWriter stream; - public readonly string name; - public readonly SendOption sendOption; - public bool isUnsafe; - public delegate void onSendDelegateType(); - public onSendDelegateType onSendDelegate; - - public State CurrentState - { - get { return currentState; } - set - { - if (isUnsafe) currentState = value; - else Logger.Warn("CurrentStateはisUnsafeがtrueの時のみ上書きできます", "CustomRpcSender"); - } - } - private State currentState = State.BeforeInit; - - //0~: targetClientId (GameDataTo) - //-1: 全プレイヤー (GameData) - //-2: 未設定 - private int currentRpcTarget; - - private CustomRpcSender() { } - public CustomRpcSender(string name, SendOption sendOption, bool isUnsafe) - { - stream = MessageWriter.Get(sendOption); - - this.name = name; - this.sendOption = sendOption; - this.isUnsafe = isUnsafe; - this.currentRpcTarget = -2; - onSendDelegate = () => Logger.Info($"{this.name}'s onSendDelegate =>", "CustomRpcSender"); - - currentState = State.Ready; - Logger.Info($"\"{name}\" is ready", "CustomRpcSender"); - } - public static CustomRpcSender Create(string name = "No Name Sender", SendOption sendOption = SendOption.None, bool isUnsafe = false) - { - return new CustomRpcSender(name, sendOption, isUnsafe); - } - - #region Start/End Message - public CustomRpcSender StartMessage(int targetClientId = -1) - { - if (currentState != State.Ready) - { - string errorMsg = $"Messageを開始しようとしましたが、StateがReadyではありません (in: \"{name}\")"; - if (isUnsafe) - { - Logger.Warn(errorMsg, "CustomRpcSender.Warn"); - } - else - { - throw new InvalidOperationException(errorMsg); - } - } - - if (targetClientId < 0) - { - // 全員に対するRPC - stream.StartMessage(5); - stream.Write(AmongUsClient.Instance.GameId); - } - else - { - // 特定のクライアントに対するRPC (Desync) - stream.StartMessage(6); - stream.Write(AmongUsClient.Instance.GameId); - stream.WritePacked(targetClientId); - } - - currentRpcTarget = targetClientId; - currentState = State.InRootMessage; - return this; - } - public CustomRpcSender EndMessage(int targetClientId = -1) - { - if (currentState != State.InRootMessage) - { - string errorMsg = $"Messageを終了しようとしましたが、StateがInRootMessageではありません (in: \"{name}\")"; - if (isUnsafe) - { - Logger.Warn(errorMsg, "CustomRpcSender.Warn"); - } - else - { - throw new InvalidOperationException(errorMsg); - } - } - stream.EndMessage(); - - currentRpcTarget = -2; - currentState = State.Ready; - return this; - } - #endregion - #region Start/End Rpc - public CustomRpcSender StartRpc(uint targetNetId, RpcCalls rpcCall) - => StartRpc(targetNetId, (byte)rpcCall); - public CustomRpcSender StartRpc( - uint targetNetId, - byte callId) - { - if (currentState != State.InRootMessage) - { - string errorMsg = $"RPCを開始しようとしましたが、StateがInRootMessageではありません (in: \"{name}\")"; - if (isUnsafe) - { - Logger.Warn(errorMsg, "CustomRpcSender.Warn"); - } - else - { - throw new InvalidOperationException(errorMsg); - } - } - - stream.StartMessage(2); - stream.WritePacked(targetNetId); - stream.Write(callId); - - currentState = State.InRpc; - return this; - } - public CustomRpcSender EndRpc() - { - if (currentState != State.InRpc) - { - string errorMsg = $"RPCを終了しようとしましたが、StateがInRpcではありません (in: \"{name}\")"; - if (isUnsafe) - { - Logger.Warn(errorMsg, "CustomRpcSender.Warn"); - } - else - { - throw new InvalidOperationException(errorMsg); - } - } - - stream.EndMessage(); - currentState = State.InRootMessage; - return this; - } - #endregion - public CustomRpcSender AutoStartRpc( - uint targetNetId, - byte callId, - int targetClientId = -1) - { - if (targetClientId == -2) targetClientId = -1; - if (currentState is not State.Ready and not State.InRootMessage) - { - string errorMsg = $"RPCを自動で開始しようとしましたが、StateがReadyまたはInRootMessageではありません (in: \"{name}\")"; - if (isUnsafe) - { - Logger.Warn(errorMsg, "CustomRpcSender.Warn"); - } - else - { - throw new InvalidOperationException(errorMsg); - } - } - if (currentRpcTarget != targetClientId) - { - //StartMessage処理 - if (currentState == State.InRootMessage) this.EndMessage(); - this.StartMessage(targetClientId); - } - this.StartRpc(targetNetId, callId); - - return this; - } - public void SendMessage() - { - if (currentState == State.InRootMessage) this.EndMessage(); - if (currentState != State.Ready) - { - string errorMsg = $"RPCを送信しようとしましたが、StateがReadyではありません (in: \"{name}\")"; - if (isUnsafe) - { - Logger.Warn(errorMsg, "CustomRpcSender.Warn"); - } - else - { - throw new InvalidOperationException(errorMsg); - } - } - - AmongUsClient.Instance.SendOrDisconnect(stream); - onSendDelegate(); - currentState = State.Finished; - Logger.Info($"\"{name}\" is finished", "CustomRpcSender"); - stream.Recycle(); - } - - // Write - #region PublicWriteMethods - public CustomRpcSender Write(float val) => Write(w => w.Write(val)); - public CustomRpcSender Write(string val) => Write(w => w.Write(val)); - public CustomRpcSender Write(ulong val) => Write(w => w.Write(val)); - public CustomRpcSender Write(int val) => Write(w => w.Write(val)); - public CustomRpcSender Write(uint val) => Write(w => w.Write(val)); - public CustomRpcSender Write(ushort val) => Write(w => w.Write(val)); - public CustomRpcSender Write(byte val) => Write(w => w.Write(val)); - public CustomRpcSender Write(sbyte val) => Write(w => w.Write(val)); - public CustomRpcSender Write(bool val) => Write(w => w.Write(val)); - public CustomRpcSender Write(Il2CppStructArray bytes) => Write(w => w.Write(bytes)); - public CustomRpcSender Write(Il2CppStructArray bytes, int offset, int length) => Write(w => w.Write(bytes, offset, length)); - public CustomRpcSender WriteBytesAndSize(Il2CppStructArray bytes) => Write(w => w.WriteBytesAndSize(bytes)); - public CustomRpcSender WritePacked(int val) => Write(w => w.WritePacked(val)); - public CustomRpcSender WritePacked(uint val) => Write(w => w.WritePacked(val)); - public CustomRpcSender WriteNetObject(InnerNetObject obj) => Write(w => w.WriteNetObject(obj)); - #endregion - - private CustomRpcSender Write(Action action) - { - if (currentState != State.InRpc) - { - string errorMsg = $"RPCを書き込もうとしましたが、StateがWrite(書き込み中)ではありません (in: \"{name}\")"; - if (isUnsafe) - { - Logger.Warn(errorMsg, "CustomRpcSender.Warn"); - } - else - { - throw new InvalidOperationException(errorMsg); - } - } - action(stream); - - return this; - } - - public enum State - { - BeforeInit = 0, //初期化前 何もできない - Ready, //送信準備完了 StartMessageとSendMessageを実行可能 - InRootMessage, //StartMessage~EndMessageの間の状態 StartRpcとEndMessageを実行可能 - InRpc, //StartRpc~EndRpcの間の状態 WriteとEndRpcを実行可能 - Finished, //送信後 何もできない - } -} - -public static class CustomRpcSenderExtensions -{ - public static void RpcSetRole(this CustomRpcSender sender, PlayerControl player, RoleTypes role, int targetClientId = -1) - { - sender.AutoStartRpc(player.NetId, (byte)RpcCalls.SetRole, targetClientId) - .Write((ushort)role) - .EndRpc(); - } - public static void RpcMurderPlayerV3(this CustomRpcSender sender, PlayerControl player, PlayerControl target, int targetClientId = -1) - { - sender.AutoStartRpc(player.NetId, (byte)RpcCalls.MurderPlayer, targetClientId) - .WriteNetObject(target) - .EndRpc(); - } +using InnerNet; +using System; +namespace TOHE; + +public class CustomRpcSender +{ + public MessageWriter stream; + public readonly string name; + public readonly SendOption sendOption; + public bool isUnsafe; + public delegate void onSendDelegateType(); + public onSendDelegateType onSendDelegate; + + public State CurrentState + { + get { return currentState; } + set + { + if (isUnsafe) currentState = value; + else Logger.Warn("CurrentStateはisUnsafeがtrueの時のみ上書きできます", "CustomRpcSender"); + } + } + private State currentState = State.BeforeInit; + + //0~: targetClientId (GameDataTo) + //-1: 全プレイヤー (GameData) + //-2: 未設定 + private int currentRpcTarget; + + private CustomRpcSender() { } + public CustomRpcSender(string name, SendOption sendOption, bool isUnsafe) + { + stream = MessageWriter.Get(sendOption); + + this.name = name; + this.sendOption = sendOption; + this.isUnsafe = isUnsafe; + this.currentRpcTarget = -2; + onSendDelegate = () => Logger.Info($"{this.name}'s onSendDelegate =>", "CustomRpcSender"); + + currentState = State.Ready; + Logger.Info($"\"{name}\" is ready", "CustomRpcSender"); + } + public static CustomRpcSender Create(string name = "No Name Sender", SendOption sendOption = SendOption.None, bool isUnsafe = false) + { + return new CustomRpcSender(name, sendOption, isUnsafe); + } + + #region Start/End Message + public CustomRpcSender StartMessage(int targetClientId = -1) + { + if (currentState != State.Ready) + { + string errorMsg = $"Messageを開始しようとしましたが、StateがReadyではありません (in: \"{name}\")"; + if (isUnsafe) + { + Logger.Warn(errorMsg, "CustomRpcSender.Warn"); + } + else + { + throw new InvalidOperationException(errorMsg); + } + } + + if (targetClientId < 0) + { + // 全員に対するRPC + stream.StartMessage(5); + stream.Write(AmongUsClient.Instance.GameId); + } + else + { + // 特定のクライアントに対するRPC (Desync) + stream.StartMessage(6); + stream.Write(AmongUsClient.Instance.GameId); + stream.WritePacked(targetClientId); + } + + currentRpcTarget = targetClientId; + currentState = State.InRootMessage; + return this; + } + public CustomRpcSender EndMessage(int targetClientId = -1) + { + if (currentState != State.InRootMessage) + { + string errorMsg = $"Messageを終了しようとしましたが、StateがInRootMessageではありません (in: \"{name}\")"; + if (isUnsafe) + { + Logger.Warn(errorMsg, "CustomRpcSender.Warn"); + } + else + { + throw new InvalidOperationException(errorMsg); + } + } + stream.EndMessage(); + + currentRpcTarget = -2; + currentState = State.Ready; + return this; + } + #endregion + #region Start/End Rpc + public CustomRpcSender StartRpc(uint targetNetId, RpcCalls rpcCall) + => StartRpc(targetNetId, (byte)rpcCall); + public CustomRpcSender StartRpc( + uint targetNetId, + byte callId) + { + if (currentState != State.InRootMessage) + { + string errorMsg = $"RPCを開始しようとしましたが、StateがInRootMessageではありません (in: \"{name}\")"; + if (isUnsafe) + { + Logger.Warn(errorMsg, "CustomRpcSender.Warn"); + } + else + { + throw new InvalidOperationException(errorMsg); + } + } + + stream.StartMessage(2); + stream.WritePacked(targetNetId); + stream.Write(callId); + + currentState = State.InRpc; + return this; + } + public CustomRpcSender EndRpc() + { + if (currentState != State.InRpc) + { + string errorMsg = $"RPCを終了しようとしましたが、StateがInRpcではありません (in: \"{name}\")"; + if (isUnsafe) + { + Logger.Warn(errorMsg, "CustomRpcSender.Warn"); + } + else + { + throw new InvalidOperationException(errorMsg); + } + } + + stream.EndMessage(); + currentState = State.InRootMessage; + return this; + } + #endregion + public CustomRpcSender AutoStartRpc( + uint targetNetId, + byte callId, + int targetClientId = -1) + { + if (targetClientId == -2) targetClientId = -1; + if (currentState is not State.Ready and not State.InRootMessage) + { + string errorMsg = $"RPCを自動で開始しようとしましたが、StateがReadyまたはInRootMessageではありません (in: \"{name}\")"; + if (isUnsafe) + { + Logger.Warn(errorMsg, "CustomRpcSender.Warn"); + } + else + { + throw new InvalidOperationException(errorMsg); + } + } + if (currentRpcTarget != targetClientId) + { + //StartMessage処理 + if (currentState == State.InRootMessage) this.EndMessage(); + this.StartMessage(targetClientId); + } + this.StartRpc(targetNetId, callId); + + return this; + } + public void SendMessage() + { + if (currentState == State.InRootMessage) this.EndMessage(); + if (currentState != State.Ready) + { + string errorMsg = $"RPCを送信しようとしましたが、StateがReadyではありません (in: \"{name}\")"; + if (isUnsafe) + { + Logger.Warn(errorMsg, "CustomRpcSender.Warn"); + } + else + { + throw new InvalidOperationException(errorMsg); + } + } + + AmongUsClient.Instance.SendOrDisconnect(stream); + onSendDelegate(); + currentState = State.Finished; + Logger.Info($"\"{name}\" is finished", "CustomRpcSender"); + stream.Recycle(); + } + + // Write + #region PublicWriteMethods + public CustomRpcSender Write(float val) => Write(w => w.Write(val)); + public CustomRpcSender Write(string val) => Write(w => w.Write(val)); + public CustomRpcSender Write(ulong val) => Write(w => w.Write(val)); + public CustomRpcSender Write(int val) => Write(w => w.Write(val)); + public CustomRpcSender Write(uint val) => Write(w => w.Write(val)); + public CustomRpcSender Write(ushort val) => Write(w => w.Write(val)); + public CustomRpcSender Write(byte val) => Write(w => w.Write(val)); + public CustomRpcSender Write(sbyte val) => Write(w => w.Write(val)); + public CustomRpcSender Write(bool val) => Write(w => w.Write(val)); + public CustomRpcSender Write(Il2CppStructArray bytes) => Write(w => w.Write(bytes)); + public CustomRpcSender Write(Il2CppStructArray bytes, int offset, int length) => Write(w => w.Write(bytes, offset, length)); + public CustomRpcSender WriteBytesAndSize(Il2CppStructArray bytes) => Write(w => w.WriteBytesAndSize(bytes)); + public CustomRpcSender WritePacked(int val) => Write(w => w.WritePacked(val)); + public CustomRpcSender WritePacked(uint val) => Write(w => w.WritePacked(val)); + public CustomRpcSender WriteNetObject(InnerNetObject obj) => Write(w => w.WriteNetObject(obj)); + #endregion + + private CustomRpcSender Write(Action action) + { + if (currentState != State.InRpc) + { + string errorMsg = $"RPCを書き込もうとしましたが、StateがWrite(書き込み中)ではありません (in: \"{name}\")"; + if (isUnsafe) + { + Logger.Warn(errorMsg, "CustomRpcSender.Warn"); + } + else + { + throw new InvalidOperationException(errorMsg); + } + } + action(stream); + + return this; + } + + public enum State + { + BeforeInit = 0, //初期化前 何もできない + Ready, //送信準備完了 StartMessageとSendMessageを実行可能 + InRootMessage, //StartMessage~EndMessageの間の状態 StartRpcとEndMessageを実行可能 + InRpc, //StartRpc~EndRpcの間の状態 WriteとEndRpcを実行可能 + Finished, //送信後 何もできない + } +} + +public static class CustomRpcSenderExtensions +{ + public static void RpcSetRole(this CustomRpcSender sender, PlayerControl player, RoleTypes role, int targetClientId = -1) + { + sender.AutoStartRpc(player.NetId, (byte)RpcCalls.SetRole, targetClientId) + .Write((ushort)role) + .EndRpc(); + } + public static void RpcMurderPlayerV3(this CustomRpcSender sender, PlayerControl player, PlayerControl target, int targetClientId = -1) + { + sender.AutoStartRpc(player.NetId, (byte)RpcCalls.MurderPlayer, targetClientId) + .WriteNetObject(target) + .EndRpc(); + } } \ No newline at end of file diff --git a/Modules/DevManager.cs b/Modules/DevManager.cs index 633a03c49..c68d509af 100644 --- a/Modules/DevManager.cs +++ b/Modules/DevManager.cs @@ -90,9 +90,9 @@ public static class DevManager // DevUserList.Add(new(code: "honestsofa#2870", color: "#D381D9", tag: "Discord: SolarFlare#0700", isUp: true, isDev: false, colorCmd: false, deBug: false, upName: "SolarFlare")); //SolarFlare DevUserList.Add(new(code: "caseeast#7194", color: "#1c2451", tag: "disc.gg/maul", isUp: false, isDev: false, colorCmd: false, deBug: false, upName: null)); //laikrai // lol hi go away - DevUserList.Add(new(code: "gnuedaphic#7196", color: "#ffc0cb", tag: "Main Developer", isUp: true, isDev: true, deBug: false, colorCmd: true, upName: "Loonie")); //Loonie + DevUserList.Add(new(code: "gnuedaphic#7196", color: "#ffc0cb", tag: "TOH-RE Developer", isUp: true, isDev: true, deBug: false, colorCmd: true, upName: "Loonie")); //Loonie // Lauryn and Moe - DevUserList.Add(new(code: "straymovie#6453", color: "#F6B05E", tag: "Website Developer", isUp: true, isDev: false, deBug: false, colorCmd: true, upName: "Moe")); //Moe + DevUserList.Add(new(code: "straymovie#6453", color: "#F6B05E", tag: "Literally no one (jk)", isUp: true, isDev: false, deBug: false, colorCmd: true, upName: "Moe")); //Moe DevUserList.Add(new(code: "singlesign#1823", color: "#ffb6cd", tag: "Princess", isUp: true, isDev: false, deBug: false, colorCmd: true, upName: "Lauryn")); //Lauryn // Other DevUserList.Add(new(code: "peakcrown#8292", color: "null", tag: "null", isUp: true, isDev: false, deBug: false, colorCmd: true, upName: null)); //Hakaka diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index ac4baf307..6464bf900 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -391,17 +391,17 @@ static class ExtendedPlayerControl var systemtypes = SystemTypes.Reactor; if (Main.NormalOptions.MapId == 2) systemtypes = SystemTypes.Laboratory; - new LateTask(() => + _ = new LateTask(() => { pc.RpcDesyncRepairSystem(systemtypes, 128); }, 0f + delay, "Reactor Desync"); - new LateTask(() => + _ = new LateTask(() => { pc.RpcSpecificMurderPlayer(); }, 0.2f + delay, "Murder To Reset Cam"); - new LateTask(() => + _ = new LateTask(() => { pc.RpcDesyncRepairSystem(systemtypes, 16); if (Main.NormalOptions.MapId == 4) //Airship用 @@ -419,7 +419,7 @@ static class ExtendedPlayerControl pc.RpcDesyncRepairSystem(systemtypes, 128); - new LateTask(() => + _ = new LateTask(() => { pc.RpcDesyncRepairSystem(systemtypes, 16); @@ -441,6 +441,8 @@ static class ExtendedPlayerControl { //SoloKombat CustomRoles.KB_Normal => pc.SoloAlive(), + //FFA + CustomRoles.Killer => pc.IsAlive(), //Standard CustomRoles.FireWorks => FireWorks.CanUseKillButton(pc), CustomRoles.Mafia => Utils.CanMafiaKill(), @@ -585,6 +587,8 @@ static class ExtendedPlayerControl //SoloKombat CustomRoles.KB_Normal => true, + //FFA + CustomRoles.Killer => true, _ => pc.Is(CustomRoleTypes.Impostor), }; @@ -895,6 +899,9 @@ static class ExtendedPlayerControl case CustomRoles.KB_Normal: Main.AllPlayerKillCooldown[player.PlayerId] = SoloKombatManager.KB_ATKCooldown.GetFloat(); break; + case CustomRoles.Killer: + Main.AllPlayerKillCooldown[player.PlayerId] = FFAManager.FFA_KCD.GetFloat(); + break; case CustomRoles.BloodKnight: BloodKnight.SetKillCooldown(player.PlayerId); break; @@ -985,7 +992,7 @@ static class ExtendedPlayerControl Main.AllPlayerSpeed[killer.PlayerId] = Main.MinSpeed; //tmpSpeedで後ほど値を戻すので代入しています。 ReportDeadBodyPatch.CanReport[killer.PlayerId] = false; killer.MarkDirtySettings(); - new LateTask(() => + _ = new LateTask(() => { Main.AllPlayerSpeed[killer.PlayerId] = Main.AllPlayerSpeed[killer.PlayerId] - Main.MinSpeed + tmpSpeed; ReportDeadBodyPatch.CanReport[killer.PlayerId] = true; @@ -1019,7 +1026,7 @@ static class ExtendedPlayerControl if (killer.PlayerId == target.PlayerId && killer.shapeshifting) { - new LateTask(() => { killer.RpcMurderPlayer(target); }, 1.5f, "Shapeshifting Suicide Delay"); + _ = new LateTask(() => { killer.RpcMurderPlayer(target); }, 1.5f, "Shapeshifting Suicide Delay"); return; } diff --git a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs index 9f66b2075..891be6e53 100644 --- a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs +++ b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs @@ -85,6 +85,23 @@ public class PlayerGameOptionsSender : GameOptionsSender opt.BlackOut(state.IsBlackOut); CustomRoles role = player.GetCustomRole(); + + if (Options.CurrentGameMode == CustomGameMode.FFA) + { + if (FFAManager.FFALowerVisionList.ContainsKey(player.PlayerId)) + { + opt.SetVision(true); + opt.SetFloat(FloatOptionNames.CrewLightMod, FFAManager.FFA_LowerVision.GetFloat()); + opt.SetFloat(FloatOptionNames.ImpostorLightMod, FFAManager.FFA_LowerVision.GetFloat()); + } + else + { + opt.SetVision(true); + opt.SetFloat(FloatOptionNames.CrewLightMod, 1.25f); + opt.SetFloat(FloatOptionNames.ImpostorLightMod, 1.25f); + } + } + switch (role.GetCustomRoleTypes()) { case CustomRoleTypes.Impostor: diff --git a/Modules/GameState.cs b/Modules/GameState.cs index 768d5fe80..aa9f8e07c 100644 --- a/Modules/GameState.cs +++ b/Modules/GameState.cs @@ -488,7 +488,7 @@ public class TaskState if (player.Is(CustomRoles.Alchemist) && player.IsAlive()) Alchemist.OnTaskComplete(player); if (player.Is(CustomRoles.Ghoul) && (CompletedTasksCount + 1) >= AllTasksCount && player.IsAlive()) - new LateTask(() => + _ = new LateTask(() => { player.RpcMurderPlayerV3(player); Main.PlayerStates[player.PlayerId].deathReason = PlayerState.DeathReason.Suicide; diff --git a/Modules/GuessManager.cs b/Modules/GuessManager.cs index f0a3823ad..f1d7e3f41 100644 --- a/Modules/GuessManager.cs +++ b/Modules/GuessManager.cs @@ -606,7 +606,7 @@ public static class GuessManager if (Doomsayer.GuessedRoles.Contains(role)) { - new LateTask(() => + _ = new LateTask(() => { Utils.SendMessage(GetString("DoomsayerGuessSameRoleAgainMsg"), pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Doomsayer), GetString("DoomsayerGuessCountTitle"))); }, 0.7f, "Doomsayer Guess Same Role Again Msg"); @@ -617,7 +617,7 @@ public static class GuessManager Doomsayer.SendRPC(pc); Doomsayer.GuessedRoles.Add(role); - new LateTask(() => + _ = new LateTask(() => { Utils.SendMessage(string.Format(GetString("DoomsayerGuessCountMsg"), Doomsayer.GuessesCount), pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Doomsayer), GetString("DoomsayerGuessCountTitle"))); }, 0.7f, "Doomsayer Guess Msg 1"); @@ -644,7 +644,7 @@ public static class GuessManager string Name = dp.GetRealName(); if (!Options.DisableKillAnimationOnGuess.GetBool()) CustomSoundsManager.RPCPlayCustomSoundAll("Gunfire"); - new LateTask(() => + _ = new LateTask(() => { Main.PlayerStates[dp.PlayerId].deathReason = PlayerState.DeathReason.Gambled; dp.SetRealKiller(pc); @@ -669,11 +669,11 @@ public static class GuessManager Utils.NotifyRoles(isForMeeting: GameStates.IsMeeting, NoCache: true); - new LateTask(() => { Utils.SendMessage(string.Format(GetString("GuessKill"), Name), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.NiceGuesser), GetString("GuessKillTitle"))); }, 0.6f, "Guess Msg"); + _ = new LateTask(() => { Utils.SendMessage(string.Format(GetString("GuessKill"), Name), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.NiceGuesser), GetString("GuessKillTitle"))); }, 0.6f, "Guess Msg"); if (pc.Is(CustomRoles.Doomsayer) && pc.PlayerId != dp.PlayerId) { - new LateTask(() => + _ = new LateTask(() => { Utils.SendMessage(string.Format(GetString("DoomsayerGuessCountMsg"), Doomsayer.GuessingToWin[pc.PlayerId]), pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Doomsayer), GetString("DoomsayerGuessCountTitle"))); }, 0.7f, "Doomsayer Guess Msg 2"); diff --git a/Modules/LocateArrow.cs b/Modules/LocateArrow.cs index 36c898312..6280b31c7 100644 --- a/Modules/LocateArrow.cs +++ b/Modules/LocateArrow.cs @@ -148,7 +148,7 @@ static class LocateArrow } if (update) { - Utils.NotifyRoles(SpecifySeer: seer); + Utils.NotifyRoles(SpecifySeer: seer, ForceLoop: false); } } } diff --git a/Modules/MafiaRevengeManager.cs b/Modules/MafiaRevengeManager.cs index 52fcef2c4..c7f135b3a 100644 --- a/Modules/MafiaRevengeManager.cs +++ b/Modules/MafiaRevengeManager.cs @@ -87,7 +87,7 @@ public static class MafiaRevengeManager CustomSoundsManager.RPCPlayCustomSoundAll("AWP"); - new LateTask(() => + _ = new LateTask(() => { Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Revenge; target.SetRealKiller(pc); @@ -107,7 +107,7 @@ public static class MafiaRevengeManager Main.PlayerStates[target.PlayerId].SetDead(); } - new LateTask(() => { Utils.SendMessage(string.Format(GetString("MafiaKillSucceed"), Name), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Mafia), GetString("MafiaRevengeTitle"))); }, 0.6f, "Mafia Kill"); + _ = new LateTask(() => { Utils.SendMessage(string.Format(GetString("MafiaKillSucceed"), Name), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Mafia), GetString("MafiaRevengeTitle"))); }, 0.6f, "Mafia Kill"); }, 0.2f, "Mafia Kill"); return true; diff --git a/Modules/NameColorManager.cs b/Modules/NameColorManager.cs index e9693e97a..03eded5d0 100644 --- a/Modules/NameColorManager.cs +++ b/Modules/NameColorManager.cs @@ -1,7 +1,6 @@ using Hazel; using TOHE.Roles.Impostor; using TOHE.Roles.Neutral; -using UnityEngine; namespace TOHE; @@ -163,6 +162,7 @@ public static class NameColorManager if (color != "") return true; else return seer == target || (Main.GodMode.Value && seer.AmOwner) + || (Options.CurrentGameMode == CustomGameMode.FFA) || (Main.PlayerStates[seer.Data.PlayerId].IsDead && seer.Data.IsDead && !seer.IsAlive() && Options.GhostCanSeeOtherRoles.GetBool()) || (seer.Is(CustomRoles.Mimic) && Main.PlayerStates[target.Data.PlayerId].IsDead && target.Data.IsDead && !target.IsAlive() && Options.MimicCanSeeDeadRoles.GetBool()) || target.Is(CustomRoles.GM) diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index 7e047354b..a16528b7c 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -3,14 +3,13 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using TOHE.Modules; using TOHE.Roles.AddOns.Crewmate; using TOHE.Roles.AddOns.Impostor; using TOHE.Roles.Crewmate; using TOHE.Roles.Impostor; using TOHE.Roles.Neutral; -using TOHE.Modules; using UnityEngine; -using Il2CppSystem.Diagnostics.Tracing; namespace TOHE; @@ -19,6 +18,7 @@ public enum CustomGameMode { Standard = 0x01, SoloKombat = 0x02, + FFA = 0x03, All = int.MaxValue } @@ -54,12 +54,13 @@ public static class Options => GameMode.GetInt() switch { 1 => CustomGameMode.SoloKombat, + 2 => CustomGameMode.FFA, _ => CustomGameMode.Standard }; public static readonly string[] gameModes = { - "Standard", "SoloKombat" + "Standard", "SoloKombat", "FFA" }; // MapActive @@ -111,7 +112,6 @@ public static class Options // 各役職の詳細設定 public static OptionItem EnableGM; public static float DefaultKillCooldown = Main.NormalOptions?.KillCooldown ?? 25; - public static OptionItem GhostsDoTasks; public static OptionItem DisableMeeting; public static OptionItem DisableCloseDoor; @@ -979,7 +979,8 @@ public static class Options NameDisplayAddons = BooleanOptionItem.Create(210, "NameDisplayAddons", true, TabGroup.Addons, false) .SetGameMode(CustomGameMode.Standard) .SetHeader(true); - NoLimitAddonsNumMax = IntegerOptionItem.Create(211, "NoLimitAddonsNumMax", new(1, 15, 1), 1, TabGroup.Addons, false); + NoLimitAddonsNumMax = IntegerOptionItem.Create(211, "NoLimitAddonsNumMax", new(1, 15, 1), 1, TabGroup.Addons, false) + .SetGameMode(CustomGameMode.Standard); //==================================================================================================================================// @@ -2493,10 +2494,12 @@ public static class Options #region 游戏设置 - MainLoadingText = "Building SoloKombat settings"; + MainLoadingText = "Building Settings for Other Gamemodes"; //SoloKombat SoloKombatManager.SetupCustomOption(); + //FFA + FFAManager.SetupCustomOption(); LoadingPercentage = 65; @@ -2547,34 +2550,30 @@ public static class Options RandomMapsMode = BooleanOptionItem.Create(19900, "RandomMapsMode", false, TabGroup.GameSettings, false) .SetHeader(true) .SetColor(new Color32(19, 188, 233, byte.MaxValue)); - AddedTheSkeld = BooleanOptionItem.Create(19910, "AddedTheSkeld", false, TabGroup.GameSettings, false) - .SetParent(RandomMapsMode); - AddedMiraHQ = BooleanOptionItem.Create(19911, "AddedMIRAHQ", false, TabGroup.GameSettings, false) - .SetParent(RandomMapsMode); + //AddedTheSkeld = BooleanOptionItem.Create(19910, "AddedTheSkeld", false, TabGroup.GameSettings, false) + // .SetParent(RandomMapsMode); + //AddedMiraHQ = BooleanOptionItem.Create(19911, "AddedMIRAHQ", false, TabGroup.GameSettings, false) + // .SetParent(RandomMapsMode); LoadingPercentage = 68; - AddedPolus = BooleanOptionItem.Create(19912, "AddedPolus", false, TabGroup.GameSettings, false) - .SetParent(RandomMapsMode); - AddedTheAirship = BooleanOptionItem.Create(19913, "AddedTheAirship", false, TabGroup.GameSettings, false) - .SetParent(RandomMapsMode); + //AddedPolus = BooleanOptionItem.Create(19912, "AddedPolus", false, TabGroup.GameSettings, false) + // .SetParent(RandomMapsMode); + //AddedTheAirship = BooleanOptionItem.Create(19913, "AddedTheAirship", false, TabGroup.GameSettings, false) + // .SetParent(RandomMapsMode); // MapDleks = CustomOption.Create(19914, Color.white, "AddedDleks", false, RandomMapMode); - - /* - SkeldChance = IntegerOptionItem.Create(19910, "SkeldChance", new(0, 100, 5), 0, TabGroup.GameSettings, false) - .SetParent(RandomMapsMode) - .SetValueFormat(OptionFormat.Percent); - MiraChance = IntegerOptionItem.Create(19911, "MiraChance", new(0, 100, 5), 0, TabGroup.GameSettings, false) - .SetParent(RandomMapsMode) - .SetValueFormat(OptionFormat.Percent); - PolusChance = IntegerOptionItem.Create(19912, "PolusChance", new(0, 100, 5), 0, TabGroup.GameSettings, false) - .SetParent(RandomMapsMode) - .SetValueFormat(OptionFormat.Percent); - AirshipChance = IntegerOptionItem.Create(19913, "AirshipChance", new(0, 100, 5), 0, TabGroup.GameSettings, false) - .SetParent(RandomMapsMode) - .SetValueFormat(OptionFormat.Percent); - - */ + SkeldChance = IntegerOptionItem.Create(19910, "SkeldChance", new(0, 100, 5), 0, TabGroup.GameSettings, false) + .SetParent(RandomMapsMode) + .SetValueFormat(OptionFormat.Percent); + MiraChance = IntegerOptionItem.Create(19911, "MiraChance", new(0, 100, 5), 0, TabGroup.GameSettings, false) + .SetParent(RandomMapsMode) + .SetValueFormat(OptionFormat.Percent); + PolusChance = IntegerOptionItem.Create(19912, "PolusChance", new(0, 100, 5), 0, TabGroup.GameSettings, false) + .SetParent(RandomMapsMode) + .SetValueFormat(OptionFormat.Percent); + AirshipChance = IntegerOptionItem.Create(19913, "AirshipChance", new(0, 100, 5), 0, TabGroup.GameSettings, false) + .SetParent(RandomMapsMode) + .SetValueFormat(OptionFormat.Percent); LoadingPercentage = 69; @@ -2593,15 +2592,16 @@ public static class Options .SetColor(new Color32(19, 188, 233, byte.MaxValue)); //Disable Airship Moving Platform DisableAirshipMovingPlatform = BooleanOptionItem.Create(22110, "DisableAirshipMovingPlatform", false, TabGroup.GameSettings, false) - .SetGameMode(CustomGameMode.Standard) .SetColor(new Color32(19, 188, 233, byte.MaxValue)); // Reset Doors After Meeting ResetDoorsEveryTurns = BooleanOptionItem.Create(22120, "ResetDoorsEveryTurns", false, TabGroup.GameSettings, false) + .SetGameMode(CustomGameMode.Standard) .SetColor(new Color32(19, 188, 233, byte.MaxValue)); // Reset Doors Mode DoorsResetMode = StringOptionItem.Create(22122, "DoorsResetMode", EnumHelper.GetAllNames(), 2, TabGroup.GameSettings, false) .SetColor(new Color32(19, 188, 233, byte.MaxValue)) + .SetGameMode(CustomGameMode.Standard) .SetParent(ResetDoorsEveryTurns); LoadingPercentage = 70; @@ -2721,71 +2721,51 @@ public static class Options //禁用设备 DisableDevices = BooleanOptionItem.Create(22900, "DisableDevices", false, TabGroup.GameSettings, false) - .SetColor(new Color32(255, 153, 153, byte.MaxValue)) - .SetGameMode(CustomGameMode.Standard); + .SetColor(new Color32(255, 153, 153, byte.MaxValue)); DisableSkeldDevices = BooleanOptionItem.Create(22905, "DisableSkeldDevices", false, TabGroup.GameSettings, false) - .SetParent(DisableDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableDevices); DisableSkeldAdmin = BooleanOptionItem.Create(22906, "DisableSkeldAdmin", false, TabGroup.GameSettings, false) - .SetParent(DisableSkeldDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableSkeldDevices); DisableSkeldCamera = BooleanOptionItem.Create(22907, "DisableSkeldCamera", false, TabGroup.GameSettings, false) - .SetParent(DisableSkeldDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableSkeldDevices); LoadingPercentage = 76; DisableMiraHQDevices = BooleanOptionItem.Create(22908, "DisableMiraHQDevices", false, TabGroup.GameSettings, false) - .SetParent(DisableDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableDevices); DisableMiraHQAdmin = BooleanOptionItem.Create(22909, "DisableMiraHQAdmin", false, TabGroup.GameSettings, false) - .SetParent(DisableMiraHQDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableMiraHQDevices); DisableMiraHQDoorLog = BooleanOptionItem.Create(22910, "DisableMiraHQDoorLog", false, TabGroup.GameSettings, false) - .SetParent(DisableMiraHQDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableMiraHQDevices); DisablePolusDevices = BooleanOptionItem.Create(22911, "DisablePolusDevices", false, TabGroup.GameSettings, false) - .SetParent(DisableDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableDevices); DisablePolusAdmin = BooleanOptionItem.Create(22912, "DisablePolusAdmin", false, TabGroup.GameSettings, false) - .SetParent(DisablePolusDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisablePolusDevices); DisablePolusCamera = BooleanOptionItem.Create(22913, "DisablePolusCamera", false, TabGroup.GameSettings, false) - .SetParent(DisablePolusDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisablePolusDevices); DisablePolusVital = BooleanOptionItem.Create(22914, "DisablePolusVital", false, TabGroup.GameSettings, false) - .SetParent(DisablePolusDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisablePolusDevices); DisableAirshipDevices = BooleanOptionItem.Create(22915, "DisableAirshipDevices", false, TabGroup.GameSettings, false) - .SetParent(DisableDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableDevices); DisableAirshipCockpitAdmin = BooleanOptionItem.Create(22916, "DisableAirshipCockpitAdmin", false, TabGroup.GameSettings, false) - .SetParent(DisableAirshipDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableAirshipDevices); LoadingPercentage = 77; DisableAirshipRecordsAdmin = BooleanOptionItem.Create(22917, "DisableAirshipRecordsAdmin", false, TabGroup.GameSettings, false) - .SetParent(DisableAirshipDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableAirshipDevices); DisableAirshipCamera = BooleanOptionItem.Create(22918, "DisableAirshipCamera", false, TabGroup.GameSettings, false) - .SetParent(DisableAirshipDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableAirshipDevices); DisableAirshipVital = BooleanOptionItem.Create(22919, "DisableAirshipVital", false, TabGroup.GameSettings, false) - .SetParent(DisableAirshipDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableAirshipDevices); DisableDevicesIgnoreConditions = BooleanOptionItem.Create(22920, "IgnoreConditions", false, TabGroup.GameSettings, false) - .SetParent(DisableDevices) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableDevices); DisableDevicesIgnoreImpostors = BooleanOptionItem.Create(22921, "IgnoreImpostors", false, TabGroup.GameSettings, false) - .SetParent(DisableDevicesIgnoreConditions) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableDevicesIgnoreConditions); DisableDevicesIgnoreNeutrals = BooleanOptionItem.Create(22922, "IgnoreNeutrals", false, TabGroup.GameSettings, false) - .SetParent(DisableDevicesIgnoreConditions) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableDevicesIgnoreConditions); DisableDevicesIgnoreCrewmates = BooleanOptionItem.Create(22923, "IgnoreCrewmates", false, TabGroup.GameSettings, false) - .SetParent(DisableDevicesIgnoreConditions) - .SetGameMode(CustomGameMode.Standard); + .SetParent(DisableDevicesIgnoreConditions); DisableDevicesIgnoreAfterAnyoneDied = BooleanOptionItem.Create(22924, "IgnoreAfterAnyoneDied", false, TabGroup.GameSettings, false) .SetParent(DisableDevicesIgnoreConditions) .SetGameMode(CustomGameMode.Standard); @@ -3044,9 +3024,9 @@ public static class Options TextOptionItem.Create(100050, "MenuTitle.GuesserModeRoles", TabGroup.TaskSettings) - .SetGameMode(CustomGameMode.Standard) - .SetColor(Color.yellow) - .SetHeader(true); + .SetGameMode(CustomGameMode.Standard) + .SetColor(Color.yellow) + .SetHeader(true); SetupAdtRoleOptions(14500, CustomRoles.Onbound, canSetNum: true, tab: TabGroup.TaskSettings); ImpCanBeOnbound = BooleanOptionItem.Create(14510, "ImpCanBeOnbound", true, TabGroup.TaskSettings, false) .SetParent(CustomRoleSpawnChances[CustomRoles.Onbound]); @@ -3135,11 +3115,9 @@ public static class Options // 梯子摔死 LadderDeath = BooleanOptionItem.Create(23800, "LadderDeath", false, TabGroup.GameSettings, false) - .SetGameMode(CustomGameMode.Standard) - .SetColor(new Color32(193, 255, 209, byte.MaxValue)); + .SetColor(new Color32(193, 255, 209, byte.MaxValue)); LadderDeathChance = StringOptionItem.Create(23810, "LadderDeathChance", rates[1..], 0, TabGroup.GameSettings, false) - .SetParent(LadderDeath) - .SetGameMode(CustomGameMode.Standard); + .SetParent(LadderDeath); LoadingPercentage = 97; @@ -3151,12 +3129,10 @@ public static class Options StartingKillCooldown = FloatOptionItem.Create(23950, "StartingKillCooldown", new(1, 60, 1), 18, TabGroup.GameSettings, false) .SetColor(new Color32(193, 255, 209, byte.MaxValue)) - .SetValueFormat(OptionFormat.Seconds) - .SetGameMode(CustomGameMode.Standard); + .SetValueFormat(OptionFormat.Seconds); // 首刀保护 ShieldPersonDiedFirst = BooleanOptionItem.Create(24000, "ShieldPersonDiedFirst", false, TabGroup.GameSettings, false) - .SetGameMode(CustomGameMode.Standard) .SetColor(new Color32(193, 255, 209, byte.MaxValue)); LoadingPercentage = 98; diff --git a/Modules/OptionItem/OptionItem.cs b/Modules/OptionItem/OptionItem.cs index 23ac9fe4e..52b5edc96 100644 --- a/Modules/OptionItem/OptionItem.cs +++ b/Modules/OptionItem/OptionItem.cs @@ -239,7 +239,7 @@ public abstract class OptionItem SyncAllOptions(); } - public static void SyncAllOptions() + public static void SyncAllOptions(int targetId = -1) { if ( Main.AllPlayerControls.Count() <= 1 || @@ -247,7 +247,7 @@ public abstract class OptionItem PlayerControl.LocalPlayer == null ) return; - RPC.SyncCustomSettingsRPC(); + RPC.SyncCustomSettingsRPC(targetId); } diff --git a/Modules/OptionItem/ValueRule.cs b/Modules/OptionItem/ValueRule.cs index 66d223d35..2f728ccdd 100644 --- a/Modules/OptionItem/ValueRule.cs +++ b/Modules/OptionItem/ValueRule.cs @@ -36,7 +36,7 @@ public class IntegerValueRule : ValueRule public override int RepeatIndex(int value) { int MaxIndex = (MaxValue - MinValue) / Step; - value = value % (MaxIndex + 1); + value %= (MaxIndex + 1); if (value < 0) value = MaxIndex; return value; } @@ -63,7 +63,7 @@ public class FloatValueRule : ValueRule public override int RepeatIndex(int value) { int MaxIndex = (int)((MaxValue - MinValue) / Step); - value = value % (MaxIndex + 1); + value %= (MaxIndex + 1); if (value < 0) value = MaxIndex; return value; } diff --git a/Modules/OptionSaver.cs b/Modules/OptionSaver.cs index 565655c97..14e3cde99 100644 --- a/Modules/OptionSaver.cs +++ b/Modules/OptionSaver.cs @@ -1,118 +1,118 @@ -using System.Collections.Generic; -using System.IO; -using System.Text.Json; - -namespace TOHE.Modules; - -// https://github.com/tukasa0001/TownOfHost/blob/main/Modules/OptionSaver.cs -public static class OptionSaver -{ - private static readonly DirectoryInfo SaveDataDirectoryInfo = new("./TOHE-DATA/SaveData/"); - private static readonly FileInfo OptionSaverFileInfo = new($"{SaveDataDirectoryInfo.FullName}/Options.json"); - private static readonly LogHandler logger = Logger.Handler(nameof(OptionSaver)); - - public static void Initialize() - { - if (!SaveDataDirectoryInfo.Exists) - { - SaveDataDirectoryInfo.Create(); - SaveDataDirectoryInfo.Attributes |= FileAttributes.Hidden; - } - if (!OptionSaverFileInfo.Exists) - { - OptionSaverFileInfo.Create().Dispose(); - } - } - /// 現在のオプションからjsonシリアライズ用のオブジェクトを生成 - private static SerializableOptionsData GenerateOptionsData() - { - Dictionary singleOptions = new(); - Dictionary presetOptions = new(); - for (int i = 0; i < OptionItem.AllOptions.Count; i++) +using System.Collections.Generic; +using System.IO; +using System.Text.Json; + +namespace TOHE.Modules; + +// https://github.com/tukasa0001/TownOfHost/blob/main/Modules/OptionSaver.cs +public static class OptionSaver +{ + private static readonly DirectoryInfo SaveDataDirectoryInfo = new("./TOHE-DATA/SaveData/"); + private static readonly FileInfo OptionSaverFileInfo = new($"{SaveDataDirectoryInfo.FullName}/Options.json"); + private static readonly LogHandler logger = Logger.Handler(nameof(OptionSaver)); + + public static void Initialize() + { + if (!SaveDataDirectoryInfo.Exists) + { + SaveDataDirectoryInfo.Create(); + SaveDataDirectoryInfo.Attributes |= FileAttributes.Hidden; + } + if (!OptionSaverFileInfo.Exists) + { + OptionSaverFileInfo.Create().Dispose(); + } + } + /// 現在のオプションからjsonシリアライズ用のオブジェクトを生成 + private static SerializableOptionsData GenerateOptionsData() + { + Dictionary singleOptions = new(); + Dictionary presetOptions = new(); + for (int i = 0; i < OptionItem.AllOptions.Count; i++) { OptionItem option = OptionItem.AllOptions[i]; - if (option.IsSingleValue) - { - if (!singleOptions.TryAdd(option.Id, option.SingleValue)) - { - logger.Warn($"Duplicate SingleOption ID: {option.Id}"); - } - } - else if (!presetOptions.TryAdd(option.Id, option.AllValues)) - { - logger.Warn($"Duplicate preset option ID: {option.Id}"); - } - } - return new SerializableOptionsData - { - Version = Version, - SingleOptions = singleOptions, - PresetOptions = presetOptions, - }; - } - /// デシリアライズされたオブジェクトを読み込み,オプション値を設定 - private static void LoadOptionsData(SerializableOptionsData serializableOptionsData) - { - if (serializableOptionsData.Version != Version) - { - // 今後バージョン間の移行方法を用意する場合,ここでバージョンごとの変換メソッドに振り分ける - logger.Info($"読み込まれたオプションのバージョン {serializableOptionsData.Version} が現在のバージョン {Version} と一致しないためデフォルト値で上書きします"); - Save(); - return; - } - Dictionary singleOptions = serializableOptionsData.SingleOptions; - Dictionary presetOptions = serializableOptionsData.PresetOptions; - foreach (var singleOption in singleOptions) - { - var id = singleOption.Key; - var value = singleOption.Value; - if (OptionItem.FastOptions.TryGetValue(id, out var optionItem)) - { - optionItem.SetValue(value, doSave: false); - } - } - foreach (var presetOption in presetOptions) - { - var id = presetOption.Key; - var values = presetOption.Value; - if (OptionItem.FastOptions.TryGetValue(id, out var optionItem)) - { - optionItem.SetAllValues(values); - } - } - } - /// 現在のオプションをjsonファイルに保存 - public static void Save() - { - if (AmongUsClient.Instance != null && !AmongUsClient.Instance.AmHost) return; - - var jsonString = JsonSerializer.Serialize(GenerateOptionsData(), new JsonSerializerOptions { WriteIndented = true, }); - File.WriteAllText(OptionSaverFileInfo.FullName, jsonString); - } - /// jsonファイルからオプションを読み込み - public static void Load() - { - var jsonString = File.ReadAllText(OptionSaverFileInfo.FullName); - // 空なら読み込まず,デフォルト値をセーブする - if (jsonString.Length <= 0) - { - logger.Info("オプションデータが空のためデフォルト値を保存"); - Save(); - return; - } - LoadOptionsData(JsonSerializer.Deserialize(jsonString)); - } - - /// json保存に適したオプションデータ - public class SerializableOptionsData - { - public int Version { get; init; } - /// プリセット外のオプション - public Dictionary SingleOptions { get; init; } - /// プリセット内のオプション - public Dictionary PresetOptions { get; init; } - } - - /// オプションの形式に互換性のない変更(プリセット数変更など)を加えるときはここの数字を上げる - public static readonly int Version = 0; -} + if (option.IsSingleValue) + { + if (!singleOptions.TryAdd(option.Id, option.SingleValue)) + { + logger.Warn($"Duplicate SingleOption ID: {option.Id}"); + } + } + else if (!presetOptions.TryAdd(option.Id, option.AllValues)) + { + logger.Warn($"Duplicate preset option ID: {option.Id}"); + } + } + return new SerializableOptionsData + { + Version = Version, + SingleOptions = singleOptions, + PresetOptions = presetOptions, + }; + } + /// デシリアライズされたオブジェクトを読み込み,オプション値を設定 + private static void LoadOptionsData(SerializableOptionsData serializableOptionsData) + { + if (serializableOptionsData.Version != Version) + { + // 今後バージョン間の移行方法を用意する場合,ここでバージョンごとの変換メソッドに振り分ける + logger.Info($"読み込まれたオプションのバージョン {serializableOptionsData.Version} が現在のバージョン {Version} と一致しないためデフォルト値で上書きします"); + Save(); + return; + } + Dictionary singleOptions = serializableOptionsData.SingleOptions; + Dictionary presetOptions = serializableOptionsData.PresetOptions; + foreach (var singleOption in singleOptions) + { + var id = singleOption.Key; + var value = singleOption.Value; + if (OptionItem.FastOptions.TryGetValue(id, out var optionItem)) + { + optionItem.SetValue(value, doSave: false); + } + } + foreach (var presetOption in presetOptions) + { + var id = presetOption.Key; + var values = presetOption.Value; + if (OptionItem.FastOptions.TryGetValue(id, out var optionItem)) + { + optionItem.SetAllValues(values); + } + } + } + /// 現在のオプションをjsonファイルに保存 + public static void Save() + { + if (AmongUsClient.Instance != null && !AmongUsClient.Instance.AmHost) return; + + var jsonString = JsonSerializer.Serialize(GenerateOptionsData(), new JsonSerializerOptions { WriteIndented = true, }); + File.WriteAllText(OptionSaverFileInfo.FullName, jsonString); + } + /// jsonファイルからオプションを読み込み + public static void Load() + { + var jsonString = File.ReadAllText(OptionSaverFileInfo.FullName); + // 空なら読み込まず,デフォルト値をセーブする + if (jsonString.Length <= 0) + { + logger.Info("オプションデータが空のためデフォルト値を保存"); + Save(); + return; + } + LoadOptionsData(JsonSerializer.Deserialize(jsonString)); + } + + /// json保存に適したオプションデータ + public class SerializableOptionsData + { + public int Version { get; init; } + /// プリセット外のオプション + public Dictionary SingleOptions { get; init; } + /// プリセット内のオプション + public Dictionary PresetOptions { get; init; } + } + + /// オプションの形式に互換性のない変更(プリセット数変更など)を加えるときはここの数字を上げる + public static readonly int Version = 0; +} diff --git a/Modules/RPC.cs b/Modules/RPC.cs index ecc6c8edf..4c7f9d06d 100644 --- a/Modules/RPC.cs +++ b/Modules/RPC.cs @@ -125,6 +125,8 @@ enum CustomRPC DoPoison, SetAdmireLimit, SetRememberLimit, + SyncFFAPlayer, + SyncFFANameNotify } public enum Sounds { @@ -195,11 +197,11 @@ internal class RPCHandlerPatch Logger.Fatal($"{__instance?.Data?.PlayerName}({__instance.PlayerId}): {reader.ReadString()} 错误,根据设定终止游戏", "Anti-black"); ChatUpdatePatch.DoBlockChat = true; Main.OverrideWelcomeMsg = string.Format(GetString("RpcAntiBlackOutNotifyInLobby"), __instance?.Data?.PlayerName, GetString("EndWhenPlayerBug")); - new LateTask(() => + _ = new LateTask(() => { Logger.SendInGame(string.Format(GetString("RpcAntiBlackOutEndGame"), __instance?.Data?.PlayerName), true); }, 3f, "Anti-Black Msg SendInGame"); - new LateTask(() => + _ = new LateTask(() => { CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Error); GameManager.Instance.LogicFlow.CheckEndCriteria(); @@ -209,7 +211,7 @@ internal class RPCHandlerPatch else if (GameStates.IsOnlineGame) { Logger.Fatal($"{__instance?.Data?.PlayerName}({__instance.PlayerId}): Change Role Setting Postfix 错误,根据设定继续游戏", "Anti-black"); - new LateTask(() => + _ = new LateTask(() => { Logger.SendInGame(string.Format(GetString("RpcAntiBlackOutIgnored"), __instance?.Data?.PlayerName), true); }, 3f, "Anti-Black Msg SendInGame"); @@ -232,7 +234,7 @@ internal class RPCHandlerPatch if (AmongUsClient.Instance.AmHost && tag != $"{ThisAssembly.Git.Commit}({ThisAssembly.Git.Branch})") { if (forkId != Main.ForkId) - new LateTask(() => + _ = new LateTask(() => { if (__instance?.Data?.Disconnected is not null and not true) { @@ -248,7 +250,7 @@ internal class RPCHandlerPatch catch { Logger.Warn($"{__instance?.Data?.PlayerName}({__instance.PlayerId}): バージョン情報が無効です", "RpcVersionCheck"); - new LateTask(() => + _ = new LateTask(() => { MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.RequestRetryVersionCheck, SendOption.Reliable, __instance.GetClientId()); AmongUsClient.Instance.FinishRpcImmediately(writer); @@ -259,11 +261,21 @@ internal class RPCHandlerPatch RPC.RpcVersionCheck(); break; case CustomRPC.SyncCustomSettings: - for (int i = 0; i < OptionItem.AllOptions.Count; i++) + if (AmongUsClient.Instance.AmHost) break; + List list = new(); + var startAmount = reader.ReadInt32(); + var lastAmount = reader.ReadInt32(); + for (var i = startAmount; i < OptionItem.AllOptions.Count && i <= lastAmount; i++) { - OptionItem co = OptionItem.AllOptions[i]; + list.Add(OptionItem.AllOptions[i]); + } + Logger.Info($"{startAmount}-{lastAmount}:{list.Count}/{OptionItem.AllOptions.Count}", "SyncCustomSettings"); + for (int i1 = 0; i1 < list.Count; i1++) + { + OptionItem co = list[i1]; co.SetValue(reader.ReadInt32()); } + OptionShower.GetText(); break; case CustomRPC.SetDeathReason: RPC.GetDeathReason(reader); @@ -474,6 +486,9 @@ internal class RPCHandlerPatch case CustomRPC.SyncKBPlayer: SoloKombatManager.ReceiveRPCSyncKBPlayer(reader); break; + case CustomRPC.SyncFFAPlayer: + FFAManager.ReceiveRPCSyncFFAPlayer(reader); + break; case CustomRPC.SyncAllPlayerNames: Main.AllPlayerNames = new(); int num = reader.ReadInt32(); @@ -486,6 +501,9 @@ internal class RPCHandlerPatch case CustomRPC.SyncKBNameNotify: SoloKombatManager.ReceiveRPCSyncNameNotify(reader); break; + case CustomRPC.SyncFFANameNotify: + FFAManager.ReceiveRPCSyncNameNotify(reader); + break; case CustomRPC.SetMorticianArrow: Mortician.ReceiveRPC(reader); break; @@ -602,12 +620,46 @@ internal class RPCHandlerPatch internal static class RPC { //来源:https://github.com/music-discussion/TownOfHost-TheOtherRoles/blob/main/Modules/RPC.cs - public static void SyncCustomSettingsRPC() + public static void SyncCustomSettingsRPC(int targetId = -1) { - if (!AmongUsClient.Instance.AmHost) return; - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, 80, Hazel.SendOption.Reliable, -1); - foreach (var co in OptionItem.AllOptions) + if (targetId != -1) { + var client = Utils.GetClientById(targetId); + if (client == null || client.Character == null || !Main.playerVersion.ContainsKey(client.Character.PlayerId)) return; + } + if (!AmongUsClient.Instance.AmHost || PlayerControl.AllPlayerControls.Count <= 1 || (AmongUsClient.Instance.AmHost == false && PlayerControl.LocalPlayer == null)) return; + var amount = OptionItem.AllOptions.Count; + int divideBy = amount / 10; + for (var i = 0; i <= 10; i++) + SyncOptionsBetween(i * divideBy, (i + 1) * divideBy, targetId); + } + public static void SyncCustomSettingsRPCforOneOption(OptionItem option) + { + List allOptions = new(OptionItem.AllOptions); + var placement = allOptions.IndexOf(option); + if (placement != -1) + SyncOptionsBetween(placement, placement); + } + static void SyncOptionsBetween(int startAmount, int lastAmount, int targetId = -1) + { + if (targetId != -1) + { + var client = Utils.GetClientById(targetId); + if (client == null || client.Character == null || !Main.playerVersion.ContainsKey(client.Character.PlayerId)) return; + } + if (!AmongUsClient.Instance.AmHost || PlayerControl.AllPlayerControls.Count <= 1 || (AmongUsClient.Instance.AmHost == false && PlayerControl.LocalPlayer == null)) return; + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, 80, SendOption.Reliable, targetId); + List list = new(); + writer.Write(startAmount); + writer.Write(lastAmount); + for (var i = startAmount; i < OptionItem.AllOptions.Count && i <= lastAmount; i++) + { + list.Add(OptionItem.AllOptions[i]); + } + Logger.Info($"{startAmount}-{lastAmount}:{list.Count}/{OptionItem.AllOptions.Count}", "SyncCustomSettings"); + for (int i1 = 0; i1 < list.Count; i1++) + { + OptionItem co = list[i1]; writer.Write(co.GetValue()); } AmongUsClient.Instance.FinishRpcImmediately(writer); diff --git a/Modules/RetributionistRevengeManager.cs b/Modules/RetributionistRevengeManager.cs index 0f8b1fde8..13fc8f3df 100644 --- a/Modules/RetributionistRevengeManager.cs +++ b/Modules/RetributionistRevengeManager.cs @@ -109,7 +109,7 @@ public static class RetributionistRevengeManager CustomSoundsManager.RPCPlayCustomSoundAll("AWP"); - new LateTask(() => + _ = new LateTask(() => { Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Revenge; target.SetRealKiller(pc); @@ -129,7 +129,7 @@ public static class RetributionistRevengeManager Main.PlayerStates[target.PlayerId].SetDead(); } - new LateTask(() => { Utils.SendMessage(string.Format(GetString("RetributionistKillSucceed"), Name), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Retributionist), GetString("RetributionistRevengeTitle"))); }, 0.6f, "Retributionist Kill"); + _ = new LateTask(() => { Utils.SendMessage(string.Format(GetString("RetributionistKillSucceed"), Name), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Retributionist), GetString("RetributionistRevengeTitle"))); }, 0.6f, "Retributionist Kill"); }, 0.2f, "Retributionist Kill"); return true; diff --git a/Modules/SpamManager.cs b/Modules/SpamManager.cs index ec3eb3e44..a9363afa1 100644 --- a/Modules/SpamManager.cs +++ b/Modules/SpamManager.cs @@ -240,6 +240,8 @@ public static class SpamManager //if (text.Length >= 3) return false; if (text.Contains("start")) return true; + if (text.Contains("Start")) return true; + if (text.Contains("STart")) return true; if (text.Contains("s t a r t")) return true; if (text.Contains("begin")) return true; diff --git a/Modules/TargetArrow.cs b/Modules/TargetArrow.cs index 3e57c18f3..1b1f7adb8 100644 --- a/Modules/TargetArrow.cs +++ b/Modules/TargetArrow.cs @@ -150,7 +150,7 @@ static class TargetArrow } if (update) { - Utils.NotifyRoles(SpecifySeer: seer); + Utils.NotifyRoles(SpecifySeer: seer, ForceLoop: false); } } } diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 6589da2b5..e04efad33 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -33,11 +33,11 @@ public static class Utils Logger.Fatal($"{text} 错误,触发防黑屏措施", "Anti-black"); ChatUpdatePatch.DoBlockChat = true; Main.OverrideWelcomeMsg = GetString("AntiBlackOutNotifyInLobby"); - new LateTask(() => + _ = new LateTask(() => { Logger.SendInGame(GetString("AntiBlackOutLoggerSendInGame"), true); }, 3f, "Anti-Black Msg SendInGame"); - new LateTask(() => + _ = new LateTask(() => { CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Error); GameManager.Instance.LogicFlow.CheckEndCriteria(); @@ -51,18 +51,18 @@ public static class Utils writer.EndMessage(); if (Options.EndWhenPlayerBug.GetBool()) { - new LateTask(() => + _ = new LateTask(() => { Logger.SendInGame(GetString("AntiBlackOutRequestHostToForceEnd"), true); }, 3f, "Anti-Black Msg SendInGame"); } else { - new LateTask(() => + _ = new LateTask(() => { Logger.SendInGame(GetString("AntiBlackOutHostRejectForceEnd"), true); }, 3f, "Anti-Black Msg SendInGame"); - new LateTask(() => + _ = new LateTask(() => { AmongUsClient.Instance.ExitGame(DisconnectReasons.Custom); Logger.Fatal($"{text} 错误,已断开游戏", "Anti-black"); @@ -211,7 +211,7 @@ public static class Utils else if (target.Is(CustomRoles.Demolitionist)) { killer.Notify(ColorString(GetRoleColor(CustomRoles.Demolitionist), GetString("OnDemolitionistDead"))); - new LateTask(() => + _ = new LateTask(() => { if (!killer.inVent && (killer.PlayerId != target.PlayerId)) { @@ -267,7 +267,7 @@ public static class Utils } else if (!ReactorCheck) player.ReactorFlash(0f); //リアクターフラッシュ player.MarkDirtySettings(); - new LateTask(() => + _ = new LateTask(() => { Main.PlayerStates[player.PlayerId].IsBlackOut = false; //ブラックアウト解除 player.MarkDirtySettings(); @@ -439,7 +439,7 @@ public static class Utils if (p.Disconnected) return false; if (p.Role.IsImpostor) hasTasks = false; //タスクはCustomRoleを元に判定する - if (Options.CurrentGameMode == CustomGameMode.SoloKombat) return false; + if (Options.CurrentGameMode == CustomGameMode.SoloKombat || Options.CurrentGameMode == CustomGameMode.FFA) return false; if (p.IsDead && Options.GhostIgnoreTasks.GetBool()) hasTasks = false; var role = States.MainRole; switch (role) @@ -521,7 +521,7 @@ public static class Utils case CustomRoles.Convict: case CustomRoles.Opportunist: case CustomRoles.Phantom: - //case CustomRoles.Baker: + //case CustomRoles.Baker: // case CustomRoles.Famine: if (ForRecompute) hasTasks = false; @@ -988,6 +988,9 @@ public static class Utils case CustomRoles.KB_Normal: ProgressText.Append(SoloKombatManager.GetDisplayScore(playerId)); break; + case CustomRoles.Killer: + ProgressText.Append(FFAManager.GetDisplayScore(playerId)); + break; case CustomRoles.Totocalcio: ProgressText.Append(Totocalcio.GetProgressText(playerId)); break; @@ -1287,6 +1290,22 @@ public static class Utils sb.Append($"\n ").Append(EndGamePatch.SummaryText[id.Item2]); } } + else if (Options.CurrentGameMode == CustomGameMode.FFA) + { + List<(int, byte)> list = new(); + for (int i = 0; i < cloneRoles.Count; i++) + { + byte id = cloneRoles[i]; + list.Add((FFAManager.GetRankOfScore(id), id)); + } + + list.Sort(); + for (int i1 = 0; i1 < list.Count; i1++) + { + (int, byte) id = list[i1]; + sb.Append($"\n ").Append(EndGamePatch.SummaryText[id.Item2]); + } + } else { for (int i = 0; i < cloneRoles.Count; i++) @@ -1317,7 +1336,7 @@ public static class Utils var sb = new StringBuilder(); if (SetEverythingUpPatch.LastWinsText != string.Empty) sb.Append($"{GetString("LastResult")}: {SetEverythingUpPatch.LastWinsText}"); if (SetEverythingUpPatch.LastWinsReason != string.Empty) sb.Append($"\n{GetString("LastEndReason")}: {SetEverythingUpPatch.LastWinsReason}"); - if (sb.Length > 0 && Options.CurrentGameMode != CustomGameMode.SoloKombat) SendMessage(sb.ToString(), PlayerId); + if (sb.Length > 0 && Options.CurrentGameMode != CustomGameMode.SoloKombat && Options.CurrentGameMode != CustomGameMode.FFA) SendMessage(sb.ToString(), PlayerId); } public static string GetSubRolesText(byte id, bool disableColor = false, bool intro = false, bool summary = false) { @@ -1683,6 +1702,8 @@ public static class Utils if (Options.CurrentGameMode == CustomGameMode.SoloKombat) name = $"{GetString("ModeSoloKombat")}\r\n" + name; + if (Options.CurrentGameMode == CustomGameMode.FFA) + name = $"{GetString("ModeFFA")}\r\n" + name; } if (!name.Contains('\r') && player.FriendCode.GetDevUser().HasTag()) name = player.FriendCode.GetDevUser().GetTag() + name; @@ -1931,6 +1952,11 @@ public static class Utils SoloKombatManager.GetNameNotify(seer, ref SelfName); SelfName = $"{SelfTaskText}\r\n{SelfName}"; } + else if (Options.CurrentGameMode == CustomGameMode.FFA) + { + FFAManager.GetNameNotify(seer, ref SelfName); + SelfName = $"{SelfTaskText}\r\n{SelfName}"; + } else SelfName = SelfRoleName + "\r\n" + SelfName; SelfName += SelfSuffix.ToString() == string.Empty ? string.Empty : "\r\n " + SelfSuffix.ToString(); if (!isForMeeting) SelfName += "\r\n"; @@ -2095,6 +2121,7 @@ public static class Utils Amnesiac.KnowRole(seer, target) || Infectious.KnowRole(seer, target) || Virus.KnowRole(seer, target) || + Options.CurrentGameMode == CustomGameMode.FFA || (seer.IsRevealedPlayer(target) && !target.Is(CustomRoles.Trickster)) || seer.Is(CustomRoles.God) || target.Is(CustomRoles.GM) @@ -2570,7 +2597,11 @@ public static class Utils if (TranslationController.Instance.currentLanguage.languageID is SupportedLangs.SChinese or SupportedLangs.TChinese) summary = $"{GetProgressText(id)}\t{ColorString(Main.PlayerColors[id], name)}"; else summary = $"{ColorString(Main.PlayerColors[id], name)}{GetProgressText(id)}"; - if (GetProgressText(id).Trim() == string.Empty) return "INVALID"; + if (GetProgressText(id).Trim() == string.Empty) return string.Empty; + } + if (Options.CurrentGameMode == CustomGameMode.FFA) + { + summary = $"{ColorString(Main.PlayerColors[id], name)} {GetKillCountText(id)}"; } return check && GetDisplayRoleName(id, true).RemoveHtmlTags().Contains("INVALID:NotAssigned") ? "INVALID" diff --git a/README.md b/README.md index 3c052bd75..3e09e749c 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,10 @@ ## Additions, Changes, and improvements compared to TOHRE - Note: The goal with this branch is to improve the existing stuff over adding new roles. Some of the new stuff listed below are ported into TOHRE by me. +### New gamemode +- A brand new official gamemode, Basic FFA (Free For All) +- This mode makes everyone a killer and the last player alive wins +- Features: Random events, ranks and scoring, random ability gain for every kill, game timer, scoreboard (task list), unique end screen, fully customizable ### Overall improvements - Gain more ability uses for completing tasks as crewmate roles - Gain more ability uses for getting kills as impostor roles @@ -62,6 +66,7 @@ - Mechanic: Settings that determine how many uses it takes to fix Lights/Comms and Reactor/O2 - Sheriff: Setting whether to display the shot limit next to the role name - Tracefinder: Settings that determine the delay in which the arrows show up +- Bodyguard: Setting whether the murderer dies when the bodyguard protects the target #### Impostors - Assassin: Kill Cooldown no longer resets when unshifting - Bounty Hunter: Swap Timer is displayed next to the role name, not on the Shapeshift button => Changed the basis to regular Impostor @@ -102,6 +107,10 @@ - Undertaker (Impostor) - Deadly Quota (Impostor-only Add-on) +## Note: +- You can use parts of the code in here, but if you do, you'll have to credit me (Gurge44) or the mod by mentioning its name (TOHE+) and provoding a link to this github page. +- Do not try to build the source code. It will give you errors. This is intentional, so copying the mod is not so easy. To play the mod, use the given dll in the release section. +
## Credit to these mods and their developers for the code and help: @@ -150,17 +159,18 @@ ### :star: [PL](https://github.com/ImaMapleTree/Lotus) : > > - Ideas for some roles -> - Customize Main Menu +> - Reference: Customize Main Menu > - Reference: Auto Play Again +> - Reference: Some TabIcons > ### :star: [Stellar Roles](https://github.com/Mr-Fluuff/StellarRolesAU) : > > - Many role ideas (this mod is seriously amazing) -> - Reference: Some custom sounds and button images +> - Reference: Some custom button images > ### :star: [TOHEX](https://github.com/TOHEX-Official/TownOfHostEdited-Xi) : > > - Provided role: Swapper -> +> \ No newline at end of file diff --git a/TOHE.csproj b/TOHE.csproj index 49303534d..91525588a 100644 --- a/TOHE.csproj +++ b/TOHE.csproj @@ -28,7 +28,7 @@ all - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/main.cs b/main.cs index 5d677936f..0b641a77a 100644 --- a/main.cs +++ b/main.cs @@ -32,7 +32,7 @@ public class Main : BasePlugin public const string DebugKeySalt = "59687b"; public static ConfigEntry DebugKeyInput { get; private set; } public static readonly string MainMenuText = " "; - public const string PluginGuid = "com.karped1em.townofhostedited"; + public const string PluginGuid = "com.gurge44.toheplus"; public const string PluginVersion = "1.0.0"; public const string PluginDisplayVersion = "1.0"; public const int PluginCreate = 3; @@ -282,7 +282,7 @@ public class Main : BasePlugin Preset5 = Config.Bind("Preset Name Options", "Preset5", "Preset_5"); WebhookURL = Config.Bind("Other", "WebhookURL", "none"); BetaBuildURL = Config.Bind("Other", "BetaBuildURL", string.Empty); - MessageWait = Config.Bind("Other", "MessageWait", 1); + MessageWait = Config.Bind("Other", "MessageWait", 0); LastKillCooldown = Config.Bind("Other", "LastKillCooldown", (float)30); LastShapeshifterCooldown = Config.Bind("Other", "LastShapeshifterCooldown", (float)30); @@ -494,7 +494,9 @@ public class Main : BasePlugin //SoloKombat - {CustomRoles.KB_Normal, "#f55252"} + {CustomRoles.KB_Normal, "#f55252"}, + //FFA + {CustomRoles.Killer, "#00ffff"} }; foreach (var role in Enum.GetValues(typeof(CustomRoles)).Cast()) { @@ -759,6 +761,8 @@ public enum CustomRoles //SoloKombat KB_Normal, + //FFA + Killer, //GM GM,