Support for Json Asset Mod
This commit is contained in:
parent
a22263b70a
commit
e18ac2a111
|
@ -25,4 +25,49 @@ ret
|
|||
ldsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
ldarg.1
|
||||
ldnull
|
||||
callvirt System.Void StardewValley.ModHooks::OnGame1_Draw(Microsoft.Xna.Framework.GameTime,Microsoft.Xna.Framework.Graphics.RenderTarget2D)
|
||||
callvirt System.Void StardewValley.ModHooks::OnGame1_Draw(Microsoft.Xna.Framework.GameTime,Microsoft.Xna.Framework.Graphics.RenderTarget2D)
|
||||
|
||||
|
||||
Optional Section
|
||||
|
||||
Fix back button£º
|
||||
|
||||
Modify class StardewValley.Game1, modify method updateAndroidMenus(), modify Instructions at beginning:
|
||||
ldsfld StardewValley.InputState StardewValley.Game1::input
|
||||
callvirt Microsoft.Xna.Framework.Input.GamePadState StardewValley.InputState::GetGamePadState()
|
||||
|
||||
|
||||
|
||||
Json Asset Support£º
|
||||
|
||||
Modify class StardewValley.Object, modify method checkForAction,insert instructions at beginning:
|
||||
ldsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
ldarg.0
|
||||
callvirt System.Boolean StardewValley.ModHooks::OnObject_checkForAction(StardewValley.Object)
|
||||
brtrue.s -> (6) ldarg.2
|
||||
ldc.i4.0
|
||||
ret
|
||||
|
||||
modify method isIndexOkForBasicShippedCategory,replace instructions:
|
||||
ldarg.0
|
||||
ldc.i4 434
|
||||
bne.un.s -> (5) ldsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
ldc.i4.0
|
||||
ret
|
||||
ldsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
ldarg.0
|
||||
ldloca.s -> (0) (System.Boolean)
|
||||
callvirt System.Void StardewValley.ModHooks::OnObject_isIndexOkForBasicShippedCategory(System.Int32,System.Boolean&)
|
||||
ldloc.0
|
||||
ret
|
||||
|
||||
modify method canBePlacedHere insert instructions at beginning:
|
||||
ldsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
ldarg.0
|
||||
ldarg.1
|
||||
ldarg.2
|
||||
ldloca.s -> (1) (System.Boolean)
|
||||
callvirt System.Boolean StardewValley.ModHooks::OnObject_canBePlacedHere(StardewValley.Object,StardewValley.GameLocation,Microsoft.Xna.Framework.Vector2,System.Boolean&)
|
||||
brtrue.s -> (9) ldarg.1
|
||||
ldloc.1
|
||||
ret
|
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace JsonAssets
|
||||
{
|
||||
public interface IApi
|
||||
{
|
||||
void LoadAssets(string path);
|
||||
|
||||
int GetObjectId(string name);
|
||||
int GetCropId(string name);
|
||||
int GetFruitTreeId(string name);
|
||||
int GetBigCraftableId(string name);
|
||||
int GetHatId(string name);
|
||||
int GetWeaponId(string name);
|
||||
|
||||
IDictionary<string, int> GetAllObjectIds();
|
||||
IDictionary<string, int> GetAllCropIds();
|
||||
IDictionary<string, int> GetAllFruitTreeIds();
|
||||
IDictionary<string, int> GetAllBigCraftableIds();
|
||||
IDictionary<string, int> GetAllHatIds();
|
||||
IDictionary<string, int> GetAllWeaponIds();
|
||||
|
||||
event EventHandler IdsAssigned;
|
||||
event EventHandler AddedItemsToShop;
|
||||
}
|
||||
|
||||
public class Api : IApi
|
||||
{
|
||||
private readonly Action<string> loadFolder;
|
||||
|
||||
public Api(Action<string> loadFolder)
|
||||
{
|
||||
this.loadFolder = loadFolder;
|
||||
}
|
||||
|
||||
public void LoadAssets(string path)
|
||||
{
|
||||
this.loadFolder(path);
|
||||
}
|
||||
|
||||
public int GetObjectId(string name)
|
||||
{
|
||||
return Mod.instance.objectIds.ContainsKey(name) ? Mod.instance.objectIds[name] : -1;
|
||||
}
|
||||
|
||||
public int GetCropId(string name)
|
||||
{
|
||||
return Mod.instance.cropIds.ContainsKey(name) ? Mod.instance.cropIds[name] : -1;
|
||||
}
|
||||
|
||||
public int GetFruitTreeId(string name)
|
||||
{
|
||||
return Mod.instance.fruitTreeIds.ContainsKey(name) ? Mod.instance.fruitTreeIds[name] : -1;
|
||||
}
|
||||
|
||||
public int GetBigCraftableId(string name)
|
||||
{
|
||||
return Mod.instance.bigCraftableIds.ContainsKey(name) ? Mod.instance.bigCraftableIds[name] : -1;
|
||||
}
|
||||
|
||||
public int GetHatId(string name)
|
||||
{
|
||||
return Mod.instance.hatIds.ContainsKey(name) ? Mod.instance.hatIds[name] : -1;
|
||||
}
|
||||
|
||||
public int GetWeaponId(string name)
|
||||
{
|
||||
return Mod.instance.weaponIds.ContainsKey(name) ? Mod.instance.weaponIds[name] : -1;
|
||||
}
|
||||
|
||||
public IDictionary<string, int> GetAllObjectIds()
|
||||
{
|
||||
return new Dictionary<string, int>(Mod.instance.objectIds);
|
||||
}
|
||||
|
||||
public IDictionary<string, int> GetAllCropIds()
|
||||
{
|
||||
return new Dictionary<string, int>(Mod.instance.cropIds);
|
||||
}
|
||||
|
||||
public IDictionary<string, int> GetAllFruitTreeIds()
|
||||
{
|
||||
return new Dictionary<string, int>(Mod.instance.fruitTreeIds);
|
||||
}
|
||||
|
||||
public IDictionary<string, int> GetAllBigCraftableIds()
|
||||
{
|
||||
return new Dictionary<string, int>(Mod.instance.bigCraftableIds);
|
||||
}
|
||||
|
||||
public IDictionary<string, int> GetAllHatIds()
|
||||
{
|
||||
return new Dictionary<string, int>(Mod.instance.hatIds);
|
||||
}
|
||||
|
||||
public IDictionary<string, int> GetAllWeaponIds()
|
||||
{
|
||||
return new Dictionary<string, int>(Mod.instance.weaponIds);
|
||||
}
|
||||
|
||||
public event EventHandler IdsAssigned;
|
||||
internal void InvokeIdsAssigned()
|
||||
{
|
||||
Log.trace("Event: IdsAssigned");
|
||||
if (IdsAssigned == null)
|
||||
return;
|
||||
Util.invokeEvent("JsonAssets.Api.IdsAssigned", IdsAssigned.GetInvocationList(), null);
|
||||
}
|
||||
|
||||
public event EventHandler AddedItemsToShop;
|
||||
internal void InvokeAddedItemsToShop()
|
||||
{
|
||||
Log.trace("Event: AddedItemsToShop");
|
||||
if (AddedItemsToShop == null)
|
||||
return;
|
||||
Util.invokeEvent("JsonAssets.Api.AddedItemsToShop", AddedItemsToShop.GetInvocationList(), null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
using JsonAssets.Data;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using StardewModdingAPI;
|
||||
using StardewValley;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace JsonAssets
|
||||
{
|
||||
public class ContentInjector : IAssetEditor
|
||||
{
|
||||
public bool CanEdit<T>(IAssetInfo asset)
|
||||
{
|
||||
if (asset.AssetNameEquals("Data\\ObjectInformation"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("Data\\Crops"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("Data\\fruitTrees"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("Data\\CookingRecipes"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("Data\\CraftingRecipes"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("Data\\BigCraftablesInformation"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("Data\\hats"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("Data\\weapons"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("Data\\NPCGiftTastes"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("Maps\\springobjects"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("TileSheets\\crops"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("TileSheets\\fruitTrees"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("TileSheets\\Craftables") || asset.AssetNameEquals("TileSheets\\Craftables_indoor") || asset.AssetNameEquals("TileSheets\\Craftables_outdoor"))
|
||||
return true; // _indoor/_outdoor for Seasonal Immersion compat
|
||||
if (asset.AssetNameEquals("Characters\\Farmer\\hats"))
|
||||
return true;
|
||||
if (asset.AssetNameEquals("TileSheets\\weapons"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Edit<T>(IAssetData asset)
|
||||
{
|
||||
if (asset.AssetNameEquals("Data\\ObjectInformation"))
|
||||
{
|
||||
IDictionary<int, string> data = asset.AsDictionary<int, string>().Data;
|
||||
foreach (ObjectData obj in Mod.instance.objects)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting to objects: {obj.GetObjectId()}: {obj.GetObjectInformation()}");
|
||||
data.Add(obj.GetObjectId(), obj.GetObjectInformation());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting object information for {obj.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("Data\\Crops"))
|
||||
{
|
||||
IDictionary<int, string> data = asset.AsDictionary<int, string>().Data;
|
||||
foreach (CropData crop in Mod.instance.crops)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting to crops: {crop.GetSeedId()}: {crop.GetCropInformation()}");
|
||||
data.Add(crop.GetSeedId(), crop.GetCropInformation());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting crop for {crop.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("Data\\fruitTrees"))
|
||||
{
|
||||
IDictionary<int, string> data = asset.AsDictionary<int, string>().Data;
|
||||
foreach (FruitTreeData fruitTree in Mod.instance.fruitTrees)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting to fruit trees: {fruitTree.GetSaplingId()}: {fruitTree.GetFruitTreeInformation()}");
|
||||
data.Add(fruitTree.GetSaplingId(), fruitTree.GetFruitTreeInformation());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting fruit tree for {fruitTree.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("Data\\CookingRecipes"))
|
||||
{
|
||||
IDictionary<string, string> data = asset.AsDictionary<string, string>().Data;
|
||||
foreach (ObjectData obj in Mod.instance.objects)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (obj.Recipe == null)
|
||||
continue;
|
||||
if (obj.Category != ObjectData.Category_.Cooking)
|
||||
continue;
|
||||
Log.trace($"Injecting to cooking recipes: {obj.Name}: {obj.Recipe.GetRecipeString(obj)}");
|
||||
data.Add(obj.Name, obj.Recipe.GetRecipeString(obj));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting cooking recipe for {obj.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("Data\\CraftingRecipes"))
|
||||
{
|
||||
IDictionary<string, string> data = asset.AsDictionary<string, string>().Data;
|
||||
foreach (ObjectData obj in Mod.instance.objects)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (obj.Recipe == null)
|
||||
continue;
|
||||
if (obj.Category == ObjectData.Category_.Cooking)
|
||||
continue;
|
||||
Log.trace($"Injecting to crafting recipes: {obj.Name}: {obj.Recipe.GetRecipeString(obj)}");
|
||||
data.Add(obj.Name, obj.Recipe.GetRecipeString(obj));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting crafting recipe for {obj.Name}: {e}");
|
||||
}
|
||||
}
|
||||
foreach (BigCraftableData big in Mod.instance.bigCraftables)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (big.Recipe == null)
|
||||
continue;
|
||||
Log.trace($"Injecting to crafting recipes: {big.Name}: {big.Recipe.GetRecipeString(big)}");
|
||||
data.Add(big.Name, big.Recipe.GetRecipeString(big));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting crafting recipe for {big.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("Data\\BigCraftablesInformation"))
|
||||
{
|
||||
IDictionary<int, string> data = asset.AsDictionary<int, string>().Data;
|
||||
foreach (BigCraftableData big in Mod.instance.bigCraftables)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting to big craftables: {big.GetCraftableId()}: {big.GetCraftableInformation()}");
|
||||
data.Add(big.GetCraftableId(), big.GetCraftableInformation());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting object information for {big.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("Data\\hats"))
|
||||
{
|
||||
IDictionary<int, string> data = asset.AsDictionary<int, string>().Data;
|
||||
foreach (HatData hat in Mod.instance.hats)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting to hats: {hat.GetHatId()}: {hat.GetHatInformation()}");
|
||||
data.Add(hat.GetHatId(), hat.GetHatInformation());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting hat information for {hat.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("Data\\weapons"))
|
||||
{
|
||||
IDictionary<int, string> data = asset.AsDictionary<int, string>().Data;
|
||||
foreach (WeaponData weapon in Mod.instance.weapons)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting to weapons: {weapon.GetWeaponId()}: {weapon.GetWeaponInformation()}");
|
||||
data.Add(weapon.GetWeaponId(), weapon.GetWeaponInformation());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting weapon information for {weapon.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("Data\\NPCGiftTastes"))
|
||||
{
|
||||
IDictionary<string, string> data = asset.AsDictionary<string, string>().Data;
|
||||
// TODO: This could be optimized from mn to... m + n?
|
||||
// Basically, iterate through objects and create Dictionary<NPC name, GiftData[]>
|
||||
// Iterate through objects, each section and add to dict[npc][approp. section]
|
||||
// Point is, I'm doing this the lazy way right now
|
||||
Dictionary<string, string> newData = new Dictionary<string, string>(data);
|
||||
foreach (KeyValuePair<string, string> npc in data)
|
||||
{
|
||||
if (npc.Key.StartsWith("Universal_"))
|
||||
continue;
|
||||
|
||||
string[] sections = npc.Value.Split('/');
|
||||
if ( sections.Length != 11 )
|
||||
{
|
||||
Log.warn($"Bad gift taste data for {npc.Key}!");
|
||||
continue;
|
||||
}
|
||||
|
||||
string loveStr = sections[0];
|
||||
List<string> loveIds = new List<string>(sections[1].Split(' '));
|
||||
string likeStr = sections[2];
|
||||
List<string> likeIds = new List<string>(sections[3].Split(' '));
|
||||
string dislikeStr = sections[4];
|
||||
List<string> dislikeIds = new List<string>(sections[5].Split(' '));
|
||||
string hateStr = sections[6];
|
||||
List<string> hateIds = new List<string>(sections[7].Split(' '));
|
||||
string neutralStr = sections[8];
|
||||
List<string> neutralIds = new List<string>(sections[9].Split(' '));
|
||||
|
||||
foreach (ObjectData obj in Mod.instance.objects )
|
||||
{
|
||||
if (obj.GiftTastes == null)
|
||||
continue;
|
||||
if (obj.GiftTastes.Love != null && obj.GiftTastes.Love.Contains(npc.Key))
|
||||
loveIds.Add(obj.GetObjectId().ToString());
|
||||
if (obj.GiftTastes.Like != null && obj.GiftTastes.Like.Contains(npc.Key))
|
||||
likeIds.Add(obj.GetObjectId().ToString());
|
||||
if (obj.GiftTastes.Neutral != null && obj.GiftTastes.Neutral.Contains(npc.Key))
|
||||
neutralIds.Add(obj.GetObjectId().ToString());
|
||||
if (obj.GiftTastes.Dislike != null && obj.GiftTastes.Dislike.Contains(npc.Key))
|
||||
dislikeIds.Add(obj.GetObjectId().ToString());
|
||||
if (obj.GiftTastes.Hate != null && obj.GiftTastes.Hate.Contains(npc.Key))
|
||||
hateIds.Add(obj.GetObjectId().ToString());
|
||||
}
|
||||
|
||||
string loveIdStr = string.Join(" ", loveIds);
|
||||
string likeIdStr = string.Join(" ", likeIds);
|
||||
string dislikeIdStr = string.Join(" ", dislikeIds);
|
||||
string hateIdStr = string.Join(" ", hateIds);
|
||||
string neutralIdStr = string.Join(" ", neutralIds);
|
||||
newData[npc.Key] = $"{loveStr}/{loveIdStr}/{likeStr}/{likeIdStr}/{dislikeStr}/{dislikeIdStr}/{hateStr}/{hateIdStr}/{neutralStr}/{neutralIdStr}/ ";
|
||||
|
||||
Log.trace($"Adding gift tastes for {npc.Key}: {newData[npc.Key]}");
|
||||
}
|
||||
asset.ReplaceWith(newData);
|
||||
}
|
||||
else if (asset.AssetNameEquals("Maps\\springobjects"))
|
||||
{
|
||||
Texture2D oldTex = asset.AsImage().Data;
|
||||
Texture2D newTex = new Texture2D(Game1.graphics.GraphicsDevice, oldTex.Width, Math.Max(oldTex.Height, 4096));
|
||||
asset.ReplaceWith(newTex);
|
||||
asset.AsImage().PatchImage(oldTex);
|
||||
|
||||
foreach (ObjectData obj in Mod.instance.objects)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting {obj.Name} sprites @ {this.objectRect(obj.GetObjectId())}");
|
||||
asset.AsImage().PatchImage(obj.texture, null, this.objectRect(obj.GetObjectId()));
|
||||
if (obj.IsColored)
|
||||
{
|
||||
Log.trace($"Injecting {obj.Name} color sprites @ {this.objectRect(obj.GetObjectId() + 1)}");
|
||||
asset.AsImage().PatchImage(obj.textureColor, null, this.objectRect(obj.GetObjectId() + 1));
|
||||
}
|
||||
}
|
||||
catch ( Exception e )
|
||||
{
|
||||
Log.error($"Exception injecting sprite for {obj.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("TileSheets\\crops"))
|
||||
{
|
||||
Texture2D oldTex = asset.AsImage().Data;
|
||||
Texture2D newTex = new Texture2D(Game1.graphics.GraphicsDevice, oldTex.Width, Math.Max(oldTex.Height, 4096));
|
||||
asset.ReplaceWith(newTex);
|
||||
asset.AsImage().PatchImage(oldTex);
|
||||
|
||||
foreach (CropData crop in Mod.instance.crops)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting {crop.Name} crop images @ {this.cropRect(crop.GetCropSpriteIndex())}");
|
||||
asset.AsImage().PatchImage(crop.texture, null, this.cropRect(crop.GetCropSpriteIndex()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting crop sprite for {crop.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("TileSheets\\fruitTrees"))
|
||||
{
|
||||
Texture2D oldTex = asset.AsImage().Data;
|
||||
Texture2D newTex = new Texture2D(Game1.graphics.GraphicsDevice, oldTex.Width, Math.Max(oldTex.Height, 4096));
|
||||
asset.ReplaceWith(newTex);
|
||||
asset.AsImage().PatchImage(oldTex);
|
||||
|
||||
foreach (FruitTreeData fruitTree in Mod.instance.fruitTrees)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting {fruitTree.Name} fruit tree images @ {this.fruitTreeRect(fruitTree.GetFruitTreeIndex())}");
|
||||
asset.AsImage().PatchImage(fruitTree.texture, null, this.fruitTreeRect(fruitTree.GetFruitTreeIndex()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting fruit tree sprite for {fruitTree.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("TileSheets\\Craftables") || asset.AssetNameEquals("TileSheets\\Craftables_indoor") || asset.AssetNameEquals("TileSheets\\Craftables_outdoor"))
|
||||
{
|
||||
Texture2D oldTex = asset.AsImage().Data;
|
||||
Texture2D newTex = new Texture2D(Game1.graphics.GraphicsDevice, oldTex.Width, Math.Max(oldTex.Height, 4096));
|
||||
asset.ReplaceWith(newTex);
|
||||
asset.AsImage().PatchImage(oldTex);
|
||||
Log.trace($"Big craftables are now ({oldTex.Width}, {Math.Max(oldTex.Height, 4096)})");
|
||||
|
||||
foreach (BigCraftableData big in Mod.instance.bigCraftables)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting {big.Name} sprites @ {this.bigCraftableRect(big.GetCraftableId())}");
|
||||
asset.AsImage().PatchImage(big.texture, null, this.bigCraftableRect(big.GetCraftableId()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting sprite for {big.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("Characters\\Farmer\\hats"))
|
||||
{
|
||||
Texture2D oldTex = asset.AsImage().Data;
|
||||
Texture2D newTex = new Texture2D(Game1.graphics.GraphicsDevice, oldTex.Width, Math.Max(oldTex.Height, 4096));
|
||||
asset.ReplaceWith(newTex);
|
||||
asset.AsImage().PatchImage(oldTex);
|
||||
Log.trace($"Hats are now ({oldTex.Width}, {Math.Max(oldTex.Height, 4096)})");
|
||||
|
||||
foreach (HatData hat in Mod.instance.hats)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting {hat.Name} sprites @ {this.hatRect(hat.GetHatId())}");
|
||||
asset.AsImage().PatchImage(hat.texture, null, this.hatRect(hat.GetHatId()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting sprite for {hat.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (asset.AssetNameEquals("TileSheets\\weapons"))
|
||||
{
|
||||
Texture2D oldTex = asset.AsImage().Data;
|
||||
Texture2D newTex = new Texture2D(Game1.graphics.GraphicsDevice, oldTex.Width, Math.Max(oldTex.Height, 4096));
|
||||
asset.ReplaceWith(newTex);
|
||||
asset.AsImage().PatchImage(oldTex);
|
||||
Log.trace($"Weapons are now ({oldTex.Width}, {Math.Max(oldTex.Height, 4096)})");
|
||||
|
||||
foreach (WeaponData weapon in Mod.instance.weapons)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.trace($"Injecting {weapon.Name} sprites @ {this.weaponRect(weapon.GetWeaponId())}");
|
||||
asset.AsImage().PatchImage(weapon.texture, null, this.weaponRect(weapon.GetWeaponId()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception injecting sprite for {weapon.Name}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private Rectangle objectRect(int index)
|
||||
{
|
||||
return new Rectangle(index % 24 * 16, index / 24 * 16, 16, 16);
|
||||
}
|
||||
private Rectangle cropRect(int index)
|
||||
{
|
||||
return new Rectangle(index % 2 * 128, index / 2 * 32, 128, 32);
|
||||
}
|
||||
private Rectangle fruitTreeRect(int index)
|
||||
{
|
||||
return new Rectangle(0, index * 80, 432, 80);
|
||||
}
|
||||
private Rectangle bigCraftableRect(int index)
|
||||
{
|
||||
return new Rectangle(index % 8 * 16, index / 8 * 32, 16, 32);
|
||||
}
|
||||
private Rectangle hatRect(int index)
|
||||
{
|
||||
return new Rectangle(index % 12 * 20, index / 12 * 80, 20, 80);
|
||||
}
|
||||
private Rectangle weaponRect(int index)
|
||||
{
|
||||
return new Rectangle(index % 8 * 16, index / 8 * 16, 16, 16);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Newtonsoft.Json;
|
||||
using StardewValley;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace JsonAssets.Data
|
||||
{
|
||||
public class BigCraftableData : DataNeedsId
|
||||
{
|
||||
[JsonIgnore]
|
||||
internal Texture2D texture;
|
||||
|
||||
public class Recipe_
|
||||
{
|
||||
public class Ingredient
|
||||
{
|
||||
public object Object { get; set; }
|
||||
public int Count { get; set; }
|
||||
}
|
||||
// Possibly friendship option (letters, like vanilla) and/or skill levels (on levelup?)
|
||||
public int ResultCount { get; set; } = 1;
|
||||
public IList<Ingredient> Ingredients { get; set; } = new List<Ingredient>();
|
||||
|
||||
public bool IsDefault { get; set; } = false;
|
||||
public bool CanPurchase { get; set; } = false;
|
||||
public int PurchasePrice { get; set; }
|
||||
public string PurchaseFrom { get; set; } = "Gus";
|
||||
public IList<string> PurchaseRequirements { get; set; } = new List<string>();
|
||||
|
||||
internal string GetRecipeString( BigCraftableData parent )
|
||||
{
|
||||
string str = "";
|
||||
foreach (Ingredient ingredient in this.Ingredients)
|
||||
str += Mod.instance.ResolveObjectId(ingredient.Object) + " " + ingredient.Count + " ";
|
||||
str = str.Substring(0, str.Length - 1);
|
||||
str += $"/what is this for?/{parent.id}/true/null";
|
||||
return str;
|
||||
}
|
||||
|
||||
internal string GetPurchaseRequirementString()
|
||||
{
|
||||
string str = $"1234567890";
|
||||
foreach (string cond in this.PurchaseRequirements)
|
||||
str += $"/{cond}";
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public string Description { get; set; }
|
||||
|
||||
public int Price { get; set; }
|
||||
|
||||
public bool ProvidesLight { get; set; } = false;
|
||||
|
||||
public Recipe_ Recipe { get; set; }
|
||||
|
||||
public bool CanPurchase { get; set; } = false;
|
||||
public int PurchasePrice { get; set; }
|
||||
public string PurchaseFrom { get; set; } = "Pierre";
|
||||
public IList<string> PurchaseRequirements { get; set; } = new List<string>();
|
||||
|
||||
public Dictionary<string, string> NameLocalization = new Dictionary<string, string>();
|
||||
public Dictionary<string, string> DescriptionLocalization = new Dictionary<string, string>();
|
||||
|
||||
public string LocalizedName()
|
||||
{
|
||||
LocalizedContentManager.LanguageCode currLang = LocalizedContentManager.CurrentLanguageCode;
|
||||
if (currLang == LocalizedContentManager.LanguageCode.en)
|
||||
return this.Name;
|
||||
if (this.NameLocalization == null || !this.NameLocalization.ContainsKey(currLang.ToString()))
|
||||
return this.Name;
|
||||
return this.NameLocalization[currLang.ToString()];
|
||||
}
|
||||
|
||||
public string LocalizedDescription()
|
||||
{
|
||||
LocalizedContentManager.LanguageCode currLang = LocalizedContentManager.CurrentLanguageCode;
|
||||
if (currLang == LocalizedContentManager.LanguageCode.en)
|
||||
return this.Description;
|
||||
if (this.DescriptionLocalization == null || !this.DescriptionLocalization.ContainsKey(currLang.ToString()))
|
||||
return this.Description;
|
||||
return this.DescriptionLocalization[currLang.ToString()];
|
||||
}
|
||||
|
||||
public int GetCraftableId() { return this.id; }
|
||||
|
||||
internal string GetCraftableInformation()
|
||||
{
|
||||
string str = $"{this.Name}/{this.Price}/-300/Crafting -9/{this.LocalizedDescription()}/true/true/0/{this.LocalizedName()}";
|
||||
if (this.ProvidesLight)
|
||||
str += "/true";
|
||||
return str;
|
||||
}
|
||||
|
||||
internal string GetPurchaseRequirementString()
|
||||
{
|
||||
string str = $"1234567890";
|
||||
foreach (string cond in this.PurchaseRequirements)
|
||||
str += $"/{cond}";
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace JsonAssets.Data
|
||||
{
|
||||
public class ContentPackData
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string Author { get; set; }
|
||||
public IList<string> UpdateKeys { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace JsonAssets.Data
|
||||
{
|
||||
public class CropData : DataNeedsId
|
||||
{
|
||||
[JsonIgnore]
|
||||
internal Texture2D texture;
|
||||
|
||||
public object Product { get; set; }
|
||||
public string SeedName { get; set; }
|
||||
public string SeedDescription { get; set; }
|
||||
|
||||
public IList<string> Seasons { get; set; } = new List<string>();
|
||||
public IList<int> Phases { get; set; } = new List<int>();
|
||||
public int RegrowthPhase { get; set; } = -1;
|
||||
public bool HarvestWithScythe { get; set; } = false;
|
||||
public bool TrellisCrop { get; set; } = false;
|
||||
public IList<Color> Colors { get; set; } = new List<Color>();
|
||||
public class Bonus_
|
||||
{
|
||||
public int MinimumPerHarvest { get; set; }
|
||||
public int MaximumPerHarvest { get; set; }
|
||||
public int MaxIncreasePerFarmLevel { get; set; }
|
||||
public double ExtraChance { get; set; }
|
||||
}
|
||||
public Bonus_ Bonus { get; set; } = null;
|
||||
|
||||
public IList<string> SeedPurchaseRequirements { get; set; } = new List<string>();
|
||||
public int SeedPurchasePrice { get; set; }
|
||||
public string SeedPurchaseFrom { get; set; } = "Pierre";
|
||||
|
||||
public Dictionary<string, string> SeedNameLocalization = new Dictionary<string, string>();
|
||||
public Dictionary<string, string> SeedDescriptionLocalization = new Dictionary<string, string>();
|
||||
|
||||
internal ObjectData seed;
|
||||
public int GetSeedId() { return this.seed.id; }
|
||||
public int GetCropSpriteIndex() { return this.id; }
|
||||
internal string GetCropInformation()
|
||||
{
|
||||
string str = "";
|
||||
//str += GetProductId() + "/";
|
||||
foreach (int phase in this.Phases )
|
||||
{
|
||||
str += phase + " ";
|
||||
}
|
||||
str = str.Substring(0, str.Length - 1) + "/";
|
||||
foreach (string season in this.Seasons)
|
||||
{
|
||||
str += season + " ";
|
||||
}
|
||||
str = str.Substring(0, str.Length - 1) + "/";
|
||||
str += $"{this.GetCropSpriteIndex()}/{Mod.instance.ResolveObjectId(this.Product)}/{this.RegrowthPhase}/";
|
||||
str += (this.HarvestWithScythe ? "1" : "0") + "/";
|
||||
if (this.Bonus != null)
|
||||
str += $"true {this.Bonus.MinimumPerHarvest} {this.Bonus.MaximumPerHarvest} {this.Bonus.MaxIncreasePerFarmLevel} {this.Bonus.ExtraChance}/";
|
||||
else str += "false/";
|
||||
str += (this.TrellisCrop ? "true" : "false") + "/";
|
||||
if (this.Colors != null && this.Colors.Count > 0)
|
||||
{
|
||||
str += "true";
|
||||
foreach (Color color in this.Colors)
|
||||
str += $" {color.R} {color.G} {color.B}";
|
||||
}
|
||||
else
|
||||
str += "false";
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace JsonAssets.Data
|
||||
{
|
||||
public abstract class DataNeedsId
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
internal int id = -1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace JsonAssets.Data
|
||||
{
|
||||
public class FruitTreeData : DataNeedsId
|
||||
{
|
||||
[JsonIgnore]
|
||||
internal Texture2D texture;
|
||||
|
||||
public object Product { get; set; }
|
||||
public string SaplingName { get; set; }
|
||||
public string SaplingDescription { get; set; }
|
||||
|
||||
public string Season { get; set; }
|
||||
|
||||
public IList<string> SaplingPurchaseRequirements { get; set; } = new List<string>();
|
||||
public int SaplingPurchasePrice { get; set; }
|
||||
public string SaplingPurchaseFrom { get; set; } = "Pierre";
|
||||
|
||||
public Dictionary<string, string> SaplingNameLocalization = new Dictionary<string, string>();
|
||||
public Dictionary<string, string> SaplingDescriptionLocalization = new Dictionary<string, string>();
|
||||
|
||||
internal ObjectData sapling;
|
||||
public int GetSaplingId() { return this.sapling.id; }
|
||||
public int GetFruitTreeIndex() { return this.id; }
|
||||
internal string GetFruitTreeInformation()
|
||||
{
|
||||
return $"{this.GetFruitTreeIndex()}/{this.Season}/{Mod.instance.ResolveObjectId(this.Product)}/what goes here?";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Newtonsoft.Json;
|
||||
using StardewValley;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace JsonAssets.Data
|
||||
{
|
||||
class HatData : DataNeedsId
|
||||
{
|
||||
[JsonIgnore]
|
||||
internal Texture2D texture;
|
||||
|
||||
public string Description { get; set; }
|
||||
public int PurchasePrice { get; set; }
|
||||
public bool ShowHair { get; set; }
|
||||
public bool IgnoreHairstyleOffset { get; set; }
|
||||
|
||||
public Dictionary<string, string> NameLocalization = new Dictionary<string, string>();
|
||||
public Dictionary<string, string> DescriptionLocalization = new Dictionary<string, string>();
|
||||
|
||||
public string LocalizedName()
|
||||
{
|
||||
LocalizedContentManager.LanguageCode currLang = LocalizedContentManager.CurrentLanguageCode;
|
||||
if (currLang == LocalizedContentManager.LanguageCode.en)
|
||||
return this.Name;
|
||||
if (this.NameLocalization == null || !this.NameLocalization.ContainsKey(currLang.ToString()))
|
||||
return this.Name;
|
||||
return this.NameLocalization[currLang.ToString()];
|
||||
}
|
||||
|
||||
public string LocalizedDescription()
|
||||
{
|
||||
LocalizedContentManager.LanguageCode currLang = LocalizedContentManager.CurrentLanguageCode;
|
||||
if (currLang == LocalizedContentManager.LanguageCode.en)
|
||||
return this.Description;
|
||||
if (this.DescriptionLocalization == null || !this.DescriptionLocalization.ContainsKey(currLang.ToString()))
|
||||
return this.Description;
|
||||
return this.DescriptionLocalization[currLang.ToString()];
|
||||
}
|
||||
|
||||
public int GetHatId() { return this.id; }
|
||||
|
||||
internal string GetHatInformation()
|
||||
{
|
||||
return $"{this.Name}/{this.LocalizedDescription()}/" + (this.ShowHair ? "true" : "false" ) + "/" + (this.IgnoreHairstyleOffset ? "true" : "false") + $"/{this.LocalizedName()}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using StardewValley;
|
||||
using System.Collections.Generic;
|
||||
using SObject = StardewValley.Object;
|
||||
|
||||
namespace JsonAssets.Data
|
||||
{
|
||||
public class ObjectData : DataNeedsId
|
||||
{
|
||||
[JsonIgnore]
|
||||
internal Texture2D texture;
|
||||
[JsonIgnore]
|
||||
internal Texture2D textureColor;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum Category_
|
||||
{
|
||||
// SDV Patcher made these static readonly, so I can't use them in the enum
|
||||
Vegetable = -75, //SObject.VegetableCategory,
|
||||
Fruit = -79, //SObject.FruitsCategory,
|
||||
Flower = -80, //SObject.flowersCategory,
|
||||
Gem = -2, //SObject.GemCategory,
|
||||
Fish = -4, //SObject.FishCategory,
|
||||
Egg = -5, //SObject.EggCategory,
|
||||
Milk = -6, //SObject.MilkCategory,
|
||||
Cooking = -7, //SObject.CookingCategory,
|
||||
Crafting = -8, //SObject.CraftingCategory,
|
||||
Mineral = -12, //SObject.mineralsCategory,
|
||||
Meat = -14, //SObject.meatCategory,
|
||||
Metal = -15, //SObject.metalResources,
|
||||
Junk = -20, //SObject.junkCategory,
|
||||
Syrup = -27, //SObject.syrupCategory,
|
||||
MonsterLoot = -28, //SObject.monsterLootCategory,
|
||||
ArtisanGoods = -26, //SObject.artisanGoodsCategory,
|
||||
Seeds = -74, //SObject.SeedsCategory,
|
||||
Ring = -96, //SObject.ringCategory,
|
||||
AnimalGoods = -18, //SObject.sellAtPierresAndMarnies
|
||||
}
|
||||
|
||||
public class Recipe_
|
||||
{
|
||||
public class Ingredient
|
||||
{
|
||||
public object Object { get; set; }
|
||||
public int Count { get; set; }
|
||||
}
|
||||
// Possibly friendship option (letters, like vanilla) and/or skill levels (on levelup?)
|
||||
public int ResultCount { get; set; } = 1;
|
||||
public IList<Ingredient> Ingredients { get; set; } = new List<Ingredient>();
|
||||
|
||||
public bool IsDefault { get; set; } = false;
|
||||
public bool CanPurchase { get; set; } = false;
|
||||
public int PurchasePrice { get; set; }
|
||||
public string PurchaseFrom { get; set; } = "Gus";
|
||||
public IList<string> PurchaseRequirements { get; set; } = new List<string>();
|
||||
|
||||
internal string GetRecipeString( ObjectData parent )
|
||||
{
|
||||
string str = "";
|
||||
foreach (Ingredient ingredient in this.Ingredients)
|
||||
str += Mod.instance.ResolveObjectId(ingredient.Object) + " " + ingredient.Count + " ";
|
||||
str = str.Substring(0, str.Length - 1);
|
||||
str += $"/what is this for?/{parent.id}/";
|
||||
if (parent.Category != Category_.Cooking)
|
||||
str += "false/";
|
||||
str += "/null"; // TODO: Requirement
|
||||
return str;
|
||||
}
|
||||
|
||||
internal string GetPurchaseRequirementString()
|
||||
{
|
||||
string str = $"1234567890";
|
||||
foreach (string cond in this.PurchaseRequirements)
|
||||
str += $"/{cond}";
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public class FoodBuffs_
|
||||
{
|
||||
public int Farming { get; set; } = 0;
|
||||
public int Fishing { get; set; } = 0;
|
||||
public int Mining { get; set; } = 0;
|
||||
public int Luck { get; set; } = 0;
|
||||
public int Foraging { get; set; } = 0;
|
||||
public int MaxStamina { get; set; } = 0;
|
||||
public int MagnetRadius { get; set; } = 0;
|
||||
public int Speed { get; set; } = 0;
|
||||
public int Defense { get; set; } = 0;
|
||||
public int Attack { get; set; } = 0;
|
||||
public int Duration { get; set; } = 0;
|
||||
}
|
||||
|
||||
public string Description { get; set; }
|
||||
public Category_ Category { get; set; }
|
||||
public bool IsColored { get; set; } = false;
|
||||
|
||||
public int Price { get; set; }
|
||||
|
||||
public Recipe_ Recipe { get; set; }
|
||||
|
||||
public int Edibility { get; set; } = SObject.inedible;
|
||||
public bool EdibleIsDrink { get; set; } = false;
|
||||
public FoodBuffs_ EdibleBuffs = new FoodBuffs_();
|
||||
|
||||
public bool CanPurchase { get; set; } = false;
|
||||
public int PurchasePrice { get; set; }
|
||||
public string PurchaseFrom { get; set; } = "Pierre";
|
||||
public IList<string> PurchaseRequirements { get; set; } = new List<string>();
|
||||
|
||||
public class GiftTastes_
|
||||
{
|
||||
public IList<string> Love = new List<string>();
|
||||
public IList<string> Like = new List<string>();
|
||||
public IList<string> Neutral = new List<string>();
|
||||
public IList<string> Dislike = new List<string>();
|
||||
public IList<string> Hate = new List<string>();
|
||||
}
|
||||
public GiftTastes_ GiftTastes;
|
||||
|
||||
public Dictionary<string, string> NameLocalization = new Dictionary<string, string>();
|
||||
public Dictionary<string, string> DescriptionLocalization = new Dictionary<string, string>();
|
||||
|
||||
public string LocalizedName()
|
||||
{
|
||||
LocalizedContentManager.LanguageCode currLang = LocalizedContentManager.CurrentLanguageCode;
|
||||
if (currLang == LocalizedContentManager.LanguageCode.en)
|
||||
return this.Name;
|
||||
if (this.NameLocalization == null || !this.NameLocalization.ContainsKey(currLang.ToString()))
|
||||
return this.Name;
|
||||
return this.NameLocalization[currLang.ToString()];
|
||||
}
|
||||
|
||||
public string LocalizedDescription()
|
||||
{
|
||||
LocalizedContentManager.LanguageCode currLang = LocalizedContentManager.CurrentLanguageCode;
|
||||
if (currLang == LocalizedContentManager.LanguageCode.en)
|
||||
return this.Description;
|
||||
if (this.DescriptionLocalization == null || !this.DescriptionLocalization.ContainsKey(currLang.ToString()))
|
||||
return this.Description;
|
||||
return this.DescriptionLocalization[currLang.ToString()];
|
||||
}
|
||||
|
||||
public int GetObjectId() { return this.id; }
|
||||
|
||||
internal string GetObjectInformation()
|
||||
{
|
||||
if (this.Edibility != SObject.inedible)
|
||||
{
|
||||
int itype = (int)this.Category;
|
||||
string str = $"{this.Name}/{this.Price}/{this.Edibility}/{this.Category} {itype}/{this.LocalizedName()}/{this.LocalizedDescription()}/";
|
||||
str += (this.EdibleIsDrink ? "drink" : "food") + "/";
|
||||
if (this.EdibleBuffs == null)
|
||||
this.EdibleBuffs = new FoodBuffs_();
|
||||
str += $"{this.EdibleBuffs.Farming} {this.EdibleBuffs.Fishing} {this.EdibleBuffs.Mining} 0 {this.EdibleBuffs.Luck} {this.EdibleBuffs.Foraging} 0 {this.EdibleBuffs.MaxStamina} {this.EdibleBuffs.MagnetRadius} {this.EdibleBuffs.Speed} {this.EdibleBuffs.Defense} {this.EdibleBuffs.Attack}/{this.EdibleBuffs.Duration}";
|
||||
return str;
|
||||
}
|
||||
else
|
||||
{
|
||||
int itype = (int)this.Category;
|
||||
return $"{this.Name}/{this.Price}/{this.Edibility}/Basic {itype}/{this.LocalizedName()}/{this.LocalizedDescription()}";
|
||||
}
|
||||
}
|
||||
|
||||
internal string GetPurchaseRequirementString()
|
||||
{
|
||||
string str = $"1234567890";
|
||||
foreach (string cond in this.PurchaseRequirements)
|
||||
str += $"/{cond}";
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using StardewValley;
|
||||
using StardewValley.Tools;
|
||||
using System.Collections.Generic;
|
||||
using SObject = StardewValley.Object;
|
||||
|
||||
namespace JsonAssets.Data
|
||||
{
|
||||
public class WeaponData : DataNeedsId
|
||||
{
|
||||
[JsonIgnore]
|
||||
internal Texture2D texture;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum Type_
|
||||
{
|
||||
Dagger = MeleeWeapon.dagger,
|
||||
Club = MeleeWeapon.club,
|
||||
Sword = MeleeWeapon.defenseSword,
|
||||
}
|
||||
|
||||
public string Description { get; set; }
|
||||
public Type_ Type { get; set; }
|
||||
|
||||
public int MinimumDamage { get; set; }
|
||||
public int MaximumDamage { get; set; }
|
||||
public double Knockback { get; set; }
|
||||
public int Speed { get; set; }
|
||||
public int Accuracy { get; set; }
|
||||
public int Defense { get; set; }
|
||||
public int MineDropVar { get; set; }
|
||||
public int MineDropMinimumLevel { get; set; }
|
||||
public int ExtraSwingArea { get; set; }
|
||||
public double CritChance { get; set; }
|
||||
public double CritMultiplier { get; set; }
|
||||
|
||||
public bool CanPurchase { get; set; } = false;
|
||||
public int PurchasePrice { get; set; }
|
||||
public string PurchaseFrom { get; set; } = "Pierre";
|
||||
public IList<string> PurchaseRequirements { get; set; } = new List<string>();
|
||||
|
||||
public Dictionary<string, string> NameLocalization = new Dictionary<string, string>();
|
||||
public Dictionary<string, string> DescriptionLocalization = new Dictionary<string, string>();
|
||||
|
||||
public string LocalizedName()
|
||||
{
|
||||
LocalizedContentManager.LanguageCode currLang = LocalizedContentManager.CurrentLanguageCode;
|
||||
if (currLang == LocalizedContentManager.LanguageCode.en)
|
||||
return this.Name;
|
||||
if (this.NameLocalization == null || !this.NameLocalization.ContainsKey(currLang.ToString()))
|
||||
return this.Name;
|
||||
return this.NameLocalization[currLang.ToString()];
|
||||
}
|
||||
|
||||
public string LocalizedDescription()
|
||||
{
|
||||
LocalizedContentManager.LanguageCode currLang = LocalizedContentManager.CurrentLanguageCode;
|
||||
if (currLang == LocalizedContentManager.LanguageCode.en)
|
||||
return this.Description;
|
||||
if (this.DescriptionLocalization == null || !this.DescriptionLocalization.ContainsKey(currLang.ToString()))
|
||||
return this.Description;
|
||||
return this.DescriptionLocalization[currLang.ToString()];
|
||||
}
|
||||
|
||||
public int GetWeaponId() { return this.id; }
|
||||
|
||||
internal string GetWeaponInformation()
|
||||
{
|
||||
return $"{this.Name}/{this.LocalizedDescription()}/{this.MinimumDamage}/{this.MaximumDamage}/{this.Knockback}/{this.Speed}/{this.Accuracy}/{this.Defense}/{(int)this.Type}/{this.MineDropVar}/{this.MineDropMinimumLevel}/{this.ExtraSwingArea}/{this.CritChance}/{this.CritMultiplier}/{this.LocalizedName()}";
|
||||
}
|
||||
|
||||
internal string GetPurchaseRequirementString()
|
||||
{
|
||||
string str = $"1234567890";
|
||||
foreach (string cond in this.PurchaseRequirements)
|
||||
str += $"/{cond}";
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{F56B5F8E-0069-4029-8DCD-89002B7285E3}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>JsonAssets</RootNamespace>
|
||||
<AssemblyName>JsonAssets</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="StardewModdingAPI">
|
||||
<HintPath>..\assemblies\StardewModdingAPI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="StardewValley">
|
||||
<HintPath>..\assemblies\StardewValley.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="BmFont, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\BmFont.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Google.Android.Vending.Expansion.Downloader, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Google.Android.Vending.Expansion.Downloader.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Google.Android.Vending.Expansion.ZipFile, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Google.Android.Vending.Expansion.ZipFile.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Google.Android.Vending.Licensing, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Google.Android.Vending.Licensing.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Java.Interop, Version=0.1.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Java.Interop.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AppCenter, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Microsoft.AppCenter.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AppCenter.Analytics, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Microsoft.AppCenter.Analytics.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AppCenter.Analytics.Android.Bindings, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Microsoft.AppCenter.Analytics.Android.Bindings.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AppCenter.Android.Bindings, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Microsoft.AppCenter.Android.Bindings.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AppCenter.Crashes, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Microsoft.AppCenter.Crashes.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AppCenter.Crashes.Android.Bindings, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Microsoft.AppCenter.Crashes.Android.Bindings.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Mono.Android.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Security, Version=2.0.5.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Mono.Security.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MonoGame.Framework, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\MonoGame.Framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e">
|
||||
<HintPath>..\assemblies\mscorlib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e">
|
||||
<HintPath>..\assemblies\System.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e">
|
||||
<HintPath>..\assemblies\System.Xml.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\assemblies\System.Net.Http.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e">
|
||||
<HintPath>..\assemblies\System.Runtime.Serialization.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Arch.Core.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Xamarin.Android.Arch.Core.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Arch.Lifecycle.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Xamarin.Android.Arch.Lifecycle.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Arch.Lifecycle.Runtime, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Xamarin.Android.Arch.Lifecycle.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Annotations, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Xamarin.Android.Support.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Compat, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Xamarin.Android.Support.Compat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Core.UI, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Xamarin.Android.Support.Core.UI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Core.Utils, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Xamarin.Android.Support.Core.Utils.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Fragment, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Xamarin.Android.Support.Fragment.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Media.Compat, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Xamarin.Android.Support.Media.Compat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v4, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\Xamarin.Android.Support.v4.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="xTile, Version=1.0.7033.16602, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\assemblies\xTile.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Api.cs" />
|
||||
<Compile Include="ContentInjector.cs" />
|
||||
<Compile Include="Data\BigCraftableData.cs" />
|
||||
<Compile Include="Data\ContentPackData.cs" />
|
||||
<Compile Include="Data\CropData.cs" />
|
||||
<Compile Include="Data\DataNeedsId.cs" />
|
||||
<Compile Include="Data\FruitTreeData.cs" />
|
||||
<Compile Include="Data\HatData.cs" />
|
||||
<Compile Include="Data\ObjectData.cs" />
|
||||
<Compile Include="Data\WeaponData.cs" />
|
||||
<Compile Include="Log.cs" />
|
||||
<Compile Include="Mod.cs" />
|
||||
<Compile Include="Overrides\Object.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Util.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,33 @@
|
|||
using StardewModdingAPI;
|
||||
using System;
|
||||
|
||||
namespace JsonAssets
|
||||
{
|
||||
class Log
|
||||
{
|
||||
public static void trace(String str)
|
||||
{
|
||||
Mod.instance.Monitor.Log(str, LogLevel.Trace);
|
||||
}
|
||||
|
||||
public static void debug(String str)
|
||||
{
|
||||
Mod.instance.Monitor.Log(str, LogLevel.Debug);
|
||||
}
|
||||
|
||||
public static void info(String str)
|
||||
{
|
||||
Mod.instance.Monitor.Log(str, LogLevel.Info);
|
||||
}
|
||||
|
||||
public static void warn(String str)
|
||||
{
|
||||
Mod.instance.Monitor.Log(str, LogLevel.Warn);
|
||||
}
|
||||
|
||||
public static void error(String str)
|
||||
{
|
||||
Mod.instance.Monitor.Log(str, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,925 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using JsonAssets.Data;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using StardewModdingAPI;
|
||||
using StardewModdingAPI.Events;
|
||||
using StardewValley;
|
||||
using StardewValley.Menus;
|
||||
using StardewValley.Locations;
|
||||
using StardewValley.TerrainFeatures;
|
||||
using StardewValley.Objects;
|
||||
using System.Reflection;
|
||||
using Netcode;
|
||||
using StardewValley.Buildings;
|
||||
using Harmony;
|
||||
using System.Text.RegularExpressions;
|
||||
using JsonAssets.Overrides;
|
||||
using Newtonsoft.Json;
|
||||
using StardewValley.Tools;
|
||||
|
||||
// TODO: Refactor recipes
|
||||
|
||||
namespace JsonAssets
|
||||
{
|
||||
public class Mod : StardewModdingAPI.Mod
|
||||
{
|
||||
public static Mod instance;
|
||||
|
||||
/// <summary>The mod entry point, called after the mod is first loaded.</summary>
|
||||
/// <param name="helper">Provides simplified APIs for writing mods.</param>
|
||||
public override void Entry(IModHelper helper)
|
||||
{
|
||||
instance = this;
|
||||
|
||||
helper.Events.Display.MenuChanged += this.onMenuChanged;
|
||||
helper.Events.GameLoop.Saved += this.onSaved;
|
||||
helper.Events.Player.InventoryChanged += this.onInventoryChanged;
|
||||
helper.Events.GameLoop.SaveCreated += this.onCreated;
|
||||
helper.Events.Specialised.LoadStageChanged += this.onLoadStageChanged;
|
||||
helper.Events.Multiplayer.PeerContextReceived += this.clientConnected;
|
||||
|
||||
Log.info("Loading content packs...");
|
||||
foreach (IContentPack contentPack in this.Helper.ContentPacks.GetOwned())
|
||||
this.loadData(contentPack);
|
||||
if (Directory.Exists(Path.Combine(this.Helper.DirectoryPath, "ContentPacks")))
|
||||
{
|
||||
foreach (string dir in Directory.EnumerateDirectories(Path.Combine(this.Helper.DirectoryPath, "ContentPacks")))
|
||||
this.loadData(dir);
|
||||
}
|
||||
|
||||
this.resetAtTitle();
|
||||
|
||||
try
|
||||
{
|
||||
helper.Events.Hook.ObjectCanBePlacedHere += (args) => ObjectCanPlantHereOverride.Prefix(args.__instance, args.location, args.tile, ref args.__result);
|
||||
helper.Events.Hook.ObjectCheckForAction += (args) => ObjectNoActionHook.Prefix(args.__instance);
|
||||
helper.Events.Hook.ObjectIsIndexOkForBasicShippedCategory += (args) => { ObjectCollectionShippingHook.Postfix(args.index, ref args.__result); return true; };
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception doing harmony stuff: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
private Api api;
|
||||
public override object GetApi()
|
||||
{
|
||||
return this.api ?? (this.api = new Api(this.loadData));
|
||||
}
|
||||
|
||||
private void loadData(string dir)
|
||||
{
|
||||
// read initial info
|
||||
IContentPack temp = this.Helper.ContentPacks.CreateFake(dir);
|
||||
ContentPackData info = temp.ReadJsonFile<ContentPackData>("content-pack.json");
|
||||
if (info == null)
|
||||
{
|
||||
Log.warn($"\tNo {dir}/content-pack.json!");
|
||||
return;
|
||||
}
|
||||
|
||||
// load content pack
|
||||
IContentPack contentPack = this.Helper.ContentPacks.CreateTemporary(dir, id: Guid.NewGuid().ToString("N"), name: info.Name, description: info.Description, author: info.Author, version: new SemanticVersion(info.Version));
|
||||
this.loadData(contentPack);
|
||||
}
|
||||
|
||||
private Dictionary<string, IContentPack> dupObjects = new Dictionary<string, IContentPack>();
|
||||
private Dictionary<string, IContentPack> dupCrops = new Dictionary<string, IContentPack>();
|
||||
private Dictionary<string, IContentPack> dupFruitTrees = new Dictionary<string, IContentPack>();
|
||||
private Dictionary<string, IContentPack> dupBigCraftables = new Dictionary<string, IContentPack>();
|
||||
private Dictionary<string, IContentPack> dupHats = new Dictionary<string, IContentPack>();
|
||||
private Dictionary<string, IContentPack> dupWeapons = new Dictionary<string, IContentPack>();
|
||||
|
||||
private readonly Regex SeasonLimiter = new Regex("(z(?: spring| summer| fall| winter){2,4})", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
private void loadData(IContentPack contentPack)
|
||||
{
|
||||
Log.info($"\t{contentPack.Manifest.Name} {contentPack.Manifest.Version} by {contentPack.Manifest.Author} - {contentPack.Manifest.Description}");
|
||||
|
||||
// load objects
|
||||
DirectoryInfo objectsDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "Objects"));
|
||||
if (objectsDir.Exists)
|
||||
{
|
||||
foreach (DirectoryInfo dir in objectsDir.EnumerateDirectories())
|
||||
{
|
||||
string relativePath = $"Objects/{dir.Name}";
|
||||
|
||||
// load data
|
||||
ObjectData obj = contentPack.ReadJsonFile<ObjectData>($"{relativePath}/object.json");
|
||||
if (obj == null)
|
||||
continue;
|
||||
|
||||
// save object
|
||||
obj.texture = contentPack.LoadAsset<Texture2D>($"{relativePath}/object.png");
|
||||
if (obj.IsColored)
|
||||
obj.textureColor = contentPack.LoadAsset<Texture2D>($"{relativePath}/color.png");
|
||||
this.objects.Add(obj);
|
||||
|
||||
// save ring
|
||||
if (obj.Category == ObjectData.Category_.Ring)
|
||||
this.myRings.Add(obj);
|
||||
|
||||
// Duplicate check
|
||||
if (this.dupObjects.ContainsKey(obj.Name))
|
||||
Log.error($"Duplicate object: {obj.Name} just added by {contentPack.Manifest.Name}, already added by {this.dupObjects[obj.Name].Manifest.Name}!");
|
||||
else
|
||||
this.dupObjects[obj.Name] = contentPack;
|
||||
}
|
||||
}
|
||||
|
||||
// load crops
|
||||
DirectoryInfo cropsDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "Crops"));
|
||||
if (cropsDir.Exists)
|
||||
{
|
||||
foreach (DirectoryInfo dir in cropsDir.EnumerateDirectories())
|
||||
{
|
||||
string relativePath = $"Crops/{dir.Name}";
|
||||
|
||||
// load data
|
||||
CropData crop = contentPack.ReadJsonFile<CropData>($"{relativePath}/crop.json");
|
||||
if (crop == null)
|
||||
continue;
|
||||
|
||||
// save crop
|
||||
crop.texture = contentPack.LoadAsset<Texture2D>($"{relativePath}/crop.png");
|
||||
this.crops.Add(crop);
|
||||
|
||||
// save seeds
|
||||
crop.seed = new ObjectData
|
||||
{
|
||||
texture = contentPack.LoadAsset<Texture2D>($"{relativePath}/seeds.png"),
|
||||
Name = crop.SeedName,
|
||||
Description = crop.SeedDescription,
|
||||
Category = ObjectData.Category_.Seeds,
|
||||
Price = crop.SeedPurchasePrice,
|
||||
CanPurchase = true,
|
||||
PurchaseFrom = crop.SeedPurchaseFrom,
|
||||
PurchasePrice = crop.SeedPurchasePrice,
|
||||
PurchaseRequirements = crop.SeedPurchaseRequirements ?? new List<string>(),
|
||||
NameLocalization = crop.SeedNameLocalization,
|
||||
DescriptionLocalization = crop.SeedDescriptionLocalization
|
||||
};
|
||||
|
||||
// TODO: Clean up this chunk
|
||||
// I copy/pasted it from the unofficial update decompiled
|
||||
string str = "";
|
||||
string[] array = new[] { "spring", "summer", "fall", "winter" }
|
||||
.Except(crop.Seasons)
|
||||
.ToArray();
|
||||
foreach (string season in array)
|
||||
{
|
||||
str += $"/z {season}";
|
||||
}
|
||||
string strtrimstart = str.TrimStart(new char[] { '/' });
|
||||
if (crop.SeedPurchaseRequirements != null && crop.SeedPurchaseRequirements.Count > 0)
|
||||
{
|
||||
for (int index = 0; index < crop.SeedPurchaseRequirements.Count; index++)
|
||||
{
|
||||
if (this.SeasonLimiter.IsMatch(crop.SeedPurchaseRequirements[index]))
|
||||
{
|
||||
crop.SeedPurchaseRequirements[index] = strtrimstart;
|
||||
Log.warn($" Faulty season requirements for {crop.SeedName}!\n Fixed season requirements: {crop.SeedPurchaseRequirements[index]}");
|
||||
}
|
||||
}
|
||||
if (!crop.SeedPurchaseRequirements.Contains(str.TrimStart('/')))
|
||||
{
|
||||
Log.trace($" Adding season requirements for {crop.SeedName}:\n New season requirements: {strtrimstart}");
|
||||
crop.seed.PurchaseRequirements.Add(strtrimstart);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.trace($" Adding season requirements for {crop.SeedName}:\n New season requirements: {strtrimstart}");
|
||||
crop.seed.PurchaseRequirements.Add(strtrimstart);
|
||||
}
|
||||
|
||||
this.objects.Add(crop.seed);
|
||||
|
||||
// Duplicate check
|
||||
if (this.dupCrops.ContainsKey(crop.Name))
|
||||
Log.error($"Duplicate crop: {crop.Name} just added by {contentPack.Manifest.Name}, already added by {this.dupCrops[crop.Name].Manifest.Name}!");
|
||||
else
|
||||
this.dupCrops[crop.Name] = contentPack;
|
||||
}
|
||||
}
|
||||
|
||||
// load fruit trees
|
||||
DirectoryInfo fruitTreesDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "FruitTrees"));
|
||||
if (fruitTreesDir.Exists)
|
||||
{
|
||||
foreach (DirectoryInfo dir in fruitTreesDir.EnumerateDirectories())
|
||||
{
|
||||
string relativePath = $"FruitTrees/{dir.Name}";
|
||||
|
||||
// load data
|
||||
FruitTreeData tree = contentPack.ReadJsonFile<FruitTreeData>($"{relativePath}/tree.json");
|
||||
if (tree == null)
|
||||
continue;
|
||||
|
||||
// save fruit tree
|
||||
tree.texture = contentPack.LoadAsset<Texture2D>($"{relativePath}/tree.png");
|
||||
this.fruitTrees.Add(tree);
|
||||
|
||||
// save seed
|
||||
tree.sapling = new ObjectData
|
||||
{
|
||||
texture = contentPack.LoadAsset<Texture2D>($"{relativePath}/sapling.png"),
|
||||
Name = tree.SaplingName,
|
||||
Description = tree.SaplingDescription,
|
||||
Category = ObjectData.Category_.Seeds,
|
||||
Price = tree.SaplingPurchasePrice,
|
||||
CanPurchase = true,
|
||||
PurchaseRequirements = tree.SaplingPurchaseRequirements,
|
||||
PurchaseFrom = tree.SaplingPurchaseFrom,
|
||||
PurchasePrice = tree.SaplingPurchasePrice,
|
||||
NameLocalization = tree.SaplingNameLocalization,
|
||||
DescriptionLocalization = tree.SaplingDescriptionLocalization
|
||||
};
|
||||
this.objects.Add(tree.sapling);
|
||||
|
||||
// Duplicate check
|
||||
if (this.dupFruitTrees.ContainsKey(tree.Name))
|
||||
Log.error($"Duplicate fruit tree: {tree.Name} just added by {contentPack.Manifest.Name}, already added by {this.dupFruitTrees[tree.Name].Manifest.Name}!");
|
||||
else
|
||||
this.dupFruitTrees[tree.Name] = contentPack;
|
||||
}
|
||||
}
|
||||
|
||||
// load big craftables
|
||||
DirectoryInfo bigCraftablesDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "BigCraftables"));
|
||||
if (bigCraftablesDir.Exists)
|
||||
{
|
||||
foreach (DirectoryInfo dir in bigCraftablesDir.EnumerateDirectories())
|
||||
{
|
||||
string relativePath = $"BigCraftables/{dir.Name}";
|
||||
|
||||
// load data
|
||||
BigCraftableData craftable = contentPack.ReadJsonFile<BigCraftableData>($"{relativePath}/big-craftable.json");
|
||||
if (craftable == null)
|
||||
continue;
|
||||
|
||||
// save craftable
|
||||
craftable.texture = contentPack.LoadAsset<Texture2D>($"{relativePath}/big-craftable.png");
|
||||
this.bigCraftables.Add(craftable);
|
||||
|
||||
// Duplicate check
|
||||
if (this.dupBigCraftables.ContainsKey(craftable.Name))
|
||||
Log.error($"Duplicate big craftable: {craftable.Name} just added by {contentPack.Manifest.Name}, already added by {this.dupBigCraftables[craftable.Name].Manifest.Name}!");
|
||||
else
|
||||
this.dupBigCraftables[craftable.Name] = contentPack;
|
||||
}
|
||||
}
|
||||
|
||||
// load hats
|
||||
DirectoryInfo hatsDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "Hats"));
|
||||
if (hatsDir.Exists)
|
||||
{
|
||||
foreach (DirectoryInfo dir in hatsDir.EnumerateDirectories())
|
||||
{
|
||||
string relativePath = $"Hats/{dir.Name}";
|
||||
|
||||
// load data
|
||||
HatData hat = contentPack.ReadJsonFile<HatData>($"{relativePath}/hat.json");
|
||||
if (hat == null)
|
||||
continue;
|
||||
|
||||
// save object
|
||||
hat.texture = contentPack.LoadAsset<Texture2D>($"{relativePath}/hat.png");
|
||||
this.hats.Add(hat);
|
||||
|
||||
// Duplicate check
|
||||
if (this.dupHats.ContainsKey(hat.Name))
|
||||
Log.error($"Duplicate hat: {hat.Name} just added by {contentPack.Manifest.Name}, already added by {this.dupHats[hat.Name].Manifest.Name}!");
|
||||
else
|
||||
this.dupBigCraftables[hat.Name] = contentPack;
|
||||
}
|
||||
}
|
||||
|
||||
// Load weapons
|
||||
// load objects
|
||||
DirectoryInfo weaponsDir = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "Weapons"));
|
||||
if (weaponsDir.Exists)
|
||||
{
|
||||
foreach (DirectoryInfo dir in weaponsDir.EnumerateDirectories())
|
||||
{
|
||||
string relativePath = $"Weapons/{dir.Name}";
|
||||
|
||||
// load data
|
||||
WeaponData weapon = contentPack.ReadJsonFile<WeaponData>($"{relativePath}/weapon.json");
|
||||
if (weapon == null)
|
||||
continue;
|
||||
|
||||
// save object
|
||||
weapon.texture = contentPack.LoadAsset<Texture2D>($"{relativePath}/weapon.png");
|
||||
this.weapons.Add(weapon);
|
||||
|
||||
// Duplicate check
|
||||
if (this.dupWeapons.ContainsKey(weapon.Name))
|
||||
Log.error($"Duplicate weapon: {weapon.Name} just added by {contentPack.Manifest.Name}, already added by {this.dupWeapons[weapon.Name].Manifest.Name}!");
|
||||
else
|
||||
this.dupBigCraftables[weapon.Name] = contentPack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resetAtTitle()
|
||||
{
|
||||
this.didInit = false;
|
||||
// When we go back to the title menu we need to reset things so things don't break when
|
||||
// going back to a save.
|
||||
this.clearIds(out this.objectIds, this.objects.ToList<DataNeedsId>());
|
||||
this.clearIds(out this.cropIds, this.crops.ToList<DataNeedsId>());
|
||||
this.clearIds(out this.fruitTreeIds, this.fruitTrees.ToList<DataNeedsId>());
|
||||
this.clearIds(out this.bigCraftableIds, this.bigCraftables.ToList<DataNeedsId>());
|
||||
this.clearIds(out this.hatIds, this.hats.ToList<DataNeedsId>());
|
||||
this.clearIds(out this.weaponIds, this.weapons.ToList<DataNeedsId>());
|
||||
|
||||
IAssetEditor editor = this.Helper.Content.AssetEditors.FirstOrDefault(p => p is ContentInjector);
|
||||
if (editor != null)
|
||||
this.Helper.Content.AssetEditors.Remove(editor);
|
||||
}
|
||||
|
||||
private void onCreated(object sender, SaveCreatedEventArgs e)
|
||||
{
|
||||
Log.debug("Loading stuff early (creation)");
|
||||
this.initStuff( loadIdFiles: false );
|
||||
}
|
||||
|
||||
private void onLoadStageChanged(object sender, LoadStageChangedEventArgs e)
|
||||
{
|
||||
if (e.NewStage == StardewModdingAPI.Enums.LoadStage.SaveParsed)
|
||||
{
|
||||
Log.debug("Loading stuff early (loading)");
|
||||
this.initStuff( loadIdFiles: true );
|
||||
}
|
||||
else if ( e.NewStage == StardewModdingAPI.Enums.LoadStage.SaveLoadedLocations )
|
||||
{
|
||||
Log.debug("Fixing IDs");
|
||||
this.fixIdsEverywhere();
|
||||
}
|
||||
else if ( e.NewStage == StardewModdingAPI.Enums.LoadStage.Loaded )
|
||||
{
|
||||
Log.debug("Adding default recipes");
|
||||
foreach (ObjectData obj in this.objects)
|
||||
{
|
||||
if (obj.Recipe != null && obj.Recipe.IsDefault && !Game1.player.knowsRecipe(obj.Name))
|
||||
{
|
||||
if (obj.Category == ObjectData.Category_.Cooking)
|
||||
{
|
||||
Game1.player.cookingRecipes.Add(obj.Name, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Game1.player.craftingRecipes.Add(obj.Name, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (BigCraftableData big in this.bigCraftables)
|
||||
{
|
||||
if (big.Recipe != null && big.Recipe.IsDefault && !Game1.player.knowsRecipe(big.Name))
|
||||
{
|
||||
Game1.player.craftingRecipes.Add(big.Name, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clientConnected(object sender, PeerContextReceivedEventArgs e)
|
||||
{
|
||||
if (!Context.IsMainPlayer && !this.didInit)
|
||||
{
|
||||
Log.debug("Loading stuff early (MP client)");
|
||||
this.initStuff( loadIdFiles: false );
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Raised after a game menu is opened, closed, or replaced.</summary>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="e">The event arguments.</param>
|
||||
private void onMenuChanged(object sender, MenuChangedEventArgs e)
|
||||
{
|
||||
if ( e.NewMenu == null )
|
||||
return;
|
||||
|
||||
if ( e.NewMenu is TitleMenu )
|
||||
{
|
||||
this.resetAtTitle();
|
||||
return;
|
||||
}
|
||||
if (e.OldMenu is ShopMenu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ShopMenu menu = e.NewMenu as ShopMenu;
|
||||
bool hatMouse = menu != null && menu.potraitPersonDialogue == Game1.parseText(Game1.content.LoadString("Strings\\StringsFromCSFiles:ShopMenu.cs.11494"), Game1.dialogueFont, Game1.tileSize * 5 - Game1.pixelZoom * 4);
|
||||
if (menu == null || menu.portraitPerson == null && !hatMouse)
|
||||
return;
|
||||
|
||||
//if (menu.portraitPerson.name == "Pierre")
|
||||
{
|
||||
Log.trace($"Adding objects to {menu.portraitPerson?.Name}'s shop");
|
||||
List<Item> forSale = this.Helper.Reflection.GetField<List<Item>>(menu, "forSale").GetValue();
|
||||
int count = forSale.Count;
|
||||
Dictionary<Item, int[]> itemPriceAndStock = this.Helper.Reflection.GetField<Dictionary<Item, int[]>>(menu, "itemPriceAndStock").GetValue();
|
||||
|
||||
IReflectedMethod precondMeth = this.Helper.Reflection.GetMethod(Game1.currentLocation, "checkEventPrecondition");
|
||||
foreach (ObjectData obj in this.objects)
|
||||
{
|
||||
if (obj.Recipe != null && obj.Recipe.CanPurchase)
|
||||
{
|
||||
bool add = true;
|
||||
// Can't use continue here or the item might not sell
|
||||
if (obj.Recipe.PurchaseFrom != menu.portraitPerson?.Name || (obj.Recipe.PurchaseFrom == "HatMouse" && hatMouse) )
|
||||
add = false;
|
||||
if (Game1.player.craftingRecipes.ContainsKey(obj.Name) || Game1.player.cookingRecipes.ContainsKey(obj.Name))
|
||||
add = false;
|
||||
if (obj.Recipe.PurchaseRequirements != null && obj.Recipe.PurchaseRequirements.Count > 0 &&
|
||||
precondMeth.Invoke<int>(new object[] { obj.Recipe.GetPurchaseRequirementString() }) == -1)
|
||||
add = false;
|
||||
if (add)
|
||||
{
|
||||
StardewValley.Object recipeObj = new StardewValley.Object(obj.id, 1, true, obj.Recipe.PurchasePrice, 0);
|
||||
forSale.Add(recipeObj);
|
||||
itemPriceAndStock.Add(recipeObj, new int[] { obj.Recipe.PurchasePrice, 1 });
|
||||
Log.trace($"\tAdding recipe for {obj.Name}");
|
||||
}
|
||||
}
|
||||
if (!obj.CanPurchase)
|
||||
continue;
|
||||
if (obj.PurchaseFrom != menu.portraitPerson?.Name || (obj.PurchaseFrom == "HatMouse" && hatMouse))
|
||||
continue;
|
||||
if (obj.PurchaseRequirements != null && obj.PurchaseRequirements.Count > 0 &&
|
||||
precondMeth.Invoke<int>(new object[] { obj.GetPurchaseRequirementString() }) == -1)
|
||||
continue;
|
||||
Item item = new StardewValley.Object(Vector2.Zero, obj.id, int.MaxValue);
|
||||
forSale.Add(item);
|
||||
itemPriceAndStock.Add(item, new int[] { obj.PurchasePrice, int.MaxValue });
|
||||
Log.trace($"\tAdding {obj.Name}");
|
||||
}
|
||||
foreach (BigCraftableData big in this.bigCraftables)
|
||||
{
|
||||
if (big.Recipe != null && big.Recipe.CanPurchase)
|
||||
{
|
||||
bool add = true;
|
||||
// Can't use continue here or the item might not sell
|
||||
if (big.Recipe.PurchaseFrom != menu.portraitPerson?.Name || (big.Recipe.PurchaseFrom == "HatMouse" && hatMouse))
|
||||
add = false;
|
||||
if (Game1.player.craftingRecipes.ContainsKey(big.Name) || Game1.player.cookingRecipes.ContainsKey(big.Name))
|
||||
add = false;
|
||||
if (big.Recipe.PurchaseRequirements != null && big.Recipe.PurchaseRequirements.Count > 0 &&
|
||||
precondMeth.Invoke<int>(new object[] { big.Recipe.GetPurchaseRequirementString() }) == -1)
|
||||
add = false;
|
||||
if (add)
|
||||
{
|
||||
StardewValley.Object recipeObj = new StardewValley.Object(new Vector2(0, 0), big.id, true);
|
||||
forSale.Add(recipeObj);
|
||||
itemPriceAndStock.Add(recipeObj, new int[] { big.Recipe.PurchasePrice, 1 });
|
||||
Log.trace($"\tAdding recipe for {big.Name}");
|
||||
}
|
||||
}
|
||||
if (!big.CanPurchase)
|
||||
continue;
|
||||
if (big.PurchaseFrom != menu.portraitPerson?.Name || (big.PurchaseFrom == "HatMouse" && hatMouse))
|
||||
continue;
|
||||
if (big.PurchaseRequirements != null && big.PurchaseRequirements.Count > 0 &&
|
||||
precondMeth.Invoke<int>(new object[] { big.GetPurchaseRequirementString() }) == -1)
|
||||
continue;
|
||||
Item item = new StardewValley.Object(Vector2.Zero, big.id, false);
|
||||
forSale.Add(item);
|
||||
itemPriceAndStock.Add(item, new int[] { big.PurchasePrice, int.MaxValue });
|
||||
Log.trace($"\tAdding {big.Name}");
|
||||
}
|
||||
if ( hatMouse )
|
||||
{
|
||||
foreach (HatData hat in this.hats )
|
||||
{
|
||||
Item item = new Hat(hat.GetHatId());
|
||||
forSale.Add(item);
|
||||
itemPriceAndStock.Add(item, new int[] { hat.PurchasePrice, int.MaxValue });
|
||||
Log.trace($"\tAdding {hat.Name}");
|
||||
}
|
||||
}
|
||||
foreach (WeaponData weapon in this.weapons)
|
||||
{
|
||||
if (!weapon.CanPurchase)
|
||||
continue;
|
||||
if (weapon.PurchaseFrom != menu.portraitPerson?.Name || (weapon.PurchaseFrom == "HatMouse" && hatMouse))
|
||||
continue;
|
||||
if (weapon.PurchaseRequirements != null && weapon.PurchaseRequirements.Count > 0 &&
|
||||
precondMeth.Invoke<int>(new object[] { weapon.GetPurchaseRequirementString() }) == -1)
|
||||
continue;
|
||||
Item item = new StardewValley.Tools.MeleeWeapon(weapon.id);
|
||||
forSale.Add(item);
|
||||
itemPriceAndStock.Add(item, new int[] { weapon.PurchasePrice, int.MaxValue });
|
||||
Log.trace($"\tAdding {weapon.Name}");
|
||||
}
|
||||
if(count != forSale.Count)
|
||||
{
|
||||
Game1.activeClickableMenu = new ShopMenu(itemPriceAndStock, this.Helper.Reflection.GetField<int>(menu, "currency").GetValue(), this.Helper.Reflection.GetField<string>(menu, "personName").GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
( ( Api )this.api ).InvokeAddedItemsToShop();
|
||||
}
|
||||
|
||||
private bool didInit = false;
|
||||
private void initStuff( bool loadIdFiles )
|
||||
{
|
||||
if (this.didInit)
|
||||
return;
|
||||
this.didInit = true;
|
||||
|
||||
// load object ID mappings from save folder
|
||||
if (loadIdFiles)
|
||||
{
|
||||
IDictionary<TKey, TValue> LoadDictionary<TKey, TValue>(string filename)
|
||||
{
|
||||
string path = Path.Combine(Constants.CurrentSavePath, "JsonAssets", filename);
|
||||
return File.Exists(path)
|
||||
? JsonConvert.DeserializeObject<Dictionary<TKey, TValue>>(File.ReadAllText(path))
|
||||
: new Dictionary<TKey, TValue>();
|
||||
}
|
||||
Directory.CreateDirectory(Path.Combine(Constants.CurrentSavePath, "JsonAssets"));
|
||||
this.oldObjectIds = LoadDictionary<string, int>("ids-objects.json");
|
||||
this.oldCropIds = LoadDictionary<string, int>("ids-crops.json");
|
||||
this.oldFruitTreeIds = LoadDictionary<string, int>("ids-fruittrees.json");
|
||||
this.oldBigCraftableIds = LoadDictionary<string, int>("ids-big-craftables.json");
|
||||
this.oldHatIds = LoadDictionary<string, int>("ids-hats.json");
|
||||
this.oldWeaponIds = LoadDictionary<string, int>("ids-weapons.json");
|
||||
|
||||
Log.trace("OLD IDS START");
|
||||
foreach (KeyValuePair<string, int> id in this.oldObjectIds)
|
||||
Log.trace("\tObject " + id.Key + " = " + id.Value);
|
||||
foreach (KeyValuePair<string, int> id in this.oldCropIds)
|
||||
Log.trace("\tCrop " + id.Key + " = " + id.Value);
|
||||
foreach (KeyValuePair<string, int> id in this.oldFruitTreeIds)
|
||||
Log.trace("\tFruit Tree " + id.Key + " = " + id.Value);
|
||||
foreach (KeyValuePair<string, int> id in this.oldBigCraftableIds)
|
||||
Log.trace("\tBigCraftable " + id.Key + " = " + id.Value);
|
||||
foreach (KeyValuePair<string, int> id in this.oldHatIds)
|
||||
Log.trace("\tHat " + id.Key + " = " + id.Value);
|
||||
foreach (KeyValuePair<string, int> id in this.oldWeaponIds)
|
||||
Log.trace("\tWeapon " + id.Key + " = " + id.Value);
|
||||
Log.trace("OLD IDS END");
|
||||
}
|
||||
|
||||
// assign IDs
|
||||
this.objectIds = this.AssignIds("objects", StartingObjectId, this.objects.ToList<DataNeedsId>());
|
||||
this.cropIds = this.AssignIds("crops", StartingCropId, this.crops.ToList<DataNeedsId>());
|
||||
this.fruitTreeIds = this.AssignIds("fruittrees", StartingFruitTreeId, this.fruitTrees.ToList<DataNeedsId>());
|
||||
this.bigCraftableIds = this.AssignIds("big-craftables", StartingBigCraftableId, this.bigCraftables.ToList<DataNeedsId>());
|
||||
this.hatIds = this.AssignIds("hats", StartingHatId, this.hats.ToList<DataNeedsId>());
|
||||
this.weaponIds = this.AssignIds("weapons", StartingWeaponId, this.weapons.ToList<DataNeedsId>());
|
||||
|
||||
this.api.InvokeIdsAssigned();
|
||||
|
||||
// init
|
||||
this.Helper.Content.AssetEditors.Add(new ContentInjector());
|
||||
}
|
||||
|
||||
/// <summary>Raised after the game finishes writing data to the save file (except the initial save creation).</summary>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="e">The event arguments.</param>
|
||||
private void onSaved(object sender, SavedEventArgs e)
|
||||
{
|
||||
if (!Directory.Exists(Path.Combine(Constants.CurrentSavePath, "JsonAssets")))
|
||||
Directory.CreateDirectory(Path.Combine(Constants.CurrentSavePath, "JsonAssets"));
|
||||
|
||||
File.WriteAllText(Path.Combine(Constants.CurrentSavePath, "JsonAssets", "ids-objects.json"), JsonConvert.SerializeObject(this.objectIds));
|
||||
File.WriteAllText(Path.Combine(Constants.CurrentSavePath, "JsonAssets", "ids-crops.json"), JsonConvert.SerializeObject(this.cropIds));
|
||||
File.WriteAllText(Path.Combine(Constants.CurrentSavePath, "JsonAssets", "ids-fruittrees.json"), JsonConvert.SerializeObject(this.fruitTreeIds));
|
||||
File.WriteAllText(Path.Combine(Constants.CurrentSavePath, "JsonAssets", "ids-big-craftables.json"), JsonConvert.SerializeObject(this.bigCraftableIds));
|
||||
File.WriteAllText(Path.Combine(Constants.CurrentSavePath, "JsonAssets", "ids-hats.json"), JsonConvert.SerializeObject(this.hatIds));
|
||||
File.WriteAllText(Path.Combine(Constants.CurrentSavePath, "JsonAssets", "ids-weapons.json"), JsonConvert.SerializeObject(this.weaponIds));
|
||||
}
|
||||
|
||||
internal IList<ObjectData> myRings = new List<ObjectData>();
|
||||
|
||||
/// <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>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="e">The event arguments.</param>
|
||||
private void onInventoryChanged(object sender, InventoryChangedEventArgs e)
|
||||
{
|
||||
if (!e.IsLocalPlayer)
|
||||
return;
|
||||
|
||||
IList<int> ringIds = new List<int>();
|
||||
foreach (ObjectData ring in this.myRings)
|
||||
ringIds.Add(ring.id);
|
||||
|
||||
for (int i = 0; i < Game1.player.Items.Count; ++i)
|
||||
{
|
||||
Item item = Game1.player.Items[i];
|
||||
if (item is StardewValley.Object obj && ringIds.Contains(obj.ParentSheetIndex))
|
||||
{
|
||||
Log.trace($"Turning a ring-object of {obj.ParentSheetIndex} into a proper ring");
|
||||
Game1.player.Items[i] = new StardewValley.Objects.Ring(obj.ParentSheetIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const int StartingObjectId = 2000;
|
||||
private const int StartingCropId = 100;
|
||||
private const int StartingFruitTreeId = 10;
|
||||
private const int StartingBigCraftableId = 300;
|
||||
private const int StartingHatId = 50;
|
||||
private const int StartingWeaponId = 64;
|
||||
|
||||
internal IList<ObjectData> objects = new List<ObjectData>();
|
||||
internal IList<CropData> crops = new List<CropData>();
|
||||
internal IList<FruitTreeData> fruitTrees = new List<FruitTreeData>();
|
||||
internal IList<BigCraftableData> bigCraftables = new List<BigCraftableData>();
|
||||
internal IList<HatData> hats = new List<HatData>();
|
||||
internal IList<WeaponData> weapons = new List<WeaponData>();
|
||||
|
||||
internal IDictionary<string, int> objectIds;
|
||||
internal IDictionary<string, int> cropIds;
|
||||
internal IDictionary<string, int> fruitTreeIds;
|
||||
internal IDictionary<string, int> bigCraftableIds;
|
||||
internal IDictionary<string, int> hatIds;
|
||||
internal IDictionary<string, int> weaponIds;
|
||||
|
||||
internal IDictionary<string, int> oldObjectIds;
|
||||
internal IDictionary<string, int> oldCropIds;
|
||||
internal IDictionary<string, int> oldFruitTreeIds;
|
||||
internal IDictionary<string, int> oldBigCraftableIds;
|
||||
internal IDictionary<string, int> oldHatIds;
|
||||
internal IDictionary<string, int> oldWeaponIds;
|
||||
|
||||
internal IDictionary<int, string> origObjects;
|
||||
internal IDictionary<int, string> origCrops;
|
||||
internal IDictionary<int, string> origFruitTrees;
|
||||
internal IDictionary<int, string> origBigCraftables;
|
||||
internal IDictionary<int, string> origHats;
|
||||
internal IDictionary<int, string> origWeapons;
|
||||
|
||||
public int ResolveObjectId(object data)
|
||||
{
|
||||
if (data.GetType() == typeof(long))
|
||||
return (int)(long)data;
|
||||
else
|
||||
{
|
||||
if (this.objectIds.ContainsKey((string)data))
|
||||
return this.objectIds[(string)data];
|
||||
|
||||
foreach (KeyValuePair<int, string> obj in Game1.objectInformation )
|
||||
{
|
||||
if (obj.Value.Split('/')[0] == (string)data)
|
||||
return obj.Key;
|
||||
}
|
||||
|
||||
Log.warn($"No idea what '{data}' is!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, int> AssignIds(string type, int starting, IList<DataNeedsId> data)
|
||||
{
|
||||
Dictionary<string, int> ids = new Dictionary<string, int>();
|
||||
|
||||
int currId = starting;
|
||||
foreach (DataNeedsId d in data)
|
||||
{
|
||||
if (d.id == -1)
|
||||
{
|
||||
Log.trace($"New ID: {d.Name} = {currId}");
|
||||
ids.Add(d.Name, currId++);
|
||||
if (type == "objects" && ((ObjectData)d).IsColored)
|
||||
++currId;
|
||||
d.id = ids[d.Name];
|
||||
}
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
private void clearIds(out IDictionary<string, int> ids, List<DataNeedsId> objs)
|
||||
{
|
||||
ids = null;
|
||||
foreach ( DataNeedsId obj in objs )
|
||||
{
|
||||
obj.id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private IDictionary<int, string> cloneIdDictAndRemoveOurs( IDictionary<int, string> full, IDictionary<string, int> ours )
|
||||
{
|
||||
Dictionary<int, string> ret = new Dictionary<int, string>(full);
|
||||
foreach (KeyValuePair<string, int> obj in ours)
|
||||
ret.Remove(obj.Value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void fixIdsEverywhere()
|
||||
{
|
||||
this.origObjects = this.cloneIdDictAndRemoveOurs(Game1.objectInformation, this.objectIds);
|
||||
this.origCrops = this.cloneIdDictAndRemoveOurs(Game1.content.Load<Dictionary<int, string>>("Data\\Crops"), this.cropIds);
|
||||
this.origFruitTrees = this.cloneIdDictAndRemoveOurs(Game1.content.Load<Dictionary<int, string>>("Data\\fruitTrees"), this.fruitTreeIds);
|
||||
this.origBigCraftables = this.cloneIdDictAndRemoveOurs(Game1.bigCraftablesInformation, this.bigCraftableIds);
|
||||
this.origHats = this.cloneIdDictAndRemoveOurs(Game1.content.Load<Dictionary<int, string>>("Data\\hats"), this.hatIds);
|
||||
this.origWeapons = this.cloneIdDictAndRemoveOurs(Game1.content.Load<Dictionary<int, string>>("Data\\weapons"), this.weaponIds);
|
||||
|
||||
this.fixItemList(Game1.player.Items);
|
||||
foreach (GameLocation loc in Game1.locations )
|
||||
this.fixLocation(loc);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage( "SMAPI.CommonErrors", "AvoidNetField") ]
|
||||
private void fixLocation( GameLocation loc )
|
||||
{
|
||||
if (loc is FarmHouse fh)
|
||||
{
|
||||
#pragma warning disable AvoidImplicitNetFieldCast
|
||||
if (fh.fridge.Value?.items != null)
|
||||
#pragma warning restore AvoidImplicitNetFieldCast
|
||||
this.fixItemList(fh.fridge.Value.items);
|
||||
}
|
||||
|
||||
IList<Vector2> toRemove = new List<Vector2>();
|
||||
foreach (Vector2 tfk in loc.terrainFeatures.Keys )
|
||||
{
|
||||
TerrainFeature tf = loc.terrainFeatures[tfk];
|
||||
if ( tf is HoeDirt hd )
|
||||
{
|
||||
if (hd.crop == null)
|
||||
continue;
|
||||
|
||||
if (this.fixId(this.oldCropIds, this.cropIds, hd.crop.rowInSpriteSheet, this.origCrops))
|
||||
hd.crop = null;
|
||||
else
|
||||
{
|
||||
string key = this.cropIds.FirstOrDefault(x => x.Value == hd.crop.rowInSpriteSheet.Value).Key;
|
||||
CropData c = this.crops.FirstOrDefault(x => x.Name == key);
|
||||
if ( c != null ) // Non-JA crop
|
||||
hd.crop.indexOfHarvest.Value = this.ResolveObjectId(c.Product);
|
||||
}
|
||||
}
|
||||
else if ( tf is FruitTree ft )
|
||||
{
|
||||
if (this.fixId(this.oldFruitTreeIds, this.fruitTreeIds, ft.treeType, this.origFruitTrees))
|
||||
toRemove.Add(tfk);
|
||||
else
|
||||
{
|
||||
string key = this.oldFruitTreeIds.FirstOrDefault(x => x.Value == ft.treeType.Value).Key;
|
||||
FruitTreeData ftt = this.fruitTrees.FirstOrDefault(x => x.Name == key);
|
||||
if ( ftt != null ) // Non-JA fruit tree
|
||||
ft.indexOfFruit.Value = this.ResolveObjectId(ftt.Product);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (Vector2 rem in toRemove)
|
||||
loc.terrainFeatures.Remove(rem);
|
||||
|
||||
toRemove.Clear();
|
||||
foreach (Vector2 objk in loc.netObjects.Keys )
|
||||
{
|
||||
StardewValley.Object obj = loc.netObjects[objk];
|
||||
if ( obj is Chest chest )
|
||||
{
|
||||
this.fixItemList(chest.items);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!obj.bigCraftable.Value)
|
||||
{
|
||||
if (this.fixId(this.oldObjectIds, this.objectIds, obj.parentSheetIndex, this.origObjects))
|
||||
toRemove.Add(objk);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.fixId(this.oldBigCraftableIds, this.bigCraftableIds, obj.parentSheetIndex, this.origBigCraftables))
|
||||
toRemove.Add(objk);
|
||||
}
|
||||
}
|
||||
|
||||
if ( obj.heldObject.Value != null )
|
||||
{
|
||||
if (this.fixId(this.oldObjectIds, this.objectIds, obj.heldObject.Value.parentSheetIndex, this.origObjects))
|
||||
obj.heldObject.Value = null;
|
||||
|
||||
if ( obj.heldObject.Value is Chest chest2 )
|
||||
{
|
||||
this.fixItemList(chest2.items);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (Vector2 rem in toRemove)
|
||||
loc.objects.Remove(rem);
|
||||
|
||||
toRemove.Clear();
|
||||
foreach (Vector2 objk in loc.overlayObjects.Keys)
|
||||
{
|
||||
StardewValley.Object obj = loc.overlayObjects[objk];
|
||||
if (obj is Chest chest)
|
||||
{
|
||||
this.fixItemList(chest.items);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!obj.bigCraftable.Value)
|
||||
{
|
||||
if (this.fixId(this.oldObjectIds, this.objectIds, obj.parentSheetIndex, this.origObjects))
|
||||
toRemove.Add(objk);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.fixId(this.oldBigCraftableIds, this.bigCraftableIds, obj.parentSheetIndex, this.origBigCraftables))
|
||||
toRemove.Add(objk);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.heldObject.Value != null)
|
||||
{
|
||||
if (this.fixId(this.oldObjectIds, this.objectIds, obj.heldObject.Value.parentSheetIndex, this.origObjects))
|
||||
obj.heldObject.Value = null;
|
||||
|
||||
if (obj.heldObject.Value is Chest chest2)
|
||||
{
|
||||
this.fixItemList(chest2.items);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (Vector2 rem in toRemove)
|
||||
loc.overlayObjects.Remove(rem);
|
||||
|
||||
if (loc is BuildableGameLocation buildLoc)
|
||||
foreach (Building building in buildLoc.buildings)
|
||||
{
|
||||
if (building.indoors.Value != null)
|
||||
this.fixLocation(building.indoors.Value);
|
||||
if ( building is Mill mill )
|
||||
{
|
||||
this.fixItemList(mill.input.Value.items);
|
||||
this.fixItemList(mill.output.Value.items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("SMAPI.CommonErrors", "AvoidNetField")]
|
||||
private void fixItemList( IList< Item > items )
|
||||
{
|
||||
for ( int i = 0; i < items.Count; ++i )
|
||||
{
|
||||
Item item = items[i];
|
||||
if ( item is StardewValley.Object obj )
|
||||
{
|
||||
if (!obj.bigCraftable.Value)
|
||||
{
|
||||
if (this.fixId(this.oldObjectIds, this.objectIds, obj.parentSheetIndex, this.origObjects))
|
||||
items[i] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.fixId(this.oldBigCraftableIds, this.bigCraftableIds, obj.parentSheetIndex, this.origBigCraftables))
|
||||
items[i] = null;
|
||||
}
|
||||
}
|
||||
else if ( item is Hat hat )
|
||||
{
|
||||
if (this.fixId(this.oldHatIds, this.hatIds, hat.which, this.origHats))
|
||||
items[i] = null;
|
||||
}
|
||||
else if ( item is MeleeWeapon weapon )
|
||||
{
|
||||
if (this.fixId(this.oldWeaponIds, this.weaponIds, weapon.initialParentTileIndex, this.origWeapons))
|
||||
items[i] = null;
|
||||
else if (this.fixId(this.oldWeaponIds, this.weaponIds, weapon.currentParentTileIndex, this.origWeapons))
|
||||
items[i] = null;
|
||||
else if (this.fixId(this.oldWeaponIds, this.weaponIds, weapon.currentParentTileIndex, this.origWeapons))
|
||||
items[i] = null;
|
||||
}
|
||||
else if ( item is Ring ring )
|
||||
{
|
||||
if (this.fixId(this.oldObjectIds, this.objectIds, ring.indexInTileSheet, this.origObjects))
|
||||
items[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if the item should be deleted, false otherwise.
|
||||
// Only remove something if old has it but not new
|
||||
private bool fixId(IDictionary<string, int> oldIds, IDictionary<string, int> newIds, NetInt id, IDictionary<int, string> origData )
|
||||
{
|
||||
if (origData.ContainsKey(id.Value))
|
||||
return false;
|
||||
|
||||
if (oldIds.Values.Contains(id.Value))
|
||||
{
|
||||
int id_ = id.Value;
|
||||
string key = oldIds.FirstOrDefault(x => x.Value == id_).Key;
|
||||
|
||||
if (newIds.ContainsKey(key))
|
||||
{
|
||||
id.Value = newIds[key];
|
||||
return false;
|
||||
}
|
||||
else return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
using StardewValley;
|
||||
using StardewValley.Objects;
|
||||
|
||||
namespace JsonAssets.Overrides
|
||||
{
|
||||
public class ObjectCanPlantHereOverride
|
||||
{
|
||||
public static bool Prefix(StardewValley.Object __instance, GameLocation l, Vector2 tile, ref bool __result)
|
||||
{
|
||||
if (!__instance.bigCraftable.Value && Mod.instance.objectIds.Values.Contains(__instance.ParentSheetIndex))
|
||||
{
|
||||
if (__instance.Category == StardewValley.Object.SeedsCategory)
|
||||
{
|
||||
bool isTree = false;
|
||||
foreach (Data.FruitTreeData tree in Mod.instance.fruitTrees)
|
||||
{
|
||||
if (tree.sapling.id == __instance.ParentSheetIndex)
|
||||
{
|
||||
isTree = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Object lobj = l.objects.ContainsKey(tile) ? l.objects[tile] : null;
|
||||
if (isTree)
|
||||
{
|
||||
__result = lobj == null && !l.isTileOccupiedForPlacement(tile, __instance);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (l.isTileHoeDirt(tile) || (lobj is IndoorPot))
|
||||
__result = l.isTileOccupiedForPlacement(tile);
|
||||
else
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ObjectNoActionHook
|
||||
{
|
||||
public static bool Prefix(StardewValley.Object __instance)
|
||||
{
|
||||
if (__instance.bigCraftable.Value && Mod.instance.bigCraftableIds.Values.Contains(__instance.ParentSheetIndex))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ObjectCollectionShippingHook
|
||||
{
|
||||
public static void Postfix(int index, ref bool __result)
|
||||
{
|
||||
foreach (Data.ObjectData ring in Mod.instance.myRings)
|
||||
{
|
||||
if (ring.GetObjectId() == index)
|
||||
{
|
||||
__result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// 有关程序集的一般信息由以下
|
||||
// 控制。更改这些特性值可修改
|
||||
// 与程序集关联的信息。
|
||||
[assembly: AssemblyTitle("JsonAssets")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("JsonAssets")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2019")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// 将 ComVisible 设置为 false 会使此程序集中的类型
|
||||
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
|
||||
//请将此类型的 ComVisible 特性设置为 true。
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
|
||||
[assembly: Guid("f56b5f8e-0069-4029-8dcd-89002b7285e3")]
|
||||
|
||||
// 程序集的版本信息由下列四个值组成:
|
||||
//
|
||||
// 主版本
|
||||
// 次版本
|
||||
// 生成号
|
||||
// 修订号
|
||||
//
|
||||
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
//通过使用 "*",如下所示:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace JsonAssets
|
||||
{
|
||||
// Copied from SpaceCore
|
||||
// TODO: Add SC as a dependency instead
|
||||
public class Util
|
||||
{
|
||||
// Stolen from SMAPI
|
||||
public static void invokeEvent(string name, IEnumerable<Delegate> handlers, object sender)
|
||||
{
|
||||
EventArgs args = new EventArgs();
|
||||
foreach (EventHandler handler in handlers.Cast<EventHandler>())
|
||||
{
|
||||
try
|
||||
{
|
||||
handler.Invoke(sender, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.error($"Exception while handling event {name}:\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ namespace UIInfoSuite
|
|||
public Point GetNewIconPosition()
|
||||
{
|
||||
int yPos = Game1.options.zoomButtons ? 320 : 290;
|
||||
int xPosition = (int)Tools.GetWidthInPlayArea() - 214 - 46 * this._amountOfVisibleIcons;
|
||||
int xPosition = (int)Tools.GetWidthInPlayArea() - 214 - 69 * this._amountOfVisibleIcons;
|
||||
++this._amountOfVisibleIcons;
|
||||
return new Point(xPosition, yPos);
|
||||
}
|
||||
|
|
|
@ -93,13 +93,13 @@ namespace UIInfoSuite.UIElements
|
|||
private void OnRenderingHud(object sender, EventArgs e)
|
||||
{
|
||||
// draw birthday icon
|
||||
if (!Game1.eventUp)
|
||||
if (!Game1.eventUp && Game1.activeClickableMenu == null)
|
||||
{
|
||||
if (this._birthdayNPC != null)
|
||||
{
|
||||
Rectangle headShot = this._birthdayNPC.GetHeadShot();
|
||||
Point iconPosition = IconHandler.Handler.GetNewIconPosition();
|
||||
float scale = 2.9f;
|
||||
float scale = 4.35f;
|
||||
|
||||
Game1.spriteBatch.Draw(
|
||||
Game1.mouseCursors,
|
||||
|
|
|
@ -146,24 +146,24 @@ namespace UIInfoSuite.UIElements
|
|||
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
|
||||
{
|
||||
// draw icon
|
||||
if (!Game1.eventUp)
|
||||
if (!Game1.eventUp && Game1.activeClickableMenu == null)
|
||||
{
|
||||
if (this._drawQueenOfSauceIcon)
|
||||
{
|
||||
Point iconPosition = IconHandler.Handler.GetNewIconPosition();
|
||||
|
||||
this._queenOfSauceIcon = new ClickableTextureComponent(
|
||||
new Rectangle(iconPosition.X, iconPosition.Y, 40, 40),
|
||||
new Rectangle(iconPosition.X, iconPosition.Y, 10 * 6, 10 * 6),
|
||||
Game1.mouseCursors,
|
||||
new Rectangle(609, 361, 28, 28),
|
||||
1.3f);
|
||||
1.95f);
|
||||
this._queenOfSauceIcon.draw(Game1.spriteBatch);
|
||||
}
|
||||
|
||||
if (this._drawDishOfDayIcon)
|
||||
{
|
||||
Point iconLocation = IconHandler.Handler.GetNewIconPosition();
|
||||
float scale = 2.9f;
|
||||
float scale = 4.35f;
|
||||
|
||||
Game1.spriteBatch.Draw(
|
||||
Game1.objectSpriteSheet,
|
||||
|
@ -188,7 +188,7 @@ namespace UIInfoSuite.UIElements
|
|||
this._gus.Name,
|
||||
this._gus.Sprite.Texture,
|
||||
this._gus.GetHeadShot(),
|
||||
2f);
|
||||
3f);
|
||||
|
||||
texture.draw(Game1.spriteBatch);
|
||||
|
||||
|
@ -210,7 +210,7 @@ namespace UIInfoSuite.UIElements
|
|||
{
|
||||
// draw hover text
|
||||
if (this._drawQueenOfSauceIcon &&
|
||||
this._queenOfSauceIcon.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
|
||||
this._queenOfSauceIcon.containsPoint((int)(Game1.getMouseX() * Game1.options.zoomLevel), (int)(Game1.getMouseY() * Game1.options.zoomLevel)))
|
||||
{
|
||||
IClickableMenu.drawHoverText(
|
||||
Game1.spriteBatch,
|
||||
|
|
|
@ -120,15 +120,15 @@ namespace UIInfoSuite.UIElements
|
|||
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
|
||||
{
|
||||
// draw tool upgrade status
|
||||
if (!Game1.eventUp && this._toolBeingUpgraded != null)
|
||||
if (!Game1.eventUp && this._toolBeingUpgraded != null && Game1.activeClickableMenu == null)
|
||||
{
|
||||
Point iconPosition = IconHandler.Handler.GetNewIconPosition();
|
||||
this._toolUpgradeIcon =
|
||||
new ClickableTextureComponent(
|
||||
new Rectangle(iconPosition.X, iconPosition.Y, 40, 40),
|
||||
new Rectangle(iconPosition.X, iconPosition.Y, 60, 60),
|
||||
Game1.toolSpriteSheet,
|
||||
this._toolTexturePosition,
|
||||
2.5f);
|
||||
3.75f);
|
||||
this._toolUpgradeIcon.draw(Game1.spriteBatch);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,15 +60,15 @@ namespace UIInfoSuite.UIElements
|
|||
private void OnRenderingHud(object sender, RenderingHudEventArgs e)
|
||||
{
|
||||
// draw traveling merchant
|
||||
if (!Game1.eventUp && this._travelingMerchantIsHere)
|
||||
if (!Game1.eventUp && this._travelingMerchantIsHere && Game1.activeClickableMenu == null)
|
||||
{
|
||||
Point iconPosition = IconHandler.Handler.GetNewIconPosition();
|
||||
this._travelingMerchantIcon =
|
||||
new ClickableTextureComponent(
|
||||
new Rectangle(iconPosition.X, iconPosition.Y, 40, 40),
|
||||
new Rectangle(iconPosition.X, iconPosition.Y, 60, 60),
|
||||
Game1.mouseCursors,
|
||||
new Rectangle(192, 1411, 20, 20),
|
||||
2f);
|
||||
3f);
|
||||
this._travelingMerchantIcon.draw(Game1.spriteBatch);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -443,6 +443,7 @@
|
|||
<Compile Include="SMAPI\Events\DayStartedEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\DebrisListChangedEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\GameLaunchedEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\IHookEvents.cs" />
|
||||
<Compile Include="SMAPI\Events\IDisplayEvents.cs" />
|
||||
<Compile Include="SMAPI\Events\IGameLoopEvents.cs" />
|
||||
<Compile Include="SMAPI\Events\IInputEvents.cs" />
|
||||
|
@ -462,6 +463,8 @@
|
|||
<Compile Include="SMAPI\Events\ModMessageReceivedEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\MouseWheelScrolledEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\NpcListChangedEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\ObjectCheckForActionEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\ObjectIsIndexOkForBasicShippedCategoryEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\ObjectListChangedEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\OneSecondUpdateTickedEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\OneSecondUpdateTickingEventArgs.cs" />
|
||||
|
@ -488,6 +491,7 @@
|
|||
<Compile Include="SMAPI\Events\UpdateTickedEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\UpdateTickingEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\WarpedEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\ObjectCanBePlacedHereEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Events\WindowResizedEventArgs.cs" />
|
||||
<Compile Include="SMAPI\Framework\Command.cs" />
|
||||
<Compile Include="SMAPI\Framework\CommandManager.cs" />
|
||||
|
@ -513,6 +517,7 @@
|
|||
<Compile Include="SMAPI\Framework\Events\ModEvents.cs" />
|
||||
<Compile Include="SMAPI\Framework\Events\ModEventsBase.cs" />
|
||||
<Compile Include="SMAPI\Framework\Events\ModGameLoopEvents.cs" />
|
||||
<Compile Include="SMAPI\Framework\Events\ModHookEvents.cs" />
|
||||
<Compile Include="SMAPI\Framework\Events\ModInputEvents.cs" />
|
||||
<Compile Include="SMAPI\Framework\Events\ModMultiplayerEvents.cs" />
|
||||
<Compile Include="SMAPI\Framework\Events\ModPlayerEvents.cs" />
|
||||
|
|
|
@ -1,19 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
using StardewValley;
|
||||
using StardewValley.Menus;
|
||||
using StardewModdingAPI.Framework;
|
||||
using StardewModdingAPI.Framework.Events;
|
||||
using StardewModdingAPI.Framework.ModHelpers;
|
||||
using StardewModdingAPI.Framework.Reflection;
|
||||
using StardewModdingAPI;
|
||||
using StardewModdingAPI.Events;
|
||||
using StardewModdingAPI.Framework.Logging;
|
||||
using System.Threading;
|
||||
using StardewModdingAPI.Internal.ConsoleWriting;
|
||||
using StardewModdingAPI.Toolkit.Serialisation;
|
||||
using StardewModdingAPI.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace SMDroid
|
||||
|
@ -68,5 +57,17 @@ namespace SMDroid
|
|||
this.core.GameInstance.OnNewDayAfterFade();
|
||||
base.OnGame1_NewDayAfterFade(action);
|
||||
}
|
||||
public override bool OnObject_canBePlacedHere(StardewValley.Object __instance, GameLocation location, Vector2 tile, ref bool __result)
|
||||
{
|
||||
return this.core.GameInstance.OnObjectCanBePlacedHere(__instance, location, tile, ref __result);
|
||||
}
|
||||
public override void OnObject_isIndexOkForBasicShippedCategory(int index, ref bool __result)
|
||||
{
|
||||
this.core.GameInstance.OnObjectIsIndexOkForBasicShippedCategory(index, ref __result);
|
||||
}
|
||||
public override bool OnObject_checkForAction(StardewValley.Object __instance)
|
||||
{
|
||||
return this.core.GameInstance.OnObjectCheckForAction(__instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using StardewValley;
|
||||
|
||||
namespace StardewModdingAPI.Events
|
||||
{
|
||||
/// <summary>Events related to UI and drawing to the screen.</summary>
|
||||
public interface IHookEvents
|
||||
{
|
||||
/// <summary>Object.canBePlacedHere hook.</summary>
|
||||
event Func<ObjectCanBePlacedHereEventArgs, bool> ObjectCanBePlacedHere;
|
||||
|
||||
/// <summary>Object.checkForAction hook.</summary>
|
||||
event Func<ObjectCheckForActionEventArgs, bool> ObjectCheckForAction;
|
||||
|
||||
/// <summary>Object.isIndexOkForBasicShippedCategory hook.</summary>
|
||||
event Func<ObjectIsIndexOkForBasicShippedCategoryEventArgs, bool> ObjectIsIndexOkForBasicShippedCategory;
|
||||
}
|
||||
}
|
|
@ -23,5 +23,6 @@ namespace StardewModdingAPI.Events
|
|||
|
||||
/// <summary>Events serving specialised edge cases that shouldn't be used by most mods.</summary>
|
||||
ISpecialisedEvents Specialised { get; }
|
||||
IHookEvents Hook { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
using StardewValley;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace StardewModdingAPI.Events
|
||||
{
|
||||
/// <summary>Event arguments for an <see cref="IHookEvents.ObjectCanBePlacedHere"/> event.</summary>
|
||||
public class ObjectCanBePlacedHereEventArgs : System.EventArgs
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
public Object __instance { get; }
|
||||
|
||||
public GameLocation location { get; }
|
||||
|
||||
public Vector2 tile { get; }
|
||||
|
||||
public bool __result;
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="oldSize">The previous window size.</param>
|
||||
/// <param name="newSize">The current window size.</param>
|
||||
internal ObjectCanBePlacedHereEventArgs(Object __instance, GameLocation location, Vector2 tile, bool __result)
|
||||
{
|
||||
this.__instance = __instance;
|
||||
this.location = location;
|
||||
this.tile = tile;
|
||||
this.__result = __result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using StardewValley;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace StardewModdingAPI.Events
|
||||
{
|
||||
/// <summary>Event arguments for an <see cref="IHookEvents.ObjectCheckForAction"/> event.</summary>
|
||||
public class ObjectCheckForActionEventArgs : System.EventArgs
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
public Object __instance { get; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
internal ObjectCheckForActionEventArgs(Object __instance)
|
||||
{
|
||||
this.__instance = __instance;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace StardewModdingAPI.Events
|
||||
{
|
||||
/// <summary>Event arguments for an <see cref="IHookEvents.ObjectIsIndexOkForBasicShippedCategoryEventArgs"/> event.</summary>
|
||||
public class ObjectIsIndexOkForBasicShippedCategoryEventArgs : EventArgs
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The index</summary>
|
||||
public int index { get; }
|
||||
|
||||
public bool __result;
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="oldSize">The previous window size.</param>
|
||||
/// <param name="newSize">The current window size.</param>
|
||||
internal ObjectIsIndexOkForBasicShippedCategoryEventArgs(int index, bool __result)
|
||||
{
|
||||
this.index = index;
|
||||
this.__result = __result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -163,6 +163,11 @@ namespace StardewModdingAPI.Framework.Events
|
|||
/// <summary>Raised after the game performs its overall update tick (≈60 times per second). See notes on <see cref="ISpecialisedEvents.UnvalidatedUpdateTicked"/>.</summary>
|
||||
public readonly ManagedEvent<UnvalidatedUpdateTickedEventArgs> UnvalidatedUpdateTicked;
|
||||
|
||||
public readonly ManagedEvent<ObjectCanBePlacedHereEventArgs> ObjectCanBePlacedHere;
|
||||
|
||||
public readonly ManagedEvent<ObjectCheckForActionEventArgs> ObjectCheckForAction;
|
||||
|
||||
public readonly ManagedEvent<ObjectIsIndexOkForBasicShippedCategoryEventArgs> ObjectIsIndexOkForBasicShippedCategory;
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
|
@ -226,6 +231,10 @@ namespace StardewModdingAPI.Framework.Events
|
|||
this.LoadStageChanged = ManageEventOf<LoadStageChangedEventArgs>(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.LoadStageChanged));
|
||||
this.UnvalidatedUpdateTicking = ManageEventOf<UnvalidatedUpdateTickingEventArgs>(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicking));
|
||||
this.UnvalidatedUpdateTicked = ManageEventOf<UnvalidatedUpdateTickedEventArgs>(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicked));
|
||||
|
||||
this.ObjectCheckForAction = ManageEventOf<ObjectCheckForActionEventArgs>(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.LoadStageChanged));
|
||||
this.ObjectCanBePlacedHere = ManageEventOf<ObjectCanBePlacedHereEventArgs>(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicking));
|
||||
this.ObjectIsIndexOkForBasicShippedCategory = ManageEventOf<ObjectIsIndexOkForBasicShippedCategoryEventArgs>(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicked));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ namespace StardewModdingAPI.Framework.Events
|
|||
/// <summary>The underlying event.</summary>
|
||||
private event EventHandler<TEventArgs> Event;
|
||||
|
||||
private event Func<TEventArgs, bool> Func;
|
||||
|
||||
/// <summary>A human-readable name for the event.</summary>
|
||||
private readonly string EventName;
|
||||
|
||||
|
@ -26,8 +28,11 @@ namespace StardewModdingAPI.Framework.Events
|
|||
/// <summary>The display names for the mods which added each delegate.</summary>
|
||||
private readonly IDictionary<EventHandler<TEventArgs>, IModMetadata> SourceMods = new Dictionary<EventHandler<TEventArgs>, IModMetadata>();
|
||||
|
||||
private readonly IDictionary<Func<TEventArgs, bool>, IModMetadata> SourceModsFunc = new Dictionary<Func<TEventArgs, bool>, IModMetadata>();
|
||||
|
||||
/// <summary>The cached invocation list.</summary>
|
||||
private EventHandler<TEventArgs>[] CachedInvocationList;
|
||||
private Func<TEventArgs, bool>[] CachedInvocationListFunc;
|
||||
|
||||
|
||||
/*********
|
||||
|
@ -57,6 +62,11 @@ namespace StardewModdingAPI.Framework.Events
|
|||
this.Add(handler, this.ModRegistry.GetFromStack());
|
||||
}
|
||||
|
||||
public void Add(Func<TEventArgs, bool> handler)
|
||||
{
|
||||
this.Add(handler, this.ModRegistry.GetFromStack());
|
||||
}
|
||||
|
||||
/// <summary>Add an event handler.</summary>
|
||||
/// <param name="handler">The event handler.</param>
|
||||
/// <param name="mod">The mod which added the event handler.</param>
|
||||
|
@ -66,6 +76,12 @@ namespace StardewModdingAPI.Framework.Events
|
|||
this.AddTracking(mod, handler, this.Event?.GetInvocationList().Cast<EventHandler<TEventArgs>>());
|
||||
}
|
||||
|
||||
public void Add(Func<TEventArgs, bool> handler, IModMetadata mod)
|
||||
{
|
||||
this.Func += handler;
|
||||
this.AddTracking(mod, handler, this.Func?.GetInvocationList().Cast<Func<TEventArgs, bool>>());
|
||||
}
|
||||
|
||||
/// <summary>Remove an event handler.</summary>
|
||||
/// <param name="handler">The event handler.</param>
|
||||
public void Remove(EventHandler<TEventArgs> handler)
|
||||
|
@ -74,6 +90,12 @@ namespace StardewModdingAPI.Framework.Events
|
|||
this.RemoveTracking(handler, this.Event?.GetInvocationList().Cast<EventHandler<TEventArgs>>());
|
||||
}
|
||||
|
||||
public void Remove(Func<TEventArgs, bool> handler)
|
||||
{
|
||||
this.Func -= handler;
|
||||
this.RemoveTracking(handler, this.Event?.GetInvocationList().Cast<Func<TEventArgs, bool>>());
|
||||
}
|
||||
|
||||
/// <summary>Raise the event and notify all handlers.</summary>
|
||||
/// <param name="args">The event arguments to pass.</param>
|
||||
public void Raise(TEventArgs args)
|
||||
|
@ -94,6 +116,31 @@ namespace StardewModdingAPI.Framework.Events
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>Raise the event and notify all handlers wait for a actively response.</summary>
|
||||
/// <param name="args">The event arguments to pass.</param>
|
||||
public bool RaiseForChainRun(TEventArgs args)
|
||||
{
|
||||
if (this.Func == null)
|
||||
return true;
|
||||
|
||||
foreach (Func<TEventArgs, bool> handler in this.CachedInvocationListFunc)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool run = handler.Invoke(args);
|
||||
if (!run)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogError(handler, ex);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Raise the event and notify all handlers.</summary>
|
||||
/// <param name="args">The event arguments to pass.</param>
|
||||
/// <param name="match">A lambda which returns true if the event should be raised for the given mod.</param>
|
||||
|
@ -132,6 +179,12 @@ namespace StardewModdingAPI.Framework.Events
|
|||
this.CachedInvocationList = invocationList?.ToArray() ?? new EventHandler<TEventArgs>[0];
|
||||
}
|
||||
|
||||
protected void AddTracking(IModMetadata mod, Func<TEventArgs, bool> handler, IEnumerable<Func<TEventArgs, bool>> invocationList)
|
||||
{
|
||||
this.SourceModsFunc[handler] = mod;
|
||||
this.CachedInvocationListFunc = invocationList?.ToArray() ?? new Func<TEventArgs, bool>[0];
|
||||
}
|
||||
|
||||
/// <summary>Remove tracking for an event handler.</summary>
|
||||
/// <param name="handler">The event handler.</param>
|
||||
/// <param name="invocationList">The updated event invocation list.</param>
|
||||
|
@ -141,6 +194,12 @@ namespace StardewModdingAPI.Framework.Events
|
|||
if (!this.CachedInvocationList.Contains(handler)) // don't remove if there's still a reference to the removed handler (e.g. it was added twice and removed once)
|
||||
this.SourceMods.Remove(handler);
|
||||
}
|
||||
protected void RemoveTracking(Func<TEventArgs, bool> handler, IEnumerable<Func<TEventArgs, bool>> invocationList)
|
||||
{
|
||||
this.CachedInvocationListFunc = invocationList?.ToArray() ?? new Func<TEventArgs, bool>[0];
|
||||
if (!this.CachedInvocationListFunc.Contains(handler)) // don't remove if there's still a reference to the removed handler (e.g. it was added twice and removed once)
|
||||
this.SourceModsFunc.Remove(handler);
|
||||
}
|
||||
|
||||
/// <summary>Get the mod which registered the given event handler, if available.</summary>
|
||||
/// <param name="handler">The event handler.</param>
|
||||
|
@ -150,7 +209,12 @@ namespace StardewModdingAPI.Framework.Events
|
|||
? mod
|
||||
: null;
|
||||
}
|
||||
|
||||
protected IModMetadata GetSourceModFunc(Func<TEventArgs, bool> handler)
|
||||
{
|
||||
return this.SourceModsFunc.TryGetValue(handler, out IModMetadata mod)
|
||||
? mod
|
||||
: null;
|
||||
}
|
||||
/// <summary>Log an exception from an event handler.</summary>
|
||||
/// <param name="handler">The event handler instance.</param>
|
||||
/// <param name="ex">The exception that was raised.</param>
|
||||
|
@ -162,5 +226,13 @@ namespace StardewModdingAPI.Framework.Events
|
|||
else
|
||||
this.Monitor.Log($"A mod failed in the {this.EventName} event. Technical details: \n{ex.GetLogSummary()}", LogLevel.Error);
|
||||
}
|
||||
protected void LogError(Func<TEventArgs, bool> handler, Exception ex)
|
||||
{
|
||||
IModMetadata mod = this.GetSourceModFunc(handler);
|
||||
if (mod != null)
|
||||
mod.LogAsMod($"This mod failed in the {this.EventName} event. Technical details: \n{ex.GetLogSummary()}", LogLevel.Error);
|
||||
else
|
||||
this.Monitor.Log($"A mod failed in the {this.EventName} event. Technical details: \n{ex.GetLogSummary()}", LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace StardewModdingAPI.Framework.Events
|
|||
/// <summary>Events serving specialised edge cases that shouldn't be used by most mods.</summary>
|
||||
public ISpecialisedEvents Specialised { get; }
|
||||
|
||||
public IHookEvents Hook { get; }
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
|
@ -45,6 +46,7 @@ namespace StardewModdingAPI.Framework.Events
|
|||
this.Player = new ModPlayerEvents(mod, eventManager);
|
||||
this.World = new ModWorldEvents(mod, eventManager);
|
||||
this.Specialised = new ModSpecialisedEvents(mod, eventManager);
|
||||
this.Hook = new ModHookEvents(mod, eventManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using StardewModdingAPI.Events;
|
||||
|
||||
namespace StardewModdingAPI.Framework.Events
|
||||
{
|
||||
/// <summary>Events raised when the player provides input using a controller, keyboard, or mouse.</summary>
|
||||
internal class ModHookEvents : ModEventsBase, IHookEvents
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>Raised after the player presses a button on the keyboard, controller, or mouse.</summary>
|
||||
public event Func<ObjectCanBePlacedHereEventArgs, bool> ObjectCanBePlacedHere
|
||||
{
|
||||
add => this.EventManager.ObjectCanBePlacedHere.Add(value);
|
||||
remove => this.EventManager.ObjectCanBePlacedHere.Remove(value);
|
||||
}
|
||||
|
||||
/// <summary>Raised after the player releases a button on the keyboard, controller, or mouse.</summary>
|
||||
public event Func<ObjectCheckForActionEventArgs, bool> ObjectCheckForAction
|
||||
{
|
||||
add => this.EventManager.ObjectCheckForAction.Add(value);
|
||||
remove => this.EventManager.ObjectCheckForAction.Remove(value);
|
||||
}
|
||||
|
||||
/// <summary>Raised after the player moves the in-game cursor.</summary>
|
||||
public event Func<ObjectIsIndexOkForBasicShippedCategoryEventArgs, bool> ObjectIsIndexOkForBasicShippedCategory
|
||||
{
|
||||
add => this.EventManager.ObjectIsIndexOkForBasicShippedCategory.Add(value);
|
||||
remove => this.EventManager.ObjectIsIndexOkForBasicShippedCategory.Remove(value);
|
||||
}
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="mod">The mod which uses this instance.</param>
|
||||
/// <param name="eventManager">The underlying event manager.</param>
|
||||
internal ModHookEvents(IModMetadata mod, EventManager eventManager)
|
||||
: base(mod, eventManager) { }
|
||||
}
|
||||
}
|
|
@ -69,6 +69,28 @@ namespace StardewModdingAPI.Framework
|
|||
/// <remarks>Skipping a few frames ensures the game finishes initialising the world before mods try to change it.</remarks>
|
||||
private readonly Countdown AfterLoadTimer = new Countdown(5);
|
||||
|
||||
internal bool OnObjectCanBePlacedHere(SObject instance, GameLocation location, Vector2 tile, ref bool result)
|
||||
{
|
||||
ObjectCanBePlacedHereEventArgs args = new ObjectCanBePlacedHereEventArgs(instance, location, tile, result);
|
||||
bool run =this.Events.ObjectCanBePlacedHere.RaiseForChainRun(args);
|
||||
result = args.__result;
|
||||
return run;
|
||||
}
|
||||
|
||||
internal void OnObjectIsIndexOkForBasicShippedCategory(int index, ref bool result)
|
||||
{
|
||||
ObjectIsIndexOkForBasicShippedCategoryEventArgs args = new ObjectIsIndexOkForBasicShippedCategoryEventArgs(index, result);
|
||||
this.Events.ObjectIsIndexOkForBasicShippedCategory.RaiseForChainRun(args);
|
||||
result = args.__result;
|
||||
}
|
||||
|
||||
internal bool OnObjectCheckForAction(SObject instance)
|
||||
{
|
||||
ObjectCheckForActionEventArgs args = new ObjectCheckForActionEventArgs(instance);
|
||||
bool run = this.Events.ObjectCheckForAction.RaiseForChainRun(args);
|
||||
return run;
|
||||
}
|
||||
|
||||
/// <summary>Whether the game is saving and SMAPI has already raised <see cref="IGameLoopEvents.Saving"/>.</summary>
|
||||
private bool IsBetweenSaveEvents;
|
||||
|
||||
|
|
Loading…
Reference in New Issue