diff options
Diffstat (limited to 'src/StardewModdingAPI')
40 files changed, 5176 insertions, 0 deletions
diff --git a/src/StardewModdingAPI/App.config b/src/StardewModdingAPI/App.config new file mode 100644 index 00000000..6664f1ed --- /dev/null +++ b/src/StardewModdingAPI/App.config @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?>
+
+<configuration>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
+ </startup>
+ <runtime>
+ <loadFromRemoteSources enabled="true" />
+ </runtime>
+</configuration>
\ No newline at end of file diff --git a/src/StardewModdingAPI/Command.cs b/src/StardewModdingAPI/Command.cs new file mode 100644 index 00000000..4214b1a7 --- /dev/null +++ b/src/StardewModdingAPI/Command.cs @@ -0,0 +1,109 @@ +using System;
+using System.Collections.Generic;
+using StardewModdingAPI.Events;
+
+namespace StardewModdingAPI
+{
+ public class Command
+ {
+ internal static List<Command> RegisteredCommands = new List<Command>();
+ public string[] CalledArgs;
+ public string[] CommandArgs;
+ public string CommandDesc;
+
+ public string CommandName;
+
+ /// <summary>
+ /// Creates a Command from a Name, Description, and Arguments
+ /// </summary>
+ /// <param name="cname">Name</param>
+ /// <param name="cdesc">Description</param>
+ /// <param name="args">Arguments</param>
+ public Command(string cname, string cdesc, string[] args = null)
+ {
+ CommandName = cname;
+ CommandDesc = cdesc;
+ if (args == null)
+ args = new string[0];
+ CommandArgs = args;
+ }
+
+ public event EventHandler<EventArgsCommand> CommandFired;
+
+ /// <summary>
+ /// Calls the specified command. (It runs the command)
+ /// </summary>
+ /// <param name="input">The command to run</param>
+ public static void CallCommand(string input)
+ {
+ input = input.TrimEnd(' ');
+ var args = new string[0];
+ Command fnd;
+ if (input.Contains(" "))
+ {
+ args = input.Split(new[] {" "}, 2, StringSplitOptions.RemoveEmptyEntries);
+ fnd = FindCommand(args[0]);
+ args = args[1].Split(new[] {" "}, StringSplitOptions.RemoveEmptyEntries);
+ }
+ else
+ {
+ fnd = FindCommand(input);
+ }
+
+ if (fnd != null)
+ {
+ fnd.CalledArgs = args;
+ fnd.Fire();
+ }
+ else
+ {
+ Log.AsyncR("Unknown Command");
+ }
+ }
+
+ /// <summary>
+ /// Registers a command to the list of commands properly
+ /// </summary>
+ /// <param name="command">Name of the command to register</param>
+ /// <param name="cdesc">Description</param>
+ /// <param name="args">Arguments (these are purely for viewing so that a user can see what an argument needs to be)</param>
+ /// <returns></returns>
+ public static Command RegisterCommand(string command, string cdesc, string[] args = null)
+ {
+ var c = new Command(command, cdesc, args);
+ if (RegisteredCommands.Contains(c))
+ {
+ Log.AsyncR($"Command already registered! [{c.CommandName}]");
+ return RegisteredCommands.Find(x => x.Equals(c));
+ }
+
+ RegisteredCommands.Add(c);
+ Log.Async("Registered command: " + command);
+
+ return c;
+ }
+
+ /// <summary>
+ /// Looks up a command in the list of registered commands. Returns null if it doesn't exist (I think)
+ /// </summary>
+ /// <param name="name">Name of command to find</param>
+ /// <returns></returns>
+ public static Command FindCommand(string name)
+ {
+ return RegisteredCommands.Find(x => x.CommandName.Equals(name));
+ }
+
+ /// <summary>
+ /// Runs a command. Fires it. Calls it. Any of those.
+ /// </summary>
+ public void Fire()
+ {
+ if (CommandFired == null)
+ {
+ Log.AsyncR("Command failed to fire because it's fire event is null: " + CommandName);
+ return;
+ }
+ CommandFired.Invoke(this, new EventArgsCommand(this));
+ }
+ }
+}
\ No newline at end of file diff --git a/src/StardewModdingAPI/Config.cs b/src/StardewModdingAPI/Config.cs new file mode 100644 index 00000000..c5b7beca --- /dev/null +++ b/src/StardewModdingAPI/Config.cs @@ -0,0 +1,176 @@ +/* + Copyright 2016 Zoey (Zoryn) +*/ + +using System; +using System.IO; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace StardewModdingAPI +{ + public class Config + { + [JsonIgnore] + public virtual string ConfigLocation { get; protected internal set; } + + [JsonIgnore] + public virtual string ConfigDir => Path.GetDirectoryName(ConfigLocation); + + public virtual Config Instance<T>() where T : Config => Activator.CreateInstance<T>(); + + /// <summary> + /// Loads the config from the json blob on disk, updating and re-writing to the disk if needed. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns></returns> + public virtual T LoadConfig<T>() where T : Config + { + if (string.IsNullOrEmpty(ConfigLocation)) + { + Log.AsyncR("A config tried to load without specifying a location on the disk."); + return null; + } + + T ret = null; + + if (!File.Exists(ConfigLocation)) + { + //no config exists, generate default values + var c = GenerateDefaultConfig<T>(); + c.ConfigLocation = ConfigLocation; + ret = c; + } + else + { + try + { + //try to load the config from a json blob on disk + var c = JsonConvert.DeserializeObject<T>(File.ReadAllText(ConfigLocation), new JsonSerializerSettings {ContractResolver = new JsonResolver()}); + + c.ConfigLocation = ConfigLocation; + + //update the config with default values if needed + ret = c.UpdateConfig<T>(); + + c = null; + } + catch (Exception ex) + { + Log.AsyncR($"Invalid JSON ({GetType().Name}): {ConfigLocation} \n{ex}"); + return GenerateDefaultConfig<T>(); + } + } + + ret.WriteConfig(); + return ret; + } + + /// <summary> + /// MUST be implemented in inheriting class! + /// </summary> + public virtual T GenerateDefaultConfig<T>() where T : Config + { + return null; + } + + /// <summary> + /// Merges a default-value config with the user-config on disk. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns></returns> + public virtual T UpdateConfig<T>() where T : Config + { + try + { + //default config + var b = JObject.FromObject(Instance<T>().GenerateDefaultConfig<T>(), new JsonSerializer {ContractResolver = new JsonResolver()}); + + //user config + var u = JObject.FromObject(this, new JsonSerializer {ContractResolver = new JsonResolver()}); + + //overwrite default values with user values + b.Merge(u, new JsonMergeSettings {MergeArrayHandling = MergeArrayHandling.Replace}); + + //cast json object to config + var c = b.ToObject<T>(); + + //re-write the location on disk to the object + c.ConfigLocation = ConfigLocation; + + return c; + } + catch (Exception ex) + { + Log.AsyncR("An error occured when updating a config: " + ex); + return this as T; + } + } + } + + public static class ConfigExtensions + { + /// <summary> + /// Initializes an instance of any class that inherits from Config. + /// This method performs the loading, saving, and merging of the config on the disk and in memory at a default state. + /// This method should not be used to re-load or to re-save a config. + /// NOTE: You MUST set your config EQUAL to the return of this method! + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="baseConfig"></param> + /// <param name="configLocation"></param> + /// <returns></returns> + public static T InitializeConfig<T>(this T baseConfig, string configLocation) where T : Config + { + if (baseConfig == null) + { + baseConfig = Activator.CreateInstance<T>(); + /* + Log.AsyncR("A config tried to initialize whilst being null."); + return null; + */ + } + + if (string.IsNullOrEmpty(configLocation)) + { + Log.AsyncR("A config tried to initialize without specifying a location on the disk."); + return null; + } + + baseConfig.ConfigLocation = configLocation; + var c = baseConfig.LoadConfig<T>(); + + return c; + } + + /// <summary> + /// Writes a config to a json blob on the disk specified in the config's properties. + /// </summary> + public static void WriteConfig<T>(this T baseConfig) where T : Config + { + if (string.IsNullOrEmpty(baseConfig?.ConfigLocation) || string.IsNullOrEmpty(baseConfig.ConfigDir)) + { + Log.AsyncR("A config attempted to save when it itself or it's location were null."); + return; + } + + var s = JsonConvert.SerializeObject(baseConfig, typeof(T), Formatting.Indented, new JsonSerializerSettings {ContractResolver = new JsonResolver()}); + + if (!Directory.Exists(baseConfig.ConfigDir)) + Directory.CreateDirectory(baseConfig.ConfigDir); + + if (!File.Exists(baseConfig.ConfigLocation) || !File.ReadAllText(baseConfig.ConfigLocation).SequenceEqual(s)) + File.WriteAllText(baseConfig.ConfigLocation, s); + } + + /// <summary> + /// Re-reads the json blob on the disk and merges its values with a default config. + /// NOTE: You MUST set your config EQUAL to the return of this method! + /// </summary> + public static T ReloadConfig<T>(this T baseConfig) where T : Config + { + return baseConfig.LoadConfig<T>(); + } + } +}
\ No newline at end of file diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs new file mode 100644 index 00000000..0dd387b3 --- /dev/null +++ b/src/StardewModdingAPI/Constants.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; +using System.Reflection; +using StardewValley; + +namespace StardewModdingAPI +{ + /// <summary> + /// Static class containing readonly values. + /// </summary> + public static class Constants + { + public static readonly Version Version = new Version(0, 40, 0, "Alpha"); + + /// <summary> + /// Not quite "constant", but it makes more sense for it to be here, at least for now + /// </summary> + public static int ModsLoaded = 0; + + /// <summary> + /// Stardew Valley's roaming app data location. + /// %AppData%//StardewValley + /// </summary> + public static string DataPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley"); + + public static string SavesPath => Path.Combine(DataPath, "Saves"); + + private static string saveFolderName => PlayerNull ? string.Empty : Game1.player.name.RemoveNumerics() + "_" + Game1.uniqueIDForThisGame; + public static string SaveFolderName => CurrentSavePathExists ? saveFolderName : ""; + + private static string currentSavePath => PlayerNull ? string.Empty : Path.Combine(SavesPath, saveFolderName); + public static string CurrentSavePath => CurrentSavePathExists ? currentSavePath : ""; + + public static bool CurrentSavePathExists => Directory.Exists(currentSavePath); + + public static bool PlayerNull => !Game1.hasLoadedGame || Game1.player == null || string.IsNullOrEmpty(Game1.player.name); + + /// <summary> + /// Execution path to execute the code. + /// </summary> + public static string ExecutionPath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + + /// <summary> + /// Title for the API console + /// </summary> + public static string ConsoleTitle => $"Stardew Modding API Console - Version {Version.VersionString} - Mods Loaded: {ModsLoaded}"; + + /// <summary> + /// Path for log files to be output to. + /// %LocalAppData%//StardewValley//ErrorLogs + /// </summary> + public static string LogDir => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley", "ErrorLogs"); + + public static string LogPath => Path.Combine(LogDir, "MODDED_ProgramLog.Log_LATEST.txt"); + + /// <summary> + /// Whether or not to enable the Render Target drawing code offered by ClxS + /// Do not mark as 'const' or else 'if' checks will complain that the expression is always true in ReSharper + /// </summary> + public static bool EnableDrawingIntoRenderTarget => true; + + /// <summary> + /// Completely overrides the base game's draw call to the one is SGame + /// </summary> + public static bool EnableCompletelyOverridingBaseCalls => true; + } +}
\ No newline at end of file diff --git a/src/StardewModdingAPI/Entities/SCharacter.cs b/src/StardewModdingAPI/Entities/SCharacter.cs new file mode 100644 index 00000000..2d941d55 --- /dev/null +++ b/src/StardewModdingAPI/Entities/SCharacter.cs @@ -0,0 +1,6 @@ +namespace StardewModdingAPI.Entities
+{
+ internal class SCharacter
+ {
+ }
+}
\ No newline at end of file diff --git a/src/StardewModdingAPI/Entities/SFarm.cs b/src/StardewModdingAPI/Entities/SFarm.cs new file mode 100644 index 00000000..c6c64681 --- /dev/null +++ b/src/StardewModdingAPI/Entities/SFarm.cs @@ -0,0 +1,6 @@ +namespace StardewModdingAPI.Entities
+{
+ internal class SFarm
+ {
+ }
+}
\ No newline at end of file diff --git a/src/StardewModdingAPI/Entities/SFarmAnimal.cs b/src/StardewModdingAPI/Entities/SFarmAnimal.cs new file mode 100644 index 00000000..fb8ee267 --- /dev/null +++ b/src/StardewModdingAPI/Entities/SFarmAnimal.cs @@ -0,0 +1,6 @@ +namespace StardewModdingAPI.Entities
+{
+ internal class SFarmAnimal
+ {
+ }
+}
\ No newline at end of file diff --git a/src/StardewModdingAPI/Entities/SNpc.cs b/src/StardewModdingAPI/Entities/SNpc.cs new file mode 100644 index 00000000..727dcff7 --- /dev/null +++ b/src/StardewModdingAPI/Entities/SNpc.cs @@ -0,0 +1,6 @@ +namespace StardewModdingAPI.Entities
+{
+ internal class SNpc
+ {
+ }
+}
\ No newline at end of file diff --git a/src/StardewModdingAPI/Entities/SPlayer.cs b/src/StardewModdingAPI/Entities/SPlayer.cs new file mode 100644 index 00000000..d464cded --- /dev/null +++ b/src/StardewModdingAPI/Entities/SPlayer.cs @@ -0,0 +1,33 @@ +using System;
+using System.Collections.Generic;
+using StardewValley;
+
+namespace StardewModdingAPI.Entities
+{
+ /// <summary>
+ /// Static class for intergrating with the player
+ /// </summary>
+ public class SPlayer
+ {
+ /// <summary>
+ /// Calls 'getAllFarmers' in Game1
+ /// </summary>
+ public static List<Farmer> AllFarmers => Game1.getAllFarmers();
+
+ /// <summary>
+ /// Do not use.
+ /// </summary>
+ [Obsolete("Use 'Player' instead.")]
+ public static Farmer CurrentFarmer => Game1.player;
+
+ /// <summary>
+ /// Gets the current player from Game1
+ /// </summary>
+ public static Farmer Player => Game1.player;
+
+ /// <summary>
+ /// Gets the player's current location from Game1
+ /// </summary>
+ public static GameLocation CurrentFarmerLocation => Player.currentLocation;
+ }
+}
\ No newline at end of file diff --git a/src/StardewModdingAPI/Events/Controls.cs b/src/StardewModdingAPI/Events/Controls.cs new file mode 100644 index 00000000..6415561a --- /dev/null +++ b/src/StardewModdingAPI/Events/Controls.cs @@ -0,0 +1,58 @@ +using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+
+namespace StardewModdingAPI.Events
+{
+ public static class ControlEvents
+ {
+ public static event EventHandler<EventArgsKeyboardStateChanged> KeyboardChanged = delegate { };
+ public static event EventHandler<EventArgsKeyPressed> KeyPressed = delegate { };
+ public static event EventHandler<EventArgsKeyPressed> KeyReleased = delegate { };
+ public static event EventHandler<EventArgsMouseStateChanged> MouseChanged = delegate { };
+ public static event EventHandler<EventArgsControllerButtonPressed> ControllerButtonPressed = delegate { };
+ public static event EventHandler<EventArgsControllerButtonReleased> ControllerButtonReleased = delegate { };
+ public static event EventHandler<EventArgsControllerTriggerPressed> ControllerTriggerPressed = delegate { };
+ public static event EventHandler<EventArgsControllerTriggerReleased> ControllerTriggerReleased = delegate { };
+
+ internal static void InvokeKeyboardChanged(KeyboardState priorState, KeyboardState newState)
+ {
+ KeyboardChanged.Invoke(null, new EventArgsKeyboardStateChanged(priorState, newState));
+ }
+
+ internal static void InvokeMouseChanged(MouseState priorState, MouseState newState)
+ {
+ MouseChanged.Invoke(null, new EventArgsMouseStateChanged(priorState, newState));
+ }
+
+ internal static void InvokeKeyPressed(Keys key)
+ {
+ KeyPressed.Invoke(null, new EventArgsKeyPressed(key));
+ }
+
+ internal static void InvokeKeyReleased(Keys key)
+ {
+ KeyReleased.Invoke(null, new EventArgsKeyPressed(key));
+ }
+
+ internal static void InvokeButtonPressed(PlayerIndex playerIndex, Buttons buttons)
+ {
+ ControllerButtonPressed.Invoke(null, new EventArgsControllerButtonPressed(playerIndex, buttons));
+ }
+
+ internal static void InvokeButtonReleased(PlayerIndex playerIndex, Buttons buttons)
+ {
+ ControllerButtonReleased.Invoke(null, new EventArgsControllerButtonReleased(playerIndex, buttons));
+ }
+
+ internal static void InvokeTriggerPressed(PlayerIndex playerIndex, Buttons buttons, float value)
+ {
+ ControllerTriggerPressed.Invoke(null, new EventArgsControllerTriggerPressed(playerIndex, buttons, value));
+ }
+
+ internal static void InvokeTriggerReleased(PlayerIndex playerIndex, Buttons buttons, float value)
+ {
+ ControllerTriggerReleased.Invoke(null, new EventArgsControllerTriggerReleased(playerIndex, buttons, value));
+ }
+ }
+}
\ No newline at end of file diff --git a/src/StardewModdingAPI/Events/EventArgs.cs b/src/StardewModdingAPI/Events/EventArgs.cs new file mode 100644 index 00000000..2bce964e --- /dev/null +++ b/src/StardewModdingAPI/Events/EventArgs.cs @@ -0,0 +1,272 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+using StardewModdingAPI.Inheritance;
+using StardewValley;
+using StardewValley.Menus;
+using Object = StardewValley.Object;
+
+namespace StardewModdingAPI.Events
+{
+ public class EventArgsKeyboardStateChanged : EventArgs
+ {
+ public EventArgsKeyboardStateChanged(KeyboardState priorState, KeyboardState newState)
+ {
+ NewState = newState;
+ NewState = newState;
+ }
+
+ public KeyboardState NewState { get; private set; }
+ public KeyboardState PriorState { get; private set; }
+ }
+
+ public class EventArgsKeyPressed : EventArgs
+ {
+ public EventArgsKeyPressed(Keys keyPressed)
+ {
+ KeyPressed = keyPressed;
+ }
+
+ public Keys KeyPressed { get; private set; }
+ }
+
+ public class EventArgsControllerButtonPressed : EventArgs
+ {
+ public EventArgsControllerButtonPressed(PlayerIndex playerIndex, Buttons buttonPressed)
+ {
+ PlayerIndex = playerIndex;
+ ButtonPressed = buttonPressed;
+ }
+
+ public PlayerIndex PlayerIndex { get; private set; }
+ public Buttons ButtonPressed { get; private set; }
+ }
+
+ public class EventArgsControllerButtonReleased : EventArgs
+ {
+ public EventArgsControllerButtonReleased(PlayerIndex playerIndex, Buttons buttonReleased)
+ {
+ PlayerIndex = playerIndex;
+ ButtonReleased = buttonReleased;
+ }
+
+ public PlayerIndex PlayerIndex { get; private set; }
+ public Buttons ButtonReleased { get; private set; }
+ }
+
+ public class EventArgsControllerTriggerPressed : EventArgs
+ {
+ public EventArgsControllerTriggerPressed(PlayerIndex playerIndex, Buttons buttonPressed, float value)
+ {
+ PlayerIndex = playerIndex;
+ ButtonPressed = buttonPressed;
+ Value = value;
+ }
+
+ public PlayerIndex PlayerIndex { get; private set; }
+ public Buttons ButtonPressed { get; private set; }
+ public float Value { get; private set; }
+ }
+
+ public class EventArgsControllerTriggerReleased : EventArgs
+ {
+ public EventArgsControllerTriggerReleased(PlayerIndex playerIndex, Buttons buttonReleased, float value)
+ {
+ PlayerIndex = playerIndex;
+ ButtonReleased = buttonReleased;
+ Value = value;
+ }
+
+ public PlayerIndex PlayerIndex { get; private set; }
+ public Buttons ButtonReleased { get; private set; }
+ public float Value { get; private set; }
+ }
+
+ public class EventArgsMouseStateChanged : EventArgs
+ {
+ public EventArgsMouseStateChanged(MouseState priorState, MouseState newState)
+ {
+ NewState = newState;
+ NewState = newState;
+ }
+
+ public MouseState NewState { get; private set; }
+ public MouseState PriorState { get; private set; }
+ }
+
+ public class EventArgsClickableMenuChanged : EventArgs
+ {
+ public EventArgsClickableMenuChanged(IClickableMenu priorMenu, IClickableMenu newMenu)
+ {
+ NewMenu = newMenu;
+ PriorMenu = priorMenu;
+ }
+
+ public IClickableMenu NewMenu { get; private set; }
+ public IClickableMenu PriorMenu { get; private set; }
+ }
+
+ public class EventArgsClickableMenuClosed : EventArgs
+ {
+ public EventArgsClickableMenuClosed(IClickableMenu priorMenu)
+ {
+ PriorMenu = priorMenu;
+ }
+
+ public IClickableMenu PriorMenu { get; private set; }
+ }
+
+ public class EventArgsGameLocationsChanged : EventArgs
+ {
+ public EventArgsGameLocationsChanged(List<GameLocation> newLocations)
+ {
+ NewLocations = newLocations;
+ }
+
+ public List<GameLocation> NewLocations { get; private set; }
+ }
+
+ public class EventArgsMineLevelChanged : EventArgs
+ {
+ public EventArgsMineLevelChanged(int previousMineLevel, int currentMineLevel)
+ {
+ PreviousMineLevel = previousMineLevel;
+ CurrentMineLevel = currentMineLevel;
+ }
+
+ public int PreviousMineLevel { get; private set; }
+ public int CurrentMineLevel { get; private set; }
+ }
+
+ public class EventArgsLocationObjectsChanged : EventArgs
+ {
+ public EventArgsLocationObjectsChanged(SerializableDictionary<Vector2, Object> newObjects)
+ {
+ NewObjects = newObjects;
+ }
+
+ public SerializableDictionary<Vector2, Object> NewObjects { get; private set; }
+ }
+
+ public class EventArgsCurrentLocationChanged : EventArgs
+ {
+ public EventArgsCurrentLocationChanged(GameLocation priorLocation, GameLocation newLocation)
+ {
+ NewLocation = newLocation;
+ PriorLocation = priorLocation;
+ }
+
+ public GameLocation NewLocation { get; private set; }
+ public GameLocation PriorLocation { get; private set; }
+ }
+
+ public class EventArgsFarmerChanged : EventArgs
+ {
+ public EventArgsFarmerChanged(Farmer priorFarmer, Farmer newFarmer)
+ {
+ NewFarmer = NewFarmer;
+ PriorFarmer = PriorFarmer;
+ }
+
+ public Farmer NewFarmer { get; }
+ public F |
