using System.Collections.Generic; using StardewModdingAPI.Enums; using StardewModdingAPI.Events; using StardewModdingAPI.Utilities; using StardewValley; using StardewValley.Menus; namespace StardewModdingAPI { /// Provides information about the current game state. public static class Context { /********* ** Fields *********/ /// Whether the player has loaded a save and the world has finished initializing. private static readonly PerScreen IsWorldReadyForScreen = new PerScreen(); /// The current stage in the game's loading process. private static readonly PerScreen LoadStageForScreen = new PerScreen(); /// Whether a player save has been loaded. internal static bool IsSaveLoaded => Game1.hasLoadedGame && !(Game1.activeClickableMenu is TitleMenu); /// Whether the game is currently writing to the save file. internal static bool IsSaving => Game1.activeClickableMenu is SaveGameMenu || Game1.activeClickableMenu is ShippingMenu; // saving is performed by SaveGameMenu, but it's wrapped by ShippingMenu on days when the player shipping something /// The active split-screen instance IDs. internal static readonly ISet ActiveScreenIds = new HashSet(); /// The last screen ID that was removed from the game, used to synchronize . internal static int LastRemovedScreenId = -1; /// The current stage in the game's loading process. internal static LoadStage LoadStage { get => Context.LoadStageForScreen.Value; set => Context.LoadStageForScreen.Value = value; } /********* ** Accessors *********/ /**** ** Game/player state ****/ /// Whether the game has performed core initialization. This becomes true right before the first update tick. public static bool IsGameLaunched { get; internal set; } /// Whether the player has loaded a save and the world has finished initializing. public static bool IsWorldReady { get => Context.IsWorldReadyForScreen.Value; set => Context.IsWorldReadyForScreen.Value = value; } /// Whether is true and the player is free to act in the world (no menu is displayed, no cutscene is in progress, etc). public static bool IsPlayerFree => Context.IsWorldReady && Game1.currentLocation != null && Game1.activeClickableMenu == null && !Game1.dialogueUp && (!Game1.eventUp || Game1.isFestival()); /// Whether is true and the player is free to move (e.g. not using a tool). public static bool CanPlayerMove => Context.IsPlayerFree && Game1.player.CanMove; /// Whether the game is currently running the draw loop. This isn't relevant to most mods, since you should use events to draw to the screen. public static bool IsInDrawLoop { get; internal set; } /**** ** Multiplayer ****/ /// The unique ID of the current screen in split-screen mode. A screen is always assigned a new ID when it's opened (so a player who quits and rejoins has a new screen ID). public static int ScreenId => Game1.game1?.instanceId ?? 0; /// Whether the game is running in multiplayer or split-screen mode (regardless of whether any other players are connected). See and for more specific checks. public static bool IsMultiplayer => Context.IsSplitScreen || (Context.IsWorldReady && Game1.multiplayerMode != Game1.singlePlayer); /// Whether this player is running on the main player's computer. This is true for both the main player and split-screen players. public static bool IsOnHostComputer => Context.IsMainPlayer || Context.IsSplitScreen; /// Whether the current player is playing in a split-screen. This is only applicable when is true, since split-screen players on another computer are just regular remote players. public static bool IsSplitScreen => LocalMultiplayer.IsLocalMultiplayer(); /// Whether there are players connected over the network. public static bool HasRemotePlayers => Context.IsMultiplayer && !Game1.hasLocalClientsOnly; /// Whether the current player is the main player. This is always true in single-player, and true when hosting in multiplayer. public static bool IsMainPlayer => Game1.IsMasterGame && !(TitleMenu.subMenu is FarmhandMenu); /********* ** Public methods *********/ /// Get whether a screen ID is still active. /// The screen ID. public static bool HasScreenId(int id) { return Context.ActiveScreenIds.Contains(id); } } }