using System; using System.Threading.Tasks; using Microsoft.Xna.Framework.Input; using StardewModdingAPI.Events; using StardewModdingAPI.Framework; using StardewValley; using StardewValley.Events; namespace StardewModdingAPI.Utilities { /// An implementation of which automatically calls the parent instance for any method that's not overridden. /// The mod hooks are primarily meant for SMAPI to use. Using this directly in mods is a last resort, since it's very easy to break SMAPI this way. This class requires that SMAPI is present in the parent chain. public class DelegatingModHooks : ModHooks { /********* ** Accessors *********/ /// The underlying instance to delegate to by default. public ModHooks Parent { get; } /********* ** Public methods *********/ /// Construct an instance. /// The underlying instance to delegate to by default. public DelegatingModHooks(ModHooks modHooks) { this.AssertSmapiInChain(modHooks); this.Parent = modHooks; } /// Raised before the in-game clock changes. /// Run the vanilla update logic. /// In mods, consider using instead. public override void OnGame1_PerformTenMinuteClockUpdate(Action action) { this.Parent.OnGame1_PerformTenMinuteClockUpdate(action); } /// Raised before initializing the new day and saving. /// Run the vanilla update logic. /// In mods, consider using or instead. public override void OnGame1_NewDayAfterFade(Action action) { this.Parent.OnGame1_NewDayAfterFade(action); } /// Raised before showing the end-of-day menus (e.g. shipping menus, level-up screen, etc). /// Run the vanilla update logic. public override void OnGame1_ShowEndOfNightStuff(Action action) { this.Parent.OnGame1_ShowEndOfNightStuff(action); } /// Raised before updating the gamepad, mouse, and keyboard input state. /// The keyboard state. /// The mouse state. /// The gamepad state. /// Run the vanilla update logic. /// In mods, consider using instead. public override void OnGame1_UpdateControlInput(ref KeyboardState keyboardState, ref MouseState mouseState, ref GamePadState gamePadState, Action action) { this.Parent.OnGame1_UpdateControlInput(ref keyboardState, ref mouseState, ref gamePadState, action); } /// Raised before a location is updated for the local player entering it. /// The location that will be updated. /// Run the vanilla update logic. /// In mods, consider using instead. public override void OnGameLocation_ResetForPlayerEntry(GameLocation location, Action action) { this.Parent.OnGameLocation_ResetForPlayerEntry(location, action); } /// Raised before the game checks for an action to trigger for a player interaction with a tile. /// The location being checked. /// The tile position being checked. /// The game's current position and size within the map, measured in pixels. /// The player interacting with the tile. /// Run the vanilla update logic. /// Returns whether the interaction was handled. public override bool OnGameLocation_CheckAction(GameLocation location, xTile.Dimensions.Location tileLocation, xTile.Dimensions.Rectangle viewport, Farmer who, Func action) { return this.Parent.OnGameLocation_CheckAction(location, tileLocation, viewport, who, action); } /// Raised before the game picks a night event to show on the farm after the player sleeps. /// Run the vanilla update logic. /// Returns the selected farm event. public override FarmEvent OnUtility_PickFarmEvent(Func action) { return this.Parent.OnUtility_PickFarmEvent(action); } /// Start an asynchronous task for the game. /// The task to start. /// A unique key which identifies the task. public override Task StartTask(Task task, string id) { return this.Parent.StartTask(task, id); } /// Start an asynchronous task for the game. /// The type returned by the task when it completes. /// The task to start. /// A unique key which identifies the task. public override Task StartTask(Task task, string id) { return this.Parent.StartTask(task, id); } /********* ** Private methods *********/ /// Assert that SMAPI's mod hook implementation is in the inheritance chain. /// The mod hooks to check. private void AssertSmapiInChain(ModHooks hooks) { // this is SMAPI if (this is SModHooks) return; // SMAPI in delegated chain for (ModHooks? cur = hooks; cur != null; cur = (cur as DelegatingModHooks)?.Parent) { if (cur is SModHooks) return; } // SMAPI not found throw new InvalidOperationException($"Can't create a {nameof(DelegatingModHooks)} instance without SMAPI's mod hooks in the parent chain."); } } }