Skip to content

Commit

Permalink
Rethink state handling for better MP behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
ceruleandeep committed Apr 25, 2022
1 parent 5a62f06 commit e5c0e3f
Show file tree
Hide file tree
Showing 7 changed files with 629 additions and 526 deletions.
402 changes: 183 additions & 219 deletions Market/MarketDay/MarketDay.cs

Large diffs are not rendered by default.

126 changes: 43 additions & 83 deletions Market/MarketDay/Patches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Xna.Framework;
using HarmonyLib;
using MarketDay.Shop;
using MarketDay.Utility;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewValley.Objects;
Expand All @@ -20,27 +21,19 @@ public class Prefix_Chest_checkForAction
public static bool Prefix(Chest __instance, Farmer who, bool justCheckingForActivity, ref bool __result)
{
if (justCheckingForActivity) return true;
var owner = MapUtility.Owner(__instance);
MarketDay.monitor.Log(
$"Prefix_Chest_checkForAction {__instance} {__instance.DisplayName} {__instance.TileLocation}",
LogLevel.Debug);

__instance.modData.TryGetValue($"{MarketDay.SMod.ModManifest.UniqueID}/GrangeStorage", out var chestOwner);
__instance.modData.TryGetValue($"{MarketDay.SMod.ModManifest.UniqueID}/GrangeSign", out var signOwner);

MarketDay.monitor.Log(
$"Prefix_Chest_checkForAction checking use access to {__instance} {__instance.DisplayName} at {__instance.TileLocation} [PeekIntoChests={MarketDay.Config.PeekIntoChests}]",
$"Prefix_Chest_checkForAction checking {__instance} {__instance.DisplayName} owner {owner} at {__instance.TileLocation}",
LogLevel.Debug);

if (owner is null) return true;
if (owner == "Player" || MarketDay.Config.PeekIntoChests) return true;

MarketDay.monitor.Log(
$"{MarketDay.SMod.ModManifest.UniqueID} {chestOwner} {signOwner}",
$"Prefix_Chest_checkForAction preventing action on object at {__instance.TileLocation} owned by {owner}",
LogLevel.Debug);

//if (MarketDay.Config.PeekIntoChests) return true;
if (signOwner is null && chestOwner is null) return true;

if (chestOwner == "Player" || MarketDay.Config.PeekIntoChests) return true;
MarketDay.monitor.Log($"Suppress and shake: stop player opening chests", LogLevel.Debug);
who.currentLocation.playSound("hammer");

who.currentLocation.playSound("clank");
__instance.shakeTimer = 500;
__result = false;
return false;
Expand All @@ -56,24 +49,18 @@ public class Prefix_Sign_checkForAction
public static bool Prefix(Sign __instance, Farmer who, bool justCheckingForActivity, ref bool __result)
{
if (justCheckingForActivity) return true;
var owner = MapUtility.Owner(__instance);
MarketDay.monitor.Log(
$"Prefix_Sign_checkForAction {__instance} {__instance.DisplayName} {__instance.TileLocation}",
LogLevel.Debug);

__instance.modData.TryGetValue($"{MarketDay.SMod.ModManifest.UniqueID}/GrangeStorage", out var chestOwner);
__instance.modData.TryGetValue($"{MarketDay.SMod.ModManifest.UniqueID}/GrangeSign", out var signOwner);

MarketDay.monitor.Log(
$"Prefix_Sign_checkForAction checking use access to {__instance} {__instance.DisplayName} at {__instance.TileLocation} [PeekIntoChests={MarketDay.Config.PeekIntoChests}]",
$"Prefix_Sign_checkForAction checking {__instance} {__instance.DisplayName} owner {owner} at {__instance.TileLocation}",
LogLevel.Debug);

if (owner is null or "Player") return true;

MarketDay.monitor.Log(
$"{MarketDay.SMod.ModManifest.UniqueID} {chestOwner} {signOwner}",
$"Prefix_Sign_checkForAction preventing action on object at {__instance.TileLocation} owned by {owner}",
LogLevel.Debug);

if (signOwner is null || signOwner == "Player") return true;
MarketDay.monitor.Log($"Suppress and shake: stop player opening signs", LogLevel.Debug);
who.currentLocation.playSound("hammer");

who.currentLocation.playSound("clank");
__instance.shakeTimer = 500;
__result = false;
return false;
Expand All @@ -84,76 +71,49 @@ public static bool Prefix(Sign __instance, Farmer who, bool justCheckingForActiv
// does not trip for Signs
[HarmonyPatch(typeof(Object))]
[HarmonyPatch("performUseAction")]
public class Prefix_performUseAction
public class Prefix_Object_performUseAction
{
public static bool Prefix(Object __instance, GameLocation location, ref bool __result)
{
var owner = MapUtility.Owner(__instance);
MarketDay.monitor.Log(
$"Prefix_performUseAction checking use access to {__instance} {__instance.DisplayName} at {__instance.TileLocation}",
$"Prefix_Object_performUseAction checking {__instance} {__instance.DisplayName} owner {owner} at {__instance.TileLocation}",
LogLevel.Debug);

if (owner is null) return true;
if (owner == "Player" || MarketDay.Config.PeekIntoChests) return true;

if (__instance is Sign sign)
{
if (sign.modData.ContainsKey($"{MarketDay.SMod.ModManifest.UniqueID}/GrangeSign"))
{
MarketDay.monitor.Log(
$"Prefix_performToolAction preventing damage to sign at {__instance.TileLocation}",
LogLevel.Debug);
location.playSound("hammer");
__instance.shakeTimer = 100;
__result = false;
return false;
}
}

if (__instance is Chest chest)
{
if (chest.modData.TryGetValue($"{MarketDay.SMod.ModManifest.UniqueID}/GrangeChest", out var chestOwner))
{
MarketDay.monitor.Log(
$"Prefix_performUseAction checking access to chest {chestOwner} at {__instance.TileLocation}",
LogLevel.Debug);
if (chestOwner is not null && chestOwner != "Player" && !MarketDay.Config.PeekIntoChests)
{
MarketDay.monitor.Log($"Suppress and shake: stop player opening chests", LogLevel.Debug);
location.playSound("hammer");
__instance.shakeTimer = 500;
__result = false;
return false;
}
}
}

return true;
MarketDay.monitor.Log(
$"Prefix_Object_performUseAction preventing use of object at {__instance.TileLocation} owned by {owner}",
LogLevel.Debug);

location.playSound("clank");
__instance.shakeTimer = 500;
__result = false;
return false;
}
}

// public virtual bool performToolAction(Tool t, GameLocation location)
// this one works leave it alone
[HarmonyPatch(typeof(Object))]
[HarmonyPatch("performToolAction")]
public class Prefix_performToolAction
public class Prefix_Object_performToolAction
{
public static bool Prefix(Object __instance, GameLocation location, ref bool __result)
{
__instance.modData.TryGetValue($"{MarketDay.SMod.ModManifest.UniqueID}/GrangeStorage", out var chestOwner);
__instance.modData.TryGetValue($"{MarketDay.SMod.ModManifest.UniqueID}/GrangeSign", out var signOwner);

MarketDay.monitor.Log(
$"Prefix_performToolAction checking tool access to {__instance} {__instance.DisplayName} at {__instance.TileLocation} [RuinTheFurniture={MarketDay.Config.RuinTheFurniture}]",
LogLevel.Debug);

var owner = MapUtility.Owner(__instance);
MarketDay.monitor.Log(
$"{MarketDay.SMod.ModManifest.UniqueID} {chestOwner} {signOwner}",
$"Prefix_Object_performToolAction checking {__instance} {__instance.DisplayName} owner {owner} at {__instance.TileLocation}",
LogLevel.Debug);

if (MarketDay.Config.RuinTheFurniture) return true;
if (signOwner is null && chestOwner is null) return true;
if (owner is null) return true;

MarketDay.monitor.Log(
$"Prefix_performToolAction preventing damage to object at {__instance.TileLocation}",
$"Prefix_Object_performToolAction preventing damage to object at {__instance.TileLocation} owned by {owner}",
LogLevel.Debug);
location.playSound("hammer");
location.playSound("clank");
__instance.shakeTimer = 100;
__result = false;
return false;
Expand All @@ -168,9 +128,9 @@ public class Postfix_draw
{
public static void Postfix(Chest __instance, SpriteBatch spriteBatch, int x, int y)
{
if (!__instance.modData.TryGetValue($"{MarketDay.SMod.ModManifest.UniqueID}/GrangeStorage",
if (!__instance.modData.TryGetValue($"{MarketDay.SMod.ModManifest.UniqueID}/{GrangeShop.StockChestKey}",
out var shopName)) return;

// get shop for shopName
if (!ShopManager.GrangeShops.TryGetValue(shopName, out var grangeShop))
{
Expand All @@ -180,7 +140,7 @@ public static void Postfix(Chest __instance, SpriteBatch spriteBatch, int x, int
return;
}

var tileLocation = new Vector2(grangeShop.X, grangeShop.Y);
var tileLocation = grangeShop.Origin;
var drawLayer = Math.Max(0f, ((tileLocation.Y + 1) * 64 - 24) / 10000f) + tileLocation.X * 1E-05f;
grangeShop.drawGrangeItems(tileLocation, spriteBatch, drawLayer);
}
Expand All @@ -197,15 +157,15 @@ public static bool Prefix(PathFindController __instance, ref Point startPoint, P
if (!MarketDay.IsMarketDay()) return true;
if (!MarketDay.Config.NPCVisitors) return true;

if (MarketDay.GrangeStandLocations is null)
if (MapUtility.ShopTiles() is null)
{
MarketDay.monitor.Log($"findPathForNPCSchedules: MarketDay.ShopLocations is null", LogLevel.Debug);
return true;
}

if (MarketDay.GrangeStandLocations.Count == 0)
if (MapUtility.ShopTiles().Count == 0)
{
MarketDay.monitor.Log($"findPathForNPCSchedules: MarketDay.ShopLocations.Count {MarketDay.GrangeStandLocations.Count}", LogLevel.Debug);
MarketDay.monitor.Log($"findPathForNPCSchedules: MarketDay.ShopLocations.Count {MapUtility.ShopTiles().Count}", LogLevel.Debug);
return true;
}

Expand All @@ -215,7 +175,7 @@ public static bool Prefix(PathFindController __instance, ref Point startPoint, P

var placesToVisit = new List<Point>();

foreach (var (shopX, shopY) in MarketDay.GrangeStandLocations)
foreach (var (shopX, shopY) in MapUtility.ShopTiles())
{
var visitPoint = new Point((int) shopX + Game1.random.Next(3), (int) shopY + 4);
if (Game1.random.NextDouble() < MarketDay.Config.StallVisitChance) placesToVisit.Add(visitPoint);
Expand Down
59 changes: 59 additions & 0 deletions Market/MarketDay/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,19 @@ Credits
Developer Notes
===

Debugging notes
---

NPC pathfinding to visit shops is OFF by default, in case it crashes. Go into GMCM and turn on NPC visitor pathfinding when you're ready to test it

Other things you can turn on/off with GMCM:
* Peek into shop chests
* Ruin the furniture
* Debug keybinds: when enabled,
* V opens the GMCM config
* R reloads the datafiles and restarts the market
* Z warps you between the Town and the Farmhouse

Market Data Model
---
A list of co-ordinates of the grange stands (top left corner),
Expand All @@ -137,6 +150,24 @@ and a map of shop name -> NPC who runs the shop.
Grange shop life cycle
---

State:
* MarketDay
MapChangesSynced

* ItemShop
STF things
* GrangeShop
recentlyLooked (LOCAL)
recentlyTended (LOCAL)
Sales (LOCAL)
* DayStockChest
Visitors today
Sales today
* DisplayChest
* ShopSign
* StockManager (LOCAL)


Day started:
* reset the sales and visitor counters
* reset the lists of sales and recent buyers
Expand All @@ -145,6 +176,34 @@ Day started:
* restock the shop from the chest
* move the furniture into position

Second moment of day:
Sync map changes
Main player:
Cache grange locations off patched map
Recalculate NPC schedules
Assign shops to grange locations
All players:
For each store:
Setup for new day

Store setup each market day:
Get references to furniture
Main player:
Reset stats
Stock chest
Stock grange
Show the furniture
Decorate the furniture

Every ten-minutes:
Get references to furniture

Every hour:
Main player:
NPC shop: restock
Player shop: restock if allowed


When world is rendered:
* draw the shop contents

Expand Down
Loading

0 comments on commit e5c0e3f

Please sign in to comment.