diff --git a/NHSE.Core/Resources/byte/item_kind.bin b/NHSE.Core/Resources/byte/item_kind.bin index 490d0b6..426fc3f 100644 Binary files a/NHSE.Core/Resources/byte/item_kind.bin and b/NHSE.Core/Resources/byte/item_kind.bin differ diff --git a/NHSE.Core/Structures/Item/ItemInfo.cs b/NHSE.Core/Structures/Item/ItemInfo.cs index 8267569..6223d4c 100644 --- a/NHSE.Core/Structures/Item/ItemInfo.cs +++ b/NHSE.Core/Structures/Item/ItemInfo.cs @@ -1,4 +1,7 @@ -namespace NHSE.Core +using System.Collections.Generic; +using static NHSE.Core.ItemKind; + +namespace NHSE.Core { /// /// Fetching metadata about an @@ -40,5 +43,147 @@ public static ItemMenuIconType GetMenuIcon(ushort id) return ItemMenuIconType.Unknown; return (ItemMenuIconType)ItemMenuIcons[id]; } + + public static bool TryGetMaxStackCount(Item item, out ushort max) => TryGetMaxStackCount(item.DisplayItemId, out max); + + public static bool TryGetMaxStackCount(ushort id, out ushort max) + { + var kind = GetItemKind(id); + return MaxCountByKind.TryGetValue(kind, out max); + } + + private static readonly Dictionary MaxCountByKind = new Dictionary + { + {Kind_Ftr, 00001}, + {Kind_RoomWall, 00001}, + {Kind_RoomFloor, 00001}, + {Kind_Rug, 00001}, + {Kind_RugMyDesign, 00001}, + {Kind_Socks, 00001}, + {Kind_Cap, 00001}, + {Kind_Helmet, 00001}, + {Kind_Accessory, 00001}, + {Kind_Bag, 00001}, + {Kind_Umbrella, 00001}, + {Kind_FishingRod, 00001}, + {Kind_Net, 00001}, + {Kind_Shovel, 00001}, + {Kind_Axe, 00001}, + {Kind_Watering, 00001}, + {Kind_Slingshot, 00001}, + {Kind_ChangeStick, 00001}, + {Kind_WoodenStickTool, 00001}, + {Kind_Ladder, 00001}, + {Kind_GroundMaker, 00001}, + {Kind_RiverMaker, 00001}, + {Kind_CliffMaker, 00001}, + {Kind_PartyPopper, 00010}, + {Kind_Ocarina, 00001}, + {Kind_Panflute, 00001}, + {Kind_Tambourine, 00001}, + {Kind_FierworkHand, 00010}, + {Kind_StickLight, 00001}, + {Kind_Uchiwa, 00001}, + {Kind_Windmill, 00001}, + {Kind_BlowBubble, 00010}, + {Kind_Partyhorn, 00001}, + {Kind_Balloon, 00001}, + {Kind_Timer, 00001}, + {Kind_HandheldPennant, 00001}, + {Kind_TreeSeedling, 00010}, + {Kind_Tree, 00001}, + {Kind_BushSeedling, 00010}, + {Kind_Bush, 00001}, + {Kind_VegeSeedling, 00010}, + {Kind_VegeTree, 00001}, + {Kind_Vegetable, 00010}, + {Kind_Weed, 00099}, + {Kind_FlowerSeed, 00010}, + {Kind_FlowerBud, 00001}, + {Kind_Flower, 00010}, + {Kind_Fruit, 00010}, + {Kind_Mushroom, 00010}, + {Kind_Turnip, 00010}, + {Kind_TurnipExpired, 00001}, + {Kind_FishBait, 00010}, + {Kind_PitFallSeed, 00010}, + {Kind_Medicine, 00010}, + {Kind_CraftMaterial, 00030}, + {Kind_CraftRemake, 00050}, + {Kind_Ore, 00030}, + {Kind_CraftPhoneCase, 00001}, + {Kind_Honeycomb, 00010}, + {Kind_Trash, 00001}, + {Kind_SnowCrystal, 00010}, + {Kind_AutumnLeaf, 00010}, + {Kind_Sakurapetal, 00010}, + {Kind_XmasDeco, 00010}, + {Kind_StarPiece, 00010}, + {Kind_Insect, 00001}, + {Kind_Fish, 00001}, + {Kind_DiveFish, 00001}, + {Kind_ShellDrift, 00010}, + {Kind_ShellFish, 00001}, + {Kind_FishToy, 00001}, + {Kind_InsectToy, 00001}, + {Kind_Fossil, 00001}, + {Kind_FossilUnknown, 00001}, + {Kind_Music, 00001}, + {Kind_MusicMiss, 00001}, + {Kind_Bromide, 00001}, + {Kind_Poster, 00001}, + {Kind_HousePost, 00001}, + {Kind_DoorDeco, 00001}, + {Kind_Fence, 00050}, + {Kind_DummyRecipe, 00001}, + {Kind_DummyDIYRecipe, 00001}, + {Kind_DummyHowtoBook, 00001}, + {Kind_LicenseItem, 00001}, + {Kind_BridgeItem, 00001}, + {Kind_SlopeItem, 00001}, + {Kind_DIYRecipe, 00001}, + {Kind_MessageBottle, 00001}, + {Kind_WrappingPaper, 00010}, + {Kind_HousingKit, 00001}, + {Kind_HousingKitRcoQuest, 00001}, + {Kind_HousingKitBirdge, 00001}, + {Kind_Money, 00010}, + {Kind_FireworkM, 00001}, + {Kind_BdayCupcake, 00010}, + {Kind_YutaroWisp, 00005}, + {Kind_JohnnyQuest, 00010}, + {Kind_JohnnyQuestDust, 00010}, + {Kind_PirateQuest, 00010}, + {Kind_QuestWrapping, 00001}, + {Kind_LostQuest, 00001}, + {Kind_LostQuestDust, 00001}, + {Kind_TailorTicket, 00010}, + {Kind_TreasureQuest, 00001}, + {Kind_TreasureQuestDust, 00001}, + {Kind_MilePlaneTicket, 00010}, + {Kind_RollanTicket, 00005}, + {Kind_EasterEgg, 00030}, + {Kind_LoveCrystal, 00030}, + {Kind_Candy, 00030}, + {Kind_Giftbox, 00001}, + {Kind_PinataStick, 00001}, + {Kind_NpcOutfit, 00001}, + {Kind_PlayerDemoOutfit, 00001}, + {Kind_Picture, 00001}, + {Kind_Sculpture, 00001}, + {Kind_PictureFake, 00001}, + {Kind_SculptureFake, 00001}, + {Kind_SmartPhone, 00001}, + {Kind_DummyFtr, 00001}, + {Kind_SequenceOnly, 00001}, + {Kind_MyDesignObject, 00001}, + {Kind_MyDesignTexture, 00001}, + {Kind_DummyWrapping, 00001}, + {Kind_DummyPresentbox, 00001}, + {Kind_DummyCardboard, 00001}, + {Kind_EventObjFtr, 00001}, + {Kind_NnpcRoomMarker, 00001}, + {Kind_PhotoStudioList, 00001}, + }; } } diff --git a/NHSE.Core/Structures/Item/ItemKind.cs b/NHSE.Core/Structures/Item/ItemKind.cs index 1f2e92c..14bde6c 100644 --- a/NHSE.Core/Structures/Item/ItemKind.cs +++ b/NHSE.Core/Structures/Item/ItemKind.cs @@ -147,6 +147,7 @@ public enum ItemKind : byte Kind_WoodenStickTool, Kind_WrappingPaper, Kind_YutaroWisp, + Kind_XmasDeco, Onepiece_Dress, Onepiece_Long, Onepiece_Middle, diff --git a/NHSE.Parsing/GameBCSVDumper.cs b/NHSE.Parsing/GameBCSVDumper.cs index 911e535..eec5d95 100644 --- a/NHSE.Parsing/GameBCSVDumper.cs +++ b/NHSE.Parsing/GameBCSVDumper.cs @@ -56,6 +56,14 @@ void DumpU(string fn, ushort[] ushorts, string dir = "bin") Console.WriteLine($"Created {fn}"); } + void DumpD(string fn, Dictionary dict, string dir = "text") + { + Directory.CreateDirectory(Path.Combine(dest, dir)); + var lines = dict.Select(z => $"{{{z.Key}, {z.Value:00000}}},"); + File.WriteAllLines(Path.Combine(dest, dir, fn), lines); + Console.WriteLine($"Created {fn}"); + } + DumpS("bcsv_map.txt", BCSV.EnumLookup.Dump()); DumpS("lifeSupportAchievement.txt", GetLifeSupportAchievementList(pathBCSV)); DumpS("recipeDictionary.txt", GetRecipeList(pathBCSV)); @@ -83,6 +91,8 @@ void DumpU(string fn, ushort[] ushorts, string dir = "bin") DumpS("DoorKind.txt", GetNumberedEnumValues(pathBCSV, "StructureHouseDoorParam.bcsv", 0x39B5A93D, 0x54706054)); DumpS("WallKind.txt", GetNumberedEnumValues(pathBCSV, "StructureHouseWallParam.bcsv", 0x39B5A93D, 0x54706054)); + DumpD("ItemStack.txt", GetItemStackDict(pathBCSV)); + DumpB("item_kind.bin", GetItemKindArray(pathBCSV)); DumpB("item_size.bin", GetItemSizeArray(pathBCSV)); DumpU("item_menuicon.bin", GetItemMenuIconArray(pathBCSV)); @@ -227,6 +237,59 @@ public static byte[] GetItemSizeArray(string pathBCSV, string fn = "ItemParam.bc return result; } + public static Dictionary GetItemStackDict(string pathBCSV, string fn = "ItemKind.bcsv") + { + var path = Path.Combine(pathBCSV, fn); + var data = File.ReadAllBytes(path); + var bcsv = new BCSV(data); + + var dict = bcsv.GetFieldDictionary(); + var fStack = dict[0x4C9BA961]; // MultiHoldMaxNum + var fKind = dict[0x87BF00E8]; // Label + + // clothing is split out more granularly in ItemKind and would cause errors + // since it's not likely to ever be stackable, we can skip + // none-type can be skipped and doesn't exist in ItemKind either + List skipLabels = new List + { + "TopsDefault", + "Tops", + "OnePiece", + "MarineSuit", + "BottomsDefault", + "Bottoms", + "Shoes", + "None" + }; + + var result = new Dictionary(); + for (int i = 0; i < bcsv.EntryCount; i++) + { + var stack = bcsv.ReadValue(i, fStack); + switch (stack) + { + case "-1": // for some reason turnips have a stack value of -1, should be 10... + stack = "10"; + break; + case "0": // the game stores items that cannot be stacked as 0, so technically they stack to 1 + stack = "1"; + break; + } + + var stackval = ushort.Parse(stack); + var kind = bcsv.ReadValue(i, fKind).TrimEnd('\0'); + if (skipLabels.Contains(kind)) + continue; + + kind = "Kind_" + kind; + if (!Enum.TryParse(kind, out var k)) + throw new InvalidEnumArgumentException($"{kind} is not a known enum value @ index {i}. Update the enum index first."); + result.Add(k, stackval); + } + + return result; + } + public static ushort[] GetItemMenuIconArray(string pathBCSV, string fn = "ItemParam.bcsv") { var path = Path.Combine(pathBCSV, fn); diff --git a/NHSE.WinForms/Controls/ItemEditor.Designer.cs b/NHSE.WinForms/Controls/ItemEditor.Designer.cs index 8258434..4dbc07c 100644 --- a/NHSE.WinForms/Controls/ItemEditor.Designer.cs +++ b/NHSE.WinForms/Controls/ItemEditor.Designer.cs @@ -154,6 +154,7 @@ private void InitializeComponent() this.L_Count.TabIndex = 7; this.L_Count.Text = "Count:"; this.L_Count.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.L_Count.DoubleClick += new System.EventHandler(this.L_Count_DoubleClick); // // L_Uses // diff --git a/NHSE.WinForms/Controls/ItemEditor.cs b/NHSE.WinForms/Controls/ItemEditor.cs index b1d9024..fca5b92 100644 --- a/NHSE.WinForms/Controls/ItemEditor.cs +++ b/NHSE.WinForms/Controls/ItemEditor.cs @@ -278,6 +278,16 @@ private void ToggleEditorVisibility(ItemKind k) } } + private void L_Count_DoubleClick(object sender, EventArgs e) + { + Item currentItem = SetItem(new Item()); + var result = ItemInfo.TryGetMaxStackCount(currentItem, out var max); + if (!result) + return; + currentItem.Count = (ushort)(max - 1); + LoadItem(currentItem); + } + private void CB_CountAlias_SelectedValueChanged(object sender,EventArgs e) { var val = WinFormsUtil.GetIndex((ComboBox)sender);