add player check-action events (#310)
This doesn't work reliably yet, since the game only calls the checkAction hook from the base GameLocation.CheckAction method.
This commit is contained in:
parent
382b5fe914
commit
678fe27f3f
|
@ -0,0 +1,56 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using StardewValley;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.Events
|
||||||
|
{
|
||||||
|
/// <summary>Event arguments for an <see cref="IPlayerEvents.CheckedForAction"/> event.</summary>
|
||||||
|
public class CheckedForActionEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Properties
|
||||||
|
*********/
|
||||||
|
/// <summary>The underlying field for <see cref="ActionPropertyValue"/>.</summary>
|
||||||
|
private readonly Lazy<string> ActionPropertyValueImpl;
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Accessors
|
||||||
|
*********/
|
||||||
|
/// <summary>The player who checked for an action.</summary>
|
||||||
|
public Farmer Player { get; }
|
||||||
|
|
||||||
|
/// <summary>The tile checked.</summary>
|
||||||
|
public Vector2 Tile { get; }
|
||||||
|
|
||||||
|
/// <summary>The value of the <c>Action</c> tile property, if any.</summary>
|
||||||
|
public string ActionPropertyValue => this.ActionPropertyValueImpl.Value;
|
||||||
|
|
||||||
|
/// <summary>The current cursor position. This may differ from <see cref="Tile"/>, due to how the game selects the target tile for actions in some cases.</summary>
|
||||||
|
public ICursorPosition Cursor { get; }
|
||||||
|
|
||||||
|
/// <summary>Whether the affected player is the local one.</summary>
|
||||||
|
public bool IsLocalPlayer => this.Player.IsLocalPlayer;
|
||||||
|
|
||||||
|
/// <summary>Whether the game performed an action in response to the check. Note that the game sometimes handles input without marking it handled (e.g. when activating a TV or fireplace).</summary>
|
||||||
|
public bool WasHandled { get; }
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
/// <param name="player">The player for whom the action was checked.</param>
|
||||||
|
/// <param name="tile">The tile checked.</param>
|
||||||
|
/// <param name="cursorPosition">The current cursor position.</param>
|
||||||
|
/// <param name="actionPropertyValue">The value of the <c>Action</c> tile property, if any.</param>
|
||||||
|
/// <param name="wasHandled">Whether the game performed an action in response to the check.</param>
|
||||||
|
internal CheckedForActionEventArgs(Farmer player, Vector2 tile, ICursorPosition cursorPosition, Lazy<string> actionPropertyValue, bool wasHandled)
|
||||||
|
{
|
||||||
|
this.Player = player;
|
||||||
|
this.Tile = tile;
|
||||||
|
this.Cursor = cursorPosition;
|
||||||
|
this.ActionPropertyValueImpl = actionPropertyValue;
|
||||||
|
this.WasHandled = wasHandled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using StardewValley;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.Events
|
||||||
|
{
|
||||||
|
/// <summary>Event arguments for an <see cref="IPlayerEvents.CheckingForAction"/> event.</summary>
|
||||||
|
public class CheckingForActionEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Properties
|
||||||
|
*********/
|
||||||
|
/// <summary>The underlying field for <see cref="ActionPropertyValue"/>.</summary>
|
||||||
|
private readonly Lazy<string> ActionPropertyValueImpl;
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Accessors
|
||||||
|
*********/
|
||||||
|
/// <summary>The player checking for an action.</summary>
|
||||||
|
public Farmer Player { get; }
|
||||||
|
|
||||||
|
/// <summary>The tile being checked.</summary>
|
||||||
|
public Vector2 Tile { get; }
|
||||||
|
|
||||||
|
/// <summary>The value of the <c>Action</c> tile property, if any.</summary>
|
||||||
|
public string ActionPropertyValue => this.ActionPropertyValueImpl.Value;
|
||||||
|
|
||||||
|
/// <summary>The current cursor position. This may differ from <see cref="Tile"/>, due to how the game selects the target tile for actions in some cases.</summary>
|
||||||
|
public ICursorPosition Cursor { get; }
|
||||||
|
|
||||||
|
/// <summary>Whether the affected player is the local one.</summary>
|
||||||
|
public bool IsLocalPlayer => this.Player.IsLocalPlayer;
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
/// <param name="player">The player for whom the action is being checked.</param>
|
||||||
|
/// <param name="tile">The tile being checked.</param>
|
||||||
|
/// <param name="cursorPosition">The current cursor position.</param>
|
||||||
|
/// <param name="actionPropertyValue">The value of the <c>Action</c> tile property, if any.</param>
|
||||||
|
internal CheckingForActionEventArgs(Farmer player, Vector2 tile, ICursorPosition cursorPosition, Lazy<string> actionPropertyValue)
|
||||||
|
{
|
||||||
|
this.Player = player;
|
||||||
|
this.Tile = tile;
|
||||||
|
this.Cursor = cursorPosition;
|
||||||
|
this.ActionPropertyValueImpl = actionPropertyValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,12 @@ namespace StardewModdingAPI.Events
|
||||||
/// <summary>Events raised when the player data changes.</summary>
|
/// <summary>Events raised when the player data changes.</summary>
|
||||||
public interface IPlayerEvents
|
public interface IPlayerEvents
|
||||||
{
|
{
|
||||||
|
/// <summary>Raised before the game checks for an action in response to a player input. That includes activating an interactive object, opening a chest, putting an item in a machine, etc. NOTE: this event is currently only raised for the current player.</summary>
|
||||||
|
event EventHandler<CheckingForActionEventArgs> CheckingForAction;
|
||||||
|
|
||||||
|
/// <summary>Raised after the game checks for an action in response to a player input. That includes activating an interactive object, opening a chest, putting an item in a machine, etc. NOTE: this event is currently only raised for the current player.</summary>
|
||||||
|
event EventHandler<CheckedForActionEventArgs> CheckedForAction;
|
||||||
|
|
||||||
/// <summary>Raised after items are added or removed to a player's inventory. NOTE: this event is currently only raised for the current player.</summary>
|
/// <summary>Raised after items are added or removed to a player's inventory. NOTE: this event is currently only raised for the current player.</summary>
|
||||||
event EventHandler<InventoryChangedEventArgs> InventoryChanged;
|
event EventHandler<InventoryChangedEventArgs> InventoryChanged;
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,12 @@ namespace StardewModdingAPI.Framework.Events
|
||||||
/****
|
/****
|
||||||
** Player
|
** Player
|
||||||
****/
|
****/
|
||||||
|
/// <summary>Raised before the game checks for an action in response to a player click.</summary>
|
||||||
|
public readonly ManagedEvent<CheckingForActionEventArgs> CheckingForAction;
|
||||||
|
|
||||||
|
/// <summary>Raised after the game checks for an action in response to a player click.</summary>
|
||||||
|
public readonly ManagedEvent<CheckedForActionEventArgs> CheckedForAction;
|
||||||
|
|
||||||
/// <summary>Raised after items are added or removed to a player's inventory.</summary>
|
/// <summary>Raised after items are added or removed to a player's inventory.</summary>
|
||||||
public readonly ManagedEvent<InventoryChangedEventArgs> InventoryChanged;
|
public readonly ManagedEvent<InventoryChangedEventArgs> InventoryChanged;
|
||||||
|
|
||||||
|
@ -407,6 +413,8 @@ namespace StardewModdingAPI.Framework.Events
|
||||||
this.ModMessageReceived = ManageEventOf<ModMessageReceivedEventArgs>(nameof(IModEvents.Multiplayer), nameof(IMultiplayerEvents.ModMessageReceived));
|
this.ModMessageReceived = ManageEventOf<ModMessageReceivedEventArgs>(nameof(IModEvents.Multiplayer), nameof(IMultiplayerEvents.ModMessageReceived));
|
||||||
this.PeerDisconnected = ManageEventOf<PeerDisconnectedEventArgs>(nameof(IModEvents.Multiplayer), nameof(IMultiplayerEvents.PeerDisconnected));
|
this.PeerDisconnected = ManageEventOf<PeerDisconnectedEventArgs>(nameof(IModEvents.Multiplayer), nameof(IMultiplayerEvents.PeerDisconnected));
|
||||||
|
|
||||||
|
this.CheckingForAction = ManageEventOf<CheckingForActionEventArgs>(nameof(IModEvents.Player), nameof(IPlayerEvents.CheckingForAction));
|
||||||
|
this.CheckedForAction = ManageEventOf<CheckedForActionEventArgs>(nameof(IModEvents.Player), nameof(IPlayerEvents.CheckedForAction));
|
||||||
this.InventoryChanged = ManageEventOf<InventoryChangedEventArgs>(nameof(IModEvents.Player), nameof(IPlayerEvents.InventoryChanged));
|
this.InventoryChanged = ManageEventOf<InventoryChangedEventArgs>(nameof(IModEvents.Player), nameof(IPlayerEvents.InventoryChanged));
|
||||||
this.LevelChanged = ManageEventOf<LevelChangedEventArgs>(nameof(IModEvents.Player), nameof(IPlayerEvents.LevelChanged));
|
this.LevelChanged = ManageEventOf<LevelChangedEventArgs>(nameof(IModEvents.Player), nameof(IPlayerEvents.LevelChanged));
|
||||||
this.Warped = ManageEventOf<WarpedEventArgs>(nameof(IModEvents.Player), nameof(IPlayerEvents.Warped));
|
this.Warped = ManageEventOf<WarpedEventArgs>(nameof(IModEvents.Player), nameof(IPlayerEvents.Warped));
|
||||||
|
|
|
@ -9,6 +9,20 @@ namespace StardewModdingAPI.Framework.Events
|
||||||
/*********
|
/*********
|
||||||
** Accessors
|
** Accessors
|
||||||
*********/
|
*********/
|
||||||
|
/// <summary>Raised before the game checks for an action in response to a player click. That includes activating an interactive object, opening a chest, talking to an NPC, putting an item in a machine, etc. NOTE: this event is currently only raised for the current player.</summary>
|
||||||
|
public event EventHandler<CheckingForActionEventArgs> CheckingForAction
|
||||||
|
{
|
||||||
|
add => this.EventManager.CheckingForAction.Add(value);
|
||||||
|
remove => this.EventManager.CheckingForAction.Remove(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Raised after the game checks for an action in response to a player input. That includes activating an interactive object, opening a chest, putting an item in a machine, etc. NOTE: this event is currently only raised for the current player.</summary>
|
||||||
|
public event EventHandler<CheckedForActionEventArgs> CheckedForAction
|
||||||
|
{
|
||||||
|
add => this.EventManager.CheckedForAction.Add(value);
|
||||||
|
remove => this.EventManager.CheckedForAction.Remove(value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Raised after items are added or removed to a player's inventory. NOTE: this event is currently only raised for the local player.</summary>
|
/// <summary>Raised after items are added or removed to a player's inventory. NOTE: this event is currently only raised for the local player.</summary>
|
||||||
public event EventHandler<InventoryChangedEventArgs> InventoryChanged
|
public event EventHandler<InventoryChangedEventArgs> InventoryChanged
|
||||||
{
|
{
|
||||||
|
|
|
@ -163,7 +163,7 @@ namespace StardewModdingAPI.Framework
|
||||||
this.OnGameExiting = onGameExiting;
|
this.OnGameExiting = onGameExiting;
|
||||||
Game1.input = new SInputState();
|
Game1.input = new SInputState();
|
||||||
Game1.multiplayer = new SMultiplayer(monitor, eventManager, jsonHelper, modRegistry, reflection, this.OnModMessageReceived);
|
Game1.multiplayer = new SMultiplayer(monitor, eventManager, jsonHelper, modRegistry, reflection, this.OnModMessageReceived);
|
||||||
Game1.hooks = new SModHooks(this.OnNewDayAfterFade);
|
Game1.hooks = new SModHooks(this.OnNewDayAfterFade, this.OnLocationCheckingAction);
|
||||||
|
|
||||||
// init observables
|
// init observables
|
||||||
Game1.locations = new ObservableCollection<GameLocation>();
|
Game1.locations = new ObservableCollection<GameLocation>();
|
||||||
|
@ -193,9 +193,38 @@ namespace StardewModdingAPI.Framework
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>A callback invoked before <see cref="Game1.newDayAfterFade"/> runs.</summary>
|
/// <summary>A callback invoked before <see cref="Game1.newDayAfterFade"/> runs.</summary>
|
||||||
protected void OnNewDayAfterFade()
|
/// <param name="resume">Resume the vanilla logic.</param>
|
||||||
|
protected void OnNewDayAfterFade(Action resume)
|
||||||
{
|
{
|
||||||
this.Events.DayEnding.RaiseEmpty();
|
this.Events.DayEnding.RaiseEmpty();
|
||||||
|
resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>A callback invoked before <see cref="GameLocation.checkAction"/> runs.</summary>
|
||||||
|
/// <param name="location">The location being checked.</param>
|
||||||
|
/// <param name="tileLocation">The tile coordinate being checked.</param>
|
||||||
|
/// <param name="who">The player checking for an action.</param>
|
||||||
|
/// <param name="resume">Resume the default logic.</param>
|
||||||
|
private bool OnLocationCheckingAction(GameLocation location, Location tileLocation, Farmer who, Func<bool> resume)
|
||||||
|
{
|
||||||
|
// check for event listeners
|
||||||
|
bool hasPreListeners = this.Events.CheckingForAction.HasListeners();
|
||||||
|
bool hasPostListeners = this.Events.CheckedForAction.HasListeners();
|
||||||
|
if (!hasPreListeners && !hasPostListeners)
|
||||||
|
return resume();
|
||||||
|
|
||||||
|
// get tile info
|
||||||
|
Vector2 tilePos = new Vector2(tileLocation.X, tileLocation.Y);
|
||||||
|
Lazy<string> actionPropertyValue = new Lazy<string>(() => location.doesTileHaveProperty(tileLocation.X, tileLocation.Y, "Action", "Buildings"));
|
||||||
|
|
||||||
|
// raise events
|
||||||
|
if (hasPreListeners)
|
||||||
|
this.Events.CheckingForAction.Raise(new CheckingForActionEventArgs(who, tilePos, this.Input.CursorPosition, actionPropertyValue));
|
||||||
|
bool result = resume();
|
||||||
|
if (hasPostListeners)
|
||||||
|
this.Events.CheckedForAction.Raise(new CheckedForActionEventArgs(who, tilePos, this.Input.CursorPosition, actionPropertyValue, result));
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>A callback invoked when a mod message is received.</summary>
|
/// <summary>A callback invoked when a mod message is received.</summary>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
|
using xTile.Dimensions;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Framework
|
namespace StardewModdingAPI.Framework
|
||||||
{
|
{
|
||||||
|
@ -10,25 +11,39 @@ namespace StardewModdingAPI.Framework
|
||||||
** Properties
|
** Properties
|
||||||
*********/
|
*********/
|
||||||
/// <summary>A callback to invoke before <see cref="Game1.newDayAfterFade"/> runs.</summary>
|
/// <summary>A callback to invoke before <see cref="Game1.newDayAfterFade"/> runs.</summary>
|
||||||
private readonly Action BeforeNewDayAfterFade;
|
private readonly Action<Action> BeforeNewDayAfterFade;
|
||||||
|
|
||||||
|
/// <summary>A callback to invoke before <see cref="GameLocation.checkAction"/> runs.</summary>
|
||||||
|
private readonly Func<GameLocation, Location, Farmer, Func<bool>, bool> BeforeCheckAction;
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
*********/
|
*********/
|
||||||
/// <summary>Construct an instance.</summary>
|
/// <summary>Construct an instance.</summary>
|
||||||
/// <param name="beforeNewDayAfterFade">A callback to invoke before <see cref="Game1.newDayAfterFade"/> runs.</param>
|
/// <param name="beforeNewDayAfterFade">A callback to invoke before <see cref="Game1.newDayAfterFade"/> runs.</param>
|
||||||
public SModHooks(Action beforeNewDayAfterFade)
|
/// <param name="beforeCheckAction">A callback to invoke before <see cref="GameLocation.checkAction"/> runs.</param>
|
||||||
|
public SModHooks(Action<Action> beforeNewDayAfterFade, Func<GameLocation, Location, Farmer, Func<bool>, bool> beforeCheckAction)
|
||||||
{
|
{
|
||||||
this.BeforeNewDayAfterFade = beforeNewDayAfterFade;
|
this.BeforeNewDayAfterFade = beforeNewDayAfterFade;
|
||||||
|
this.BeforeCheckAction = beforeCheckAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>A hook invoked when <see cref="Game1.newDayAfterFade"/> is called.</summary>
|
/// <summary>A hook invoked when <see cref="Game1.newDayAfterFade"/> is called.</summary>
|
||||||
/// <param name="action">The vanilla <see cref="Game1.newDayAfterFade"/> logic.</param>
|
/// <param name="resume">Resume the vanilla logic.</param>
|
||||||
public override void OnGame1_NewDayAfterFade(Action action)
|
public override void OnGame1_NewDayAfterFade(Action resume)
|
||||||
{
|
{
|
||||||
this.BeforeNewDayAfterFade?.Invoke();
|
this.BeforeNewDayAfterFade(resume);
|
||||||
action();
|
}
|
||||||
|
|
||||||
|
/// <summary>A hook invoked when <see cref="GameLocation.checkAction"/> is called.</summary>
|
||||||
|
/// <param name="location">The location being checked.</param>
|
||||||
|
/// <param name="tileLocation">The tile coordinate being checked.</param>
|
||||||
|
/// <param name="viewport">The current viewport.</param>
|
||||||
|
/// <param name="who">The player checking for an action.</param>
|
||||||
|
/// <param name="resume">Resume the default logic.</param>
|
||||||
|
public override bool OnGameLocation_CheckAction(GameLocation location, Location tileLocation, Rectangle viewport, Farmer who, Func<bool> resume)
|
||||||
|
{
|
||||||
|
return this.BeforeCheckAction(location, tileLocation, who, resume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,8 @@
|
||||||
<Compile Include="Events\ButtonPressedEventArgs.cs" />
|
<Compile Include="Events\ButtonPressedEventArgs.cs" />
|
||||||
<Compile Include="Events\ButtonReleasedEventArgs.cs" />
|
<Compile Include="Events\ButtonReleasedEventArgs.cs" />
|
||||||
<Compile Include="Events\ChangeType.cs" />
|
<Compile Include="Events\ChangeType.cs" />
|
||||||
|
<Compile Include="Events\CheckedForActionEventArgs.cs" />
|
||||||
|
<Compile Include="Events\CheckingForActionEventArgs.cs" />
|
||||||
<Compile Include="Events\ContentEvents.cs" />
|
<Compile Include="Events\ContentEvents.cs" />
|
||||||
<Compile Include="Events\ControlEvents.cs" />
|
<Compile Include="Events\ControlEvents.cs" />
|
||||||
<Compile Include="Events\CursorMovedEventArgs.cs" />
|
<Compile Include="Events\CursorMovedEventArgs.cs" />
|
||||||
|
|
Loading…
Reference in New Issue