diff --git a/GameMode/RoomRush.cs b/GameMode/RoomRush.cs index 76bbd671a..cd7f21989 100644 --- a/GameMode/RoomRush.cs +++ b/GameMode/RoomRush.cs @@ -492,13 +492,20 @@ public static class RoomRush PlayerControl[] lateAapc = Main.AllAlivePlayerControls; PlayerControl[] playersOutsideRoom = lateAapc.ExceptBy(DonePlayers, x => x.PlayerId).ToArray(); - if (!WinByPointsInsteadOfDeaths.GetBool()) + if (WinByPointsInsteadOfDeaths.GetBool()) + { + if (playersOutsideRoom.Length == lateAapc.Length) + { + var roomPos = Map.Positions.GetValueOrDefault(RoomGoal, RoomGoal.GetRoomClass().transform.position); + playersOutsideRoom.Do(x => x.TP(roomPos)); + } + else playersOutsideRoom.Do(x => x.TP(DonePlayers.RandomElement().GetPlayer())); + } + else { if (playersOutsideRoom.Length == lateAapc.Length) CustomWinnerHolder.ResetAndSetWinner(CustomWinner.None); playersOutsideRoom.Do(x => x.Suicide()); } - else if (playersOutsideRoom.Length != lateAapc.Length) - playersOutsideRoom.Do(x => x.TP(DonePlayers.RandomElement().GetPlayer())); StartNewRound(); @@ -525,4 +532,4 @@ public class RRPlayer : RoleBase Utils.SendRPC(CustomRPC.RoomRushDataSync, 2, RoomRush.VentLimit[pc.PlayerId], pc.PlayerId); if (RoomRush.VentLimit[pc.PlayerId] <= 0) pc.RpcChangeRoleBasis(CustomRoles.RRPlayer); } -} +} \ No newline at end of file diff --git a/Main.cs b/Main.cs index 1d5880e90..e25f2517e 100644 --- a/Main.cs +++ b/Main.cs @@ -112,6 +112,17 @@ public class Main : BasePlugin public static string FirstDied = string.Empty; public static string ShieldPlayer = string.Empty; + public static readonly Dictionary> HasPlayedGM = new() + { + [CustomGameMode.SoloKombat] = [], + [CustomGameMode.FFA] = [], + [CustomGameMode.HotPotato] = [], + [CustomGameMode.HideAndSeek] = [], + [CustomGameMode.Speedrun] = [], + [CustomGameMode.CaptureTheFlag] = [], + [CustomGameMode.NaturalDisasters] = [] + }; + public static readonly Dictionary> NumWinsPerGM = []; public static HashSet DiedThisRound = []; public static List LoversPlayers = []; diff --git a/Modules/CustomNetObject.cs b/Modules/CustomNetObject.cs index 1e7653ce1..4cbf6e01a 100644 --- a/Modules/CustomNetObject.cs +++ b/Modules/CustomNetObject.cs @@ -155,7 +155,7 @@ namespace EHR protected virtual void OnFixedUpdate() { if (!AmongUsClient.Instance.AmHost) return; - + PlayerControlTimer += Time.fixedDeltaTime; if (PlayerControlTimer > 20f) @@ -327,12 +327,12 @@ namespace EHR PlayerControlTimer = 0f; return; } - + var nt = playerControl.NetTransform; if (nt == null) return; - + playerControl.Collider.enabled = false; - + if (Position != nt.body.position) { Transform transform = nt.transform; @@ -351,7 +351,7 @@ namespace EHR protected void CreateNetObject(string sprite, Vector2 position) { - if (GameStates.IsEnded || GameStates.CurrentServerType == GameStates.ServerType.Modded || !AmongUsClient.Instance.AmHost) return; + if (GameStates.IsEnded || GameStates.CurrentServerType == GameStates.ServerType.ModdedWithoutCNOSupport || !AmongUsClient.Instance.AmHost) return; Logger.Info($" Create Custom Net Object {GetType().Name} (ID {MaxId + 1}) at {position}", "CNO.CreateNetObject"); playerControl = Object.Instantiate(AmongUsClient.Instance.PlayerPrefab, Vector2.zero, Quaternion.identity); playerControl.PlayerId = 255; diff --git a/Modules/DoorsReset.cs b/Modules/DoorsReset.cs index 54418e0bb..0f7cb2fcf 100644 --- a/Modules/DoorsReset.cs +++ b/Modules/DoorsReset.cs @@ -11,7 +11,7 @@ public static class DoorsReset private static bool IsEnabled; private static ResetMode Mode; - private static DoorsSystemType DoorsSystem => ShipStatus.Instance.Systems.TryGetValue(SystemTypes.Doors, out ISystemType system) ? system.TryCast() : null; + private static DoorsSystemType DoorsSystem => ShipStatus.Instance.Systems.TryGetValue(SystemTypes.Doors, out ISystemType system) ? system.CastFast() : null; public static void Initialize() { diff --git a/Modules/EAC.cs b/Modules/EAC.cs index 48a55c1a7..91df94e87 100644 --- a/Modules/EAC.cs +++ b/Modules/EAC.cs @@ -92,7 +92,7 @@ internal static class EAC return true; } - if (GameManager.Instance.TryCast()) + if (GameManager.Instance.CastFast() != null) { WarnHost(); Report(pc, "Try to Report body in Hide and Seek"); diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index bb289a1ce..f87476c6c 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -106,7 +106,7 @@ internal static class ExtendedPlayerControl public static void RpcSetVentInteraction(this PlayerControl player) { - var ventilationSystem = ShipStatus.Instance.Systems[SystemTypes.Ventilation].TryCast(); + var ventilationSystem = ShipStatus.Instance.Systems[SystemTypes.Ventilation].CastFast(); if (ventilationSystem != null) VentilationSystemDeterioratePatch.SerializeV2(ventilationSystem, player); } @@ -312,7 +312,7 @@ internal static class ExtendedPlayerControl (byte, byte) key = (player.PlayerId, GetClientById(clientId).Character.PlayerId); StartGameHostPatch.RpcSetRoleReplacer.RoleMap[key] = (role, StartGameHostPatch.RpcSetRoleReplacer.RoleMap[key].CustomRole); } - + if (AmongUsClient.Instance.ClientId == clientId) { player.SetRole(role); diff --git a/Modules/GameState.cs b/Modules/GameState.cs index 521baa09c..bbff57bef 100644 --- a/Modules/GameState.cs +++ b/Modules/GameState.cs @@ -520,7 +520,8 @@ public static class GameStates public enum ServerType { Vanilla, - Modded, + ModdedWithCNOSupport, + ModdedWithoutCNOSupport, Niko, Custom } @@ -553,7 +554,8 @@ public static class GameStates { "Local Game" => ServerType.Custom, "EU" or "NA" or "AS" => ServerType.Vanilla, - "MEU" or "MNA" or "MAS" => ServerType.Modded, + "MNA" => ServerType.ModdedWithoutCNOSupport, + "MEU" or "MAS" => ServerType.ModdedWithCNOSupport, _ => regionName.Contains("Niko", StringComparison.OrdinalIgnoreCase) ? ServerType.Niko : ServerType.Custom }; } diff --git a/Modules/RPC.cs b/Modules/RPC.cs index e885fbfe1..1727c7175 100644 --- a/Modules/RPC.cs +++ b/Modules/RPC.cs @@ -468,13 +468,13 @@ internal static class RPCHandlerPatch { var cno = reader.ReadNetObject(); bool active = reader.ReadBoolean(); - + if (cno != null) { cno.transform.FindChild("Names").FindChild("NameText_TMP").gameObject.SetActive(active); cno.Collider.enabled = false; } - + break; } case CustomRPC.SyncGeneralOptions: @@ -1484,9 +1484,9 @@ internal static class StartRpcPatch [HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.StartRpcImmediately))] internal static class StartRpcImmediatelyPatch { - public static bool Prefix(InnerNetClient __instance, [HarmonyArgument(0)] uint targetNetId, [HarmonyArgument(1)] byte callId, [HarmonyArgument(2)] SendOption option, [HarmonyArgument(3)] int targetClientId = -1) + public static bool Prefix(InnerNetClient __instance, [HarmonyArgument(0)] uint targetNetId, [HarmonyArgument(1)] byte callId, [HarmonyArgument(2)] SendOption option, [HarmonyArgument(3)] int targetClientId = -1, ref MessageWriter __result) { - if (!__instance.AmHost) __instance.StartRpc(targetNetId, callId, option); + if (!__instance.AmHost) __result = __instance.StartRpc(targetNetId, callId, option); RPC.SendRpcLogger(targetNetId, callId, option, targetClientId); return __instance.AmHost; } @@ -1503,7 +1503,7 @@ static class FinishRpcImmediatelyPatch msg.EndMessage(); return false; } - + return true; } } \ No newline at end of file diff --git a/Modules/Utils.cs b/Modules/Utils.cs index c50b20bb2..49a785e9a 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -184,10 +184,7 @@ public static class Utils public static ClientData GetClientById(int id) { - try - { - return AmongUsClient.Instance.allClients.ToArray().FirstOrDefault(cd => cd.Id == id); - } + try { return AmongUsClient.Instance.allClients.ToArray().FirstOrDefault(cd => cd.Id == id); } catch { return null; } } @@ -205,8 +202,8 @@ public static class Utils { if (mapId == 5) return false; // if The Fungle return false - var SwitchSystem = systemType.TryCast(); - return SwitchSystem is { IsActive: true }; + var switchSystem = systemType.CastFast(); + return switchSystem is { IsActive: true }; } case SystemTypes.Reactor: { @@ -217,13 +214,13 @@ public static class Utils // Only Airhip case 4: { - var HeliSabotageSystem = systemType.TryCast(); - return HeliSabotageSystem != null && HeliSabotageSystem.IsActive; + var heliSabotageSystem = systemType.CastFast(); + return heliSabotageSystem != null && heliSabotageSystem.IsActive; } default: { - var ReactorSystemType = systemType.TryCast(); - return ReactorSystemType is { IsActive: true }; + var reactorSystemType = systemType.CastFast(); + return reactorSystemType is { IsActive: true }; } } } @@ -231,40 +228,40 @@ public static class Utils { if (mapId != 2) return false; // Only Polus - var ReactorSystemType = systemType.TryCast(); - return ReactorSystemType is { IsActive: true }; + var reactorSystemType = systemType.CastFast(); + return reactorSystemType is { IsActive: true }; } case SystemTypes.LifeSupp: { if (mapId is 2 or 4 or 5) return false; // Only Skeld & Mira HQ - var LifeSuppSystemType = systemType.TryCast(); - return LifeSuppSystemType is { IsActive: true }; + var lifeSuppSystemType = systemType.CastFast(); + return lifeSuppSystemType is { IsActive: true }; } case SystemTypes.Comms: { if (mapId is 1 or 5) // Only Mira HQ & The Fungle { - var HqHudSystemType = systemType.TryCast(); - return HqHudSystemType is { IsActive: true }; + var hqHudSystemType = systemType.CastFast(); + return hqHudSystemType is { IsActive: true }; } - var HudOverrideSystemType = systemType.TryCast(); - return HudOverrideSystemType is { IsActive: true }; + var hudOverrideSystemType = systemType.CastFast(); + return hudOverrideSystemType is { IsActive: true }; } case SystemTypes.HeliSabotage: { if (mapId != 4) return false; // Only Airhip - var HeliSabotageSystem = systemType.TryCast(); - return HeliSabotageSystem != null && HeliSabotageSystem.IsActive; + var heliSabotageSystem = systemType.CastFast(); + return heliSabotageSystem != null && heliSabotageSystem.IsActive; } case SystemTypes.MushroomMixupSabotage: { if (mapId != 5) return false; // Only The Fungle - var MushroomMixupSabotageSystem = systemType.TryCast(); - return MushroomMixupSabotageSystem != null && MushroomMixupSabotageSystem.IsActive; + var mushroomMixupSabotageSystem = systemType.CastFast(); + return mushroomMixupSabotageSystem != null && mushroomMixupSabotageSystem.IsActive; } default: return false; @@ -728,7 +725,7 @@ public static class Utils public static void SetAllVentInteractions() { - var ventilationSystem = ShipStatus.Instance.Systems[SystemTypes.Ventilation].TryCast(); + var ventilationSystem = ShipStatus.Instance.Systems[SystemTypes.Ventilation].CastFast(); if (ventilationSystem != null) VentilationSystemDeterioratePatch.SerializeV2(ventilationSystem); } @@ -2246,16 +2243,19 @@ public static class Utils { if (GameStartTimeStamp + 40 > now) SeerRealName = CustomHnS.GetRoleInfoText(seer); } - else if (Options.ChangeNameToRoleInfo.GetBool() && !seer.IsModClient()) + else if (Options.ChangeNameToRoleInfo.GetBool() && !seer.IsModClient() && Options.CurrentGameMode == CustomGameMode.Standard) { bool showLongInfo = LongRoleDescriptions.TryGetValue(seer.PlayerId, out (string Text, int Duration, bool Long) description) && GameStartTimeStamp + description.Duration > now; - string mHelp = (!showLongInfo || description.Long) && CustomGameMode.Standard.IsActiveOrIntegrated() ? "\n" + GetString("MyRoleCommandHelp") : string.Empty; + string mHelp = (!showLongInfo || description.Long) ? "\n" + GetString("MyRoleCommandHelp") : string.Empty; string color = seerTeam.GetTextColor(); string teamStr = seerTeam == Team.Impostor && seer.IsMadmate() ? "Madmate" : seerTeam.ToString(); string info = (showLongInfo ? description.Text : seer.GetRoleInfo()) + mHelp; SeerRealName = $"{GetString($"YouAre{teamStr}")}\n{info}"; } } + + if (GameStartTimeStamp + 50 > TimeStamp && Main.HasPlayedGM.TryGetValue(Options.CurrentGameMode, out var playedFCs) && !playedFCs.Contains(seer.FriendCode)) + SelfSuffix.Append("\n\n" + GetString($"GameModeTutorial.{Options.CurrentGameMode}")); } // Combine seer's job title and SelfTaskText with seer's player name and SelfMark @@ -3373,15 +3373,22 @@ public static class Utils return null; } - public static Texture2D LoadTextureFromResources(string path) + private static unsafe Texture2D LoadTextureFromResources(string path) { try { - Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(path); - Texture2D texture = new(1, 1, TextureFormat.ARGB32, false); - using MemoryStream ms = new(); - stream?.CopyTo(ms); - texture.LoadImage(ms.ToArray(), false); + Texture2D texture = new(2, 2, TextureFormat.ARGB32, true); + Assembly assembly = Assembly.GetExecutingAssembly(); + Stream stream = assembly.GetManifestResourceStream(path); + + if (stream != null) + { + var length = stream.Length; + var byteTexture = new Il2CppStructArray(length); + stream.Read(new Span(IntPtr.Add(byteTexture.Pointer, IntPtr.Size * 4).ToPointer(), (int)length)); + texture.LoadImage(byteTexture, false); + } + return texture; } catch { Logger.Error($"Error loading texture: {path}", "LoadImage"); } diff --git a/Patches/AprilFoolsPatch.cs b/Patches/AprilFoolsPatch.cs index c31cc8ed9..0d69c60b1 100644 --- a/Patches/AprilFoolsPatch.cs +++ b/Patches/AprilFoolsPatch.cs @@ -142,6 +142,13 @@ public static class LongBoiPatches return false; } + [HarmonyPatch(nameof(LongBoiPlayerBody.SetHeightFromColor))] + [HarmonyPrefix] + public static bool SetHeightFromColor_Prefix(int colorIndex) + { + return colorIndex != byte.MaxValue; + } + [HarmonyPatch(nameof(LongBoiPlayerBody.SetHeighFromDistanceHnS))] [HarmonyPrefix] public static bool LongBoyNeckSize_Patch(LongBoiPlayerBody __instance, ref float distance) diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index ad23b9c86..bc0eb9fc1 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -58,7 +58,7 @@ internal static class GameEndChecker { Ended = true; LoadingEndScreen = true; - + Main.AllPlayerControls.Do(pc => Camouflage.RpcSetSkin(pc, true, true, true)); NameNotifyManager.Reset(); @@ -981,7 +981,7 @@ internal static class GameEndChecker LifeSuppSystemType LifeSupp; if (systems.ContainsKey(SystemTypes.LifeSupp) && // Confirmation of sabotage existence - (LifeSupp = systems[SystemTypes.LifeSupp].TryCast()) != null && // Confirmation that cast is possible + (LifeSupp = systems[SystemTypes.LifeSupp].CastFast()) != null && // Confirmation that cast is possible LifeSupp.Countdown <= 0f) // Time up confirmation { // oxygen sabotage @@ -1002,7 +1002,7 @@ internal static class GameEndChecker ICriticalSabotage critical; if (sys != null && // Confirmation of sabotage existence - (critical = sys.TryCast()) != null && // Confirmation that cast is possible + (critical = sys.CastFast()) != null && // Confirmation that cast is possible critical.Countdown <= 0f) // Time up confirmation { // reactor sabotage diff --git a/Patches/ClientPatch.cs b/Patches/ClientPatch.cs index b52283632..2e8437139 100644 --- a/Patches/ClientPatch.cs +++ b/Patches/ClientPatch.cs @@ -376,6 +376,65 @@ public class InnerNetClientPatch } } +[HarmonyPatch(typeof(NetworkedPlayerInfo), nameof(NetworkedPlayerInfo.Serialize))] +internal static class NetworkedPlayerInfoSerializePatch +{ + public static bool Prefix(NetworkedPlayerInfo __instance, MessageWriter writer, bool initialState, ref bool __result) + { + writer.Write(__instance.PlayerId); + writer.WritePacked(__instance.ClientId); + writer.Write((byte)__instance.Outfits.Count); + + foreach (var keyValuePair in __instance.Outfits) + { + writer.Write((byte)keyValuePair.Key); + + if (initialState) + { + var oldOutfit = keyValuePair.Value; + NetworkedPlayerInfo.PlayerOutfit playerOutfit = new(); + Main.AllClientRealNames.TryGetValue(__instance.ClientId, out var name); + playerOutfit.Set(name ?? " ", oldOutfit.ColorId, oldOutfit.HatId, oldOutfit.SkinId, oldOutfit.VisorId, oldOutfit.PetId, oldOutfit.NamePlateId); + playerOutfit.Serialize(writer); + } + else { keyValuePair.Value.Serialize(writer); } + } + + writer.WritePacked(__instance.PlayerLevel); + + byte b = 0; + if (__instance.Disconnected) b |= 1; + if (__instance.IsDead) b |= 4; + + writer.Write(b); + writer.Write((ushort)__instance.Role.Role); + writer.Write(false); + + if (__instance.Tasks != null) + { + writer.Write((byte)__instance.Tasks.Count); + + for (int i = 0; i < __instance.Tasks.Count; i++) + { + __instance.Tasks[i].Serialize(writer); // False error + } + } + else + writer.Write(0); + + writer.Write(__instance.FriendCode ?? string.Empty); + + if (GameStates.CurrentServerType == GameStates.ServerType.Vanilla) + writer.Write(__instance.Puid ?? string.Empty); + else + writer.Write(string.Empty); + + if (!initialState) __instance.ClearDirtyBits(); + __result = true; + return false; + } +} + [HarmonyPatch(typeof(GameData), nameof(GameData.DirtyAllData))] internal class DirtyAllDataPatch { diff --git a/Patches/GameOptionsMenuPatch.cs b/Patches/GameOptionsMenuPatch.cs index 0ced05e95..8d8af7c9e 100644 --- a/Patches/GameOptionsMenuPatch.cs +++ b/Patches/GameOptionsMenuPatch.cs @@ -1284,9 +1284,9 @@ public class GameSettingMenuPatch ModSettingsButtons = []; ModSettingsTabs = []; - if ((CustomGameMode.NaturalDisasters.IsActiveOrIntegrated() || CustomGameMode.CaptureTheFlag.IsActiveOrIntegrated()) && GameStates.CurrentServerType == GameStates.ServerType.Modded && GameOptionsMenuPatch.UIReloadTS + 1 < Utils.TimeStamp) + if ((CustomGameMode.NaturalDisasters.IsActiveOrIntegrated() || CustomGameMode.CaptureTheFlag.IsActiveOrIntegrated()) && GameStates.CurrentServerType == GameStates.ServerType.ModdedWithoutCNOSupport && GameOptionsMenuPatch.UIReloadTS + 1 < Utils.TimeStamp) FastDestroyableSingleton.Instance.ShowPopUp(Translator.GetString("ModdedServerDoesntSupportCNOMessage")); - + Main.Instance.StartCoroutine(OptionShower.GetText()); } } diff --git a/Patches/GameStartManagerPatch.cs b/Patches/GameStartManagerPatch.cs index 851c9e02c..69c40c197 100644 --- a/Patches/GameStartManagerPatch.cs +++ b/Patches/GameStartManagerPatch.cs @@ -64,7 +64,7 @@ public static class GameStartManagerPatch __instance.HostViewButton.activeSprites.GetComponent().color = new(0f, 0.847f, 1f, 1f); __instance.HostViewButton.inactiveSprites.transform.Find("Shine").GetComponent().color = new(0f, 1f, 1f, 0.5f); } - else + else { __instance.ClientViewButton.activeTextColor = __instance.HostViewButton.inactiveTextColor = Color.black; __instance.ClientViewButton.inactiveSprites.GetComponent().color = new(0f, 0.647f, 1f, 1f); @@ -308,6 +308,7 @@ public static class GameStartManagerPatch var mismatchedClientName = string.Empty; string warningMessage = ""; + if (AmongUsClient.Instance.AmHost) { foreach (ClientData client in AmongUsClient.Instance.allClients) @@ -340,14 +341,13 @@ public static class GameStartManagerPatch AmongUsClient.Instance.ExitGame(DisconnectReasons.ExitGame); SceneChanger.ChangeScene("MainMenu"); } + if (ExitTimer != 0) warningMessage = Utils.ColorString(Color.red, string.Format(GetString("Warning.AutoExitAtMismatchedVersion"), $"{Main.ModName}", Math.Round(5 - ExitTimer).ToString())); } } - if (warningMessage == "") - { - warningText.gameObject.SetActive(false); - } + + if (warningMessage == "") { warningText.gameObject.SetActive(false); } else { warningText.text = warningMessage; @@ -357,7 +357,7 @@ public static class GameStartManagerPatch __instance.RulesPresetText.text = GetString($"Preset_{OptionItem.CurrentPreset + 1}"); // Lobby timer - if (!GameData.Instance || AmongUsClient.Instance.NetworkMode == NetworkModes.LocalGame || GameStates.CurrentServerType == GameStates.ServerType.Modded && GameStates.CurrentServerType == GameStates.ServerType.Custom) return; + if (!GameData.Instance || AmongUsClient.Instance.NetworkMode == NetworkModes.LocalGame || GameStates.CurrentServerType != GameStates.ServerType.Vanilla) return; float timer = Timer; @@ -561,4 +561,4 @@ public static class GameStartManagerBeginPatch return false; } } -} +} \ No newline at end of file diff --git a/Patches/LobbyPatch.cs b/Patches/LobbyPatch.cs index f2f0095cc..731a6a389 100644 --- a/Patches/LobbyPatch.cs +++ b/Patches/LobbyPatch.cs @@ -35,6 +35,11 @@ public static class HostInfoPanelSetUpPatch { private static TextMeshPro HostText; + public static bool Prefix(HostInfoPanel __instance) + { + return GameStates.IsLobby && __instance.player.ColorId != byte.MaxValue; + } + public static void Postfix(HostInfoPanel __instance) { try diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index 5204af5e8..c621be5df 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -272,8 +272,9 @@ internal static class CheckForEndVotingPatch if (tie) { var target = byte.MaxValue; + var playerNum = Main.AllPlayerControls.Length; - foreach (KeyValuePair data in VotingData.Where(x => x.Key < 15 && x.Value == max)) + foreach (KeyValuePair data in VotingData.Where(x => x.Key < playerNum && x.Value == max)) { if (Main.BrakarVoteFor.Contains(data.Key)) { diff --git a/Patches/OutroPatch.cs b/Patches/OutroPatch.cs index 55a92e6e4..09a043ac5 100644 --- a/Patches/OutroPatch.cs +++ b/Patches/OutroPatch.cs @@ -124,6 +124,11 @@ internal static class EndGamePatch case CustomGameMode.RoomRush: Main.AllPlayerControls.Do(x => RoomRush.HasPlayedFriendCodes.Add(x.FriendCode)); break; + default: + if (Main.HasPlayedGM.TryGetValue(Options.CurrentGameMode, out var playedFCs)) + playedFCs.UnionWith(Main.AllPlayerControls.Select(x => x.FriendCode)); + + break; } } } @@ -559,4 +564,4 @@ internal static class SetEverythingUpPatch return name; } } -} +} \ No newline at end of file diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index bdbabea73..b04bacbd8 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -283,7 +283,7 @@ internal static class CheckMurderPatch } if (Pursuer.OnClientMurder(killer)) return false; - + Seamstress.OnAnyoneCheckMurder(killer, target); if (killer.PlayerId != target.PlayerId) @@ -593,12 +593,12 @@ internal static class MurderPlayerPatch public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] PlayerControl target, [HarmonyArgument(1)] MurderResultFlags resultFlags, ref bool __state) { if (GameStates.IsLobby || AntiBlackout.SkipTasks) return false; - + var protectedByClient = resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.IsProtected(); var protectedByHost = resultFlags.HasFlag(MurderResultFlags.FailedProtected); var failed = resultFlags.HasFlag(MurderResultFlags.FailedError); __state = !protectedByClient && !protectedByHost && !failed; - + Logger.Info($"{__instance.GetNameWithRole().RemoveHtmlTags()} => {target.GetNameWithRole().RemoveHtmlTags()} - {nameof(protectedByClient)}: {protectedByClient}, {nameof(protectedByHost)}: {protectedByHost}, {nameof(failed)}: {failed}", "MurderPlayer"); RandomSpawn.CustomNetworkTransformHandleRpcPatch.HasSpawned.Add(__instance.PlayerId); @@ -1635,10 +1635,8 @@ internal static class FixedUpdatePatch switch (seer.GetCustomRole()) { - case CustomRoles.Lookout: - if (seer.IsAlive() && target.IsAlive()) - Mark.Append(ColorString(GetRoleColor(CustomRoles.Lookout), " " + target.PlayerId) + " "); - + case CustomRoles.Lookout when seer.IsAlive() && target.IsAlive(): + Mark.Append(ColorString(GetRoleColor(CustomRoles.Lookout), " " + target.PlayerId) + " "); break; case CustomRoles.PlagueBearer when PlagueBearer.IsPlagued(seer.PlayerId, target.PlayerId): Mark.Append($"●"); @@ -1669,20 +1667,14 @@ internal static class FixedUpdatePatch Mark.Append($"○"); break; - case CustomRoles.Analyst: - if ((Main.PlayerStates[seer.PlayerId].Role as Analyst).CurrentTarget.ID == target.PlayerId) - Mark.Append($"○"); - + case CustomRoles.Analyst when (Main.PlayerStates[seer.PlayerId].Role as Analyst).CurrentTarget.ID == target.PlayerId: + Mark.Append($"○"); break; - case CustomRoles.Samurai: - if ((Main.PlayerStates[seer.PlayerId].Role as Samurai).Target.Id == target.PlayerId) - Mark.Append($"○"); - + case CustomRoles.Samurai when (Main.PlayerStates[seer.PlayerId].Role as Samurai).Target.Id == target.PlayerId: + Mark.Append($"○"); break; - case CustomRoles.Puppeteer: - if (Puppeteer.PuppeteerList.ContainsValue(seer.PlayerId) && Puppeteer.PuppeteerList.ContainsKey(target.PlayerId)) - Mark.Append($"◆"); - + case CustomRoles.Puppeteer when Puppeteer.PuppeteerList.ContainsValue(seer.PlayerId) && Puppeteer.PuppeteerList.ContainsKey(target.PlayerId): + Mark.Append($"◆"); break; case CustomRoles.EvilTracker: Mark.Append(EvilTracker.GetTargetMark(seer, target)); @@ -1778,6 +1770,9 @@ internal static class FixedUpdatePatch break; } + if (self && GameStartTimeStamp + 50 > TimeStamp && Main.HasPlayedGM.TryGetValue(Options.CurrentGameMode, out var playedFCs) && !playedFCs.Contains(seer.FriendCode)) + Suffix.Append("\n\n" + GetString($"GameModeTutorial.{Options.CurrentGameMode}")); + if (MeetingStates.FirstMeeting && Main.ShieldPlayer == target.FriendCode && !string.IsNullOrEmpty(target.FriendCode) && !self && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.SoloKombat or CustomGameMode.FFA) Suffix.Append(GetString("DiedR1Warning")); // Devourer diff --git a/Patches/ShipStatusPatch.cs b/Patches/ShipStatusPatch.cs index 087515ce4..faba8ea05 100644 --- a/Patches/ShipStatusPatch.cs +++ b/Patches/ShipStatusPatch.cs @@ -233,7 +233,7 @@ internal static class RepairSystemPatch } } - if (new List {SystemTypes.Electrical, SystemTypes.Reactor, SystemTypes.Laboratory, SystemTypes.LifeSupp, SystemTypes.Comms, SystemTypes.HeliSabotage, SystemTypes.MushroomMixupSabotage}.Contains(systemType) && !Utils.IsActive(systemType)) + if (new List { SystemTypes.Electrical, SystemTypes.Reactor, SystemTypes.Laboratory, SystemTypes.LifeSupp, SystemTypes.Comms, SystemTypes.HeliSabotage, SystemTypes.MushroomMixupSabotage }.Contains(systemType) && !Utils.IsActive(systemType)) { bool petcd = !Options.UseUnshiftTrigger.GetBool() && !Options.UsePhantomBasis.GetBool(); @@ -440,7 +440,7 @@ internal static class ShipStatusSerializePatch if (!AmongUsClient.Instance.AmHost || initialState) return; bool cancel = Main.AllPlayerControls.Any(VentilationSystemDeterioratePatch.BlockVentInteraction); - var ventilationSystem = __instance.Systems[SystemTypes.Ventilation].TryCast(); + var ventilationSystem = __instance.Systems[SystemTypes.Ventilation].CastFast(); if (cancel && ventilationSystem is { IsDirty: true }) { diff --git a/Patches/UsablesPatch.cs b/Patches/UsablesPatch.cs index fff382ddd..65076de6e 100644 --- a/Patches/UsablesPatch.cs +++ b/Patches/UsablesPatch.cs @@ -77,7 +77,7 @@ internal static class CanUseVentPatch // Check vent cleaning if (ShipStatus.Instance.Systems.TryGetValue(SystemTypes.Ventilation, out ISystemType systemType)) { - var ventilationSystem = systemType.TryCast(); + var ventilationSystem = systemType.CastFast(); // If someone is cleaning a vent, you can't get into that vent if (ventilationSystem != null && ventilationSystem.IsVentCurrentlyBeingCleaned(__instance.Id)) couldUse = false; } diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 5c62b7278..92a33185c 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -4600,6 +4600,13 @@ "Grappler": "Grappler", "GrapplerInfo": "Prevent kills and pull players towards you", "GrapplerInfoLong": "(Crewmates):\\nAs the Grappler, you can prevent 1 kill in each round if you have an ability use left at the end of the last round. The first kill attempt will be blocked and the target will be teleported to you and not die. Your ability doesn't work during sabotages.", + "GameModeTutorial.SoloKombat": "The current game mode is <#f55252>Solo PVP.\\nUse your kill button to damage others. If someone's HP goes below 0,\\nthey get teleported outside the map for some time, then respawn.\\nWhen you kill someone, you get a small buff and 1 point.\\nThe person with the most points at the end of the time limit wins!", + "GameModeTutorial.FFA": "The current game mode is <#00ffff>Free For All (FFA).\\nEveryone can kill. Be the last one alive to win!\\nWhen you kill someone, you get a random buff or debuff.", + "GameModeTutorial.HotPotato": "The current game mode is <#e8cd46>Hot Potato.\\nThe person with a black name holds the hot potato.\\nThey can pass it to someone by going near them.\\nThe previous holder's name is colored in cyan, they cannot regain\\nthe hot potato until someone else got it, unless only 2 players remain.\\nWhen the time limit ends, the holder of the hot potato dies, and the timer resets,\\nthe hot potato is assigned to a random player. Be the last player alive to win!", + "GameModeTutorial.HideAndSeek": "The current game mode is <#345eeb>Hide And Seek (Modded).\\nSeekers must kill all hiders to win,\\nhiders must complete tasks to lower the timer.\\nIf the time limit ends, hiders win.", + "GameModeTutorial.Speedrun": "The current game mode is <#800080>Speedrun.\\nComplete all tasks to gain the kill button, then kill everyone to win.\\nYou must do a task or kill within your time limit, else you suicide.", + "GameModeTutorial.CaptureTheFlag": "The current game mode is <#1313c2>Capture The Flag.\\nGet to the base of the enemy team and press Vanish to pick up their flag.\\nBring it back to your own base for your team to win.\\nPress Kill on someone to tag them, this will get rid of that player temporarily or permanently.", + "GameModeTutorial.NaturalDisasters": "The current game mode is <#03fc4a>Natural Disasters.\\nVarious disasters will constantly spawn on the map.\\nSome kill you instantly, some just debuff you temporarily.\\nBe the last player alive to win!", "GameModeListTitle": "Game Modes", "ModeStandard": "Gamemode: Standard", "FFA": "Free For All", diff --git a/Roles/Crewmate/Adventurer.cs b/Roles/Crewmate/Adventurer.cs index 706bba6d8..345f94613 100644 --- a/Roles/Crewmate/Adventurer.cs +++ b/Roles/Crewmate/Adventurer.cs @@ -213,7 +213,7 @@ internal class Adventurer : RoleBase case Weapon.Wrench: if (Utils.IsActive(SystemTypes.Electrical)) { - var switchSystem = ShipStatus.Instance?.Systems?[SystemTypes.Electrical]?.TryCast(); + var switchSystem = ShipStatus.Instance?.Systems?[SystemTypes.Electrical]?.CastFast(); if (switchSystem != null) { diff --git a/Roles/Crewmate/SabotageMaster.cs b/Roles/Crewmate/SabotageMaster.cs index fd8442753..318da5a8e 100644 --- a/Roles/Crewmate/SabotageMaster.cs +++ b/Roles/Crewmate/SabotageMaster.cs @@ -127,7 +127,7 @@ public class SabotageMaster : RoleBase switch (activeSystem) { case SystemTypes.Electrical: - var switchSystem = ShipStatus.Instance.Systems[SystemTypes.Electrical].TryCast(); + var switchSystem = ShipStatus.Instance.Systems[SystemTypes.Electrical].CastFast(); if (switchSystem == null) break; switchSystem.ActualSwitches = switchSystem.ExpectedSwitches; switchSystem.IsDirty = true;