diff --git a/UtilityGrid/GridPipe.cs b/UtilityGrid/GridPipe.cs new file mode 100644 index 00000000..6f64058e --- /dev/null +++ b/UtilityGrid/GridPipe.cs @@ -0,0 +1,8 @@ +namespace UtilityGrid +{ + public class GridPipe + { + public int index; + public int rotation; + } +} \ No newline at end of file diff --git a/UtilityGrid/HelperEvents.cs b/UtilityGrid/HelperEvents.cs new file mode 100644 index 00000000..9898b1be --- /dev/null +++ b/UtilityGrid/HelperEvents.cs @@ -0,0 +1,245 @@ +using HarmonyLib; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using StardewModdingAPI; +using StardewValley; +using System; +using System.Collections.Generic; + +namespace UtilityGrid +{ + /// The mod entry point. + public partial class ModEntry + { + + + public void Input_ButtonPressed(object sender, StardewModdingAPI.Events.ButtonPressedEventArgs e) + { + if (!Config.EnableMod || !(Game1.currentLocation is Farm) || !Game1.currentLocation.IsOutdoors || Game1.activeClickableMenu != null) + return; + + if (e.Button == Config.ToggleGrid) + { + Helper.Input.Suppress(e.Button); + ShowingGrid = !ShowingGrid; + Monitor.Log($"Showing grid: {ShowingGrid}"); + } + if (!ShowingGrid) + return; + if (e.Button == Config.SwitchGrid) + { + Helper.Input.Suppress(e.Button); + CurrentGrid = CurrentGrid == GridType.water ? GridType.electric : GridType.water; + Monitor.Log($"Showing grid: {CurrentGrid}"); + } + else if (e.Button == Config.SwitchTile) + { + Helper.Input.Suppress(e.Button); + CurrentTile++; + CurrentTile %= 6; + CurrentRotation = 0; + //Monitor.Log($"Showing tile: {CurrentTile},{CurrentRotation}"); + } + else if (e.Button == Config.RotateTile) + { + Helper.Input.Suppress(e.Button); + CurrentRotation++; + if (CurrentTile == 1) + CurrentRotation %= 2; + else if (CurrentTile == 4) + CurrentRotation = 0; + else + CurrentRotation %= 4; + //Monitor.Log($"Showing tile: {CurrentTile},{CurrentRotation}"); + } + else if (e.Button == Config.PlaceTile) + { + Helper.Input.Suppress(e.Button); + Dictionary pipeDict; + if (CurrentGrid == GridType.electric) + { + pipeDict = electricPipes; + } + else + { + pipeDict = waterPipes; + } + if(CurrentTile == 5) + { + Monitor.Log($"Removing tile at {Game1.currentCursorTile}"); + pipeDict.Remove(Game1.lastCursorTile); + } + else + { + Monitor.Log($"Placing tile {CurrentTile},{CurrentRotation} at {Game1.currentCursorTile}"); + pipeDict[Game1.lastCursorTile] = new GridPipe() { index = CurrentTile, rotation = CurrentRotation }; + } + RemakeGroups(CurrentGrid); + } + } + public void Display_RenderedWorld(object sender, StardewModdingAPI.Events.RenderedWorldEventArgs e) + { + if (!Config.EnableMod || !ShowingGrid) + return; + + if(!(Game1.currentLocation is Farm) || !Game1.currentLocation.IsOutdoors || Game1.activeClickableMenu != null) + { + ShowingGrid = false; + return; + } + + List groupList; + Color color; + Dictionary pipeDict; + if (CurrentGrid == GridType.electric) + { + groupList = electricGroups; + pipeDict = electricPipes; + color = Config.ElectricityColor; + } + else + { + groupList = waterGroups; + pipeDict = waterPipes; + color = Config.WaterColor; + } + foreach (var group in groupList) + { + Vector2 power = GetGroupPower(group, CurrentGrid); + float netPower = power.X + power.Y; + bool powered = power.X > 0 && netPower >= 0; + foreach (var pipe in group.pipes) + { + if (Utility.isOnScreen(new Vector2(pipe.X * 64 + 32, pipe.Y * 64 + 32), 32)) + { + if (pipe != Game1.currentCursorTile) + { + if (pipeDict[pipe].index == 4) + DrawTile(e.SpriteBatch, pipe, new GridPipe() { index = 1, rotation = 2 }, powered ? color : Color.White); + else + DrawTile(e.SpriteBatch, pipe, pipeDict[pipe], powered ? color : Color.White); + } + } + } + foreach (var kvp in group.objects) + { + float objPower; + bool enough = netPower >= 0; + if (CurrentGrid == GridType.electric) + { + objPower = kvp.Value.electric; + } + else + { + objPower = kvp.Value.water; + if (kvp.Value.electric < 0 && GetTileNetElectricPower(kvp.Key) < 0) + enough = false; + } + + if (objPower == 0) + continue; + + + string str = "" + Math.Round(objPower); + + e.SpriteBatch.DrawString(Game1.dialogueFont, str, Game1.GlobalToLocal(Game1.viewport, kvp.Key * 64) - new Vector2(16, 16) + new Vector2(-2, 2), Color.Black, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.999999f); + e.SpriteBatch.DrawString(Game1.dialogueFont, str, Game1.GlobalToLocal(Game1.viewport, kvp.Key * 64) - new Vector2(16, 16), enough ? color : Config.InsufficientColor, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.9999999f); + } + + } + if (CurrentTile == 4) + DrawTile(e.SpriteBatch, Game1.currentCursorTile, new GridPipe() { index = 1, rotation = 2 }, color); + else if(CurrentTile != 5) + DrawTile(e.SpriteBatch, Game1.currentCursorTile, new GridPipe() { index = CurrentTile, rotation = CurrentRotation }, color); + } + + private void GameLoop_OneSecondUpdateTicked(object sender, StardewModdingAPI.Events.OneSecondUpdateTickedEventArgs e) + { + if (!Config.EnableMod || !(Game1.currentLocation is Farm)) + return; + + RemakeAllGroups(); + + } + + private void GameLoop_SaveLoaded(object sender, StardewModdingAPI.Events.SaveLoadedEventArgs e) + { + waterPipes = new Dictionary(); + electricPipes = new Dictionary(); + ShowingGrid = false; + CurrentRotation = 0; + CurrentTile = 0; + + objectDict = SHelper.Content.Load>(dictPath, ContentSource.GameContent); + UtilityGridData gridData = Helper.Data.ReadSaveData(saveKey) ?? new UtilityGridData(); + Monitor.Log($"reading {gridData.electricData.Count} ep and {gridData.waterData.Count} wp from save"); + foreach (var arr in gridData.waterData) + { + waterPipes[new Vector2(arr[0], arr[1])] = new GridPipe() { index = arr[2], rotation = arr[3] }; + } + foreach(var arr in gridData.electricData) + { + electricPipes[new Vector2(arr[0], arr[1])] = new GridPipe() { index = arr[2], rotation = arr[3] }; + } + RemakeAllGroups(); + } + private void GameLoop_Saving(object sender, StardewModdingAPI.Events.SavingEventArgs e) + { + + UtilityGridData gridData = new UtilityGridData(waterPipes, electricPipes); + Monitor.Log($"writing {gridData.electricData.Count} ep and {gridData.waterData.Count} wp to save"); + Helper.Data.WriteSaveData(saveKey, gridData); + } + public void GameLoop_GameLaunched(object sender, StardewModdingAPI.Events.GameLaunchedEventArgs e) + { + // get Generic Mod Config Menu's API (if it's installed) + var configMenu = Helper.ModRegistry.GetApi("spacechase0.GenericModConfigMenu"); + if (configMenu is null) + return; + + // register mod + configMenu.Register( + mod: ModManifest, + reset: () => Config = new ModConfig(), + save: () => Helper.WriteConfig(Config) + ); + + configMenu.AddBoolOption( + mod: ModManifest, + name: () => "Mod Enabled?", + getValue: () => Config.EnableMod, + setValue: value => Config.EnableMod = value + ); + configMenu.AddKeybind( + mod: ModManifest, + name: () => "Toggle Grid Key", + getValue: () => Config.ToggleGrid, + setValue: value => Config.ToggleGrid = value + ); + configMenu.AddKeybind( + mod: ModManifest, + name: () => "Switch Grid Key", + getValue: () => Config.ToggleGrid, + setValue: value => Config.ToggleGrid = value + ); + configMenu.AddKeybind( + mod: ModManifest, + name: () => "Change Tile Key", + getValue: () => Config.SwitchTile, + setValue: value => Config.SwitchTile = value + ); + configMenu.AddKeybind( + mod: ModManifest, + name: () => "Rotate Tile Key", + getValue: () => Config.RotateTile, + setValue: value => Config.RotateTile = value + ); + configMenu.AddKeybind( + mod: ModManifest, + name: () => "Place Tile Key", + getValue: () => Config.PlaceTile, + setValue: value => Config.PlaceTile = value + ); + } + } +} \ No newline at end of file diff --git a/UtilityGrid/MethodPatches.cs b/UtilityGrid/MethodPatches.cs new file mode 100644 index 00000000..5de9b8c1 --- /dev/null +++ b/UtilityGrid/MethodPatches.cs @@ -0,0 +1,58 @@ +using Microsoft.Xna.Framework; +using StardewValley; + +namespace UtilityGrid +{ + public partial class ModEntry + { + // dga_add "aedenthorn.WaterPumpDGA/Bronze Water Pump" 1 + public static bool Utility_playerCanPlaceItemHere_Prefix(GameLocation location, Item item, int x, int y, ref bool __result) + { + //SMonitor.Log($"placing {item.Name}, {objectDict.ContainsKey(item.Name)}"); + if (!Config.EnableMod || !objectDict.ContainsKey(item.Name) || !(location is Farm) || !objectDict[item.Name].onlyInWater || location.Objects.ContainsKey(new Vector2(x, y))) + return true; + __result = location.isWaterTile(x / 64, y / 64); + return false; + } + public static void Object_IsSprinkler_Postfix(Object __instance, ref bool __result) + { + //SMonitor.Log($"placing {item.Name}, {objectDict.ContainsKey(item.Name)}"); + if (!Config.EnableMod || !__result || !objectDict.ContainsKey(__instance.Name)) + return; + __result = IsObjectPowered(__instance.TileLocation, objectDict[__instance.Name]); + + } + public static void Object_updateWhenCurrentLocation_Prefix(Object __instance) + { + //SMonitor.Log($"placing {item.Name}, {objectDict.ContainsKey(item.Name)}"); + if (!Config.EnableMod || !objectDict.ContainsKey(__instance.Name)) + return; + __instance.IsOn = IsObjectPowered(__instance.TileLocation, objectDict[__instance.Name]); + + } + public static Object preItem = null; + public static int minutesUntilReady = 0; + public static void Object_DayUpdate_Prefix(Object __instance, ref bool __state) + { + //SMonitor.Log($"placing {item.Name}, {objectDict.ContainsKey(item.Name)}"); + if (!Config.EnableMod || !objectDict.ContainsKey(__instance.Name)) + return; + if(!IsObjectPowered(__instance.TileLocation, objectDict[__instance.Name])) + { + preItem = __instance.heldObject.Value; + minutesUntilReady = __instance.MinutesUntilReady; + __instance.heldObject.Value = null; + __state = true; + } + + } + public static void Object_DayUpdate_Postfix(Object __instance, bool __state) + { + //SMonitor.Log($"placing {item.Name}, {objectDict.ContainsKey(item.Name)}"); + if (!__state) + return; + __instance.MinutesUntilReady = minutesUntilReady; + __instance.heldObject.Value = preItem; + } + } +} \ No newline at end of file diff --git a/UtilityGrid/ModConfig.cs b/UtilityGrid/ModConfig.cs index 63dbaaa3..cfdefd56 100644 --- a/UtilityGrid/ModConfig.cs +++ b/UtilityGrid/ModConfig.cs @@ -15,5 +15,6 @@ public class ModConfig public SButton PlaceTile { get; set; } = SButton.MouseLeft; public Color WaterColor { get; set; } = Color.Aqua; public Color ElectricityColor { get; set; } = Color.Yellow; + public Color InsufficientColor { get; set; } = Color.Red; } } diff --git a/UtilityGrid/ModEntry.cs b/UtilityGrid/ModEntry.cs index 3156a28f..90ea0ab8 100644 --- a/UtilityGrid/ModEntry.cs +++ b/UtilityGrid/ModEntry.cs @@ -5,11 +5,12 @@ using StardewValley; using System; using System.Collections.Generic; +using Object = StardewValley.Object; namespace UtilityGrid { /// The mod entry point. - public partial class ModEntry : Mod + public partial class ModEntry : Mod, IAssetLoader { public static IMonitor SMonitor; @@ -19,17 +20,32 @@ public partial class ModEntry : Mod public static ModEntry context; public static Texture2D pipeTexture; + public static readonly string dictPath = "utility_grid_object_dictionary"; + public enum GridType { + water, + electric + } + public static readonly string saveKey = "utility-grid-data"; + public static bool ShowingGrid { get; set; } = false; - public static bool ElectricGrid { get; set; } = false; + public static GridType CurrentGrid { get; set; } = GridType.water; public static int CurrentTile { get; set; } = 0; public static int CurrentRotation { get; set; } = 0; - public static int[][] intakeArray = { new int[]{0, 1, 1, 1}, new int[]{1, 0, 0, 1}, new int[]{1, 0, 0, 0}, new int[]{0, 1, 0, 1}, new int[]{1, 1, 1, 1} }; - - public Dictionary waterPipes = new Dictionary(); - public Dictionary electricPipes = new Dictionary(); - public List waterGroups = new List(); - public List electricGroups = new List(); + public static int[][] intakeArray = { + new int[] { 1, 0, 0, 0 }, + new int[] { 0, 1, 0, 1 }, + new int[] { 1, 0, 0, 1 }, + new int[] { 0, 1, 1, 1 }, + new int[] { 1, 1, 1, 1 } + }; + + public static Dictionary waterPipes = new Dictionary(); + public static Dictionary electricPipes = new Dictionary(); + public static Dictionary objectDict = new Dictionary(); + + public static List waterGroups = new List(); + public static List electricGroups = new List(); /// The mod entry point, called after the mod is first loaded. /// Provides simplified APIs for writing mods. @@ -47,168 +63,53 @@ public override void Entry(IModHelper helper) helper.Events.Input.ButtonPressed += Input_ButtonPressed; helper.Events.GameLoop.GameLaunched += GameLoop_GameLaunched; + helper.Events.GameLoop.SaveLoaded += GameLoop_SaveLoaded; + helper.Events.GameLoop.Saving += GameLoop_Saving; helper.Events.Display.RenderedWorld += Display_RenderedWorld; + helper.Events.GameLoop.OneSecondUpdateTicked += GameLoop_OneSecondUpdateTicked; var harmony = new Harmony(ModManifest.UniqueID); - // Game1 Patches - /* - harmony.Patch( - original: AccessTools.Method(typeof(Game1), "_newDayAfterFade"), - prefix: new HarmonyMethod(typeof(ModEntry), nameof(ModEntry.Game1__newDayAfterFade_Prefix)) - ); - */ + harmony.Patch( + original: AccessTools.Method(typeof(Utility), nameof(Utility.playerCanPlaceItemHere)), + prefix: new HarmonyMethod(typeof(ModEntry), nameof(ModEntry.Utility_playerCanPlaceItemHere_Prefix)) + ); + harmony.Patch( + original: AccessTools.Method(typeof(Object), nameof(Object.IsSprinkler)), + postfix: new HarmonyMethod(typeof(ModEntry), nameof(ModEntry.Object_IsSprinkler_Postfix)) + ); + harmony.Patch( + original: AccessTools.Method(typeof(Object), nameof(Object.updateWhenCurrentLocation)), + prefix: new HarmonyMethod(typeof(ModEntry), nameof(ModEntry.Object_updateWhenCurrentLocation_Prefix)) + ); pipeTexture = Helper.Content.Load("assets/pipes.png"); } - private void Input_ButtonPressed(object sender, StardewModdingAPI.Events.ButtonPressedEventArgs e) - { - if (!Config.EnableMod || !(Game1.currentLocation is Farm) || !Game1.currentLocation.IsOutdoors) - return; - if (e.Button == Config.ToggleGrid) - { - Helper.Input.Suppress(e.Button); - ShowingGrid = !ShowingGrid; - Monitor.Log($"Showing grid: {ShowingGrid}"); - } - if (!ShowingGrid) - return; - if (e.Button == Config.SwitchGrid) - { - Helper.Input.Suppress(e.Button); - ElectricGrid = !ElectricGrid; - Monitor.Log($"Showing Electric grid: {ElectricGrid}"); - } - else if (e.Button == Config.SwitchTile) - { - Helper.Input.Suppress(e.Button); - CurrentTile++; - CurrentTile %= 6; - CurrentRotation = 0; - Monitor.Log($"Showing tile: {CurrentTile},{CurrentRotation}"); - } - else if (e.Button == Config.RotateTile) - { - Helper.Input.Suppress(e.Button); - CurrentRotation++; - if (CurrentTile < 3) - CurrentRotation %= 4; - else if (CurrentTile == 3) - CurrentRotation %= 2; - else - CurrentRotation = 0; - Monitor.Log($"Showing tile: {CurrentTile},{CurrentRotation}"); - } - else if (e.Button == Config.PlaceTile) - { - Helper.Input.Suppress(e.Button); - Dictionary pipeDict; - if (ElectricGrid) - { - pipeDict = electricPipes; - } - else - { - pipeDict = waterPipes; - } - if(CurrentTile == 5) - pipeDict.Remove(Game1.lastCursorTile); - else - pipeDict[Game1.lastCursorTile] = new Point(CurrentTile, CurrentRotation); - RemakeGroups(ElectricGrid); - Monitor.Log($"Placing tile: {CurrentTile},{CurrentRotation} at {Game1.currentCursorTile}; connected? {PipeIsPowered(Game1.currentCursorTile, ElectricGrid)}"); - } - } - private void Display_RenderedWorld(object sender, StardewModdingAPI.Events.RenderedWorldEventArgs e) + + public override object GetApi() { - if (!Config.EnableMod || !ShowingGrid) - return; - Dictionary pipeDict; - Color color; - if (ElectricGrid) - { - pipeDict = electricPipes; - color = Config.ElectricityColor; - } - else - { - pipeDict = waterPipes; - color = Config.WaterColor; - } - foreach (var kvp in pipeDict) - { - if (kvp.Key == Game1.currentCursorTile) - { - continue; - } - if (Utility.isOnScreen(new Vector2(kvp.Key.X * 64 + 32, kvp.Key.Y * 64 + 32), 32)) - { - if (kvp.Value.X == 4) - DrawTile(e.SpriteBatch, kvp.Key, new Point(3, 2), ElectricGrid, PipeIsPowered(kvp.Key, ElectricGrid) ? color : Color.White); - else - DrawTile(e.SpriteBatch, kvp.Key, kvp.Value, ElectricGrid, PipeIsPowered(kvp.Key, ElectricGrid) ? color : Color.White); - } - } - if (CurrentTile == 4) - DrawTile(e.SpriteBatch, Game1.currentCursorTile, new Point(3, 2), ElectricGrid, color); - else if(CurrentTile != 5) - DrawTile(e.SpriteBatch, Game1.currentCursorTile, new Point(CurrentTile, CurrentRotation), ElectricGrid, color); + return new UtilityGridApi(); } - - - private void GameLoop_GameLaunched(object sender, StardewModdingAPI.Events.GameLaunchedEventArgs e) + /// Get whether this instance can load the initial version of the given asset. + /// Basic metadata about the asset being loaded. + public bool CanLoad(IAssetInfo asset) { - // get Generic Mod Config Menu's API (if it's installed) - var configMenu = Helper.ModRegistry.GetApi("spacechase0.GenericModConfigMenu"); - if (configMenu is null) - return; + if (!Config.EnableMod) + return false; - // register mod - configMenu.Register( - mod: ModManifest, - reset: () => Config = new ModConfig(), - save: () => Helper.WriteConfig(Config) - ); + return asset.AssetNameEquals(dictPath); + } - configMenu.AddBoolOption( - mod: ModManifest, - name: () => "Mod Enabled?", - getValue: () => Config.EnableMod, - setValue: value => Config.EnableMod = value - ); - configMenu.AddKeybind( - mod: ModManifest, - name: () => "Toggle Grid Key", - getValue: () => Config.ToggleGrid, - setValue: value => Config.ToggleGrid = value - ); - configMenu.AddKeybind( - mod: ModManifest, - name: () => "Switch Grid Key", - getValue: () => Config.ToggleGrid, - setValue: value => Config.ToggleGrid = value - ); - configMenu.AddKeybind( - mod: ModManifest, - name: () => "Change Tile Key", - getValue: () => Config.SwitchTile, - setValue: value => Config.SwitchTile = value - ); - configMenu.AddKeybind( - mod: ModManifest, - name: () => "Rotate Tile Key", - getValue: () => Config.RotateTile, - setValue: value => Config.RotateTile = value - ); - configMenu.AddKeybind( - mod: ModManifest, - name: () => "Place Tile Key", - getValue: () => Config.PlaceTile, - setValue: value => Config.PlaceTile = value - ); + /// Load a matched asset. + /// Basic metadata about the asset being loaded. + public T Load(IAssetInfo asset) + { + Monitor.Log("Loading dictionary"); + + return (T)(object)new Dictionary(); } } } \ No newline at end of file diff --git a/UtilityGrid/PipeGroup.cs b/UtilityGrid/PipeGroup.cs index da441209..413e570a 100644 --- a/UtilityGrid/PipeGroup.cs +++ b/UtilityGrid/PipeGroup.cs @@ -5,7 +5,7 @@ namespace UtilityGrid { public class PipeGroup { - public List pipes; - public float power; + public List pipes = new List(); + public Dictionary objects = new Dictionary(); } } \ No newline at end of file diff --git a/UtilityGrid/UtilityGrid.csproj b/UtilityGrid/UtilityGrid.csproj index 41b3b4bb..4c9921fa 100644 --- a/UtilityGrid/UtilityGrid.csproj +++ b/UtilityGrid/UtilityGrid.csproj @@ -4,6 +4,11 @@ net5.0 true + + + + + diff --git a/UtilityGrid/UtilityGridApi.cs b/UtilityGrid/UtilityGridApi.cs new file mode 100644 index 00000000..74059fd0 --- /dev/null +++ b/UtilityGrid/UtilityGridApi.cs @@ -0,0 +1,25 @@ +using Microsoft.Xna.Framework; + +namespace UtilityGrid +{ + public class UtilityGridApi + { + public Vector2 ElectricityToFromTile(int x, int y) + { + return ModEntry.GetTileElectricPower(new Vector2(x, y)); + } + public Vector2 WaterToFromTile(int x, int y) + { + return ModEntry.GetTileWaterPower(new Vector2(x, y)); + + } + public void RefreshElectricGrid() + { + ModEntry.RemakeGroups(ModEntry.GridType.electric); + } + public void RefreshWaterGrid() + { + ModEntry.RemakeGroups(ModEntry.GridType.water); + } + } +} \ No newline at end of file diff --git a/UtilityGrid/UtilityGridData.cs b/UtilityGrid/UtilityGridData.cs new file mode 100644 index 00000000..d6803a04 --- /dev/null +++ b/UtilityGrid/UtilityGridData.cs @@ -0,0 +1,27 @@ +using Microsoft.Xna.Framework; +using System.Collections.Generic; + +namespace UtilityGrid +{ + public class UtilityGridData + { + public List waterData = new List(); + public List electricData = new List(); + + public UtilityGridData() + { + } + + public UtilityGridData(Dictionary waterPipes, Dictionary electricPipes) + { + foreach (var kvp in waterPipes) + { + waterData.Add(new int[]{ (int)kvp.Key.X, (int)kvp.Key.Y, (int)kvp.Value.index, (int)kvp.Value.rotation }); + } + foreach (var kvp in electricPipes) + { + electricData.Add(new int[] { (int)kvp.Key.X, (int)kvp.Key.Y, (int)kvp.Value.index, (int)kvp.Value.rotation }); + } + } + } +} \ No newline at end of file diff --git a/UtilityGrid/UtilityObject.cs b/UtilityGrid/UtilityObject.cs new file mode 100644 index 00000000..8c5243ff --- /dev/null +++ b/UtilityGrid/UtilityObject.cs @@ -0,0 +1,13 @@ +using StardewValley; + +namespace UtilityGrid +{ + public class UtilityObject + { + public float water; + public float electric; + public bool onlyInWater; + public bool mustBeOn; + public Object worldObj; + } +} \ No newline at end of file diff --git a/UtilityGrid/Utils.cs b/UtilityGrid/Utils.cs index 22bf7eed..0e12550c 100644 --- a/UtilityGrid/Utils.cs +++ b/UtilityGrid/Utils.cs @@ -13,35 +13,17 @@ namespace UtilityGrid public partial class ModEntry { - private void DrawTile(SpriteBatch b, Vector2 tile, Point which, bool electric, Color color) + public static void DrawTile(SpriteBatch b, Vector2 tile, GridPipe which, Color color) { float layerDepth = (tile.Y * (16 * Game1.pixelZoom) + 16 * Game1.pixelZoom) / 10000f; - b.Draw(pipeTexture, Game1.GlobalToLocal(Game1.viewport, tile * 64), new Rectangle(which.Y * 64, which.X * 64, 64, 64), color, 0, Vector2.Zero, 1, SpriteEffects.None, layerDepth); + b.Draw(pipeTexture, Game1.GlobalToLocal(Game1.viewport, tile * 64), new Rectangle(which.rotation * 64, which.index * 64, 64, 64), color, 0, Vector2.Zero, 1, SpriteEffects.None, layerDepth); } - private bool PipeIsPowered(Vector2 tile, bool electric) + public static bool PipesAreJoined(Vector2 tile, Vector2 tile2, GridType gridType) { - List groupList; - if (electric) - { - groupList = electricGroups; - } - else - { - groupList = waterGroups; - } - foreach (var g in groupList) - { - if (g.pipes.Contains(tile)) - return g.power > 0; - } - return false; - } - private bool PipesAreJoined(Vector2 tile, Vector2 tile2, bool electric) - { - Dictionary pipeDict; - if (electric) + Dictionary pipeDict; + if (gridType == GridType.electric) { pipeDict = electricPipes; } @@ -69,22 +51,27 @@ private bool PipesAreJoined(Vector2 tile, Vector2 tile2, bool electric) return false; } - private bool HasIntake(Point pipeRot, int which) + public static bool HasIntake(GridPipe pipe, int which) { - return intakeArray[pipeRot.X][(which + pipeRot.Y) % 4] == 1; + return intakeArray[pipe.index][(which + pipe.rotation) % 4] == 1; } - private void RemakeGroups(bool electric) + public static void RemakeAllGroups() { - Dictionary pipeDict; + RemakeGroups(GridType.water); + RemakeGroups(GridType.electric); + } + public static void RemakeGroups(GridType gridType) + { + Dictionary pipeDict; List groupList; - if (electric) + if (gridType == GridType.electric) { - pipeDict = new Dictionary(electricPipes); + pipeDict = new Dictionary(electricPipes); groupList = electricGroups; } else { - pipeDict = new Dictionary(waterPipes); + pipeDict = new Dictionary(waterPipes); groupList = waterGroups; } groupList.Clear(); @@ -92,15 +79,21 @@ private void RemakeGroups(bool electric) while(pipeDict.Count > 0) { var tile = pipeDict.Keys.ToArray()[0]; - var group = new PipeGroup { pipes = new List() { tile }, power = PipePower(tile, electric) }; - Monitor.Log($"Creating new group; power: {group.power}"); + var group = new PipeGroup { pipes = new List() { tile } }; + var obj = GetUtilityObjectAtTile(tile); + if(obj != null) + { + group.objects[tile] = obj; + } + + //SMonitor.Log($"Creating new group; power: {group.input}"); pipeDict.Remove(tile); - AddTilesToGroup(tile, ref group, pipeDict, electric); + AddTilesToGroup(tile, ref group, pipeDict, gridType); groupList.Add(group); } } - private void AddTilesToGroup(Vector2 tile, ref PipeGroup group, Dictionary pipeDict, bool electric) + public static void AddTilesToGroup(Vector2 tile, ref PipeGroup group, Dictionary pipeDict, GridType gridType) { Vector2[] adjecents = new Vector2[] { tile + new Vector2(0,1),tile + new Vector2(1,0),tile + new Vector2(-1,0),tile + new Vector2(0,-1)}; @@ -108,31 +101,138 @@ private void AddTilesToGroup(Vector2 tile, ref PipeGroup group, Dictionary 0) + power.X += obj.electric; + else if (obj.electric < 0) + power.Y += obj.electric; + } + return power; + } + public static Vector2 GetTileWaterPower(Vector2 tile) + { + Vector2 power = Vector2.Zero; + foreach (var group in waterGroups) + { + if (group.objects.ContainsKey(tile)) + { + return GetGroupWaterPower(group); + } + } + return power; + } + public static Vector2 GetGroupWaterPower(PipeGroup group) + { + Vector2 power = Vector2.Zero; + foreach (var kvp in group.objects) + { + if (kvp.Value.electric < 0) + { + Vector2 ePower = GetTileElectricPower(kvp.Key); + if (ePower.X + ePower.Y < 0) // unpowered + continue; + } + var obj = kvp.Value; + + if (obj.water > 0) + power.X += obj.water; + else if (obj.water < 0) + power.Y += obj.water; + } + return power; + } + + public static bool IsObjectPowered(Vector2 tile, UtilityObject obj) + { + if(obj.water < 0) + { + var netPower = GetTileNetWaterPower(tile); + if (netPower < 0) + return false; + } + if(obj.electric < 0) + { + var netPower = GetTileNetElectricPower(tile); + if (netPower < 0) + return false; + } + return true; + } + + public static UtilityObject GetUtilityObjectAtTile(Vector2 tile) + { + if (!Game1.getFarm().Objects.ContainsKey(tile)) + return null; + var obj = Game1.getFarm().Objects[tile]; + if (!objectDict.ContainsKey(obj.Name) && (obj.modData.ContainsKey("aedenthorn.UtilityGrid/" + GridType.water) || obj.modData.ContainsKey("aedenthorn.UtilityGrid/" + GridType.electric))) + { + objectDict[obj.Name] = new UtilityObject(); + if (obj.modData.ContainsKey("aedenthorn.UtilityGrid/" + GridType.water)) + { + objectDict[obj.Name].water = float.Parse(obj.modData["aedenthorn.UtilityGrid/" + GridType.water], CultureInfo.InvariantCulture); + } + if (obj.modData.ContainsKey("aedenthorn.UtilityGrid/" + GridType.electric)) + { + objectDict[obj.Name].electric = float.Parse(obj.modData["aedenthorn.UtilityGrid/" + GridType.electric], CultureInfo.InvariantCulture); + } + } + if (objectDict.ContainsKey(obj.Name)) + { + UtilityObject outObj = objectDict[obj.Name]; + outObj.worldObj = obj; + return outObj; + } + return null; } } } \ No newline at end of file diff --git a/UtilityGrid/assets/pipes.png b/UtilityGrid/assets/pipes.png index 99849369..de682c21 100644 Binary files a/UtilityGrid/assets/pipes.png and b/UtilityGrid/assets/pipes.png differ diff --git a/UtilityGrid/assets/pipes.xcf b/UtilityGrid/assets/pipes.xcf new file mode 100644 index 00000000..969c7a6a Binary files /dev/null and b/UtilityGrid/assets/pipes.xcf differ diff --git a/UtilityGrid/assets/water_pump.png b/UtilityGrid/assets/water_pump.png new file mode 100644 index 00000000..36852c59 Binary files /dev/null and b/UtilityGrid/assets/water_pump.png differ diff --git a/UtilityGrid/assets/water_pump.xcf b/UtilityGrid/assets/water_pump.xcf new file mode 100644 index 00000000..9ac2ef4f Binary files /dev/null and b/UtilityGrid/assets/water_pump.xcf differ diff --git a/WaterPump/IGenericModConfigMenuApi.cs b/WaterPump/IGenericModConfigMenuApi.cs new file mode 100644 index 00000000..eb776cc6 --- /dev/null +++ b/WaterPump/IGenericModConfigMenuApi.cs @@ -0,0 +1,49 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using StardewModdingAPI; +using StardewModdingAPI.Utilities; +using StardewValley; + +namespace GroundhogDay +{ + /// The API which lets other mods add a config UI through Generic Mod Config Menu. + public interface IGenericModConfigMenuApi + { + /********* + ** Methods + *********/ + /**** + ** Must be called first + ****/ + /// Register a mod whose config can be edited through the UI. + /// The mod's manifest. + /// Reset the mod's config to its default values. + /// Save the mod's current config to the config.json file. + /// Whether the options can only be edited from the title screen. + /// Each mod can only be registered once, unless it's deleted via before calling this again. + void Register(IManifest mod, Action reset, Action save, bool titleScreenOnly = false); + + /// Add a key binding at the current position in the form. + /// The mod's manifest. + /// Get the current value from the mod config. + /// Set a new value in the mod config. + /// The label text to show in the form. + /// The tooltip text shown when the cursor hovers on the field, or null to disable the tooltip. + /// The unique field ID for use with , or null to auto-generate a randomized ID. + void AddKeybind(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, string fieldId = null); + + /// Add a boolean option at the current position in the form. + /// The mod's manifest. + /// Get the current value from the mod config. + /// Set a new value in the mod config. + /// The label text to show in the form. + /// The tooltip text shown when the cursor hovers on the field, or null to disable the tooltip. + /// The unique field ID for use with , or null to auto-generate a randomized ID. + void AddBoolOption(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, string fieldId = null); + + /// Remove a mod from the config UI and delete all its options and pages. + /// The mod's manifest. + void Unregister(IManifest mod); + } +} diff --git a/WaterPump/MethodPatches.cs b/WaterPump/MethodPatches.cs new file mode 100644 index 00000000..7ec05776 --- /dev/null +++ b/WaterPump/MethodPatches.cs @@ -0,0 +1,18 @@ +using StardewValley; + +namespace WaterPump +{ + /// The mod entry point. + public partial class ModEntry + { + + private static bool Utility_playerCanPlaceItemHere_Prefix() + { + if (Config.EnableMod && (Game1.dayOfMonth != 0 || Game1.year != 1 || Game1.currentSeason != "spring")) + { + SMonitor.Log($"Repeating {Utility.getDateString()}"); + Game1.dayOfMonth--; + } + } + } +} \ No newline at end of file diff --git a/WaterPump/ModConfig.cs b/WaterPump/ModConfig.cs new file mode 100644 index 00000000..a156d18d --- /dev/null +++ b/WaterPump/ModConfig.cs @@ -0,0 +1,11 @@ + +using StardewModdingAPI; + +namespace WaterPump +{ + public class ModConfig + { + public bool EnableMod { get; set; } = true; + + } +} diff --git a/WaterPump/ModEntry.cs b/WaterPump/ModEntry.cs new file mode 100644 index 00000000..988b2df0 --- /dev/null +++ b/WaterPump/ModEntry.cs @@ -0,0 +1,43 @@ +using HarmonyLib; +using StardewModdingAPI; +using StardewValley; + +namespace WaterPump +{ + /// The mod entry point. + public partial class ModEntry : Mod + { + + public static IMonitor SMonitor; + public static IModHelper SHelper; + public static ModConfig Config; + + public static ModEntry context; + + /// The mod entry point, called after the mod is first loaded. + /// Provides simplified APIs for writing mods. + public override void Entry(IModHelper helper) + { + Config = Helper.ReadConfig(); + + if (!Config.EnableMod) + return; + + context = this; + + SMonitor = Monitor; + SHelper = helper; + + helper.Events.Input.ButtonPressed += Input_ButtonPressed; + helper.Events.GameLoop.GameLaunched += GameLoop_GameLaunched; + + var harmony = new Harmony(ModManifest.UniqueID); + + harmony.Patch( + original: AccessTools.Method(typeof(Utility), nameof(Utility.playerCanPlaceItemHere)), + prefix: new HarmonyMethod(typeof(ModEntry), nameof(ModEntry.Utility_playerCanPlaceItemHere_Prefix)) + ); + + } + } +} \ No newline at end of file diff --git a/WaterPump/WaterPump.csproj b/WaterPump/WaterPump.csproj new file mode 100644 index 00000000..95bd1a14 --- /dev/null +++ b/WaterPump/WaterPump.csproj @@ -0,0 +1,20 @@ + + + 1.0.0 + net5.0 + true + + + + + + + + + PreserveNewest + + + PreserveNewest + + + \ No newline at end of file diff --git a/WaterPump/manifest.json b/WaterPump/manifest.json new file mode 100644 index 00000000..35a997fd --- /dev/null +++ b/WaterPump/manifest.json @@ -0,0 +1,22 @@ +{ + "Name": "Water Pump", + "Author": "aedenthorn", + "Version": "0.1.0", + "Description": "Water Pump.", + "UniqueID": "aedenthorn.WaterPump", + "EntryDll": "WaterPump.dll", + "MinimumApiVersion": "3.13.0-beta.20211018", + "ModUpdater": { + "Repository": "StardewValleyMods", + "User": "aedenthorn", + "Directory": "_releases", + "ModFolder": "WaterPump" + }, + "UpdateKeys": [ "Nexus:" ], + "Dependencies": [ + { + "UniqueID": "Platonymous.ModUpdater", + "IsRequired": false + } + ] +} \ No newline at end of file