From 760c7d597fbe735786102c8135ca3802f22429e2 Mon Sep 17 00:00:00 2001 From: Gurge44 Date: Fri, 10 Oct 2025 14:37:26 +0200 Subject: [PATCH] New game mode: Mingle --- CustomRoles.cs | 3 + GameMode/Mingle.cs | 320 ++++++++++++++++++ GameMode/NaturalDisasters.cs | 1 + GameMode/RoomRush.cs | 2 +- Main.cs | 5 +- Modules/AutoHaunt.cs | 5 +- Modules/CustomRoleSelector.cs | 3 +- Modules/CustomRolesHelper.cs | 5 +- Modules/ExtendedPlayerControl.cs | 5 +- Modules/FixedUpdateCaller.cs | 5 +- .../PlayerGameOptionsSender.cs | 1 + Modules/GameState.cs | 2 +- Modules/NameColorManager.cs | 1 + Modules/OptionHolder.cs | 8 +- Modules/Utils.cs | 22 +- Patches/CheckGameEndPatch.cs | 19 ++ Patches/DeconSystemPatch.cs | 2 +- Patches/HudPatch.cs | 10 + Patches/IntroPatch.cs | 22 ++ Patches/OnGameStartedPatch.cs | 3 + Patches/OutroPatch.cs | 14 +- Patches/PlayerControlPatch.cs | 6 +- Patches/PlayerJoinAndLeftPatch.cs | 10 +- Patches/ShipStatusPatch.cs | 2 +- Patches/TaskAssignPatch.cs | 2 +- Resources/Lang/en_US.json | 14 + 26 files changed, 470 insertions(+), 22 deletions(-) create mode 100644 GameMode/Mingle.cs diff --git a/CustomRoles.cs b/CustomRoles.cs index 53214a8ec..1fdb1306f 100644 --- a/CustomRoles.cs +++ b/CustomRoles.cs @@ -466,6 +466,9 @@ public enum CustomRoles // Deathrace Racer, + + // Mingle + MinglePlayer, // GM GM, diff --git a/GameMode/Mingle.cs b/GameMode/Mingle.cs new file mode 100644 index 000000000..f956ec5f4 --- /dev/null +++ b/GameMode/Mingle.cs @@ -0,0 +1,320 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AmongUs.GameOptions; +using UnityEngine; + +namespace EHR; + +public static class Mingle +{ + public static readonly HashSet HasPlayedFCs = []; + + public static bool GameGoing; + public static DateTime GameStartDateTime; + public static Dictionary RequiredPlayerCount = []; + public static HashSet AllRooms = []; + public static long TimeEndTS; + public static long LastUpdateTS; + public static int Time; + + public static int TimeLimit; + public static int TimeDecreaseOnNoDeath; + public static int ExtraTimeOnAirship; + public static int ExtraTimeOnFungle; + public static bool DisplayCurrentPlayerCountInEachRoom; + public static int MinTime; + public static int MaxRequiredPlayersPerRoom; + + public static OptionItem TimeLimitOption; + public static OptionItem TimeDecreaseOnNoDeathOption; + public static OptionItem ExtraTimeOnAirshipOption; + public static OptionItem ExtraTimeOnFungleOption; + public static OptionItem DisplayCurrentPlayerCountInEachRoomOption; + public static OptionItem MinTimeOption; + public static OptionItem MaxRequiredPlayersPerRoomOption; + + public static void SetupCustomOption() + { + var id = 69_224_001; + Color color = Utils.GetRoleColor(CustomRoles.MinglePlayer); + const CustomGameMode gameMode = CustomGameMode.Mingle; + const TabGroup tab = TabGroup.GameSettings; + + TimeLimitOption = new IntegerOptionItem(id++, "Mingle.TimeLimitOption", new(1, 300, 1), 60, tab) + .SetHeader(true) + .SetColor(color) + .SetGameMode(gameMode); + + TimeDecreaseOnNoDeathOption = new IntegerOptionItem(id++, "Mingle.TimeDecreaseOnNoDeathOption", new(0, 60, 1), 5, tab) + .SetColor(color) + .SetGameMode(gameMode); + + ExtraTimeOnAirshipOption = new IntegerOptionItem(id++, "Mingle.ExtraTimeOnAirshipOption", new(0, 300, 1), 30, tab) + .SetColor(color) + .SetGameMode(gameMode); + + ExtraTimeOnFungleOption = new IntegerOptionItem(id++, "Mingle.ExtraTimeOnFungleOption", new(0, 300, 1), 20, tab) + .SetColor(color) + .SetGameMode(gameMode); + + DisplayCurrentPlayerCountInEachRoomOption = new BooleanOptionItem(id++, "Mingle.DisplayCurrentPlayerCountInEachRoomOption", true, tab) + .SetColor(color) + .SetGameMode(gameMode); + + MinTimeOption = new IntegerOptionItem(id++, "Mingle.MinTimeOption", new(1, 300, 1), 15, tab) + .SetColor(color) + .SetGameMode(gameMode); + + MaxRequiredPlayersPerRoomOption = new IntegerOptionItem(id++, "Mingle.MaxRequiredPlayersPerRoomOption", new(1, 30, 1), 10, tab) + .SetColor(color) + .SetGameMode(gameMode); + } + + public static string GetSuffix(PlayerControl seer) + { + return seer.IsModdedClient() ? string.Empty : GetRoomsInfo(seer); + } + + public static int GetSurvivalTime(byte id) + { + if (!Main.PlayerStates.TryGetValue(id, out PlayerState state) || ChatCommands.Spectators.Contains(id) || (id == 0 && Main.GM.Value) || state.deathReason == PlayerState.DeathReason.Disconnected) return -1; + + if (!state.IsDead) return 0; + + DateTime died = state.RealKiller.TimeStamp; + TimeSpan time = died - GameStartDateTime; + return (int)time.TotalSeconds; + } + + public static bool CheckGameEnd(out GameOverReason reason) + { + reason = GameOverReason.ImpostorsByKill; + return false; + } + + public static string GetTaskBarText() + { + return GetRoomsInfo(PlayerControl.LocalPlayer); + } + + private static string GetRoomsInfo(PlayerControl pc) + { + StringBuilder sb = new("<#ffffff>"); + PlainShipRoom plainShipRoom = pc.GetPlainShipRoom(); + + foreach ((SystemTypes room, int required) in RequiredPlayerCount) + { + int count = GetNumPlayersInRoom(room); + + if (plainShipRoom != null && plainShipRoom.RoomId == room) + sb.Append(""); + + if (DisplayCurrentPlayerCountInEachRoom) + { + string color = count > required ? "FF4647" : count == required ? "91FF65" : "FFDE59"; + sb.Append($"<#{color}>"); + } + + sb.Append(Translator.GetString(room.ToString())); + sb.Append(':'); + sb.Append(' '); + + if (DisplayCurrentPlayerCountInEachRoom) + { + sb.Append(count); + sb.Append(" / "); + } + + sb.Append(required); + + if (DisplayCurrentPlayerCountInEachRoom) + { + sb.Append(count > required ? "+ <#ff0000>╳" : count == required ? "= <#00ff00>✓" : "- <#ff0000>╳"); + sb.Append(""); + } + + if (plainShipRoom != null && plainShipRoom.RoomId == room) + sb.Append(""); + + sb.Append('\n'); + } + + sb.Append(""); + + long timeLeft = TimeEndTS - Utils.TimeStamp; + + if (timeLeft >= 0) + { + sb.Append('\n'); + sb.Append(timeLeft); + } + + if (plainShipRoom == null || !RequiredPlayerCount.ContainsKey(plainShipRoom.RoomId)) + { + sb.Append('\n'); + sb.Append("<#ffff00>"); + sb.Append('⚠'); + sb.Append(' '); + sb.Append(Translator.GetString("Mingle.NotInRequiredRoom")); + sb.Append(' '); + sb.Append('⚠'); + sb.Append(""); + } + + return sb.ToString().TrimEnd(); + } + + public static System.Collections.IEnumerator GameStart() + { + GameGoing = false; + + TimeLimit = TimeLimitOption.GetInt(); + TimeDecreaseOnNoDeath = TimeDecreaseOnNoDeathOption.GetInt(); + ExtraTimeOnAirship = ExtraTimeOnAirshipOption.GetInt(); + ExtraTimeOnFungle = ExtraTimeOnFungleOption.GetInt(); + DisplayCurrentPlayerCountInEachRoom = DisplayCurrentPlayerCountInEachRoomOption.GetBool(); + MinTime = MinTimeOption.GetInt(); + MaxRequiredPlayersPerRoom = MaxRequiredPlayersPerRoomOption.GetInt(); + + RequiredPlayerCount = []; + Time = TimeLimit + Main.CurrentMap switch + { + MapNames.Airship => ExtraTimeOnAirship, + MapNames.Fungle => ExtraTimeOnFungle, + _ => 0 + }; + TimeEndTS = 0; + LastUpdateTS = 0; + NameNotifyManager.Reset(); + + AllRooms = ShipStatus.Instance.AllRooms.Select(x => x.RoomId).ToHashSet(); + AllRooms.Remove(SystemTypes.Hallway); + AllRooms.Remove(SystemTypes.Outside); + AllRooms.Remove(SystemTypes.Ventilation); + AllRooms.RemoveWhere(x => x.ToString().Contains("Decontamination")); + if (SubmergedCompatibility.IsSubmerged()) AllRooms.RemoveWhere(x => (byte)x > 135); + + yield return new WaitForSeconds(Main.CurrentMap == MapNames.Airship ? 8f : 3f); + + List players = Main.AllAlivePlayerControls.ToList(); + if (Main.GM.Value) players.RemoveAll(x => x.IsHost()); + if (ChatCommands.Spectators.Count > 0) players.RemoveAll(x => ChatCommands.Spectators.Contains(x.PlayerId)); + + bool showTutorial = players.ExceptBy(HasPlayedFCs, x => x.FriendCode).Count() > players.Count / 2; + + if (showTutorial) + { + players.NotifyPlayers("<#ffffff>" + Translator.GetString("Mingle.Tutorial"), 100f); + yield return new WaitForSeconds(9f); + NameNotifyManager.Reset(); + } + + for (var i = 3; i > 0; i--) + { + NameNotifyManager.Reset(); + players.NotifyPlayers(string.Format(Translator.GetString("RR_ReadyQM"), i)); + yield return new WaitForSeconds(1f); + } + + NameNotifyManager.Reset(); + StartNewRound(); + GameGoing = true; + GameStartDateTime = DateTime.Now; + } + + private static void StartNewRound() + { + if (!GameGoing) return; + + RequiredPlayerCount = []; + int playerCount = Main.AllAlivePlayerControls.Length; + + while (playerCount > 0) + { + var room = AllRooms.Except(RequiredPlayerCount.Keys).RandomElement(); + var count = IRandom.Instance.Next(1, MaxRequiredPlayersPerRoom + 1); + if (count > playerCount) count = playerCount; + RequiredPlayerCount[room] = count; + playerCount -= count; + } + + TimeEndTS = Utils.TimeStamp + Time; + Main.AllPlayerSpeed.SetAllValues(Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod)); + Utils.SyncAllSettings(); + } + + private static void KillPlayers() + { + var aapc = Main.AllAlivePlayerControls; + Dictionary playerRooms = aapc.Select(x => (pc: x, room: x.GetPlainShipRoom())).ToDictionary(x => x.pc, x => x.room == null ? SystemTypes.Outside : x.room.RoomId); + Dictionary playerCount = []; + HashSet toKill = []; + + foreach ((PlayerControl pc, SystemTypes room) in playerRooms) + { + if (room == SystemTypes.Outside || !RequiredPlayerCount.ContainsKey(room)) + toKill.Add(pc); + else if (!playerCount.TryAdd(room, 1)) + playerCount[room]++; + } + + foreach ((SystemTypes room, int required) in RequiredPlayerCount) + { + int count = playerCount.GetValueOrDefault(room, 0); + if (count == 0 || required == count) continue; + playerRooms.DoIf(x => x.Value == room, x => toKill.Add(x.Key)); + } + + switch (toKill.Count) + { + case 0: + Time = Math.Clamp(Time - TimeDecreaseOnNoDeath, MinTime, TimeLimit); + break; + case var x when x == aapc.Length: + CustomWinnerHolder.ResetAndSetWinner(CustomWinner.None); + GameGoing = false; + break; + default: + toKill.Do(x => x.Suicide()); + break; + } + } + + private static int GetNumPlayersInRoom(SystemTypes room) => Main.AllAlivePlayerControls.Select(x => x.GetPlainShipRoom()).Count(x => x != null && x.RoomId == room); + + public static void HandleDisconnect() + { + RequiredPlayerCount[AllRooms.First(x => RequiredPlayerCount.TryGetValue(x, out var required) && GetNumPlayersInRoom(x) < required)]--; + } + + public static class FixedUpdatePatch + { + public static void Postfix() + { + long now = Utils.TimeStamp; + if (LastUpdateTS == now) return; + LastUpdateTS = now; + + if (TimeEndTS == now) + { + Main.AllPlayerSpeed.SetAllValues(Main.MinSpeed); + Utils.SyncAllSettings(); + } + else if (TimeEndTS < now) + { + KillPlayers(); + StartNewRound(); + } + else if (RequiredPlayerCount.All(x => GetNumPlayersInRoom(x.Key) == x.Value)) + { + Main.AllAlivePlayerControls.NotifyPlayers(Utils.ColorString(Color.green, "✓")); + Time = Math.Clamp(Time - TimeDecreaseOnNoDeath, MinTime, TimeLimit); + StartNewRound(); + return; + } + + Utils.NotifyRoles(); + } + } +} \ No newline at end of file diff --git a/GameMode/NaturalDisasters.cs b/GameMode/NaturalDisasters.cs index ecb5cce3f..29a860f74 100644 --- a/GameMode/NaturalDisasters.cs +++ b/GameMode/NaturalDisasters.cs @@ -216,6 +216,7 @@ public static class NaturalDisasters CustomGameMode.KingOfTheZones => (0, !KingOfTheZones.GameGoing), CustomGameMode.RoomRush => (40, false), CustomGameMode.Deathrace => (0, !Deathrace.GameGoing), + CustomGameMode.Mingle => (60, !Mingle.GameGoing), _ => (0, false) }; diff --git a/GameMode/RoomRush.cs b/GameMode/RoomRush.cs index fe11098ca..dfd8a196b 100644 --- a/GameMode/RoomRush.cs +++ b/GameMode/RoomRush.cs @@ -171,7 +171,7 @@ public static class RoomRush public static int GetSurvivalTime(byte id) { - if (!Main.PlayerStates.TryGetValue(id, out PlayerState state) || ChatCommands.Spectators.Contains(id) || (id == 0 && Main.GM.Value)) return -1; + if (!Main.PlayerStates.TryGetValue(id, out PlayerState state) || ChatCommands.Spectators.Contains(id) || (id == 0 && Main.GM.Value) || state.deathReason == PlayerState.DeathReason.Disconnected) return -1; if (!state.IsDead) return 0; diff --git a/Main.cs b/Main.cs index 0ae7618e6..df846d029 100644 --- a/Main.cs +++ b/Main.cs @@ -759,6 +759,8 @@ public class Main : BasePlugin { CustomRoles.BedWarsPlayer, "#fc03f8" }, // Deathrace { CustomRoles.Racer, "#AFAFAF" }, + // Mingle + { CustomRoles.MinglePlayer, "#FE9900" }, // Hide And Seek { CustomRoles.Seeker, "#ff1919" }, { CustomRoles.Hider, "#345eeb" }, @@ -840,7 +842,8 @@ public class Main : BasePlugin [CustomGameMode.Quiz] = Utils.GetRoleColor(CustomRoles.QuizMaster), [CustomGameMode.TheMindGame] = Color.yellow, [CustomGameMode.BedWars] = Utils.GetRoleColor(CustomRoles.BedWarsPlayer), - [CustomGameMode.Deathrace] = Utils.GetRoleColor(CustomRoles.Racer) + [CustomGameMode.Deathrace] = Utils.GetRoleColor(CustomRoles.Racer), + [CustomGameMode.Mingle] = Utils.GetRoleColor(CustomRoles.MinglePlayer) }; IL2CPPChainloader.Instance.Finished += () => diff --git a/Modules/AutoHaunt.cs b/Modules/AutoHaunt.cs index 6d92ae6d2..2f8db131f 100644 --- a/Modules/AutoHaunt.cs +++ b/Modules/AutoHaunt.cs @@ -23,9 +23,10 @@ public static class AutoHaunt CustomGameMode.HideAndSeek => validPCs.OrderByDescending(x => x.GetCustomRole() is CustomRoles.Venter or CustomRoles.Dasher).ThenByDescending(x => CustomHnS.PlayerRoles.TryGetValue(x.PlayerId, out (IHideAndSeekRole Interface, CustomRoles Role) info) && info.Interface.Team == Team.Impostor).ThenByDescending(x => CustomHnS.PlayerRoles.TryGetValue(x.PlayerId, out (IHideAndSeekRole Interface, CustomRoles Role) info) && info.Interface.Team == Team.Neutral).FirstOrDefault(), CustomGameMode.Speedrun => Speedrun.CanKill.Count > 0 ? Speedrun.CanKill.ToValidPlayers().RandomElement() : validPCs.MaxBy(x => x.GetTaskState().CompletedTasksCount), CustomGameMode.CaptureTheFlag => validPCs.OrderByDescending(x => CaptureTheFlag.IsCarrier(x.PlayerId)).ThenByDescending(x => CaptureTheFlag.GetFlagTime(x.PlayerId)).ThenByDescending(x => CaptureTheFlag.GetTagCount(x.PlayerId)).FirstOrDefault(), - CustomGameMode.RoomRush => RoomRush.PointsSystem ? validPCs.MaxBy(x => RoomRush.GetPoints(x.PlayerId)) : validPCs.FirstOrDefault(), + CustomGameMode.RoomRush => RoomRush.PointsSystem ? validPCs.MaxBy(x => RoomRush.GetPoints(x.PlayerId)) : validPCs.RandomElement(), CustomGameMode.KingOfTheZones => validPCs.MaxBy(x => KingOfTheZones.GetZoneTime(x.PlayerId)), - _ => validPCs.FirstOrDefault() + CustomGameMode.Deathrace => validPCs.MaxBy(x => Deathrace.Data.TryGetValue(x.PlayerId, out var drData) ? drData.Lap : 0), + _ => validPCs.RandomElement() }; } diff --git a/Modules/CustomRoleSelector.cs b/Modules/CustomRoleSelector.cs index 55ac7ad77..08190178a 100644 --- a/Modules/CustomRoleSelector.cs +++ b/Modules/CustomRoleSelector.cs @@ -35,7 +35,8 @@ internal static class CustomRoleSelector { CustomGameMode.Quiz, CustomRoles.QuizPlayer }, { CustomGameMode.TheMindGame, CustomRoles.TMGPlayer }, { CustomGameMode.BedWars, CustomRoles.BedWarsPlayer }, - { CustomGameMode.Deathrace, CustomRoles.Racer } + { CustomGameMode.Deathrace, CustomRoles.Racer }, + { CustomGameMode.Mingle, CustomRoles.MinglePlayer } }; public static void SelectCustomRoles() diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 38aec1642..89d52010b 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -72,7 +72,8 @@ internal static class CustomRolesHelper CustomRoles.QuizPlayer or CustomRoles.TMGPlayer or CustomRoles.BedWarsPlayer or - CustomRoles.Racer; + CustomRoles.Racer or + CustomRoles.MinglePlayer; } public static RoleBase GetRoleClass(this CustomRoles role) @@ -406,6 +407,8 @@ internal static class CustomRolesHelper CustomRoles.QuizPlayer => CustomRoles.Crewmate, // The Mind Game CustomRoles.TMGPlayer => CustomRoles.Crewmate, + // Mingle + CustomRoles.MinglePlayer => CustomRoles.Crewmate, _ => role.IsImpostor() ? CustomRoles.Impostor : CustomRoles.Crewmate }; diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index 3557b262f..8e94181cb 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -1087,7 +1087,7 @@ internal static class ExtendedPlayerControl { try { - bool addRoleName = GameStates.IsInGame && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace; + bool addRoleName = GameStates.IsInGame && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace and not CustomGameMode.Mingle; return $"{player?.Data?.PlayerName}" + (addRoleName ? $" ({player?.GetAllRoleName(forUser).RemoveHtmlTags().Replace('\n', ' ')})" : string.Empty); } catch (Exception e) @@ -1360,7 +1360,7 @@ internal static class ExtendedPlayerControl switch (Options.CurrentGameMode) { - case CustomGameMode.MoveAndStop or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.TheMindGame: + case CustomGameMode.MoveAndStop or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.TheMindGame or CustomGameMode.Mingle: case CustomGameMode.Speedrun when !Speedrun.CanKill.Contains(pc.PlayerId): return false; case CustomGameMode.HotPotato: @@ -1430,6 +1430,7 @@ internal static class ExtendedPlayerControl CustomGameMode.RoomRush => false, CustomGameMode.Quiz => false, CustomGameMode.TheMindGame => false, + CustomGameMode.Mingle => false, CustomGameMode.Deathrace => Deathrace.CanUseVent(pc, pc.GetClosestVent().Id), CustomGameMode.Standard when CopyCat.Instances.Any(x => x.CopyCatPC.PlayerId == pc.PlayerId) => true, diff --git a/Modules/FixedUpdateCaller.cs b/Modules/FixedUpdateCaller.cs index 13d8d75b6..af0c3cd67 100644 --- a/Modules/FixedUpdateCaller.cs +++ b/Modules/FixedUpdateCaller.cs @@ -204,7 +204,10 @@ public static class FixedUpdateCaller goto default; case CustomGameMode.Deathrace: Deathrace.FixedUpdatePatch.Postfix(); - break; + goto default; + case CustomGameMode.Mingle: + Mingle.FixedUpdatePatch.Postfix(); + goto default; default: if (Options.IntegrateNaturalDisasters.GetBool()) goto case CustomGameMode.NaturalDisasters; break; diff --git a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs index 79c322e06..67f3ee190 100644 --- a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs +++ b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs @@ -174,6 +174,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS AURoleOptions.EngineerCooldown = 0.01f; AURoleOptions.EngineerInVentMaxTime = 0f; goto case CustomGameMode.RoomRush; + case CustomGameMode.Mingle: case CustomGameMode.RoomRush: case CustomGameMode.Speedrun: case CustomGameMode.HotPotato: diff --git a/Modules/GameState.cs b/Modules/GameState.cs index 82477807f..e5b7e1b6e 100644 --- a/Modules/GameState.cs +++ b/Modules/GameState.cs @@ -445,7 +445,7 @@ public class TaskState GameData.Instance.RecomputeTaskCounts(); Logger.Info($"TotalTaskCounts = {GameData.Instance.CompletedTasks}/{GameData.Instance.TotalTasks}", "TaskState.Update"); - if (Options.CurrentGameMode is CustomGameMode.HotPotato or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.Quiz or CustomGameMode.TheMindGame) + if (Options.CurrentGameMode is CustomGameMode.HotPotato or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.Mingle) player.Notify(Translator.GetString("DoingTasksIsPointlessInThisGameMode"), 10f); if (AllTasksCount == -1) Init(player); diff --git a/Modules/NameColorManager.cs b/Modules/NameColorManager.cs index 5263ffeef..1d901abb2 100644 --- a/Modules/NameColorManager.cs +++ b/Modules/NameColorManager.cs @@ -46,6 +46,7 @@ public static class NameColorManager if (FreeForAll.PlayerTeams.TryGetValue(target.PlayerId, out int team)) color = FreeForAll.TeamColors.GetValueOrDefault(team, "#00ffff"); return true; + case CustomGameMode.Mingle: case CustomGameMode.RoomRush: case CustomGameMode.NaturalDisasters: case CustomGameMode.MoveAndStop: diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index 14e94dbff..f95bbf116 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -34,6 +34,7 @@ public enum CustomGameMode TheMindGame = 0x0D, BedWars = 0x0E, Deathrace = 0x0F, + Mingle = 0x10, All = int.MaxValue } @@ -86,7 +87,8 @@ public static class Options "Quiz", "TheMindGame", "BedWars", - "Deathrace" + "Deathrace", + "Mingle" ]; private static Dictionary roleCounts; @@ -904,6 +906,7 @@ public static class Options 12 => CustomGameMode.TheMindGame, 13 => CustomGameMode.BedWars, 14 => CustomGameMode.Deathrace, + 15 => CustomGameMode.Mingle, _ => CustomGameMode.Standard }; @@ -1808,6 +1811,8 @@ public static class Options BedWars.SetupCustomOption(); // Deathrace Deathrace.SetupCustomOption(); + // Mingle + Mingle.SetupCustomOption(); yield return null; @@ -3149,6 +3154,7 @@ public static class Options 12 => CustomGameMode.TheMindGame, 13 => CustomGameMode.BedWars, 14 => CustomGameMode.Deathrace, + 15 => CustomGameMode.Mingle, _ => CustomGameMode.Standard }; AutoGMRotationCompiled.AddRange(Enumerable.Repeat(gm, times)); diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 960aed376..6c304684f 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -816,6 +816,7 @@ public static class Utils case CustomGameMode.Quiz: case CustomGameMode.BedWars: case CustomGameMode.Deathrace: + case CustomGameMode.Mingle: return false; case CustomGameMode.HideAndSeek: return CustomHnS.HasTasks(p); @@ -1035,7 +1036,7 @@ public static class Utils { switch (Options.CurrentGameMode) { - case CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace: + case CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle: case CustomGameMode.Standard when IsRevivingRoleAlive() && Main.DiedThisRound.Contains(PlayerControl.LocalPlayer.PlayerId): return PlayerControl.LocalPlayer.Is(CustomRoles.GM); case CustomGameMode.FFA or CustomGameMode.SoloKombat or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato or CustomGameMode.Speedrun: @@ -1540,6 +1541,7 @@ public static class Utils } break; + case CustomGameMode.Mingle: case CustomGameMode.Deathrace: case CustomGameMode.BedWars: case CustomGameMode.RoomRush: @@ -2140,6 +2142,7 @@ public static class Utils CustomGameMode.Quiz => ColorString(GetRoleColor(CustomRoles.QuizMaster), $"{modeText}\r\n") + name, CustomGameMode.BedWars => ColorString(GetRoleColor(CustomRoles.BedWarsPlayer), $"{modeText}\r\n") + name, CustomGameMode.Deathrace => ColorString(GetRoleColor(CustomRoles.Racer), $"{modeText}\r\n") + name, + CustomGameMode.Mingle => ColorString(GetRoleColor(CustomRoles.MinglePlayer), $"{modeText}\r\n") + name, _ => name }; } @@ -2602,6 +2605,9 @@ public static class Utils case CustomGameMode.Deathrace: additionalSuffixes.Add(Deathrace.GetSuffix(seer, seer, false)); break; + case CustomGameMode.Mingle: + additionalSuffixes.Add(Mingle.GetSuffix(seer)); + break; } List addSuff = additionalSuffixes.ConvertAll(x => x.Trim()).FindAll(x => !string.IsNullOrEmpty(x)); @@ -2618,7 +2624,7 @@ public static class Utils if ((Options.CurrentGameMode == CustomGameMode.FFA && FreeForAll.FFATeamMode.GetBool()) || Options.CurrentGameMode == CustomGameMode.HotPotato) seerRealName = seerRealName.ApplyNameColorData(seer, seer, forMeeting); - if (!forMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool() && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.KingOfTheZones and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace) + if (!forMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool() && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.KingOfTheZones and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace and not CustomGameMode.Mingle) { CustomTeamManager.CustomTeam team = CustomTeamManager.GetCustomTeam(seer.PlayerId); @@ -2656,7 +2662,7 @@ public static class Utils SelfSuffix.Append($"\n\n<#ffffff>{GetString($"GameModeTutorial.{Options.CurrentGameMode}")}\n"); } - bool noRoleText = GameStates.IsLobby || Options.CurrentGameMode is CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace; + bool noRoleText = GameStates.IsLobby || Options.CurrentGameMode is CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle; // Combine the seer's job title and SelfTaskText with the seer's player name and SelfMark string selfRoleName = noRoleText ? string.Empty : $"{seer.GetDisplayRoleName()}{selfTaskText}"; @@ -2690,7 +2696,7 @@ public static class Utils if (!GameStates.IsLobby) { - if (Options.CurrentGameMode is CustomGameMode.Quiz or CustomGameMode.BedWars) + if (Options.CurrentGameMode is CustomGameMode.Quiz or CustomGameMode.BedWars || (Options.CurrentGameMode == CustomGameMode.Mingle && !seer.IsModdedClient())) selfName = string.Empty; if (Options.CurrentGameMode != CustomGameMode.BedWars && NameNotifyManager.GetNameNotify(seer, out string name) && name.Length > 0) @@ -2730,6 +2736,8 @@ public static class Utils CustomGameMode.RoomRush => true, CustomGameMode.KingOfTheZones => true, CustomGameMode.Quiz => true, + CustomGameMode.Deathrace => true, + CustomGameMode.Mingle => true, _ => false }; @@ -2825,7 +2833,7 @@ public static class Utils if (IsRevivingRoleAlive() && Main.DiedThisRound.Contains(seer.PlayerId)) targetRoleText = string.Empty; - if (Options.CurrentGameMode is CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace) + if (Options.CurrentGameMode is CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle) targetRoleText = string.Empty; if (!GameStates.IsLobby) @@ -3842,6 +3850,10 @@ public static class Utils case CustomGameMode.Deathrace: summary = $"{ColorString(Main.PlayerColors[id], name)} - {Deathrace.GetStatistics(id)}"; break; + case CustomGameMode.Mingle: + int time3 = Mingle.GetSurvivalTime(id); + summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time3 == 0 ? $"{GetString("SurvivedUntilTheEnd")}" : $"{time3}s")} ({GetVitalText(id, true)})"; + break; } return check && GetDisplayRoleName(id, true).RemoveHtmlTags().Contains("INVALID:NotAssigned") diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index 5556e069a..02c4d8d34 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -553,6 +553,11 @@ internal static class GameEndChecker { Predicate = new DeathraceGameEndPredicate(); } + + public static void SetPredicateToMingle() + { + Predicate = new MingleGameEndPredicate(); + } private class NormalGameEndPredicate : GameEndPredicate { @@ -1107,6 +1112,20 @@ internal static class GameEndChecker } } + private class MingleGameEndPredicate : GameEndPredicate + { + public override bool CheckForGameEnd(out GameOverReason reason) + { + reason = GameOverReason.ImpostorsByKill; + return WinnerIds.Count <= 0 && CheckGameEndByLivingPlayers(out reason); + } + + private static bool CheckGameEndByLivingPlayers(out GameOverReason reason) + { + return Mingle.CheckGameEnd(out reason); + } + } + public abstract class GameEndPredicate { /// Checks the game ending condition and stores the value in CustomWinnerHolder. diff --git a/Patches/DeconSystemPatch.cs b/Patches/DeconSystemPatch.cs index 87947a05b..5b8343f1d 100644 --- a/Patches/DeconSystemPatch.cs +++ b/Patches/DeconSystemPatch.cs @@ -9,7 +9,7 @@ public static class DeconSystemUpdateSystemPatch { if (!AmongUsClient.Instance.AmHost || SubmergedCompatibility.IsSubmerged()) return; - bool fastDecon = Options.CurrentGameMode is CustomGameMode.BedWars or CustomGameMode.RoomRush or CustomGameMode.Deathrace; + bool fastDecon = Options.CurrentGameMode is CustomGameMode.BedWars or CustomGameMode.RoomRush or CustomGameMode.Deathrace or CustomGameMode.Mingle; if (fastDecon || Options.ChangeDecontaminationTime.GetBool()) { diff --git a/Patches/HudPatch.cs b/Patches/HudPatch.cs index b38d1e29c..a04af7660 100644 --- a/Patches/HudPatch.cs +++ b/Patches/HudPatch.cs @@ -630,6 +630,9 @@ internal static class SetHudActivePatch __instance.KillButton?.ToggleVisible(true); __instance.SabotageButton?.ToggleVisible(false); return; + case CustomGameMode.Mingle: + __instance.ReportButton?.ToggleVisible(false); + return; } PlayerControl player = PlayerControl.LocalPlayer; @@ -1139,6 +1142,13 @@ internal static class TaskPanelBehaviourPatch finalText += ""; break; } + case CustomGameMode.Mingle: + { + finalText += "\r\n\r\n"; + finalText += Mingle.GetTaskBarText(); + finalText += ""; + break; + } } panel.SetTaskText(finalText); diff --git a/Patches/IntroPatch.cs b/Patches/IntroPatch.cs index dd9d3d58b..876dbf620 100644 --- a/Patches/IntroPatch.cs +++ b/Patches/IntroPatch.cs @@ -294,6 +294,16 @@ internal static class SetUpRoleTextPatch __instance.RoleBlurbText.text = GetString("RacerInfo"); break; } + case CustomGameMode.Mingle: + { + Color color = Utils.GetRoleColor(CustomRoles.MinglePlayer); + __instance.YouAreText.transform.gameObject.SetActive(false); + __instance.RoleText.text = GetString("MinglePlayer"); + __instance.RoleText.color = color; + __instance.RoleBlurbText.color = color; + __instance.RoleBlurbText.text = GetString("MinglePlayerInfo"); + break; + } default: { CustomRoles role = lp.GetCustomRole(); @@ -963,6 +973,15 @@ internal static class BeginCrewmatePatch __instance.ImpostorText.text = GetString("RacerInfo"); break; } + case CustomGameMode.Mingle: + { + __instance.TeamTitle.text = GetString("MinglePlayer"); + __instance.TeamTitle.color = __instance.BackgroundBar.material.color = Utils.GetRoleColor(CustomRoles.MinglePlayer); + PlayerControl.LocalPlayer.Data.Role.IntroSound = GetIntroSound(RoleTypes.Detective); + __instance.ImpostorText.gameObject.SetActive(true); + __instance.ImpostorText.text = GetString("MinglePlayerInfo"); + break; + } } return; @@ -1261,6 +1280,9 @@ internal static class IntroCutsceneDestroyPatch case CustomGameMode.Deathrace: Main.Instance.StartCoroutine(Deathrace.GameStart()); break; + case CustomGameMode.Mingle: + Main.Instance.StartCoroutine(Mingle.GameStart()); + break; } Utils.CheckAndSetVentInteractions(); diff --git a/Patches/OnGameStartedPatch.cs b/Patches/OnGameStartedPatch.cs index 28df9707f..9f9b975d3 100644 --- a/Patches/OnGameStartedPatch.cs +++ b/Patches/OnGameStartedPatch.cs @@ -1094,6 +1094,9 @@ internal static class StartGameHostPatch case CustomGameMode.Deathrace: GameEndChecker.SetPredicateToDeathrace(); break; + case CustomGameMode.Mingle: + GameEndChecker.SetPredicateToMingle(); + break; } // Add players with unclassified roles to the list of players who require ResetCam. diff --git a/Patches/OutroPatch.cs b/Patches/OutroPatch.cs index 7075f9cf5..31c97cc89 100644 --- a/Patches/OutroPatch.cs +++ b/Patches/OutroPatch.cs @@ -66,7 +66,7 @@ internal static class EndGamePatch byte killerId = value.GetRealKiller(); bool gmIsFm = Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop; - bool gmIsFmhh = gmIsFm || Options.CurrentGameMode is CustomGameMode.HotPotato or CustomGameMode.HideAndSeek or CustomGameMode.Speedrun or CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace; + bool gmIsFmhh = gmIsFm || Options.CurrentGameMode is CustomGameMode.HotPotato or CustomGameMode.HideAndSeek or CustomGameMode.Speedrun or CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle; sb.Append($"\n{date:T} {Main.AllPlayerNames[key]} ({(gmIsFmhh ? string.Empty : Utils.GetDisplayRoleName(key, true))}{(gmIsFm ? string.Empty : Utils.GetSubRolesText(key, summary: true))}) [{Utils.GetVitalText(key)}]"); if (killerId != byte.MaxValue && killerId != key) sb.Append($"\n\t⇐ {Main.AllPlayerNames[killerId]} ({(gmIsFmhh ? string.Empty : Utils.GetDisplayRoleName(killerId, true))}{(gmIsFm ? string.Empty : Utils.GetSubRolesText(killerId, summary: true))})"); } @@ -151,6 +151,9 @@ internal static class EndGamePatch maps.Add(map); } + break; + case CustomGameMode.Mingle: + Main.AllPlayerControls.Do(x => Mingle.HasPlayedFCs.Add(x.FriendCode)); break; default: if (Main.HasPlayedGM.TryGetValue(Options.CurrentGameMode, out HashSet playedFCs)) @@ -318,6 +321,14 @@ internal static class SetEverythingUpPatch winnerText.color = Main.PlayerColors[winnerId]; goto EndOfText; } + case CustomGameMode.Mingle: + { + byte winnerId = CustomWinnerHolder.WinnerIds.FirstOrDefault(); + __instance.BackgroundBar.material.color = Utils.GetRoleColor(CustomRoles.MinglePlayer); + winnerText.text = Main.AllPlayerNames[winnerId] + GetString("Win"); + winnerText.color = Main.PlayerColors[winnerId]; + goto EndOfText; + } } if (CustomWinnerHolder.WinnerTeam == CustomWinner.CustomTeam) @@ -525,6 +536,7 @@ internal static class SetEverythingUpPatch break; } + case CustomGameMode.Mingle: case CustomGameMode.Deathrace: case CustomGameMode.BedWars: case CustomGameMode.Quiz: diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 731fb43f8..5ad5e4f31 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -279,6 +279,7 @@ internal static class CheckMurderPatch if (HotPotato.CanPassViaKillButton && holderID == killer.PlayerId && (lastHolderID != target.PlayerId || Main.AllAlivePlayerControls.Length <= 2)) HotPotato.FixedUpdatePatch.PassHotPotato(target, false); return false; + case CustomGameMode.Mingle: case CustomGameMode.TheMindGame: case CustomGameMode.MoveAndStop: case CustomGameMode.RoomRush: @@ -1659,7 +1660,7 @@ internal static class FixedUpdatePatch bool self = lpId == __instance.PlayerId; - bool shouldUpdateRegardlessOfLowLoad = self && GameStates.InGame && PlayerControl.LocalPlayer.IsAlive() && ((PlayerControl.AllPlayerControls.Count > 30 && LastSelfNameUpdateTS != now && Options.CurrentGameMode is CustomGameMode.MoveAndStop or CustomGameMode.HotPotato or CustomGameMode.Speedrun or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz) || DirtyName.Remove(lpId)); + bool shouldUpdateRegardlessOfLowLoad = self && GameStates.InGame && PlayerControl.LocalPlayer.IsAlive() && ((PlayerControl.AllPlayerControls.Count > 30 && LastSelfNameUpdateTS != now && Options.CurrentGameMode is CustomGameMode.MoveAndStop or CustomGameMode.HotPotato or CustomGameMode.Speedrun or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.Mingle) || DirtyName.Remove(lpId)); if (__instance == null || (lowLoad && !shouldUpdateRegardlessOfLowLoad)) return; @@ -1948,6 +1949,9 @@ internal static class FixedUpdatePatch case CustomGameMode.Deathrace: additionalSuffixes.Add(Deathrace.GetSuffix(seer, target, false)); break; + case CustomGameMode.Mingle when self: + additionalSuffixes.Add(Mingle.GetSuffix(seer)); + break; } if (MeetingStates.FirstMeeting && Main.ShieldPlayer == target.FriendCode && !string.IsNullOrEmpty(target.FriendCode) && !self && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.SoloKombat or CustomGameMode.FFA) diff --git a/Patches/PlayerJoinAndLeftPatch.cs b/Patches/PlayerJoinAndLeftPatch.cs index c45b4c065..8bf0ad6f4 100644 --- a/Patches/PlayerJoinAndLeftPatch.cs +++ b/Patches/PlayerJoinAndLeftPatch.cs @@ -478,7 +478,15 @@ internal static class OnPlayerLeftPatch { if (GameStates.IsInGame && data != null && data.Character != null) { - if (Options.CurrentGameMode == CustomGameMode.HideAndSeek) CustomHnS.PlayerRoles.Remove(data.Character.PlayerId); + switch (Options.CurrentGameMode) + { + case CustomGameMode.HideAndSeek: + CustomHnS.PlayerRoles.Remove(data.Character.PlayerId); + break; + case CustomGameMode.Mingle: + Mingle.HandleDisconnect(); + break; + } if (data.Character.Is(CustomRoles.Lovers) && data.Character.IsAlive()) { diff --git a/Patches/ShipStatusPatch.cs b/Patches/ShipStatusPatch.cs index 30a2fd3e5..4044f9c91 100644 --- a/Patches/ShipStatusPatch.cs +++ b/Patches/ShipStatusPatch.cs @@ -253,7 +253,7 @@ internal static class CloseDoorsPatch { public static bool Prefix( /*ShipStatus __instance, */ [HarmonyArgument(0)] SystemTypes room) { - bool allow = !Options.DisableSabotage.GetBool() && Options.CurrentGameMode is not CustomGameMode.SoloKombat and not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.KingOfTheZones and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace; + bool allow = !Options.DisableSabotage.GetBool() && Options.CurrentGameMode is not CustomGameMode.SoloKombat and not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.KingOfTheZones and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace and not CustomGameMode.Mingle; if (SecurityGuard.BlockSabo.Count > 0) allow = false; if (Options.DisableCloseDoor.GetBool()) allow = false; diff --git a/Patches/TaskAssignPatch.cs b/Patches/TaskAssignPatch.cs index f2700aabe..f42f0f6f9 100644 --- a/Patches/TaskAssignPatch.cs +++ b/Patches/TaskAssignPatch.cs @@ -177,7 +177,7 @@ internal static class RpcSetTasksPatch } // GM and Lazy Guy have no tasks - if (pc.Is(CustomRoles.GM) || pc.Is(CustomRoles.Needy) || Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.HotPotato or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.Quiz or CustomGameMode.CaptureTheFlag or CustomGameMode.KingOfTheZones or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace) + if (pc.Is(CustomRoles.GM) || pc.Is(CustomRoles.Needy) || Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.HotPotato or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.Quiz or CustomGameMode.CaptureTheFlag or CustomGameMode.KingOfTheZones or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle) { hasCommonTasks = false; numShortTasks = 0; diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 5a6463f6a..cb6f5e573 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -5357,6 +5357,20 @@ "Racer": "DEATHRACE", "RacerInfo": "COMPLETE ALL LAPS FIRST TO WIN", "RacerInfoLong": "COMPLETE ALL LAPS FIRST TO WIN", + "Mingle": "MINGLE", + "ModeMingle": "Gamemode: Mingle", + "ModeDescribe.Mingle": "In the Mingle gamemode, the game assigns a certain number of players to certain rooms on the map. At the end of the time limit, exactly that number of players need to be in the rooms. If there are too many or too few players in a room, all players in that room die. If you are not in any of the assigned rooms, you die. Oh, and if no one dies in a round, the time limit permanently decreases. The last player alive wins!", + "Mingle.Tutorial": "The current game mode is <#CF2472>Mingle.\\nThe game assigns a certain number of players to certain rooms on the map.\\nAt the end of the time limit, exactly those numbers of players need to be in the rooms.\\nIf there are too many or too few players in a room, all players in that room die.\\nIf you are not in any of the assigned rooms, you die.\\nOh, and if no one dies in a round, the time limit permanently decreases.\\nThe last player alive wins!", + "Mingle.TimeLimitOption": "Starting Time Limit", + "Mingle.TimeDecreaseOnNoDeathOption": "Time Decrease on No Death", + "Mingle.ExtraTimeOnAirshipOption": "Extra Time on Airship", + "Mingle.ExtraTimeOnFungleOption": "Extra Time on Fungle", + "Mingle.DisplayCurrentPlayerCountInEachRoomOption": "Display Current Player Count in Each Room", + "Mingle.MinTimeOption": "Minimum Time Limit", + "Mingle.MaxRequiredPlayersPerRoomOption": "Maximum Required Players Per Room", + "MinglePlayer": "MINGLE", + "MinglePlayerInfo": "SURVIVE BY BEING IN THE RIGHT ROOMS", + "MinglePlayerInfoLong": "SURVIVE BY BEING IN THE RIGHT ROOMS", "Quiz": "QUIZ", "ModeQuiz": "Gamemode: Quiz", "ModeDescribe.Quiz": "In the Quiz game mode, you must answer questions correctly to survive. There are various difficulties with varying time limits. Each question has 5 answer choices: A, B, C, D, E. Each choice has a room name next to the answer. To pick an answer, you must enter the room corresponding to the answer. If you answer correctly, you survive. If you don't answer enough questions correctly, you eventually die or compete in a Free For All (FFA) match with the other players who got the questions wrong.",