From b40a14ae623f86a1df1efe61d3c42f2d87610ee3 Mon Sep 17 00:00:00 2001 From: James Melkonian Date: Mon, 28 Nov 2022 19:50:44 -0800 Subject: [PATCH 1/5] Initial recipe component work Insert RecipeBook into ECS Add recipe command Fully functional recipes Recipe items Update item_image_manifest.ron scroll item image/drop model Colored recipe vox models Reset recipes command Move RecipeBook into Inventory Persistence for recipe book Recipe book migration Recipe items can now be dynamically generated from the recipe book Removed recipe items and recipe item generation bin Fix rebase Made recipes available in world Fix rebase Default recipes to manifest Recipes now have prices in economy. Changed recipes to go into kind before reaching item visual manifests. Default recipes automatically added to recipe book on character load. Loot table rebalancing Addressed balance feedback MR review feedback Recipes are now unlocked in groups Modular weapon component recipes now require learning recipes Metal weapon recipes now also teach how to craft their requisite ingots Added test for all recipes being valid --- assets/common/component_recipe_book.ron | 252 ++++++++++++++++++ assets/common/economy/trading_goods.ron | 5 +- assets/common/entity/village/alchemist.ron | 1 + assets/common/entity/village/blacksmith.ron | 5 +- assets/common/entity/village/chef.ron | 1 + assets/common/entity/village/herbalist.ron | 2 + assets/common/entity/village/merchant.ron | 7 +- assets/common/item_i18n_manifest.ron | 43 +++ assets/common/items/armor/pirate/belt.ron | 3 +- .../common/items/recipes/armor/bloodsteel.ron | 18 ++ .../common/items/recipes/armor/brinestone.ron | 18 ++ .../common/items/recipes/armor/carapace.ron | 17 ++ assets/common/items/recipes/armor/cobalt.ron | 18 ++ .../items/recipes/armor/dragonscale.ron | 17 ++ assets/common/items/recipes/armor/druid.ron | 18 ++ assets/common/items/recipes/armor/iron.ron | 18 ++ assets/common/items/recipes/armor/leather.ron | 18 ++ .../common/items/recipes/armor/moonweave.ron | 18 ++ .../common/items/recipes/armor/orichalcum.ron | 18 ++ assets/common/items/recipes/armor/primal.ron | 18 ++ assets/common/items/recipes/armor/scale.ron | 17 ++ assets/common/items/recipes/armor/silken.ron | 18 ++ assets/common/items/recipes/armor/steel.ron | 18 ++ assets/common/items/recipes/armor/sunsilk.ron | 18 ++ assets/common/items/recipes/armor/woolen.ron | 18 ++ assets/common/items/recipes/charms.ron | 13 + assets/common/items/recipes/default.ron | 82 ++++++ .../items/recipes/equipment/advanced.ron | 17 ++ .../common/items/recipes/equipment/basic.ron | 15 ++ .../items/recipes/equipment/moderate.ron | 22 ++ assets/common/items/recipes/explosives.ron | 17 ++ assets/common/items/recipes/food.ron | 17 ++ assets/common/items/recipes/gliders.ron | 20 ++ assets/common/items/recipes/instruments.ron | 20 ++ assets/common/items/recipes/potions.ron | 14 + .../items/recipes/unique/abyssal_gorget.ron | 11 + .../recipes/unique/mindflayer_spellbag.ron | 11 + .../common/items/recipes/unique/polaris.ron | 11 + .../recipes/unique/seashell_necklace.ron | 11 + .../items/recipes/unique/troll_hide_pack.ron | 11 + .../items/recipes/unique/winged_coronet.ron | 11 + assets/common/items/recipes/utility.ron | 15 ++ .../common/items/recipes/weapons/bamboo.ron | 11 + .../items/recipes/weapons/bloodsteel.ron | 12 + .../common/items/recipes/weapons/cobalt.ron | 12 + .../common/items/recipes/weapons/eldwood.ron | 11 + .../items/recipes/weapons/frostwood.ron | 11 + .../common/items/recipes/weapons/hardwood.ron | 11 + assets/common/items/recipes/weapons/iron.ron | 12 + .../common/items/recipes/weapons/ironwood.ron | 11 + .../items/recipes/weapons/orichalcum.ron | 12 + assets/common/items/recipes/weapons/steel.ron | 12 + .../loot_tables/dungeon/adlet/chest.ron | 10 + .../loot_tables/dungeon/cultist/chest.ron | 10 +- .../loot_tables/dungeon/gnarling/chest.ron | 8 + .../loot_tables/dungeon/haniwa/chest.ron | 11 + .../loot_tables/dungeon/myrmidon/chest.ron | 12 + .../loot_tables/dungeon/sahagin/chest.ron | 13 + .../dungeon/sea_chapel/chest_coral.ron | 2 + .../dungeon/sea_chapel/sea_bishop.ron | 2 +- .../dungeon/sea_chapel/sea_cleric.ron | 5 +- .../loot_tables/humanoids/grim_salvager.ron | 5 +- .../common/loot_tables/humanoids/humanoid.ron | 2 +- .../common/loot_tables/humanoids/pirate.ron | 6 +- assets/common/loot_tables/humanoids/witch.ron | 3 +- .../loot_tables/sprite/chest-buried.ron | 3 +- assets/common/loot_tables/sprite/chest.ron | 10 +- assets/common/loot_tables/sprite/crate.ron | 5 +- .../world/world_bosses/gigas_frost/boss.ron | 1 + ...cipe_book.ron => recipe_book_manifest.ron} | 14 +- .../common/trading/item_price_calculation.ron | 5 + assets/common/trading/sellable_recipe.ron | 17 ++ assets/common/trading/unsellable_recipe.ron | 45 ++++ assets/voxygen/i18n/en/common.ftl | 1 + assets/voxygen/i18n/en/hud/crafting.ftl | 2 + assets/voxygen/i18n/en/item/recipe.ftl | 86 ++++++ assets/voxygen/item_image_manifest.ron | 168 ++++++++++++ assets/voxygen/voxel/item_drop_manifest.ron | 8 + .../voxygen/voxel/object/recipe_alchemy.vox | 3 + .../voxel/object/recipe_blacksmithing.vox | 3 + .../voxygen/voxel/object/recipe_carpentry.vox | 3 + .../voxygen/voxel/object/recipe_cooking.vox | 3 + .../voxygen/voxel/object/recipe_lapidary.vox | 3 + .../voxel/object/recipe_leatherworking.vox | 3 + .../voxygen/voxel/object/recipe_weaving.vox | 3 + assets/voxygen/voxel/object/scroll.vox | 3 + assets/voxygen/voxel/object_manifest.ron | 10 + client/src/lib.rs | 72 +++-- common/Cargo.toml | 1 + common/net/src/msg/server.rs | 10 +- common/src/bin/csv_import/main.rs | 10 +- common/src/bin/recipe_graphviz.rs | 5 +- common/src/cmd.rs | 11 +- common/src/comp/body/object.rs | 8 +- common/src/comp/inventory/item/item_key.rs | 1 + common/src/comp/inventory/item/mod.rs | 66 +++-- common/src/comp/inventory/mod.rs | 66 ++++- common/src/comp/inventory/recipe_book.rs | 116 ++++++++ common/src/comp/inventory/test.rs | 12 +- common/src/comp/inventory/trade_pricing.rs | 16 +- common/src/comp/mod.rs | 1 + common/src/recipe.rs | 24 +- common/src/states/use_item.rs | 15 +- common/src/terrain/sprite.rs | 44 +-- common/src/trade.rs | 3 +- server/src/character_creator.rs | 4 + server/src/client.rs | 3 +- server/src/cmd.rs | 22 ++ server/src/events/inventory_manip.rs | 75 ++++-- server/src/lib.rs | 3 + server/src/migrations/V60__recipe_book.sql | 32 +++ server/src/persistence/character.rs | 49 +++- .../src/persistence/character/conversions.rs | 77 ++++-- server/src/sys/msg/register.rs | 5 +- voxygen/src/hud/crafting.rs | 93 ++++--- voxygen/src/hud/item_imgs.rs | 34 ++- voxygen/src/hud/mod.rs | 3 + voxygen/src/hud/util.rs | 1 + voxygen/src/scene/figure/cache.rs | 7 +- voxygen/src/scene/figure/load.rs | 5 +- voxygen/src/session/mod.rs | 23 +- world/src/site/economy/map_types.rs | 3 +- 122 files changed, 2187 insertions(+), 258 deletions(-) create mode 100644 assets/common/items/recipes/armor/bloodsteel.ron create mode 100644 assets/common/items/recipes/armor/brinestone.ron create mode 100644 assets/common/items/recipes/armor/carapace.ron create mode 100644 assets/common/items/recipes/armor/cobalt.ron create mode 100644 assets/common/items/recipes/armor/dragonscale.ron create mode 100644 assets/common/items/recipes/armor/druid.ron create mode 100644 assets/common/items/recipes/armor/iron.ron create mode 100644 assets/common/items/recipes/armor/leather.ron create mode 100644 assets/common/items/recipes/armor/moonweave.ron create mode 100644 assets/common/items/recipes/armor/orichalcum.ron create mode 100644 assets/common/items/recipes/armor/primal.ron create mode 100644 assets/common/items/recipes/armor/scale.ron create mode 100644 assets/common/items/recipes/armor/silken.ron create mode 100644 assets/common/items/recipes/armor/steel.ron create mode 100644 assets/common/items/recipes/armor/sunsilk.ron create mode 100644 assets/common/items/recipes/armor/woolen.ron create mode 100644 assets/common/items/recipes/charms.ron create mode 100644 assets/common/items/recipes/default.ron create mode 100644 assets/common/items/recipes/equipment/advanced.ron create mode 100644 assets/common/items/recipes/equipment/basic.ron create mode 100644 assets/common/items/recipes/equipment/moderate.ron create mode 100644 assets/common/items/recipes/explosives.ron create mode 100644 assets/common/items/recipes/food.ron create mode 100644 assets/common/items/recipes/gliders.ron create mode 100644 assets/common/items/recipes/instruments.ron create mode 100644 assets/common/items/recipes/potions.ron create mode 100644 assets/common/items/recipes/unique/abyssal_gorget.ron create mode 100644 assets/common/items/recipes/unique/mindflayer_spellbag.ron create mode 100644 assets/common/items/recipes/unique/polaris.ron create mode 100644 assets/common/items/recipes/unique/seashell_necklace.ron create mode 100644 assets/common/items/recipes/unique/troll_hide_pack.ron create mode 100644 assets/common/items/recipes/unique/winged_coronet.ron create mode 100644 assets/common/items/recipes/utility.ron create mode 100644 assets/common/items/recipes/weapons/bamboo.ron create mode 100644 assets/common/items/recipes/weapons/bloodsteel.ron create mode 100644 assets/common/items/recipes/weapons/cobalt.ron create mode 100644 assets/common/items/recipes/weapons/eldwood.ron create mode 100644 assets/common/items/recipes/weapons/frostwood.ron create mode 100644 assets/common/items/recipes/weapons/hardwood.ron create mode 100644 assets/common/items/recipes/weapons/iron.ron create mode 100644 assets/common/items/recipes/weapons/ironwood.ron create mode 100644 assets/common/items/recipes/weapons/orichalcum.ron create mode 100644 assets/common/items/recipes/weapons/steel.ron rename assets/common/{recipe_book.ron => recipe_book_manifest.ron} (99%) create mode 100644 assets/common/trading/sellable_recipe.ron create mode 100644 assets/common/trading/unsellable_recipe.ron create mode 100644 assets/voxygen/i18n/en/item/recipe.ftl create mode 100644 assets/voxygen/voxel/object/recipe_alchemy.vox create mode 100644 assets/voxygen/voxel/object/recipe_blacksmithing.vox create mode 100644 assets/voxygen/voxel/object/recipe_carpentry.vox create mode 100644 assets/voxygen/voxel/object/recipe_cooking.vox create mode 100644 assets/voxygen/voxel/object/recipe_lapidary.vox create mode 100644 assets/voxygen/voxel/object/recipe_leatherworking.vox create mode 100644 assets/voxygen/voxel/object/recipe_weaving.vox create mode 100644 assets/voxygen/voxel/object/scroll.vox create mode 100644 common/src/comp/inventory/recipe_book.rs create mode 100644 server/src/migrations/V60__recipe_book.sql diff --git a/assets/common/component_recipe_book.ron b/assets/common/component_recipe_book.ron index f485e9bdac..b45b6840f6 100644 --- a/assets/common/component_recipe_book.ron +++ b/assets/common/component_recipe_book.ron @@ -1,6 +1,7 @@ [ /// MODULAR WEAPONS/HAMMERS/PRIMARY COMPONENTS ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.hammer", @@ -11,6 +12,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.hammer", @@ -21,6 +23,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.hammer", @@ -33,6 +36,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.hammer", @@ -45,6 +49,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.hammer", @@ -57,6 +62,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.hammer", @@ -69,6 +75,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.spikedmace", @@ -79,6 +86,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.spikedmace", @@ -89,6 +97,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.spikedmace", @@ -101,6 +110,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.spikedmace", @@ -113,6 +123,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.spikedmace", @@ -125,6 +136,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.spikedmace", @@ -137,6 +149,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.warhammer", @@ -147,6 +160,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.warhammer", @@ -157,6 +171,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.warhammer", @@ -169,6 +184,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.warhammer", @@ -181,6 +197,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.warhammer", @@ -193,6 +210,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.warhammer", @@ -205,6 +223,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.maul", @@ -215,6 +234,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.maul", @@ -225,6 +245,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.maul", @@ -237,6 +258,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.maul", @@ -249,6 +271,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.maul", @@ -261,6 +284,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.maul", @@ -273,6 +297,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greatmace", @@ -283,6 +308,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greatmace", @@ -293,6 +319,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greatmace", @@ -305,6 +332,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greatmace", @@ -317,6 +345,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greatmace", @@ -329,6 +358,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greatmace", @@ -341,6 +371,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greathammer", @@ -351,6 +382,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greathammer", @@ -361,6 +393,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greathammer", @@ -373,6 +406,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greathammer", @@ -385,6 +419,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greathammer", @@ -397,6 +432,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.greathammer", @@ -409,6 +445,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.ornate", @@ -419,6 +456,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.ornate", @@ -429,6 +467,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.ornate", @@ -441,6 +480,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.ornate", @@ -453,6 +493,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.ornate", @@ -465,6 +506,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Hammer, item: "common.items.modular.weapon.primary.hammer.ornate", @@ -478,6 +520,7 @@ ), /// MODULAR WEAPONS/SWORDS/PRIMARY COMPONENTS ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.longsword", @@ -488,6 +531,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.longsword", @@ -498,6 +542,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.longsword", @@ -510,6 +555,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.longsword", @@ -522,6 +568,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.longsword", @@ -534,6 +581,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.longsword", @@ -546,6 +594,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sawblade", @@ -556,6 +605,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sawblade", @@ -566,6 +616,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sawblade", @@ -578,6 +629,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sawblade", @@ -590,6 +642,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sawblade", @@ -602,6 +655,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sawblade", @@ -614,6 +668,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.katana", @@ -624,6 +679,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.katana", @@ -634,6 +690,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.katana", @@ -646,6 +703,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.katana", @@ -658,6 +716,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.katana", @@ -670,6 +729,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.katana", @@ -682,6 +742,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.zweihander", @@ -692,6 +753,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.zweihander", @@ -702,6 +764,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.zweihander", @@ -714,6 +777,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.zweihander", @@ -726,6 +790,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.zweihander", @@ -738,6 +803,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.zweihander", @@ -750,6 +816,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sabre", @@ -760,6 +827,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sabre", @@ -770,6 +838,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sabre", @@ -782,6 +851,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sabre", @@ -794,6 +864,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sabre", @@ -806,6 +877,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.sabre", @@ -818,6 +890,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.greatsword", @@ -828,6 +901,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.greatsword", @@ -838,6 +912,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.greatsword", @@ -850,6 +925,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.greatsword", @@ -862,6 +938,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.greatsword", @@ -874,6 +951,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.greatsword", @@ -886,6 +964,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.ornate", @@ -896,6 +975,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.ornate", @@ -906,6 +986,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.ornate", @@ -918,6 +999,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.ornate", @@ -930,6 +1012,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.ornate", @@ -942,6 +1025,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Sword, item: "common.items.modular.weapon.primary.sword.ornate", @@ -955,6 +1039,7 @@ ), /// MODULAR WEAPONS/AXES/PRIMARY COMPONENTS ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.axe", @@ -965,6 +1050,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.axe", @@ -975,6 +1061,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.axe", @@ -987,6 +1074,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.axe", @@ -999,6 +1087,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.axe", @@ -1011,6 +1100,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.axe", @@ -1023,6 +1113,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.jagged", @@ -1033,6 +1124,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.jagged", @@ -1043,6 +1135,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.jagged", @@ -1055,6 +1148,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.jagged", @@ -1067,6 +1161,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.jagged", @@ -1079,6 +1174,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.jagged", @@ -1091,6 +1187,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.battleaxe", @@ -1101,6 +1198,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.battleaxe", @@ -1111,6 +1209,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.battleaxe", @@ -1123,6 +1222,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.battleaxe", @@ -1135,6 +1235,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.battleaxe", @@ -1147,6 +1248,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.battleaxe", @@ -1159,6 +1261,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.poleaxe", @@ -1169,6 +1272,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.poleaxe", @@ -1179,6 +1283,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.poleaxe", @@ -1191,6 +1296,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.poleaxe", @@ -1203,6 +1309,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.poleaxe", @@ -1215,6 +1322,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.poleaxe", @@ -1227,6 +1335,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.labrys", @@ -1237,6 +1346,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.labrys", @@ -1247,6 +1357,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.labrys", @@ -1259,6 +1370,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.labrys", @@ -1271,6 +1383,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.labrys", @@ -1283,6 +1396,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.labrys", @@ -1295,6 +1409,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.greataxe", @@ -1305,6 +1420,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.greataxe", @@ -1315,6 +1431,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.greataxe", @@ -1327,6 +1444,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.greataxe", @@ -1339,6 +1457,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.greataxe", @@ -1351,6 +1470,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.greataxe", @@ -1363,6 +1483,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bronze_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.ornate", @@ -1373,6 +1494,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "iron_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.ornate", @@ -1383,6 +1505,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "steel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.ornate", @@ -1395,6 +1518,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "cobalt_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.ornate", @@ -1407,6 +1531,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "bloodsteel_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.ornate", @@ -1419,6 +1544,7 @@ craft_sprite: Some(Anvil), ), ( + recipe_book_key: "orichalcum_weapons", output: ToolPrimaryComponent( toolkind: Axe, item: "common.items.modular.weapon.primary.axe.ornate", @@ -1432,6 +1558,7 @@ ), /// MODULAR WEAPONS/BOWS/PRIMARY COMPONENTS ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.bow", @@ -1443,6 +1570,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.bow", @@ -1454,6 +1582,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.bow", @@ -1465,6 +1594,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.bow", @@ -1476,6 +1606,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.bow", @@ -1487,6 +1618,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.bow", @@ -1498,6 +1630,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.composite", @@ -1509,6 +1642,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.composite", @@ -1520,6 +1654,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.composite", @@ -1531,6 +1666,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.composite", @@ -1542,6 +1678,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.composite", @@ -1553,6 +1690,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.composite", @@ -1564,6 +1702,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.greatbow", @@ -1575,6 +1714,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.greatbow", @@ -1586,6 +1726,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.greatbow", @@ -1597,6 +1738,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.greatbow", @@ -1608,6 +1750,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.greatbow", @@ -1619,6 +1762,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.greatbow", @@ -1630,6 +1774,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.longbow", @@ -1641,6 +1786,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.longbow", @@ -1652,6 +1798,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.longbow", @@ -1663,6 +1810,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.longbow", @@ -1674,6 +1822,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.longbow", @@ -1685,6 +1834,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.longbow", @@ -1696,6 +1846,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.ornate", @@ -1707,6 +1858,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.ornate", @@ -1718,6 +1870,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.ornate", @@ -1729,6 +1882,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.ornate", @@ -1740,6 +1894,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.ornate", @@ -1751,6 +1906,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.ornate", @@ -1762,6 +1918,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.shortbow", @@ -1773,6 +1930,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.shortbow", @@ -1784,6 +1942,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.shortbow", @@ -1795,6 +1954,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.shortbow", @@ -1806,6 +1966,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.shortbow", @@ -1817,6 +1978,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.shortbow", @@ -1828,6 +1990,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.warbow", @@ -1839,6 +2002,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.warbow", @@ -1850,6 +2014,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.warbow", @@ -1861,6 +2026,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.warbow", @@ -1872,6 +2038,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.warbow", @@ -1883,6 +2050,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Bow, item: "common.items.modular.weapon.primary.bow.warbow", @@ -1895,6 +2063,7 @@ ), /// MODULAR WEAPONS/FIRE STAFFS/PRIMARY COMPONENTS ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.brand", @@ -1907,6 +2076,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.brand", @@ -1919,6 +2089,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.brand", @@ -1931,6 +2102,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.brand", @@ -1943,6 +2115,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.brand", @@ -1955,6 +2128,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.brand", @@ -1967,6 +2141,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.grandstaff", @@ -1979,6 +2154,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.grandstaff", @@ -1991,6 +2167,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.grandstaff", @@ -2003,6 +2180,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.grandstaff", @@ -2015,6 +2193,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.grandstaff", @@ -2027,6 +2206,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.grandstaff", @@ -2039,6 +2219,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.longpole", @@ -2051,6 +2232,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.longpole", @@ -2063,6 +2245,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.longpole", @@ -2075,6 +2258,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.longpole", @@ -2087,6 +2271,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.longpole", @@ -2099,6 +2284,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.longpole", @@ -2111,6 +2297,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.ornate", @@ -2123,6 +2310,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.ornate", @@ -2135,6 +2323,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.ornate", @@ -2147,6 +2336,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.ornate", @@ -2159,6 +2349,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.ornate", @@ -2171,6 +2362,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.ornate", @@ -2183,6 +2375,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.pole", @@ -2195,6 +2388,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.pole", @@ -2207,6 +2401,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.pole", @@ -2219,6 +2414,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.pole", @@ -2231,6 +2427,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.pole", @@ -2243,6 +2440,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.pole", @@ -2255,6 +2453,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.rod", @@ -2267,6 +2466,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.rod", @@ -2279,6 +2479,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.rod", @@ -2291,6 +2492,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.rod", @@ -2303,6 +2505,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.rod", @@ -2315,6 +2518,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.rod", @@ -2327,6 +2531,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.staff", @@ -2339,6 +2544,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.staff", @@ -2351,6 +2557,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.staff", @@ -2363,6 +2570,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.staff", @@ -2375,6 +2583,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.staff", @@ -2387,6 +2596,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Staff, item: "common.items.modular.weapon.primary.staff.staff", @@ -2400,6 +2610,7 @@ ), /// MODULAR WEAPONS/NATURE SCEPTRES/PRIMARY COMPONENTS ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.arbor", @@ -2412,6 +2623,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.arbor", @@ -2424,6 +2636,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.arbor", @@ -2436,6 +2649,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.arbor", @@ -2448,6 +2662,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.arbor", @@ -2460,6 +2675,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.arbor", @@ -2472,6 +2688,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.cane", @@ -2484,6 +2701,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.cane", @@ -2496,6 +2714,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.cane", @@ -2508,6 +2727,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.cane", @@ -2520,6 +2740,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.cane", @@ -2532,6 +2753,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.cane", @@ -2544,6 +2766,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crook", @@ -2556,6 +2779,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crook", @@ -2568,6 +2792,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crook", @@ -2580,6 +2805,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crook", @@ -2592,6 +2818,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crook", @@ -2604,6 +2831,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crook", @@ -2616,6 +2844,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crozier", @@ -2628,6 +2857,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crozier", @@ -2640,6 +2870,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crozier", @@ -2652,6 +2883,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crozier", @@ -2664,6 +2896,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crozier", @@ -2676,6 +2909,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.crozier", @@ -2688,6 +2922,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.grandsceptre", @@ -2700,6 +2935,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.grandsceptre", @@ -2712,6 +2948,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.grandsceptre", @@ -2724,6 +2961,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.grandsceptre", @@ -2736,6 +2974,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.grandsceptre", @@ -2748,6 +2987,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.grandsceptre", @@ -2760,6 +3000,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.ornate", @@ -2772,6 +3013,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.ornate", @@ -2784,6 +3026,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.ornate", @@ -2796,6 +3039,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.ornate", @@ -2808,6 +3052,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.ornate", @@ -2820,6 +3065,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.ornate", @@ -2832,6 +3078,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "wood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.sceptre", @@ -2844,6 +3091,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "bamboo_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.sceptre", @@ -2856,6 +3104,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "hardwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.sceptre", @@ -2868,6 +3117,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "ironwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.sceptre", @@ -2880,6 +3130,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "frostwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.sceptre", @@ -2892,6 +3143,7 @@ craft_sprite: Some(CraftingBench), ), ( + recipe_book_key: "eldwood_weapons", output: ToolPrimaryComponent( toolkind: Sceptre, item: "common.items.modular.weapon.primary.sceptre.sceptre", diff --git a/assets/common/economy/trading_goods.ron b/assets/common/economy/trading_goods.ron index 1dd30330f7..59ffe9d682 100644 --- a/assets/common/economy/trading_goods.ron +++ b/assets/common/economy/trading_goods.ron @@ -40,6 +40,9 @@ Ingredients: ( decay_rate: 0.1, // revisit ), + Recipe: ( + decay_rate: 0.1, // revisit + ), Tools: ( // TODO: Separate stone, metal, bone, wood // decay_rate: 0.05, // 14 years half-life @@ -48,4 +51,4 @@ //decay_rate: 0.1, // 6 years half-life //transport_effort: 0.05, // 2kg/40kg ), -} \ No newline at end of file +} diff --git a/assets/common/entity/village/alchemist.ron b/assets/common/entity/village/alchemist.ron index d8c9c53eda..ad3aeea4b6 100644 --- a/assets/common/entity/village/alchemist.ron +++ b/assets/common/entity/village/alchemist.ron @@ -19,6 +19,7 @@ items: [ (10, "common.items.consumable.potion_big"), (10, "common.items.food.sunflower_icetea"), + (1, "common.items.recipes.potions"), ], ), meta: [ diff --git a/assets/common/entity/village/blacksmith.ron b/assets/common/entity/village/blacksmith.ron index 93fb72e5ec..ca4180786f 100644 --- a/assets/common/entity/village/blacksmith.ron +++ b/assets/common/entity/village/blacksmith.ron @@ -18,7 +18,10 @@ )), items: [ (10, "common.items.consumable.potion_big"), - (10, "common.items.food.sunflower_icetea"), + (1, "common.items.recipes.equipment.basic"), + (1, "common.items.recipes.armor.iron"), + (1, "common.items.recipes.weapons.iron"), + (1, "common.items.recipes.utility"), ], ), meta: [ diff --git a/assets/common/entity/village/chef.ron b/assets/common/entity/village/chef.ron index a0e6bbbdae..e8ad5a30d1 100644 --- a/assets/common/entity/village/chef.ron +++ b/assets/common/entity/village/chef.ron @@ -19,6 +19,7 @@ items: [ (10, "common.items.consumable.potion_big"), (10, "common.items.food.sunflower_icetea"), + (1, "common.items.recipes.food"), ], ), meta: [ diff --git a/assets/common/entity/village/herbalist.ron b/assets/common/entity/village/herbalist.ron index fbed25db5b..e4d7fc60a1 100644 --- a/assets/common/entity/village/herbalist.ron +++ b/assets/common/entity/village/herbalist.ron @@ -15,6 +15,8 @@ items: [ (10, "common.items.food.cheese"), (10, "common.items.food.plainsalad"), + (1, "common.items.recipes.food"), + (1, "common.items.recipes.armor.woolen"), ], ), meta: [], diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron index 64123e4156..ac5bda43c6 100644 --- a/assets/common/entity/village/merchant.ron +++ b/assets/common/entity/village/merchant.ron @@ -14,8 +14,13 @@ (2, ModularWeapon(tool: Staff, material: Ironwood, hands: None)), ]), None)), )), + items: [ + (1, "common.items.recipes.equipment.basic"), + (1, "common.items.recipes.armor.leather"), + (1, "common.items.recipes.weapons.bamboo"), + ], ), meta: [ SkillSetAsset("common.skillset.preset.rank3.fullskill"), ], -) \ No newline at end of file +) diff --git a/assets/common/item_i18n_manifest.ron b/assets/common/item_i18n_manifest.ron index 9d01cfe405..159ee79682 100644 --- a/assets/common/item_i18n_manifest.ron +++ b/assets/common/item_i18n_manifest.ron @@ -7074,5 +7074,48 @@ One, ), ): "weapon-hammer-hammer-cobalt-1h", + Simple("common.items.recipes.armor.bloodsteel"): "recipe-armor-bloodsteel", + Simple("common.items.recipes.armor.brinestone"): "recipe-armor-brinestone", + Simple("common.items.recipes.armor.carapace"): "recipe-armor-carapace", + Simple("common.items.recipes.armor.cobalt"): "recipe-armor-cobalt", + Simple("common.items.recipes.armor.dragonscale"): "recipe-armor-dragonscale", + Simple("common.items.recipes.armor.druid"): "recipe-armor-druid", + Simple("common.items.recipes.armor.iron"): "recipe-armor-iron", + Simple("common.items.recipes.armor.leather"): "recipe-armor-leather", + Simple("common.items.recipes.armor.moonweave"): "recipe-armor-moonweave", + Simple("common.items.recipes.armor.orichalcum"): "recipe-armor-orichalcum", + Simple("common.items.recipes.armor.primal"): "recipe-armor-primal", + Simple("common.items.recipes.armor.scale"): "recipe-armor-scale", + Simple("common.items.recipes.armor.silken"): "recipe-armor-silken", + Simple("common.items.recipes.armor.steel"): "recipe-armor-steel", + Simple("common.items.recipes.armor.sunsilk"): "recipe-armor-sunsilk", + Simple("common.items.recipes.armor.woolen"): "recipe-armor-woolen", + Simple("common.items.recipes.equipment.basic"): "recipe-equipment-basic", + Simple("common.items.recipes.equipment.moderate"): "recipe-equipment-moderate", + Simple("common.items.recipes.equipment.advanced"): "recipe-equipment-advanced", + Simple("common.items.recipes.unique.abyssal_gorget"): "recipe-unique-abyssal_gorget", + Simple("common.items.recipes.unique.mindflayer_spellbag"): "recipe-unique-mindflayer_spellbag", + Simple("common.items.recipes.unique.polaris"): "recipe-unique-polaris", + Simple("common.items.recipes.unique.seashell_necklace"): "recipe-unique-seashell_necklace", + Simple("common.items.recipes.unique.troll_hide_pack"): "recipe-unique-troll_hide_pack", + Simple("common.items.recipes.unique.winged_coronet"): "recipe-unique-winged_coronet", + Simple("common.items.recipes.charms"): "recipe-charms", + Simple("common.items.recipes.explosives"): "recipe-explosives", + Simple("common.items.recipes.food"): "recipe-food", + Simple("common.items.recipes.gliders"): "recipe-gliders", + Simple("common.items.recipes.instruments"): "recipe-instruments", + Simple("common.items.recipes.potions"): "recipe-potions", + Simple("common.items.recipes.utility"): "recipe-utility", + Simple("common.items.recipes.weapons.iron"): "recipe-weapons-iron", + Simple("common.items.recipes.weapons.steel"): "recipe-weapons-steel", + Simple("common.items.recipes.weapons.cobalt"): "recipe-weapons-cobalt", + Simple("common.items.recipes.weapons.bloodsteel"): "recipe-weapons-bloodsteel", + Simple("common.items.recipes.weapons.orichalcum"): "recipe-weapons-orichalcum", + Simple("common.items.recipes.weapons.bamboo"): "recipe-weapons-bamboo", + Simple("common.items.recipes.weapons.hardwood"): "recipe-weapons-hardwood", + Simple("common.items.recipes.weapons.ironwood"): "recipe-weapons-ironwood", + Simple("common.items.recipes.weapons.frostwood"): "recipe-weapons-frostwood", + Simple("common.items.recipes.weapons.eldwood"): "recipe-weapons-eldwood", + Simple("common.items.recipes.default"): "recipe-default", }, ) diff --git a/assets/common/items/armor/pirate/belt.ron b/assets/common/items/armor/pirate/belt.ron index e2258c0f74..2bd3592324 100644 --- a/assets/common/items/armor/pirate/belt.ron +++ b/assets/common/items/armor/pirate/belt.ron @@ -6,6 +6,5 @@ ItemDef( stats: FromSet("Pirate"), )), quality: Epic, - tags: [ - ], + tags: [], ) diff --git a/assets/common/items/recipes/armor/bloodsteel.ron b/assets/common/items/recipes/armor/bloodsteel.ron new file mode 100644 index 0000000000..69e305502d --- /dev/null +++ b/assets/common/items/recipes/armor/bloodsteel.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "bloodsteel_ingot", + "bloodsteel_back", + "bloodsteel_belt", + "bloodsteel_chest", + "bloodsteel_feet", + "bloodsteel_hands", + "bloodsteel_pants", + "bloodsteel_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/brinestone.ron b/assets/common/items/recipes/armor/brinestone.ron new file mode 100644 index 0000000000..093e46b1fc --- /dev/null +++ b/assets/common/items/recipes/armor/brinestone.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "brinestone_back", + "brinestone_belt", + "brinestone_chest", + "brinestone_feet", + "brinestone_hands", + "brinestone_pants", + "brinestone_shoulder", + "brinestone_crown", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/carapace.ron b/assets/common/items/recipes/armor/carapace.ron new file mode 100644 index 0000000000..dcaf19590c --- /dev/null +++ b/assets/common/items/recipes/armor/carapace.ron @@ -0,0 +1,17 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "carapace_back", + "carapace_belt", + "carapace_chest", + "carapace_feet", + "carapace_hands", + "carapace_pants", + "carapace_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/cobalt.ron b/assets/common/items/recipes/armor/cobalt.ron new file mode 100644 index 0000000000..01403bc683 --- /dev/null +++ b/assets/common/items/recipes/armor/cobalt.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "cobalt_ingot", + "cobalt_back", + "cobalt_belt", + "cobalt_chest", + "cobalt_feet", + "cobalt_hands", + "cobalt_pants", + "cobalt_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/dragonscale.ron b/assets/common/items/recipes/armor/dragonscale.ron new file mode 100644 index 0000000000..59df397ca0 --- /dev/null +++ b/assets/common/items/recipes/armor/dragonscale.ron @@ -0,0 +1,17 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "dragonscale_back", + "dragonscale_belt", + "dragonscale_chest", + "dragonscale_feet", + "dragonscale_hands", + "dragonscale_pants", + "dragonscale_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/druid.ron b/assets/common/items/recipes/armor/druid.ron new file mode 100644 index 0000000000..f4db3cacb2 --- /dev/null +++ b/assets/common/items/recipes/armor/druid.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "lifecloth", + "druid_back", + "druid_belt", + "druid_chest", + "druid_feet", + "druid_hands", + "druid_pants", + "druid_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/iron.ron b/assets/common/items/recipes/armor/iron.ron new file mode 100644 index 0000000000..ee20a9d426 --- /dev/null +++ b/assets/common/items/recipes/armor/iron.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "iron_ingot", + "iron_back", + "iron_belt", + "iron_chest", + "iron_feet", + "iron_hands", + "iron_pants", + "iron_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/leather.ron b/assets/common/items/recipes/armor/leather.ron new file mode 100644 index 0000000000..88f05728be --- /dev/null +++ b/assets/common/items/recipes/armor/leather.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "thick_leather", + "leather_back", + "leather_belt", + "leather_chest", + "leather_feet", + "leather_hands", + "leather_pants", + "leather_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/moonweave.ron b/assets/common/items/recipes/armor/moonweave.ron new file mode 100644 index 0000000000..8e363c702d --- /dev/null +++ b/assets/common/items/recipes/armor/moonweave.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "moonweave", + "moonweave_back", + "moonweave_belt", + "moonweave_chest", + "moonweave_feet", + "moonweave_hands", + "moonweave_pants", + "moonweave_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/orichalcum.ron b/assets/common/items/recipes/armor/orichalcum.ron new file mode 100644 index 0000000000..d6b6d04b50 --- /dev/null +++ b/assets/common/items/recipes/armor/orichalcum.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "orichalcum_ingot", + "orichalcum_back", + "orichalcum_belt", + "orichalcum_chest", + "orichalcum_feet", + "orichalcum_hands", + "orichalcum_pants", + "orichalcum_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/primal.ron b/assets/common/items/recipes/armor/primal.ron new file mode 100644 index 0000000000..0050ea3a09 --- /dev/null +++ b/assets/common/items/recipes/armor/primal.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "rigid_leather", + "primal_back", + "primal_belt", + "primal_chest", + "primal_feet", + "primal_hands", + "primal_pants", + "primal_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/scale.ron b/assets/common/items/recipes/armor/scale.ron new file mode 100644 index 0000000000..ab5be20fc1 --- /dev/null +++ b/assets/common/items/recipes/armor/scale.ron @@ -0,0 +1,17 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "scale_back", + "scale_belt", + "scale_chest", + "scale_feet", + "scale_hands", + "scale_pants", + "scale_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/silken.ron b/assets/common/items/recipes/armor/silken.ron new file mode 100644 index 0000000000..a99124da5a --- /dev/null +++ b/assets/common/items/recipes/armor/silken.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "silk", + "silken_back", + "silken_belt", + "silken_chest", + "silken_feet", + "silken_hands", + "silken_pants", + "silken_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/steel.ron b/assets/common/items/recipes/armor/steel.ron new file mode 100644 index 0000000000..378bfccb31 --- /dev/null +++ b/assets/common/items/recipes/armor/steel.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "steel_ingot", + "steel_back", + "steel_belt", + "steel_chest", + "steel_feet", + "steel_hands", + "steel_pants", + "steel_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/sunsilk.ron b/assets/common/items/recipes/armor/sunsilk.ron new file mode 100644 index 0000000000..f133bd2087 --- /dev/null +++ b/assets/common/items/recipes/armor/sunsilk.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "sunsilk", + "sunsilk_back", + "sunsilk_belt", + "sunsilk_chest", + "sunsilk_feet", + "sunsilk_hands", + "sunsilk_pants", + "sunsilk_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/armor/woolen.ron b/assets/common/items/recipes/armor/woolen.ron new file mode 100644 index 0000000000..58b41e3884 --- /dev/null +++ b/assets/common/items/recipes/armor/woolen.ron @@ -0,0 +1,18 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "cotton", + "woolen_back", + "woolen_belt", + "woolen_chest", + "woolen_feet", + "woolen_hands", + "woolen_pants", + "woolen_shoulder", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/charms.ron b/assets/common/items/recipes/charms.ron new file mode 100644 index 0000000000..937ccb3b49 --- /dev/null +++ b/assets/common/items/recipes/charms.ron @@ -0,0 +1,13 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "burning_charm", + "frozen_charm", + "lifesteal_charm", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/default.ron b/assets/common/items/recipes/default.ron new file mode 100644 index 0000000000..0d9dc4d447 --- /dev/null +++ b/assets/common/items/recipes/default.ron @@ -0,0 +1,82 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + // Food + "fish_cooked", + "bird_cooked", + "bird_large_cooked", + "beast_small_cooked", + "beast_large_cooked", + "tough_cooked", + // Armors + "rawhide_back", + "rawhide_belt", + "rawhide_chest", + "rawhide_feet", + "rawhide_hands", + "rawhide_pants", + "rawhide_shoulder", + "linen_back", + "linen_belt", + "linen_chest", + "linen_feet", + "linen_hands", + "linen_pants", + "linen_shoulder", + "bronze_back", + "bronze_belt", + "bronze_chest", + "bronze_feet", + "bronze_hands", + "bronze_pants", + "bronze_shoulder", + // Weapon components + "short_hilt", + "medium_hilt", + "long_hilt", + "short_hammer_haft", + "medium_hammer_haft", + "long_hammer_haft", + "short_axe_haft", + "medium_axe_haft", + "long_axe_haft", + "short_bow_grip", + "medium_bow_grip", + "long_bow_grip", + "light_pyrocore", + "medium_pyrocore", + "heavy_pyrocore", + "light_biocore", + "medium_biocore", + "heavy_biocore", + // Weapons + "burnt_drumstick", + "healing_sceptre", + "bronze_weapons", + "wood_weapons", + // Materials + "tin_ingot", + "copper_ingot", + "bronze_ingot", + "red_cloth", + "twig", + "leather_strips", + "simple_leather", + "linen_flax", + "cloth_strips", + // Tools + "craftsman_hammer", + "mortar_pestle", + "sewing_set", + "tin_pickaxe", + "shovel", + // Bags + "tiny_red_pouch", + "tiny_leather_pouch", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/equipment/advanced.ron b/assets/common/items/recipes/equipment/advanced.ron new file mode 100644 index 0000000000..7cc3352856 --- /dev/null +++ b/assets/common/items/recipes/equipment/advanced.ron @@ -0,0 +1,17 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "diamond_ring", + "diamond_necklace", + "ruby_ring", + "ruby_necklace", + "emerald_ring", + "emerald_necklace", + "merchant_turban", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/equipment/basic.ron b/assets/common/items/recipes/equipment/basic.ron new file mode 100644 index 0000000000..747042a116 --- /dev/null +++ b/assets/common/items/recipes/equipment/basic.ron @@ -0,0 +1,15 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "seashell_necklace", + "knitted_red_pouch", + "steel_pickaxe", + "fang_necklace", + "black_lantern", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/equipment/moderate.ron b/assets/common/items/recipes/equipment/moderate.ron new file mode 100644 index 0000000000..03594b946c --- /dev/null +++ b/assets/common/items/recipes/equipment/moderate.ron @@ -0,0 +1,22 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "sapphire_ring", + "sapphire_necklace", + "topaz_ring", + "topaz_necklace", + "amethyst_ring", + "amethyst_necklace", + "reliable_leather_pack", + "woven_red_bag", + "honeycomb_pendant", + "traveler_backpack", + "sturdy_red_backpack", + "troll_hide_pack", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/explosives.ron b/assets/common/items/recipes/explosives.ron new file mode 100644 index 0000000000..601a66eb85 --- /dev/null +++ b/assets/common/items/recipes/explosives.ron @@ -0,0 +1,17 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "bomb_coconut", + "firework_blue", + "firework_green", + "firework_purple", + "firework_red", + "firework_white", + "firework_yellow", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/food.ron b/assets/common/items/recipes/food.ron new file mode 100644 index 0000000000..ebbabb1268 --- /dev/null +++ b/assets/common/items/recipes/food.ron @@ -0,0 +1,17 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "cactus_colada", + "apple_shroom_curry", + "salad_plain", + "salad_tomato", + "apples_stick", + "mushroom_stick", + "sunflower_icetea", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/gliders.ron b/assets/common/items/recipes/gliders.ron new file mode 100644 index 0000000000..852bb3a56c --- /dev/null +++ b/assets/common/items/recipes/gliders.ron @@ -0,0 +1,20 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "plain_cloth_glider", + "red_cloth_glider", + "cloverleaf_glider", + "leaves_glider", + "sand_raptor_wings", + "snow_raptor_wings", + "wood_raptor_wings", + "moonlit_love_glider", + "horizon_glider", + "winter_wings", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/instruments.ron b/assets/common/items/recipes/instruments.ron new file mode 100644 index 0000000000..c8903d8142 --- /dev/null +++ b/assets/common/items/recipes/instruments.ron @@ -0,0 +1,20 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "double_bass", + "flute", + "guitar", + "lyre", + "kalimba", + "lute", + "melodica", + "washboard", + "sitar", + "icy_talharpa", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/potions.ron b/assets/common/items/recipes/potions.ron new file mode 100644 index 0000000000..ce11966021 --- /dev/null +++ b/assets/common/items/recipes/potions.ron @@ -0,0 +1,14 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "potion_combustion", + "potion_agility", + "potion_minor", + "potion_medium", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/unique/abyssal_gorget.ron b/assets/common/items/recipes/unique/abyssal_gorget.ron new file mode 100644 index 0000000000..caa838f0eb --- /dev/null +++ b/assets/common/items/recipes/unique/abyssal_gorget.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "abyssal_gorget", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/unique/mindflayer_spellbag.ron b/assets/common/items/recipes/unique/mindflayer_spellbag.ron new file mode 100644 index 0000000000..2b6dbc4138 --- /dev/null +++ b/assets/common/items/recipes/unique/mindflayer_spellbag.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "mindflayer_spellbag", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/unique/polaris.ron b/assets/common/items/recipes/unique/polaris.ron new file mode 100644 index 0000000000..acf3baa848 --- /dev/null +++ b/assets/common/items/recipes/unique/polaris.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "polaris", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/unique/seashell_necklace.ron b/assets/common/items/recipes/unique/seashell_necklace.ron new file mode 100644 index 0000000000..25dae4bec3 --- /dev/null +++ b/assets/common/items/recipes/unique/seashell_necklace.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "seashell_necklace", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/unique/troll_hide_pack.ron b/assets/common/items/recipes/unique/troll_hide_pack.ron new file mode 100644 index 0000000000..b4b620229e --- /dev/null +++ b/assets/common/items/recipes/unique/troll_hide_pack.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "troll_hide_pack", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/unique/winged_coronet.ron b/assets/common/items/recipes/unique/winged_coronet.ron new file mode 100644 index 0000000000..5fcbfcaa67 --- /dev/null +++ b/assets/common/items/recipes/unique/winged_coronet.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "winged_coronet", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/utility.ron b/assets/common/items/recipes/utility.ron new file mode 100644 index 0000000000..b98c84a61e --- /dev/null +++ b/assets/common/items/recipes/utility.ron @@ -0,0 +1,15 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "collar_basic", + "velorite_frag", + "lockpick", + "gold_ingot", + "silver_ingot", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/weapons/bamboo.ron b/assets/common/items/recipes/weapons/bamboo.ron new file mode 100644 index 0000000000..25fbcaaf6b --- /dev/null +++ b/assets/common/items/recipes/weapons/bamboo.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "bamboo_weapons", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/weapons/bloodsteel.ron b/assets/common/items/recipes/weapons/bloodsteel.ron new file mode 100644 index 0000000000..889f9688f0 --- /dev/null +++ b/assets/common/items/recipes/weapons/bloodsteel.ron @@ -0,0 +1,12 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "bloodsteel_ingot", + "bloodsteel_weapons", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/weapons/cobalt.ron b/assets/common/items/recipes/weapons/cobalt.ron new file mode 100644 index 0000000000..e6ba527911 --- /dev/null +++ b/assets/common/items/recipes/weapons/cobalt.ron @@ -0,0 +1,12 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "cobalt_ingot", + "cobalt_weapons", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/weapons/eldwood.ron b/assets/common/items/recipes/weapons/eldwood.ron new file mode 100644 index 0000000000..09377c5c15 --- /dev/null +++ b/assets/common/items/recipes/weapons/eldwood.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "eldwood_weapons", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/weapons/frostwood.ron b/assets/common/items/recipes/weapons/frostwood.ron new file mode 100644 index 0000000000..981c965f80 --- /dev/null +++ b/assets/common/items/recipes/weapons/frostwood.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "frostwood_weapons", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/weapons/hardwood.ron b/assets/common/items/recipes/weapons/hardwood.ron new file mode 100644 index 0000000000..617b9e6a6d --- /dev/null +++ b/assets/common/items/recipes/weapons/hardwood.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "hardwood_weapons", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/weapons/iron.ron b/assets/common/items/recipes/weapons/iron.ron new file mode 100644 index 0000000000..894afbae67 --- /dev/null +++ b/assets/common/items/recipes/weapons/iron.ron @@ -0,0 +1,12 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "iron_ingot", + "iron_weapons", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/weapons/ironwood.ron b/assets/common/items/recipes/weapons/ironwood.ron new file mode 100644 index 0000000000..ace59d633d --- /dev/null +++ b/assets/common/items/recipes/weapons/ironwood.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "ironwood_weapons", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/weapons/orichalcum.ron b/assets/common/items/recipes/weapons/orichalcum.ron new file mode 100644 index 0000000000..00c2bc57a1 --- /dev/null +++ b/assets/common/items/recipes/weapons/orichalcum.ron @@ -0,0 +1,12 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "orichalcum_ingot", + "orichalcum_weapons", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/weapons/steel.ron b/assets/common/items/recipes/weapons/steel.ron new file mode 100644 index 0000000000..56bf84e143 --- /dev/null +++ b/assets/common/items/recipes/weapons/steel.ron @@ -0,0 +1,12 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "steel_ingot", + "steel_weapons", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/loot_tables/dungeon/adlet/chest.ron b/assets/common/loot_tables/dungeon/adlet/chest.ron index 8aadf90ad5..c7d4d2c27f 100644 --- a/assets/common/loot_tables/dungeon/adlet/chest.ron +++ b/assets/common/loot_tables/dungeon/adlet/chest.ron @@ -14,4 +14,14 @@ (2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)), // Food (1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 1, 3)), + // Recipes + (0.2, Item("common.items.recipes.explosives")), + (1.0, Item("common.items.recipes.utility")), + (1.0, Item("common.items.recipes.equipment.basic")), + (1.0, Item("common.items.recipes.potions")), + (1.0, Item("common.items.recipes.armor.silken")), + (1.0, Item("common.items.recipes.armor.steel")), + (1.0, Item("common.items.recipes.armor.scale")), + (1.0, Item("common.items.recipes.weapons.steel")), + (1.0, Item("common.items.recipes.weapons.hardwood")), ] diff --git a/assets/common/loot_tables/dungeon/cultist/chest.ron b/assets/common/loot_tables/dungeon/cultist/chest.ron index 6508db2140..a150bbd6ec 100644 --- a/assets/common/loot_tables/dungeon/cultist/chest.ron +++ b/assets/common/loot_tables/dungeon/cultist/chest.ron @@ -4,7 +4,7 @@ // Gear (0.25, LootTable("common.loot_tables.weapons.cultist")), (0.25, LootTable("common.loot_tables.armor.cultist")), - (0.25, LootTable("common.loot_tables.weapons.cave")), + (0.25, LootTable("common.loot_tables.weapons.cave")), // Currency (3.0, MultiDrop(Item("common.items.utility.coins"), 1000, 2000)), // Consumables @@ -12,8 +12,12 @@ (0.1, MultiDrop(Item("common.items.food.spore_corruption"), 1, 3)), // Food (1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 3, 6)), - // Ingredients - (0.75, MultiDrop(Item("common.items.crafting_ing.alkahest"), 1, 3)), + // Ingredients + (0.75, MultiDrop(Item("common.items.crafting_ing.alkahest"), 1, 3)), + // Recipe + (1.0, Item("common.items.recipes.unique.abyssal_gorget")), + (1.0, Item("common.items.recipes.unique.mindflayer_spellbag")), + (1.0, Item("common.items.recipes.equipment.advanced")), ]), Lottery([ (0.6, Nothing), diff --git a/assets/common/loot_tables/dungeon/gnarling/chest.ron b/assets/common/loot_tables/dungeon/gnarling/chest.ron index 130b2a5a61..885ad69ab3 100644 --- a/assets/common/loot_tables/dungeon/gnarling/chest.ron +++ b/assets/common/loot_tables/dungeon/gnarling/chest.ron @@ -12,4 +12,12 @@ (2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)), // Food (1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 1, 2)), + // Recipes + (1.0, Item("common.items.recipes.armor.iron")), + (1.0, Item("common.items.recipes.armor.woolen")), + (1.0, Item("common.items.recipes.armor.leather")), + (1.0, Item("common.items.recipes.weapons.iron")), + (1.0, Item("common.items.recipes.weapons.bamboo")), + (1.0, Item("common.items.recipes.food")), + (1.0, Item("common.items.recipes.utility")), ] diff --git a/assets/common/loot_tables/dungeon/haniwa/chest.ron b/assets/common/loot_tables/dungeon/haniwa/chest.ron index 10df9fafd8..6e6321c0bc 100644 --- a/assets/common/loot_tables/dungeon/haniwa/chest.ron +++ b/assets/common/loot_tables/dungeon/haniwa/chest.ron @@ -11,6 +11,17 @@ (2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)), // Food (1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 2, 4)), + // Recipe + (0.2, Item("common.items.recipes.explosives")), + (1.0, Item("common.items.recipes.instruments")), + (1.0, Item("common.items.recipes.charms")), + (1.0, Item("common.items.recipes.potions")), + (1.0, Item("common.items.recipes.equipment.moderate")), + (1.0, Item("common.items.recipes.armor.bloodsteel")), + (1.0, Item("common.items.recipes.armor.moonweave")), + (1.0, Item("common.items.recipes.armor.primal")), + (1.0, Item("common.items.recipes.weapons.bloodsteel")), + (1.0, Item("common.items.recipes.weapons.frostwood")), ]), Lottery([ (0.7, Nothing), diff --git a/assets/common/loot_tables/dungeon/myrmidon/chest.ron b/assets/common/loot_tables/dungeon/myrmidon/chest.ron index 76bb691f3a..b5db2d6f55 100644 --- a/assets/common/loot_tables/dungeon/myrmidon/chest.ron +++ b/assets/common/loot_tables/dungeon/myrmidon/chest.ron @@ -12,6 +12,18 @@ (2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 4, 8)), // Food (1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 2, 5)), + // Recipes + (0.2, Item("common.items.recipes.explosives")), + (1.0, Item("common.items.recipes.instruments")), + (1.0, Item("common.items.recipes.potions")), + (1.0, Item("common.items.recipes.gliders")), + (0.5, Item("common.items.recipes.equipment.moderate")), + (0.5, Item("common.items.recipes.equipment.advanced")), + (1.0, Item("common.items.recipes.armor.orichalcum")), + (1.0, Item("common.items.recipes.armor.sunsilk")), + (1.0, Item("common.items.recipes.armor.dragonscale")), + (1.0, Item("common.items.recipes.weapons.orichalcum")), + (1.0, Item("common.items.recipes.weapons.eldwood")), ]), Lottery([ (0.6, Nothing), diff --git a/assets/common/loot_tables/dungeon/sahagin/chest.ron b/assets/common/loot_tables/dungeon/sahagin/chest.ron index f3f367e6f2..1469bc1de5 100644 --- a/assets/common/loot_tables/dungeon/sahagin/chest.ron +++ b/assets/common/loot_tables/dungeon/sahagin/chest.ron @@ -11,6 +11,19 @@ (2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)), // Food (1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 1, 4)), + // Recipes + (0.2, Item("common.items.recipes.explosives")), + (1.0, Item("common.items.recipes.charms")), + (1.0, Item("common.items.recipes.instruments")), + (1.0, Item("common.items.recipes.potions")), + (1.0, Item("common.items.recipes.utility")), + (0.5, Item("common.items.recipes.equipment.basic")), + (0.5, Item("common.items.recipes.equipment.moderate")), + (1.0, Item("common.items.recipes.armor.cobalt")), + (1.0, Item("common.items.recipes.armor.druid")), + (1.0, Item("common.items.recipes.armor.carapace")), + (1.0, Item("common.items.recipes.weapons.cobalt")), + (1.0, Item("common.items.recipes.weapons.ironwood")), ]), Lottery([ (0.8, Nothing), diff --git a/assets/common/loot_tables/dungeon/sea_chapel/chest_coral.ron b/assets/common/loot_tables/dungeon/sea_chapel/chest_coral.ron index 0e56b3018f..a8cb2c2b23 100644 --- a/assets/common/loot_tables/dungeon/sea_chapel/chest_coral.ron +++ b/assets/common/loot_tables/dungeon/sea_chapel/chest_coral.ron @@ -4,6 +4,8 @@ // Ingredients (2.0, Item("common.items.crafting_ing.coral_branch")), (0.5, Item("common.items.crafting_ing.pearl")), + (0.25, Item("common.items.recipes.unique.seashell_necklace")), + (0.25, Item("common.items.recipes.unique.winged_coronet")), ]), Lottery([ (0.6, Nothing), diff --git a/assets/common/loot_tables/dungeon/sea_chapel/sea_bishop.ron b/assets/common/loot_tables/dungeon/sea_chapel/sea_bishop.ron index dc706cc739..fcb6f2a6f6 100644 --- a/assets/common/loot_tables/dungeon/sea_chapel/sea_bishop.ron +++ b/assets/common/loot_tables/dungeon/sea_chapel/sea_bishop.ron @@ -1,4 +1,4 @@ [ (1.0, MultiDrop(Item("common.items.crafting_ing.brinestone"), 3, 6)), (1.0, MultiDrop(Item("common.items.crafting_ing.pearl"), 1, 2)), -] \ No newline at end of file +] diff --git a/assets/common/loot_tables/dungeon/sea_chapel/sea_cleric.ron b/assets/common/loot_tables/dungeon/sea_chapel/sea_cleric.ron index 931cf783c6..51247523b8 100644 --- a/assets/common/loot_tables/dungeon/sea_chapel/sea_cleric.ron +++ b/assets/common/loot_tables/dungeon/sea_chapel/sea_cleric.ron @@ -1,4 +1,3 @@ [ - // Nothing - (1.0, Nothing), -] \ No newline at end of file + (1.0, Item("common.items.recipes.armor.brinestone")), +] diff --git a/assets/common/loot_tables/humanoids/grim_salvager.ron b/assets/common/loot_tables/humanoids/grim_salvager.ron index cd1d959272..92254f507e 100644 --- a/assets/common/loot_tables/humanoids/grim_salvager.ron +++ b/assets/common/loot_tables/humanoids/grim_salvager.ron @@ -14,4 +14,7 @@ (0.4, Item("common.items.armor.misc.head.bandana.red")), (0.4, Item("common.items.armor.misc.head.hood")), (0.4, Item("common.items.armor.misc.head.hood_dark")), -] \ No newline at end of file + // Recipes + (2.0, Item("common.items.recipes.equipment.basic")), + (1.0, Item("common.items.recipes.equipment.moderate")), +] diff --git a/assets/common/loot_tables/humanoids/humanoid.ron b/assets/common/loot_tables/humanoids/humanoid.ron index 97718d5c58..d947631c95 100644 --- a/assets/common/loot_tables/humanoids/humanoid.ron +++ b/assets/common/loot_tables/humanoids/humanoid.ron @@ -14,4 +14,4 @@ // Weapons (1.0, LootTable("common.loot_tables.weapons.starter")), (1.0, LootTable("common.loot_tables.weapons.tier-0")), -] \ No newline at end of file +] diff --git a/assets/common/loot_tables/humanoids/pirate.ron b/assets/common/loot_tables/humanoids/pirate.ron index 3b148a5546..5d7dabfaa1 100644 --- a/assets/common/loot_tables/humanoids/pirate.ron +++ b/assets/common/loot_tables/humanoids/pirate.ron @@ -17,4 +17,8 @@ // Food (1.0, LootTable("common.loot_tables.food.wild_ingredients")), (0.5, Item("common.items.food.meat.fish_raw")), -] \ No newline at end of file + // Recipes + (2.0, Item("common.items.recipes.equipment.basic")), + (1.0, Item("common.items.recipes.equipment.moderate")), + (0.5, Item("common.items.recipes.equipment.advanced")), +] diff --git a/assets/common/loot_tables/humanoids/witch.ron b/assets/common/loot_tables/humanoids/witch.ron index 34b6a8d0df..91db15f01a 100644 --- a/assets/common/loot_tables/humanoids/witch.ron +++ b/assets/common/loot_tables/humanoids/witch.ron @@ -5,4 +5,5 @@ (0.5, LootTable("common.loot_tables.weapons.tier-3")), (0.1, LootTable("common.loot_tables.weapons.tier-4")), (0.05, Item("common.items.armor.witch.hat")), -] \ No newline at end of file + (0.1, Item("common.items.recipes.unique.troll_hide_pack")), +] diff --git a/assets/common/loot_tables/sprite/chest-buried.ron b/assets/common/loot_tables/sprite/chest-buried.ron index d5b7a55c25..def2f90c5b 100644 --- a/assets/common/loot_tables/sprite/chest-buried.ron +++ b/assets/common/loot_tables/sprite/chest-buried.ron @@ -1,4 +1,5 @@ [ (1.0, LootTable("common.loot_tables.weapons.components.tier-1")), (1.0, LootTable("common.loot_tables.armor.cloth")), -] \ No newline at end of file + (0.5, Item("common.items.recipes.explosives")), +] diff --git a/assets/common/loot_tables/sprite/chest.ron b/assets/common/loot_tables/sprite/chest.ron index f44e90a59f..0c1737ad16 100644 --- a/assets/common/loot_tables/sprite/chest.ron +++ b/assets/common/loot_tables/sprite/chest.ron @@ -2,4 +2,12 @@ (1.0, LootTable("common.loot_tables.weapons.components.tier-0")), (1.0, LootTable("common.loot_tables.weapons.components.tier-1")), (1.0, LootTable("common.loot_tables.armor.cloth")), -] \ No newline at end of file + (0.2, Item("common.items.recipes.explosives")), + (0.5, Item("common.items.recipes.instruments")), + (0.2, Item("common.items.recipes.charms")), + (1.0, Item("common.items.recipes.food")), + (0.3, Item("common.items.recipes.gliders")), + (0.5, Item("common.items.recipes.potions")), + (1.0, Item("common.items.recipes.utility")), + (0.5, Item("common.items.recipes.equipment.basic")), +] diff --git a/assets/common/loot_tables/sprite/crate.ron b/assets/common/loot_tables/sprite/crate.ron index 8336ee9b25..35254daf3f 100644 --- a/assets/common/loot_tables/sprite/crate.ron +++ b/assets/common/loot_tables/sprite/crate.ron @@ -2,4 +2,7 @@ (2.0, LootTable("common.loot_tables.materials.common")), (3.0, LootTable("common.loot_tables.food.prepared")), (1.0, Item("common.items.armor.misc.head.straw")), -] \ No newline at end of file + (0.5, Item("common.items.recipes.instruments")), + (1.0, Item("common.items.recipes.food")), + (1.0, Item("common.items.recipes.utility")), +] diff --git a/assets/common/loot_tables/world/world_bosses/gigas_frost/boss.ron b/assets/common/loot_tables/world/world_bosses/gigas_frost/boss.ron index 01dd3a74e7..44a1b0cb0a 100644 --- a/assets/common/loot_tables/world/world_bosses/gigas_frost/boss.ron +++ b/assets/common/loot_tables/world/world_bosses/gigas_frost/boss.ron @@ -1,4 +1,5 @@ [ (1.0, LootTable("common.loot_tables.armor.boreal")), (1.0, Item("common.items.crafting_ing.glacial_crystal")), + (1.0, Item("common.items.recipes.unique.polaris")), ] \ No newline at end of file diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book_manifest.ron similarity index 99% rename from assets/common/recipe_book.ron rename to assets/common/recipe_book_manifest.ron index 4a07e17a7f..babab7a4ba 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book_manifest.ron @@ -1,5 +1,5 @@ { - "crafting_hammer": ( + "craftsman_hammer": ( output: ("common.items.tool.craftsman_hammer", 1), inputs: [ (Item("common.items.log.wood"), 1, false), @@ -50,7 +50,7 @@ ], craft_sprite: Some(Cauldron), ), - "potion_s": ( + "potion_minor": ( output: ("common.items.consumable.potion_minor", 1), inputs: [ (Item("common.items.crafting_ing.empty_vial"), 1, false), @@ -59,7 +59,7 @@ ], craft_sprite: Some(Cauldron), ), - "potion_m": ( + "potion_medium": ( output: ("common.items.consumable.potion_med", 1), inputs: [ (Item("common.items.consumable.potion_minor"), 2, false), @@ -329,14 +329,6 @@ ], craft_sprite: Some(Forge), ), - /* Only for Christmas event so comment out once done - "diamonds": ( - output: ("common.items.mineral.gem.diamond", 1), - inputs: [ - (Item("common.items.mineral.ore.coal"), 20, false), - ], - craft_sprite: Some(Forge), - ), */ "cotton": ( output: ("common.items.crafting_ing.cloth.cotton", 1), inputs: [ diff --git a/assets/common/trading/item_price_calculation.ron b/assets/common/trading/item_price_calculation.ron index 8397802523..8b1e8d2bdf 100644 --- a/assets/common/trading/item_price_calculation.ron +++ b/assets/common/trading/item_price_calculation.ron @@ -23,6 +23,10 @@ loot_tables: [ // Food Ingredients (20.375, true, "common.trading.food"), + // Recipes + (1.0, true, "common.trading.sellable_recipe"), + (1.0, false, "common.trading.unsellable_recipe"), + // Potions // // crafted from food, no need to duplicate it here. @@ -46,4 +50,5 @@ good_scaling: [ (Armor, 0.025), // common.items.armor.misc.pants.worker_blue (Tools, 0.015487), // common.items.weapons.staff.starter_staff (Ingredients, 0.034626), // common.items.crafting_ing.leather_scraps + (Recipe, 0.01), // common.items.recipes ]) diff --git a/assets/common/trading/sellable_recipe.ron b/assets/common/trading/sellable_recipe.ron new file mode 100644 index 0000000000..5d0f3ad91c --- /dev/null +++ b/assets/common/trading/sellable_recipe.ron @@ -0,0 +1,17 @@ +// Loot table that exists purely for price rationalisation +// +// This loot table should be marked as sellable. +// +// Please keep it sorted by rarity so it's easier to reason about things +[ + // Recipes + (1.0, Item("common.items.recipes.food")), + (1.0, Item("common.items.recipes.utility")), + (1.0, Item("common.items.recipes.equipment.basic")), + (1.0, Item("common.items.recipes.potions")), + (1.0, Item("common.items.recipes.armor.iron")), + (1.0, Item("common.items.recipes.armor.woolen")), + (1.0, Item("common.items.recipes.armor.leather")), + (1.0, Item("common.items.recipes.weapons.iron")), + (1.0, Item("common.items.recipes.weapons.bamboo")), +] diff --git a/assets/common/trading/unsellable_recipe.ron b/assets/common/trading/unsellable_recipe.ron new file mode 100644 index 0000000000..c2966dec8d --- /dev/null +++ b/assets/common/trading/unsellable_recipe.ron @@ -0,0 +1,45 @@ +// Loot table that exists purely for price rationalisation +// +// This loot table should be marked as unsellable. +// +// Please keep it sorted by rarity so it's easier to reason about things +[ + // Recipes + // Misc Groups + (1.0, Item("common.items.recipes.charms")), + (1.0, Item("common.items.recipes.explosives")), + (1.0, Item("common.items.recipes.gliders")), + (1.0, Item("common.items.recipes.instruments")), + // Equipment + (1.0, Item("common.items.recipes.equipment.moderate")), + (0.2, Item("common.items.recipes.equipment.advanced")), + // Armors + (0.7, Item("common.items.recipes.armor.steel")), + (0.7, Item("common.items.recipes.armor.silken")), + (0.7, Item("common.items.recipes.armor.scale")), + (0.7, Item("common.items.recipes.weapons.steel")), + (0.7, Item("common.items.recipes.weapons.hardwood")), + (0.4, Item("common.items.recipes.armor.cobalt")), + (0.4, Item("common.items.recipes.armor.druid")), + (0.4, Item("common.items.recipes.armor.carapace")), + (0.4, Item("common.items.recipes.weapons.cobalt")), + (0.4, Item("common.items.recipes.weapons.ironwood")), + (0.2, Item("common.items.recipes.armor.bloodsteel")), + (0.2, Item("common.items.recipes.armor.moonweave")), + (0.2, Item("common.items.recipes.armor.primal")), + (0.2, Item("common.items.recipes.weapons.bloodsteel")), + (0.2, Item("common.items.recipes.weapons.frostwood")), + (0.1, Item("common.items.recipes.armor.orichalcum")), + (0.1, Item("common.items.recipes.armor.sunsilk")), + (0.1, Item("common.items.recipes.armor.dragonscale")), + (0.1, Item("common.items.recipes.armor.brinestone")), + (0.1, Item("common.items.recipes.weapons.orichalcum")), + (0.1, Item("common.items.recipes.weapons.eldwood")), + // Unique items + (1.0, Item("common.items.recipes.unique.seashell_necklace")), + (0.8, Item("common.items.recipes.unique.winged_coronet")), + (0.5, Item("common.items.recipes.unique.troll_hide_pack")), + (0.3, Item("common.items.recipes.unique.abyssal_gorget")), + (0.1, Item("common.items.recipes.unique.mindflayer_spellbag")), + (0.1, Item("common.items.recipes.unique.polaris")), +] diff --git a/assets/voxygen/i18n/en/common.ftl b/assets/voxygen/i18n/en/common.ftl index 43bee53758..f1e7eb60ec 100644 --- a/assets/voxygen/i18n/en/common.ftl +++ b/assets/voxygen/i18n/en/common.ftl @@ -86,6 +86,7 @@ common-kind-throwable = Can be thrown common-kind-utility = Utility common-kind-ingredient = Ingredient common-kind-lantern = Lantern +common-kind-recipegroup = Recipes common-hands-one = One-Handed common-hands-two = Two-Handed common-rand_appearance = Random appearance diff --git a/assets/voxygen/i18n/en/hud/crafting.ftl b/assets/voxygen/i18n/en/hud/crafting.ftl index 081a0be97b..97cd38586b 100644 --- a/assets/voxygen/i18n/en/hud/crafting.ftl +++ b/assets/voxygen/i18n/en/hud/crafting.ftl @@ -49,3 +49,5 @@ hud-crafting-mod_comp_sec_slot_title = Animal Material hud-crafting-mod_comp_sec_slot_desc = Optionally place an animal crafting ingredient, only certain ingredients can be used to augment weapons. hud-crafting-repair_slot_title = Damaged Item hud-crafting-repair_slot_desc = Place an item here to see the cost of repairing it at its current durability level. +hud-crafting-recipe-uncraftable = Recipe Cannot be Crafted +hud-crafting-recipe-unlearned = You must first learn how to craft this recipe. diff --git a/assets/voxygen/i18n/en/item/recipe.ftl b/assets/voxygen/i18n/en/item/recipe.ftl new file mode 100644 index 0000000000..f31f7d513b --- /dev/null +++ b/assets/voxygen/i18n/en/item/recipe.ftl @@ -0,0 +1,86 @@ +recipe-armor-bloodsteel = Bloodsteel Armor Recipes + .desc = Bloodsteel Armor Recipes +recipe-armor-brinestone = Brinestone Armor Recipes + .desc = Brinestone Armor Recipes +recipe-armor-carapace = Carapace Armor Recipes + .desc = Carapace Armor Recipes +recipe-armor-cobalt = Cobalt Armor Recipes + .desc = Cobalt Armor Recipes +recipe-armor-dragonscale = Dragonscale Armor Recipes + .desc = Dragonscale Armor Recipes +recipe-armor-druid = Druid Armor Recipes + .desc = Druid Armor Recipes +recipe-armor-iron = Iron Armor Recipes + .desc = Iron Armor Recipes +recipe-armor-leather = Leather Armor Recipes + .desc = Leather Armor Recipes +recipe-armor-moonweave = Moonweave Armor Recipes + .desc = Moonweave Armor Recipes +recipe-armor-orichalcum = Orichalcum Armor Recipes + .desc = Orichalcum Armor Recipes +recipe-armor-primal = Primal Armor Recipes + .desc = Primal Armor Recipes +recipe-armor-scale = Scale Armor Recipes + .desc = Scale Armor Recipes +recipe-armor-silken = Silken Armor Recipes + .desc = Silken Armor Recipes +recipe-armor-steel = Steel Armor Recipes + .desc = Steel Armor Recipes +recipe-armor-sunsilk = Sunsilk Armor Recipes + .desc = Sunsilk Armor Recipes +recipe-armor-woolen = Woolen Armor Recipes + .desc = Woolen Armor Recipes +recipe-equipment-basic = Basic Equipment Recipes + .desc = Basic Equipment Recipes +recipe-equipment-moderate = Moderate Equipment Recipes + .desc = Moderate Equipment Recipes +recipe-equipment-advanced = Advanced Equipment Recipes + .desc = Advanced Equipment Recipes +recipe-unique-abyssal_gorget = Abyssal Gorget Recipe + .desc = Abyssal Gorget Recipe +recipe-unique-mindflayer_spellbag = Mindflayer Spellbag Recipe + .desc = Mindflayer Spellbag Recipe +recipe-unique-polaris = Polaris Recipe + .desc = Polaris Recipe +recipe-unique-seashell_necklace = Seashell Necklace Recipe + .desc = Seashell Necklace Recipe +recipe-unique-troll_hide_pack = Troll Hide Pack Recipe + .desc = Troll Hide Pack Recipe +recipe-unique-winged_coronet = Winged Coronet Recipe + .desc = Winged Coronet Recipe +recipe-charms = Charms Recipes + .desc = Charms Recipes +recipe-explosives = Explosives Recipes + .desc = Explosives Recipes +recipe-food = Food Recipes + .desc = Food Recipes +recipe-gliders = Gliders Recipes + .desc = Gliders Recipes +recipe-instruments = Instruments Recipes + .desc = Instruments Recipes +recipe-potions = Potions Recipes + .desc = Potions Recipes +recipe-utility = Utility Recipes + .desc = Utility Recipes +recipe-weapons-iron = Iron Weapon Recipes + .desc = Iron Weapon Recipes +recipe-weapons-steel = Steel Weapon Recipes + .desc = Steel Weapon Recipes +recipe-weapons-cobalt = Cobalt Weapon Recipes + .desc = Cobalt Weapon Recipes +recipe-weapons-bloodsteel = Bloodsteel Weapon Recipes + .desc = Bloodsteel Weapon Recipes +recipe-weapons-orichalcum = Orichalcum Weapon Recipes + .desc = Orichalcum Weapon Recipes +recipe-weapons-bamboo = Bamboo Weapon Recipes + .desc = Bamboo Weapon Recipes +recipe-weapons-hardwood = Hardwood Weapon Recipes + .desc = Hardwood Weapon Recipes +recipe-weapons-ironwood = Ironwood Weapon Recipes + .desc = Ironwood Weapon Recipes +recipe-weapons-frostwood = Frostwood Weapon Recipes + .desc = Frostwood Weapon Recipes +recipe-weapons-eldwood = Eldwood Weapon Recipes + .desc = Eldwood Weapon Recipes +recipe-default = Default Recipes + .desc = Default Recipes \ No newline at end of file diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index f5f9ad62f3..359c50b284 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -1708,6 +1708,174 @@ "voxel.object.collar", (0.1, 0.0, 0.0), (-60.0, 20.0, 10.0), 0.9, ), + Simple("common.items.recipes.potions"): VoxTrans( + "voxel.object.recipe_alchemy", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.explosives"): VoxTrans( + "voxel.object.recipe_alchemy", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.charms"): VoxTrans( + "voxel.object.recipe_alchemy", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.iron"): VoxTrans( + "voxel.object.recipe_blacksmithing", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.steel"): VoxTrans( + "voxel.object.recipe_blacksmithing", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.cobalt"): VoxTrans( + "voxel.object.recipe_blacksmithing", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.bloodsteel"): VoxTrans( + "voxel.object.recipe_blacksmithing", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.orichalcum"): VoxTrans( + "voxel.object.recipe_blacksmithing", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.unique.polaris"): VoxTrans( + "voxel.object.recipe_blacksmithing", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.weapons.iron"): VoxTrans( + "voxel.object.recipe_blacksmithing", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.weapons.steel"): VoxTrans( + "voxel.object.recipe_blacksmithing", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.weapons.cobalt"): VoxTrans( + "voxel.object.recipe_blacksmithing", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.weapons.bloodsteel"): VoxTrans( + "voxel.object.recipe_blacksmithing", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.weapons.orichalcum"): VoxTrans( + "voxel.object.recipe_blacksmithing", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.gliders"): VoxTrans( + "voxel.object.recipe_carpentry", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.instruments"): VoxTrans( + "voxel.object.recipe_carpentry", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.weapons.bamboo"): VoxTrans( + "voxel.object.recipe_carpentry", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.weapons.hardwood"): VoxTrans( + "voxel.object.recipe_carpentry", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.weapons.ironwood"): VoxTrans( + "voxel.object.recipe_carpentry", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.weapons.frostwood"): VoxTrans( + "voxel.object.recipe_carpentry", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.weapons.eldwood"): VoxTrans( + "voxel.object.recipe_carpentry", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.food"): VoxTrans( + "voxel.object.recipe_cooking", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.brinestone"): VoxTrans( + "voxel.object.recipe_lapidary", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.unique.abyssal_gorget"): VoxTrans( + "voxel.object.recipe_lapidary", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.unique.seashell_necklace"): VoxTrans( + "voxel.object.recipe_lapidary", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.leather"): VoxTrans( + "voxel.object.recipe_leatherworking", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.scale"): VoxTrans( + "voxel.object.recipe_leatherworking", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.carapace"): VoxTrans( + "voxel.object.recipe_leatherworking", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.primal"): VoxTrans( + "voxel.object.recipe_leatherworking", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.dragonscale"): VoxTrans( + "voxel.object.recipe_leatherworking", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.unique.mindflayer_spellbag"): VoxTrans( + "voxel.object.recipe_leatherworking", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.unique.troll_hide_pack"): VoxTrans( + "voxel.object.recipe_leatherworking", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.unique.winged_coronet"): VoxTrans( + "voxel.object.recipe_leatherworking", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.woolen"): VoxTrans( + "voxel.object.recipe_weaving", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.silken"): VoxTrans( + "voxel.object.recipe_weaving", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.druid"): VoxTrans( + "voxel.object.recipe_weaving", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.moonweave"): VoxTrans( + "voxel.object.recipe_weaving", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.armor.sunsilk"): VoxTrans( + "voxel.object.recipe_weaving", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.utility"): VoxTrans( + "voxel.object.scroll", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.equipment.basic"): VoxTrans( + "voxel.object.scroll", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.equipment.moderate"): VoxTrans( + "voxel.object.scroll", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), + Simple("common.items.recipes.equipment.advanced"): VoxTrans( + "voxel.object.scroll", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), // Armor // Starter Parts Simple("common.items.armor.misc.foot.sandals"): VoxTrans( diff --git a/assets/voxygen/voxel/item_drop_manifest.ron b/assets/voxygen/voxel/item_drop_manifest.ron index fb843bb54f..3940ef308d 100644 --- a/assets/voxygen/voxel/item_drop_manifest.ron +++ b/assets/voxygen/voxel/item_drop_manifest.ron @@ -418,6 +418,14 @@ // Other Simple("common.items.utility.coins"): "voxel.object.v-coin", Simple("common.items.utility.collar"): "voxel.object.collar", + Recipe("recipe_alchemy"): "voxel.object.recipe_alchemy", + Recipe("recipe_blacksmithing"): "voxel.object.recipe_blacksmithing", + Recipe("recipe_carpentry"): "voxel.object.recipe_carpentry", + Recipe("recipe_cooking"): "voxel.object.recipe_cooking", + Recipe("recipe_lapidary"): "voxel.object.recipe_lapidary", + Recipe("recipe_leatherworking"): "voxel.object.recipe_leatherworking", + Recipe("recipe_weaving"): "voxel.object.recipe_weaving", + Recipe("scroll"): "voxel.object.scroll", // Armor // Starter Parts Simple("common.items.armor.misc.foot.sandals"): "voxel.armor.misc.foot.cloth_sandal", diff --git a/assets/voxygen/voxel/object/recipe_alchemy.vox b/assets/voxygen/voxel/object/recipe_alchemy.vox new file mode 100644 index 0000000000..9e4d828b59 --- /dev/null +++ b/assets/voxygen/voxel/object/recipe_alchemy.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c676f8a806894692fb1dc1e7c352698ea9de968cf9a2ba88aacf901cb5a93b0 +size 2000 diff --git a/assets/voxygen/voxel/object/recipe_blacksmithing.vox b/assets/voxygen/voxel/object/recipe_blacksmithing.vox new file mode 100644 index 0000000000..ee83936e1d --- /dev/null +++ b/assets/voxygen/voxel/object/recipe_blacksmithing.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcee3d4ef0290a58f9363c0400b50397f4765a9c64044ecce9dccfe2c6f4d5f0 +size 2060 diff --git a/assets/voxygen/voxel/object/recipe_carpentry.vox b/assets/voxygen/voxel/object/recipe_carpentry.vox new file mode 100644 index 0000000000..f00dbd67fb --- /dev/null +++ b/assets/voxygen/voxel/object/recipe_carpentry.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b2a2558b9c4fc0311bdcd93e761b1bc99a9429e91dff47f9e6af34c84ce1308 +size 2000 diff --git a/assets/voxygen/voxel/object/recipe_cooking.vox b/assets/voxygen/voxel/object/recipe_cooking.vox new file mode 100644 index 0000000000..917c63954c --- /dev/null +++ b/assets/voxygen/voxel/object/recipe_cooking.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72a82fc13cffad0ab785aec4f2dc5dabfb3b1405cf495a6a63fdb91ce49ce5f2 +size 2024 diff --git a/assets/voxygen/voxel/object/recipe_lapidary.vox b/assets/voxygen/voxel/object/recipe_lapidary.vox new file mode 100644 index 0000000000..90577e200c --- /dev/null +++ b/assets/voxygen/voxel/object/recipe_lapidary.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bbb1d827b5003f1fd70679a85023402eebe916eb6f622ef8cdddb11485528d56 +size 1992 diff --git a/assets/voxygen/voxel/object/recipe_leatherworking.vox b/assets/voxygen/voxel/object/recipe_leatherworking.vox new file mode 100644 index 0000000000..aeb1b6d1ee --- /dev/null +++ b/assets/voxygen/voxel/object/recipe_leatherworking.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:09a0529e44a3a1615444cdf3d94fbb13e784606e5c815cca83a3e4dafd28ee5d +size 2004 diff --git a/assets/voxygen/voxel/object/recipe_weaving.vox b/assets/voxygen/voxel/object/recipe_weaving.vox new file mode 100644 index 0000000000..d6ee164e4f --- /dev/null +++ b/assets/voxygen/voxel/object/recipe_weaving.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b990ad1556bbe473263d88a8ec7eaf18b504ee7222db73b2b0fbabb9c428ac2 +size 1976 diff --git a/assets/voxygen/voxel/object/scroll.vox b/assets/voxygen/voxel/object/scroll.vox new file mode 100644 index 0000000000..7be3b78f8e --- /dev/null +++ b/assets/voxygen/voxel/object/scroll.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb7d54277c8f4d632bdf54513c0f9fb19ff0cb3a110b38d6405e7a88d46172eb +size 23504 diff --git a/assets/voxygen/voxel/object_manifest.ron b/assets/voxygen/voxel/object_manifest.ron index 525e3b8106..347b478a5a 100644 --- a/assets/voxygen/voxel/object_manifest.ron +++ b/assets/voxygen/voxel/object_manifest.ron @@ -1131,4 +1131,14 @@ central: ("armor.empty"), ) ), + Scroll: ( + bone0: ( + offset: (0.0, 0.0, 0.0), + central: ("object.recipe"), + ), + bone1: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), }) diff --git a/client/src/lib.rs b/client/src/lib.rs index c700e5d61a..cbe8668eb0 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -37,7 +37,7 @@ use common::{ lod, mounting::{Rider, VolumePos, VolumeRider}, outcome::Outcome, - recipe::{ComponentRecipeBook, RecipeBook, RepairRecipeBook}, + recipe::{ComponentRecipeBook, RecipeBookManifest, RepairRecipeBook}, resources::{GameMode, PlayerEntity, Time, TimeOfDay}, shared_server_config::ServerConstants, spiral::Spiral2d, @@ -278,7 +278,6 @@ pub struct Client { possible_starting_sites: Vec, pois: Vec, pub chat_mode: ChatMode, - recipe_book: RecipeBook, component_recipe_book: ComponentRecipeBook, repair_recipe_book: RepairRecipeBook, available_recipes: HashMap>, @@ -692,6 +691,7 @@ impl Client { *state.ecs_mut().write_resource() = PlayerEntity(Some(entity)); state.ecs_mut().insert(material_stats); state.ecs_mut().insert(ability_map); + state.ecs_mut().insert(recipe_book); let map_size = map_size_lg.chunks(); let max_height = world_map.max_height; @@ -953,7 +953,6 @@ impl Client { world_map.sites, world_map.possible_starting_sites, world_map.pois, - recipe_book, component_recipe_book, repair_recipe_book, max_group_size, @@ -972,7 +971,6 @@ impl Client { sites, possible_starting_sites, pois, - recipe_book, component_recipe_book, repair_recipe_book, max_group_size, @@ -1019,7 +1017,6 @@ impl Client { .collect(), possible_starting_sites, pois, - recipe_book, component_recipe_book, repair_recipe_book, available_recipes: HashMap::default(), @@ -1492,8 +1489,6 @@ impl Client { pub fn world_data(&self) -> &WorldData { &self.world_data } - pub fn recipe_book(&self) -> &RecipeBook { &self.recipe_book } - pub fn component_recipe_book(&self) -> &ComponentRecipeBook { &self.component_recipe_book } pub fn repair_recipe_book(&self) -> &RepairRecipeBook { &self.repair_recipe_book } @@ -1508,21 +1503,6 @@ impl Client { /// entity does not have a position. pub fn set_lod_pos_fallback(&mut self, pos: Vec2) { self.lod_pos_fallback = Some(pos); } - /// Returns whether the specified recipe can be crafted and the sprite, if - /// any, that is required to do so. - pub fn can_craft_recipe(&self, recipe: &str, amount: u32) -> (bool, Option) { - self.recipe_book - .get(recipe) - .zip(self.inventories().get(self.entity())) - .map(|(recipe, inv)| { - ( - recipe.inventory_contains_ingredients(inv, amount).is_ok(), - recipe.craft_sprite, - ) - }) - .unwrap_or((false, None)) - } - pub fn craft_recipe( &mut self, recipe: &str, @@ -1530,8 +1510,20 @@ impl Client { craft_sprite: Option<(VolumePos, SpriteKind)>, amount: u32, ) -> bool { - let (can_craft, required_sprite) = self.can_craft_recipe(recipe, amount); - let has_sprite = required_sprite.map_or(true, |s| Some(s) == craft_sprite.map(|(_, s)| s)); + let (can_craft, has_sprite) = if let Some(inventory) = self + .state + .ecs() + .read_storage::() + .get(self.entity()) + { + let rbm = self.state.ecs().read_resource::(); + let (can_craft, required_sprite) = inventory.can_craft_recipe(recipe, 1, &rbm); + let has_sprite = + required_sprite.map_or(true, |s| Some(s) == craft_sprite.map(|(_, s)| s)); + (can_craft, has_sprite) + } else { + (false, false) + }; if can_craft && has_sprite { self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent( InventoryEvent::CraftRecipe { @@ -1680,19 +1672,22 @@ impl Client { } fn update_available_recipes(&mut self) { - self.available_recipes = self - .recipe_book - .iter() - .map(|(name, _)| name.clone()) - .filter_map(|name| { - let (can_craft, required_sprite) = self.can_craft_recipe(&name, 1); - if can_craft { - Some((name, required_sprite)) - } else { - None - } - }) - .collect(); + let rbm = self.state.ecs().read_resource::(); + let inventories = self.state.ecs().read_storage::(); + if let Some(inventory) = inventories.get(self.entity()) { + self.available_recipes = inventory + .recipes_iter() + .cloned() + .filter_map(|name| { + let (can_craft, required_sprite) = inventory.can_craft_recipe(&name, 1, &rbm); + if can_craft { + Some((name, required_sprite)) + } else { + None + } + }) + .collect(); + } } /// Unstable, likely to be removed in a future release @@ -2844,6 +2839,9 @@ impl Client { ServerGeneral::SpectatePosition(pos) => { frontend_events.push(Event::SpectatePosition(pos)); }, + ServerGeneral::UpdateRecipes => { + self.update_available_recipes(); + }, _ => unreachable!("Not a in_game message"), } Ok(()) diff --git a/common/Cargo.toml b/common/Cargo.toml index a75f4aa4a3..9e649bf9ca 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -10,6 +10,7 @@ hot-reloading = ["common-assets/hot-reloading"] simd = ["vek/platform_intrinsics"] bin_csv = ["ron", "csv", "clap"] bin_graphviz = ["petgraph", "clap"] +bin_recipe_gen = ["ron"] bin_cmd_doc_gen = [] bin_asset_migrate = ["ron"] rrt_pathfinding = ["kiddo"] diff --git a/common/net/src/msg/server.rs b/common/net/src/msg/server.rs index 5715a15d85..66d58e2ab1 100644 --- a/common/net/src/msg/server.rs +++ b/common/net/src/msg/server.rs @@ -10,7 +10,7 @@ use common::{ event::{PluginHash, UpdateCharacterMetadata}, lod, outcome::Outcome, - recipe::{ComponentRecipeBook, RecipeBook, RepairRecipeBook}, + recipe::{ComponentRecipeBook, RecipeBookManifest, RepairRecipeBook}, resources::{Time, TimeOfDay, TimeScale}, shared_server_config::ServerConstants, terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, @@ -68,7 +68,7 @@ pub enum ServerInit { max_group_size: u32, client_timeout: Duration, world_map: crate::msg::world_msg::WorldMapMsg, - recipe_book: RecipeBook, + recipe_book: RecipeBookManifest, component_recipe_book: ComponentRecipeBook, repair_recipe_book: RepairRecipeBook, material_stats: MaterialStatManifest, @@ -222,6 +222,9 @@ pub enum ServerGeneral { SpectatePosition(Vec3), /// Plugin data requested from the server PluginData(Vec), + /// Update the list of available recipes. Usually called after a new recipe + /// is acquired + UpdateRecipes, } impl ServerGeneral { @@ -345,7 +348,8 @@ impl ServerMsg { | ServerGeneral::MapMarker(_) | ServerGeneral::WeatherUpdate(_) | ServerGeneral::LocalWindUpdate(_) - | ServerGeneral::SpectatePosition(_) => { + | ServerGeneral::SpectatePosition(_) + | ServerGeneral::UpdateRecipes => { c_type == ClientType::Game && presence.is_some() }, // Always possible diff --git a/common/src/bin/csv_import/main.rs b/common/src/bin/csv_import/main.rs index 83be0d513c..f2ac169592 100644 --- a/common/src/bin/csv_import/main.rs +++ b/common/src/bin/csv_import/main.rs @@ -6,7 +6,7 @@ use clap::Parser; use hashbrown::HashMap; use ron::ser::{to_string_pretty, PrettyConfig}; use serde::Serialize; -use std::{error::Error, fs::File, io::Write}; +use std::{borrow::Cow, error::Error, fs::File, io::Write}; use veloren_common::{ assets::ASSETS_PATH, @@ -79,9 +79,9 @@ fn armor_stats() -> Result<(), Box> { if let Ok(ref record) = record { if item.item_definition_id() - == ItemDefinitionId::Simple( + == ItemDefinitionId::Simple(Cow::Borrowed( record.get(headers["Path"]).expect("No file path in csv?"), - ) + )) { let protection = if let Some(protection_raw) = record.get(headers["Protection"]) { @@ -287,9 +287,9 @@ fn weapon_stats() -> Result<(), Box> { if let ItemKind::Tool(tool) = &*item.kind() { if let Ok(ref record) = record { if item.item_definition_id() - == ItemDefinitionId::Simple( + == ItemDefinitionId::Simple(Cow::Borrowed( record.get(headers["Path"]).expect("No file path in csv?"), - ) + )) { let kind = tool.kind; let equip_time_secs: f32 = record diff --git a/common/src/bin/recipe_graphviz.rs b/common/src/bin/recipe_graphviz.rs index 89ca057d82..09b2c83c95 100644 --- a/common/src/bin/recipe_graphviz.rs +++ b/common/src/bin/recipe_graphviz.rs @@ -5,13 +5,12 @@ use petgraph::{ }; use std::{fs::File, io::Write}; use veloren_common::{ - assets::AssetExt, comp::item::ItemDesc, - recipe::{RecipeBook, RecipeInput}, + recipe::{RecipeBookManifest, RecipeInput}, }; fn main() { - let recipes = RecipeBook::load_expect_cloned("common.recipe_book"); + let recipes = RecipeBookManifest::load().read(); let mut graph = Graph::new(); let mut nodes = HashMap::new(); let mut add_node = |graph: &mut Graph<_, _>, node: &str| { diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 80157817ef..a5b96c135a 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -6,7 +6,9 @@ use crate::{ AdminRole as Role, Skill, }, generation::try_all_entity_configs, - npc, terrain, + npc, + recipe::RecipeBookManifest, + terrain, }; use hashbrown::HashMap; use lazy_static::lazy_static; @@ -137,6 +139,10 @@ lazy_static! { .iter() .map(|o| o.to_string().to_string()) .collect(); + static ref RECIPES: Vec = { + let rbm = RecipeBookManifest::load().cloned(); + rbm.keys().cloned().collect::>() + }; static ref TIMES: Vec = [ "midnight", "night", "dawn", "morning", "day", "noon", "dusk" ] @@ -378,6 +384,7 @@ pub enum ServerChatCommand { ReloadChunks, RemoveLights, RepairEquipment, + ResetRecipes, Respawn, RevokeBuild, RevokeBuildAll, @@ -734,6 +741,7 @@ impl ServerChatCommand { "Reloads chunks loaded on the server", Some(Admin), ), + ServerChatCommand::ResetRecipes => cmd(vec![], "Resets your recipe book", Some(Admin)), ServerChatCommand::RemoveLights => cmd( vec![Float("radius", 20.0, Optional)], "Removes all lights spawned by players", @@ -1030,6 +1038,7 @@ impl ServerChatCommand { ServerChatCommand::PermitBuild => "permit_build", ServerChatCommand::Players => "players", ServerChatCommand::Portal => "portal", + ServerChatCommand::ResetRecipes => "reset_recipes", ServerChatCommand::Region => "region", ServerChatCommand::ReloadChunks => "reload_chunks", ServerChatCommand::RemoveLights => "remove_lights", diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index d9403a46e0..73f6d16687 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -125,6 +125,7 @@ make_case_elim!( BubbleBomb = 110, IronPikeBomb = 111, Lavathrower = 112, + Scroll = 113, } ); @@ -135,7 +136,7 @@ impl Body { } } -pub const ALL_OBJECTS: [Body; 113] = [ +pub const ALL_OBJECTS: [Body; 114] = [ Body::Arrow, Body::Bomb, Body::Scarecrow, @@ -249,6 +250,7 @@ pub const ALL_OBJECTS: [Body; 113] = [ Body::BubbleBomb, Body::IronPikeBomb, Body::Lavathrower, + Body::Scroll, ]; impl From for super::Body { @@ -371,6 +373,7 @@ impl Body { Body::SurpriseEgg => "surprise_egg", Body::BubbleBomb => "bubble_bomb", Body::IronPikeBomb => "iron_pike_bomb", + Body::Scroll => "recipe", } } @@ -411,6 +414,7 @@ impl Body { Body::TrainingDummy => 2000.0, Body::Snowball => 0.9 * WATER_DENSITY, Body::Pebble => 1000.0, + Body::Scroll => 0.5 * WATER_DENSITY, // let them sink _ => 1.1 * WATER_DENSITY, }; @@ -462,7 +466,7 @@ impl Body { | Body::ChestOpen | Body::ChestSkull | Body::ChestVines => 100.0, - Body::Coins => 1.0, + Body::Coins | Body::Scroll => 1.0, Body::CraftingBench => 100.0, Body::Crate => 50.0, Body::Crossbow => 200.0, diff --git a/common/src/comp/inventory/item/item_key.rs b/common/src/comp/inventory/item/item_key.rs index 6af01d7b43..ac116c60af 100644 --- a/common/src/comp/inventory/item/item_key.rs +++ b/common/src/comp/inventory/item/item_key.rs @@ -5,6 +5,7 @@ use crate::{ use serde::{Deserialize, Serialize}; use std::sync::Arc; +/// ItemKey should only be used for front-end identification purposes #[derive(Clone, Debug, Serialize, Deserialize, Hash, Eq, PartialEq)] pub enum ItemKey { Simple(String), diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 2e2e170baf..0ece16619d 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -366,6 +366,9 @@ pub enum ItemKind { /// through item_ids: Vec, }, + RecipeGroup { + recipes: Vec, + }, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -374,6 +377,7 @@ pub enum ConsumableKind { Food, ComplexFood, Charm, + Recipe, } impl ItemKind { @@ -404,6 +408,7 @@ impl ItemKind { #[allow(deprecated)] ItemKind::Ingredient { descriptor } => format!("Ingredient: {}", descriptor), ItemKind::TagExamples { item_ids } => format!("TagExamples: {:?}", item_ids), + ItemKind::RecipeGroup { .. } => String::from("Recipes:"), } } @@ -418,7 +423,8 @@ impl ItemKind { | ItemKind::Throwable { .. } | ItemKind::Utility { .. } | ItemKind::Ingredient { .. } - | ItemKind::TagExamples { .. } => false, + | ItemKind::TagExamples { .. } + | ItemKind::RecipeGroup { .. } => false, } } } @@ -582,10 +588,7 @@ impl Serialize for ItemBase { where S: Serializer, { - serializer.serialize_str(match self { - ItemBase::Simple(item_def) => &item_def.item_definition_id, - ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(), - }) + serializer.serialize_str(&self.serialization_item_id()) } } @@ -609,13 +612,7 @@ impl<'de> Deserialize<'de> for ItemBase { where E: de::Error, { - Ok( - if serialized_item_base.starts_with(crate::modular_item_id_prefix!()) { - ItemBase::Modular(ModularBase::load_from_pseudo_id(serialized_item_base)) - } else { - ItemBase::Simple(Arc::::load_expect_cloned(serialized_item_base)) - }, - ) + Ok(ItemBase::from_item_id_string(serialized_item_base)) } } @@ -630,6 +627,23 @@ impl ItemBase { ItemBase::Modular(_) => 0, } } + + // Should be kept the same as the persistence_item_id function in Item + // TODO: Maybe use Cow? + fn serialization_item_id(&self) -> String { + match &self { + ItemBase::Simple(item_def) => item_def.item_definition_id.clone(), + ItemBase::Modular(mod_base) => String::from(mod_base.pseudo_item_id()), + } + } + + fn from_item_id_string(item_id_string: &str) -> Self { + if item_id_string.starts_with(crate::modular_item_id_prefix!()) { + ItemBase::Modular(ModularBase::load_from_pseudo_id(item_id_string)) + } else { + ItemBase::Simple(Arc::::load_expect_cloned(item_id_string)) + } + } } // TODO: could this theorectically hold a ref to the actual components and @@ -637,7 +651,7 @@ impl ItemBase { // `Vec`s) #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum ItemDefinitionId<'a> { - Simple(&'a str), + Simple(Cow<'a, str>), Modular { pseudo_base: &'a str, components: Vec>, @@ -664,7 +678,7 @@ pub enum ItemDefinitionIdOwned { impl ItemDefinitionIdOwned { pub fn as_ref(&self) -> ItemDefinitionId<'_> { match *self { - Self::Simple(ref id) => ItemDefinitionId::Simple(id), + Self::Simple(ref id) => ItemDefinitionId::Simple(Cow::Borrowed(id)), Self::Modular { ref pseudo_base, ref components, @@ -694,7 +708,7 @@ impl<'a> ItemDefinitionId<'a> { pub fn to_owned(&self) -> ItemDefinitionIdOwned { match self { - Self::Simple(id) => ItemDefinitionIdOwned::Simple(String::from(*id)), + Self::Simple(id) => ItemDefinitionIdOwned::Simple(String::from(&**id)), Self::Modular { pseudo_base, components, @@ -969,7 +983,7 @@ impl Item { ) -> Result { let (base, components) = match item_definition_id { ItemDefinitionId::Simple(spec) => { - let base = ItemBase::Simple(Arc::::load_cloned(spec)?); + let base = ItemBase::Simple(Arc::::load_cloned(&spec)?); (base, Vec::new()) }, ItemDefinitionId::Modular { @@ -1023,11 +1037,7 @@ impl Item { /// Creates a new instance of an `Item from the provided asset identifier if /// it exists pub fn new_from_asset(asset: &str) -> Result { - let inner_item = if asset.starts_with("veloren.core.pseudo_items.modular") { - ItemBase::Modular(ModularBase::load_from_pseudo_id(asset)) - } else { - ItemBase::Simple(Arc::::load_cloned(asset)?) - }; + let inner_item = ItemBase::from_item_id_string(asset); // TODO: Get msm and ability_map less hackily let msm = &MaterialStatManifest::load().read(); let ability_map = &AbilityMap::load().read(); @@ -1206,7 +1216,7 @@ impl Item { match &self.item_base { ItemBase::Simple(item_def) => { if self.components.is_empty() { - ItemDefinitionId::Simple(&item_def.item_definition_id) + ItemDefinitionId::Simple(Cow::Borrowed(&item_def.item_definition_id)) } else { ItemDefinitionId::Compound { simple_base: &item_def.item_definition_id, @@ -1388,10 +1398,10 @@ impl Item { pub fn item_hash(&self) -> u64 { self.hash } - pub fn persistence_item_id(&self) -> &str { + pub fn persistence_item_id(&self) -> String { match &self.item_base { - ItemBase::Simple(item_def) => &item_def.item_definition_id, - ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(), + ItemBase::Simple(item_def) => item_def.item_definition_id.clone(), + ItemBase::Modular(mod_base) => String::from(mod_base.pseudo_item_id()), } } @@ -1545,6 +1555,10 @@ impl Item { Err(other) } } + + // Probably doesn't need to be limited to persistence, but nothing else should + // really need to look at item base + pub fn persistence_item_base(&self) -> &ItemBase { &self.item_base } } impl FrontendItem { @@ -1816,7 +1830,7 @@ impl ItemDesc for ItemDef { fn num_slots(&self) -> u16 { self.slots } fn item_definition_id(&self) -> ItemDefinitionId<'_> { - ItemDefinitionId::Simple(&self.item_definition_id) + ItemDefinitionId::Simple(Cow::Borrowed(&self.item_definition_id)) } fn tags(&self) -> Vec { self.tags.to_vec() } diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index f9984d4763..5c5d8996ec 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -15,13 +15,16 @@ use crate::{ MaterialStatManifest, TagExampleInfo, }, loadout::Loadout, + recipe_book::RecipeBook, slot::{EquipSlot, Slot, SlotError}, }, loot_owner::LootOwnerKind, slot::{InvSlotId, SlotId}, Item, }, + recipe::{Recipe, RecipeBookManifest}, resources::Time, + terrain::SpriteKind, uid::Uid, LoadoutBuilder, }; @@ -31,6 +34,7 @@ use super::FrontendItem; pub mod item; pub mod loadout; pub mod loadout_builder; +pub mod recipe_book; pub mod slot; #[cfg(test)] mod test; #[cfg(test)] mod test_helpers; @@ -52,6 +56,8 @@ pub struct Inventory { /// These slots are "remove-only" meaning that during normal gameplay items /// can only be removed from these slots and never entered. overflow_items: Vec, + /// Recipes that are available for use + recipe_book: RecipeBook, } /// Errors which the methods on `Inventory` produce @@ -131,6 +137,7 @@ impl Inventory { loadout, slots: vec![None; DEFAULT_INVENTORY_SLOTS], overflow_items: Vec::new(), + recipe_book: RecipeBook::default(), } } @@ -140,10 +147,16 @@ impl Inventory { loadout, slots: vec![None; 1], overflow_items: Vec::new(), + recipe_book: RecipeBook::default(), } } - /// Total number of slots in the inventory. + pub fn with_recipe_book(mut self, recipe_book: RecipeBook) -> Inventory { + self.recipe_book = recipe_book; + self + } + + /// Total number of slots in in the inventory. pub fn capacity(&self) -> usize { self.slots().count() } /// An iterator of all inventory slots @@ -1085,6 +1098,57 @@ impl Inventory { pub fn persistence_push_overflow_items>(&mut self, overflow_items: I) { self.overflow_items.extend(overflow_items); } + + pub fn recipes_iter(&self) -> impl ExactSizeIterator { self.recipe_book.iter() } + + pub fn available_recipes_iter<'a>( + &'a self, + rbm: &'a RecipeBookManifest, + ) -> impl Iterator + '_ { + self.recipe_book.get_available_iter(rbm) + } + + pub fn recipe_book_len(&self) -> usize { self.recipe_book.len() } + + pub fn get_recipe<'a>( + &'a self, + recipe_key: &str, + rbm: &'a RecipeBookManifest, + ) -> Option<&Recipe> { + self.recipe_book.get(recipe_key, rbm) + } + + pub fn push_recipe_group(&mut self, recipe_group: Item) -> Result<(), Item> { + self.recipe_book.push_group(recipe_group) + } + + /// Returns whether the specified recipe can be crafted and the sprite, if + /// any, that is required to do so. + pub fn can_craft_recipe( + &self, + recipe_key: &str, + amount: u32, + rbm: &RecipeBookManifest, + ) -> (bool, Option) { + if let Some(recipe) = self.recipe_book.get(recipe_key, rbm) { + ( + recipe.inventory_contains_ingredients(self, amount).is_ok(), + recipe.craft_sprite, + ) + } else { + (false, None) + } + } + + pub fn recipe_is_known(&self, recipe_key: &str) -> bool { + self.recipe_book.is_known(recipe_key) + } + + pub fn reset_recipes(&mut self) { self.recipe_book.reset(); } + + pub fn persistence_recipes_iter_with_index(&self) -> impl Iterator { + self.recipe_book.persistence_recipes_iter_with_index() + } } impl Component for Inventory { diff --git a/common/src/comp/inventory/recipe_book.rs b/common/src/comp/inventory/recipe_book.rs new file mode 100644 index 0000000000..38b72a6a90 --- /dev/null +++ b/common/src/comp/inventory/recipe_book.rs @@ -0,0 +1,116 @@ +use crate::{ + comp::item::{Item, ItemKind}, + recipe::{Recipe, RecipeBookManifest}, +}; +use hashbrown::HashSet; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct RecipeBook { + recipe_groups: Vec, + recipes: HashSet, +} + +impl RecipeBook { + pub(super) fn get<'a>( + &'a self, + recipe_key: &str, + rbm: &'a RecipeBookManifest, + ) -> Option<&Recipe> { + if self.recipes.iter().any(|r| r == recipe_key) { + rbm.get(recipe_key) + } else { + None + } + } + + pub(super) fn len(&self) -> usize { self.recipes.len() } + + pub(super) fn iter(&self) -> impl ExactSizeIterator { self.recipes.iter() } + + pub(super) fn get_available_iter<'a>( + &'a self, + rbm: &'a RecipeBookManifest, + ) -> impl Iterator + '_ { + self.recipes + .iter() + .filter_map(|recipe: &String| rbm.get(recipe).map(|rbm_recipe| (recipe, rbm_recipe))) + } + + pub(super) fn reset(&mut self) { + self.recipe_groups.clear(); + self.recipes.clear(); + } + + /// Pushes a group of recipes to the recipe book. If group already exists + /// return the recipe group. + pub(super) fn push_group(&mut self, group: Item) -> Result<(), Item> { + if self + .recipe_groups + .iter() + .any(|rg| rg.item_definition_id() == group.item_definition_id()) + { + Err(group) + } else { + self.recipe_groups.push(group); + self.update(); + Ok(()) + } + } + + /// Syncs recipes hashset with recipe_groups vec + pub(super) fn update(&mut self) { + self.recipe_groups.iter().for_each(|group| { + if let ItemKind::RecipeGroup { recipes } = &*group.kind() { + self.recipes.extend(recipes.iter().map(String::from)) + } + }) + } + + pub fn recipe_book_from_persistence(recipe_groups: Vec) -> Self { + let mut book = Self { + recipe_groups, + recipes: HashSet::new(), + }; + book.update(); + book + } + + pub fn persistence_recipes_iter_with_index(&self) -> impl Iterator { + self.recipe_groups.iter().enumerate() + } + + pub(super) fn is_known(&self, recipe_key: &str) -> bool { self.recipes.contains(recipe_key) } +} + +#[cfg(test)] +mod tests { + use crate::{ + comp::item::{Item, ItemKind}, + recipe::{complete_recipe_book, default_component_recipe_book}, + }; + + fn valid_recipe(recipe: &str) -> bool { + let recipe_book = complete_recipe_book(); + let component_recipe_book = default_component_recipe_book(); + + recipe_book.read().keys().any(|key| key == recipe) + || component_recipe_book + .read() + .iter() + .any(|(_, cr)| cr.recipe_book_key == recipe) + } + + /// Verify that all recipes in recipe items point to a valid recipe + #[test] + fn validate_recipes() { + let groups = Item::new_from_asset_glob("common.items.recipes.*") + .expect("The directory should exist"); + for group in groups { + let ItemKind::RecipeGroup { recipes } = &*group.kind() else { + panic!("Expected item to be of kind RecipeGroup") + }; + assert!(recipes.iter().all(|r| valid_recipe(r))); + } + } +} diff --git a/common/src/comp/inventory/test.rs b/common/src/comp/inventory/test.rs index 268ea14c84..fcfd1b7e2d 100644 --- a/common/src/comp/inventory/test.rs +++ b/common/src/comp/inventory/test.rs @@ -5,6 +5,7 @@ use crate::comp::{ Item, }; use lazy_static::lazy_static; +use std::borrow::Cow; lazy_static! { static ref TEST_ITEMS: Vec = vec![Item::new_from_asset_expect( "common.items.debug.admin_stick" @@ -24,6 +25,7 @@ fn push_full() { .collect(), loadout: LoadoutBuilder::empty().build(), overflow_items: vec![], + recipe_book: RecipeBook::default(), }; assert_eq!( inv.push(TEST_ITEMS[0].duplicate(ability_map, msm)) @@ -45,6 +47,7 @@ fn push_all_full() { .collect(), loadout: LoadoutBuilder::empty().build(), overflow_items: vec![], + recipe_book: RecipeBook::default(), }; let Error::Full(leftovers) = inv .push_all( @@ -76,6 +79,7 @@ fn push_unique_all_full() { .collect(), loadout: LoadoutBuilder::empty().build(), overflow_items: vec![], + recipe_book: RecipeBook::default(), }; inv.push_all_unique( TEST_ITEMS @@ -96,6 +100,7 @@ fn push_all_empty() { slots: vec![None, None], loadout: LoadoutBuilder::empty().build(), overflow_items: vec![], + recipe_book: RecipeBook::default(), }; inv.push_all( TEST_ITEMS @@ -116,6 +121,7 @@ fn push_all_unique_empty() { slots: vec![None, None], loadout: LoadoutBuilder::empty().build(), overflow_items: vec![], + recipe_book: RecipeBook::default(), }; inv.push_all_unique( TEST_ITEMS @@ -339,7 +345,7 @@ fn equip_equipping_smaller_bag_from_last_slot_of_big_bag() { assert_eq!( inv.get(InvSlotId::new(0, 0)).unwrap().item_definition_id(), - ItemDefinitionId::Simple(LARGE_BAG_ID) + ItemDefinitionId::Simple(Cow::Borrowed(LARGE_BAG_ID)) ); assert!(result.is_empty()); } @@ -369,7 +375,7 @@ fn unequip_unequipping_bag_into_its_own_slot_with_no_other_free_slots_returns_on // by itself, the bag is returned to the caller assert_eq!( result[0].item_definition_id(), - ItemDefinitionId::Simple("common.items.testing.test_bag") + ItemDefinitionId::Simple(Cow::Borrowed("common.items.testing.test_bag")) ); } @@ -517,7 +523,7 @@ fn backpack_crash() { ); assert_eq!(18, returned_items.len()); assert_eq!( - ItemDefinitionId::Simple("common.items.armor.misc.back.backpack"), + ItemDefinitionId::Simple(Cow::Borrowed("common.items.armor.misc.back.backpack")), returned_items[0].item_definition_id() ); } diff --git a/common/src/comp/inventory/trade_pricing.rs b/common/src/comp/inventory/trade_pricing.rs index f7089c805c..e219da3f5d 100644 --- a/common/src/comp/inventory/trade_pricing.rs +++ b/common/src/comp/inventory/trade_pricing.rs @@ -9,7 +9,7 @@ use crate::{ tool::AbilityMap, }, lottery::LootSpec, - recipe::{default_component_recipe_book, default_recipe_book, RecipeInput}, + recipe::{complete_recipe_book, default_component_recipe_book, RecipeInput}, trade::Good, }; use assets::AssetReadGuard; @@ -294,7 +294,7 @@ lazy_static! { // Load recipe book (done to check that material is valid for a particular component) //use crate::recipe::ComponentKey; - let recipes = default_recipe_book().read(); + let recipes = complete_recipe_book().read(); recipes .iter() @@ -463,8 +463,9 @@ impl From)>> for ProbabilityFile { #[derive(Debug, Deserialize)] struct TradingPriceFile { + /// Tuple format: (frequency, can_sell, asset_path) pub loot_tables: Vec<(f32, bool, String)>, - // the amount of Good equivalent to the most common item + /// the amount of Good equivalent to the most common item pub good_scaling: Vec<(Good, f32)>, } @@ -628,17 +629,16 @@ impl TradePricing { ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.flowers.") => { Good::Ingredients }, - ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.consumable.") => { Good::Potions }, - ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.food.") => { Good::Food }, - ItemDefinitionIdOwned::Simple(name) if name.as_str() == Self::COIN_ITEM => Good::Coin, - + ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.recipes.") => { + Good::Recipe + }, ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.glider.") => { Good::default() }, @@ -809,7 +809,7 @@ impl TradePricing { // Apply recipe book let mut secondaries: HashMap> = HashMap::new(); - let book = default_recipe_book().read(); + let book = complete_recipe_book().read(); let mut ordered_recipes: Vec = Vec::new(); for (_, recipe) in book.iter() { let (ref asset_path, amount) = recipe.output; diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 492d80c216..0a232f425d 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -80,6 +80,7 @@ pub use self::{ tool::{self, AbilityItem}, FrontendItem, Item, ItemConfig, ItemDrops, PickupItem, }, + recipe_book::RecipeBook, slot, CollectFailedReason, Inventory, InventoryUpdate, InventoryUpdateEvent, }, last::Last, diff --git a/common/src/recipe.rs b/common/src/recipe.rs index bb71fb5fa5..1877af0a13 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -428,15 +428,19 @@ pub fn modular_weapon( } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct RecipeBook { +pub struct RecipeBookManifest { recipes: HashMap, } -impl RecipeBook { +impl RecipeBookManifest { + pub fn load() -> AssetHandle { Self::load_expect("common.recipe_book_manifest") } + pub fn get(&self, recipe: &str) -> Option<&Recipe> { self.recipes.get(recipe) } pub fn iter(&self) -> impl ExactSizeIterator { self.recipes.iter() } + pub fn keys(&self) -> impl ExactSizeIterator { self.recipes.keys() } + pub fn get_available(&self, inv: &Inventory) -> Vec<(String, Recipe)> { self.recipes .iter() @@ -451,8 +455,8 @@ mod tests { use super::*; #[test] - fn default_recipe_valid_key_check() { - let recipe_book = default_recipe_book().read(); + fn complete_recipe_book_valid_key_check() { + let recipe_book = complete_recipe_book().read(); let is_invalid_key = |input: &str| input.chars().any(|c| c.is_uppercase() || c.is_whitespace()); assert!(!recipe_book.iter().any(|(k, _)| is_invalid_key(k))); @@ -517,7 +521,7 @@ impl assets::Asset for ItemList { const EXTENSION: &'static str = "ron"; } -impl assets::Compound for RecipeBook { +impl assets::Compound for RecipeBookManifest { fn load( cache: assets::AnyCache, specifier: &assets::SharedString, @@ -562,7 +566,7 @@ impl assets::Compound for RecipeBook { ) .collect::>()?; - Ok(RecipeBook { recipes }) + Ok(RecipeBookManifest { recipes }) } } @@ -617,6 +621,7 @@ pub struct ComponentKey { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ComponentRecipe { + pub recipe_book_key: String, output: ComponentOutput, material: (RecipeInput, u32), modifier: Option<(RecipeInput, u32)>, @@ -810,6 +815,7 @@ impl<'a> ExactSizeIterator for ComponentRecipeInputsIterator<'a> { #[derive(Clone, Deserialize)] struct RawComponentRecipe { + recipe_book_key: String, output: RawComponentOutput, /// String refers to an item definition id material: (String, u32), @@ -881,7 +887,9 @@ impl assets::Compound for ComponentRecipeBook { .iter() .map(|(input, amount)| input.load_recipe_input().map(|input| (input, *amount))) .collect::, _>>()?; + let recipe_book_key = String::from(&raw_recipe.recipe_book_key); Ok(ComponentRecipe { + recipe_book_key, output, material, modifier, @@ -1112,8 +1120,8 @@ impl assets::Compound for RepairRecipeBook { } } -pub fn default_recipe_book() -> AssetHandle { - RecipeBook::load_expect("common.recipe_book") +pub fn complete_recipe_book() -> AssetHandle { + RecipeBookManifest::load_expect("common.recipe_book_manifest") } pub fn default_component_recipe_book() -> AssetHandle { diff --git a/common/src/states/use_item.rs b/common/src/states/use_item.rs index ac32afde4f..0d851f2e19 100644 --- a/common/src/states/use_item.rs +++ b/common/src/states/use_item.rs @@ -53,7 +53,9 @@ impl CharacterBehavior for Data { let mut update = StateUpdate::from(data); match self.static_data.item_kind { - ItemUseKind::Consumable(ConsumableKind::Drink | ConsumableKind::Charm) => { + ItemUseKind::Consumable( + ConsumableKind::Drink | ConsumableKind::Charm | ConsumableKind::Recipe, + ) => { handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); }, @@ -64,9 +66,9 @@ impl CharacterBehavior for Data { } let use_point = match self.static_data.item_kind { - ItemUseKind::Consumable(ConsumableKind::Drink | ConsumableKind::Food) => { - UsePoint::BuildupUse - }, + ItemUseKind::Consumable( + ConsumableKind::Drink | ConsumableKind::Food | ConsumableKind::Recipe, + ) => UsePoint::BuildupUse, ItemUseKind::Consumable(ConsumableKind::ComplexFood | ConsumableKind::Charm) => { UsePoint::UseRecover }, @@ -198,6 +200,11 @@ impl ItemUseKind { Duration::from_secs_f32(0.8), Duration::from_secs_f32(0.1), ), + Self::Consumable(ConsumableKind::Recipe) => ( + Duration::from_secs_f32(0.0), + Duration::from_secs_f32(0.0), + Duration::from_secs_f32(0.0), + ), } } } diff --git a/common/src/terrain/sprite.rs b/common/src/terrain/sprite.rs index 56ea20e17e..7d2db98ba1 100644 --- a/common/src/terrain/sprite.rs +++ b/common/src/terrain/sprite.rs @@ -39,10 +39,7 @@ pub use self::magic::{Attribute, AttributeError}; use crate::{ attributes, - comp::{ - item::{ItemDefinitionId, ItemDefinitionIdOwned}, - tool::ToolKind, - }, + comp::{item::ItemDefinitionIdOwned, tool::ToolKind}, lottery::LootSpec, make_case_elim, sprites, terrain::Block, @@ -805,27 +802,32 @@ impl SpriteKind { pub fn unlock_condition(&self, cfg: Option) -> UnlockKind { cfg.and_then(|cfg| cfg.unlock) .unwrap_or_else(|| match self { - // Example, do not let this merge with twigs requiring cheese to pick up SpriteKind::CommonLockedChest => UnlockKind::Consumes( - ItemDefinitionId::Simple("common.items.utility.lockpick_0").to_owned(), - ), - SpriteKind::BoneKeyhole => UnlockKind::Consumes( - ItemDefinitionId::Simple("common.items.keys.bone_key").to_owned(), - ), - SpriteKind::HaniwaKeyhole => UnlockKind::Consumes( - ItemDefinitionId::Simple("common.items.keys.haniwa_key").to_owned(), - ), - SpriteKind::GlassKeyhole => UnlockKind::Consumes( - ItemDefinitionId::Simple("common.items.keys.glass_key").to_owned(), - ), - SpriteKind::SahaginKeyhole => UnlockKind::Consumes( - ItemDefinitionId::Simple("common.items.keys.sahagin_key").to_owned(), + ItemDefinitionIdOwned::Simple(String::from("common.items.utility.lockpick_0")), ), + SpriteKind::SahaginKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple( + String::from("common.items.keys.sahagin_key"), + )), + SpriteKind::BoneKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple( + String::from("common.items.keys.bone_key"), + )), + SpriteKind::HaniwaKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple( + String::from("common.items.keys.haniwa_key"), + )), + SpriteKind::GlassKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple( + String::from("common.items.keys.glass_key"), + )), SpriteKind::TerracottaChest => UnlockKind::Consumes( - ItemDefinitionId::Simple("common.items.keys.terracotta_key_chest").to_owned(), + ItemDefinitionIdOwned::Simple(String::from( + "common.items.keys.terracotta_key_chest", + )) + .to_owned(), ), SpriteKind::TerracottaKeyhole => UnlockKind::Consumes( - ItemDefinitionId::Simple("common.items.keys.terracotta_key_door").to_owned(), + ItemDefinitionIdOwned::Simple(String::from( + "common.items.keys.terracotta_key_door", + )) + .to_owned(), ), _ => UnlockKind::Free, }) @@ -865,11 +867,9 @@ pub enum UnlockKind { Free, /// The sprite requires that the opening character has a given item in their /// inventory - // TODO: use ItemKey here? Requires(ItemDefinitionIdOwned), /// The sprite will consume the given item from the opening character's /// inventory - // TODO: use ItemKey here? Consumes(ItemDefinitionIdOwned), } diff --git a/common/src/trade.rs b/common/src/trade.rs index 3b3ca0f7eb..9c4d9afa5c 100644 --- a/common/src/trade.rs +++ b/common/src/trade.rs @@ -337,6 +337,7 @@ pub enum Good { Potions, Coin, // exchange material across sites RoadSecurity, + Recipe, } impl Default for Good { @@ -351,7 +352,7 @@ impl Good { match self { Good::Tools | Good::Armor => 0.5, Good::Food | Good::Potions | Good::Ingredients => 0.75, - Good::Coin => 1.0, + Good::Coin | Good::Recipe => 1.0, // Certain abstract goods (like Territory) shouldn't be attached to concrete items; // give a sale price of 0 if the player is trying to sell a concrete item that somehow // has one of these categories diff --git a/server/src/character_creator.rs b/server/src/character_creator.rs index 2d24d1ae4f..0a784957ea 100644 --- a/server/src/character_creator.rs +++ b/server/src/character_creator.rs @@ -55,6 +55,7 @@ pub fn create_character( .active_offhand(character_offhand.map(|x| Item::new_from_asset_expect(&x))) .build(); let mut inventory = Inventory::with_loadout_humanoid(loadout); + let stats = Stats::new(character_alias.to_string(), body); let skill_set = SkillSet::default(); // Default items for new characters @@ -66,6 +67,9 @@ pub fn create_character( inventory .push(Item::new_from_asset_expect("common.items.food.cheese")) .expect("Inventory has at least 1 slot left!"); + inventory + .push_recipe_group(Item::new_from_asset_expect("common.items.recipes.default")) + .expect("New inventory should not already have default recipe group."); let map_marker = None; diff --git a/server/src/client.rs b/server/src/client.rs index 6df53d894e..436426fba4 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -193,7 +193,8 @@ impl Client { | ServerGeneral::MapMarker(_) | ServerGeneral::WeatherUpdate(_) | ServerGeneral::LocalWindUpdate(_) - | ServerGeneral::SpectatePosition(_) => { + | ServerGeneral::SpectatePosition(_) + | ServerGeneral::UpdateRecipes => { PreparedMsg::new(2, &g, &self.in_game_stream_params) }, // Terrain diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 00b5fc16cc..5850f34a87 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -184,6 +184,7 @@ fn do_command( ServerChatCommand::PermitBuild => handle_permit_build, ServerChatCommand::Players => handle_players, ServerChatCommand::Portal => handle_spawn_portal, + ServerChatCommand::ResetRecipes => handle_reset_recipes, ServerChatCommand::Region => handle_region, ServerChatCommand::ReloadChunks => handle_reload_chunks, ServerChatCommand::RemoveLights => handle_remove_lights, @@ -3364,6 +3365,27 @@ fn handle_group_promote( } } +fn handle_reset_recipes( + server: &mut Server, + _client: EcsEntity, + target: EcsEntity, + _args: Vec, + action: &ServerChatCommand, +) -> CmdResult<()> { + if let Some(mut inventory) = server + .state + .ecs() + .write_storage::() + .get_mut(target) + { + inventory.reset_recipes(); + server.notify_client(target, ServerGeneral::UpdateRecipes); + Ok(()) + } else { + Err(Content::Plain(action.help_string())) + } +} + fn handle_region( server: &mut Server, client: EcsEntity, diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 36bf473dd2..0e2a5d2788 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -23,9 +23,7 @@ use common::{ }, event_emitters, mounting::VolumePos, - recipe::{ - self, default_component_recipe_book, default_recipe_book, default_repair_recipe_book, - }, + recipe::{self, default_component_recipe_book, default_repair_recipe_book, RecipeBookManifest}, resources::{ProgramTime, Time}, terrain::{Block, SpriteKind}, trade::Trades, @@ -85,6 +83,7 @@ pub struct InventoryManipData<'a> { program_time: ReadExpect<'a, ProgramTime>, ability_map: ReadExpect<'a, AbilityMap>, msm: ReadExpect<'a, MaterialStatManifest>, + rbm: ReadExpect<'a, RecipeBookManifest>, inventories: WriteStorage<'a, comp::Inventory>, items: WriteStorage<'a, comp::PickupItem>, inventory_updates: WriteStorage<'a, comp::InventoryUpdate>, @@ -610,6 +609,25 @@ impl ServerEvent for InventoryManipEvent { Some(InventoryUpdateEvent::Used) }, + ItemKind::RecipeGroup { .. } => { + match inventory.push_recipe_group(item) { + Ok(()) => { + if let Some(client) = data.clients.get(entity) { + client.send_fallible( + ServerGeneral::UpdateRecipes, + ); + } + Some(InventoryUpdateEvent::Used) + }, + Err(item) => { + inventory.insert_or_stack_at(slot, item).expect( + "slot was just vacated of item, so it \ + definitely fits there.", + ); + None + }, + } + }, _ => { inventory.insert_or_stack_at(slot, item).expect( "slot was just vacated of item, so it definitely fits \ @@ -861,7 +879,6 @@ impl ServerEvent for InventoryManipEvent { } => { use comp::controller::CraftEvent; use recipe::ComponentKey; - let recipe_book = default_recipe_book().read(); let get_craft_sprite = |sprite_pos: Option| { sprite_pos @@ -902,32 +919,38 @@ impl ServerEvent for InventoryManipEvent { recipe, slots, amount, - } => recipe_book - .get(&recipe) - .filter(|r| { - if let Some(needed_sprite) = r.craft_sprite { - let sprite = get_craft_sprite(craft_sprite); - Some(needed_sprite) == sprite - } else { - true - } - }) - .and_then(|r| { + } => { + let filtered_recipe = inventory + .get_recipe(&recipe, &data.rbm) + .cloned() + .filter(|r| { + if let Some(needed_sprite) = r.craft_sprite { + let sprite = get_craft_sprite(craft_sprite); + Some(needed_sprite) == sprite + } else { + true + } + }); + if let Some(recipe) = filtered_recipe { let items = (0..amount) .filter_map(|_| { - r.craft_simple( - &mut inventory, - slots.clone(), - &data.ability_map, - &data.msm, - ) - .ok() + recipe + .craft_simple( + &mut inventory, + slots.clone(), + &data.ability_map, + &data.msm, + ) + .ok() }) .flatten() .collect::>(); if items.is_empty() { None } else { Some(items) } - }), + } else { + None + } + }, CraftEvent::Salvage(slot) => { let sprite = get_craft_sprite(craft_sprite); if matches!(sprite, Some(SpriteKind::DismantlingBench)) { @@ -981,12 +1004,14 @@ impl ServerEvent for InventoryManipEvent { modifier: modifier.and_then(item_id), }) .filter(|r| { - if let Some(needed_sprite) = r.craft_sprite { + let sprite = if let Some(needed_sprite) = r.craft_sprite { let sprite = get_craft_sprite(craft_sprite); Some(needed_sprite) == sprite } else { true - } + }; + let known = inventory.recipe_is_known(&r.recipe_book_key); + sprite && known }) .and_then(|r| { r.craft_component( diff --git a/server/src/lib.rs b/server/src/lib.rs index 0788fe3c25..724b0c89f3 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -429,6 +429,9 @@ impl Server { let msm = comp::inventory::item::MaterialStatManifest::load().cloned(); state.ecs_mut().insert(msm); + let rbm = common::recipe::RecipeBookManifest::load().cloned(); + state.ecs_mut().insert(rbm); + state.ecs_mut().insert(CharacterLoader::new( Arc::>::clone(&database_settings), )?); diff --git a/server/src/migrations/V60__recipe_book.sql b/server/src/migrations/V60__recipe_book.sql new file mode 100644 index 0000000000..d34e9364e0 --- /dev/null +++ b/server/src/migrations/V60__recipe_book.sql @@ -0,0 +1,32 @@ +CREATE TEMP TABLE _temp_character_recipe_pairings +( + temp_recipe_book_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + character_id INT NOT NULL, + recipe_book_id INT +); + +INSERT +INTO _temp_character_recipe_pairings +SELECT NULL, + i.item_id, + NULL +FROM item i +WHERE i.item_definition_id = 'veloren.core.pseudo_containers.character'; + +UPDATE _temp_character_recipe_pairings +SET recipe_book_id = ((SELECT MAX(entity_id) FROM entity) + temp_recipe_book_id); + +INSERT +INTO entity +SELECT t.recipe_book_id +FROM _temp_character_recipe_pairings t; + +INSERT +INTO item +SELECT t.recipe_book_id, + t.character_id, + 'veloren.core.pseudo_containers.recipe_book', + 1, + 'recipe_book', + '' +FROM _temp_character_recipe_pairings t; \ No newline at end of file diff --git a/server/src/persistence/character.rs b/server/src/persistence/character.rs index 42ca7c6106..e38c3d5cfe 100644 --- a/server/src/persistence/character.rs +++ b/server/src/persistence/character.rs @@ -15,9 +15,9 @@ use crate::{ convert_body_from_database, convert_body_to_database_json, convert_character_from_database, convert_inventory_from_database_items, convert_items_to_database_items, convert_loadout_from_database_items, - convert_skill_groups_to_database, convert_skill_set_from_database, - convert_stats_from_database, convert_waypoint_from_database_json, - convert_waypoint_to_database_json, + convert_recipe_book_from_database_items, convert_skill_groups_to_database, + convert_skill_set_from_database, convert_stats_from_database, + convert_waypoint_from_database_json, convert_waypoint_to_database_json, }, character_loader::{CharacterCreationResult, CharacterDataResult, CharacterListResult}, character_updater::PetPersistenceData, @@ -48,9 +48,11 @@ const INVENTORY_PSEUDO_CONTAINER_DEF_ID: &str = "veloren.core.pseudo_containers. const LOADOUT_PSEUDO_CONTAINER_DEF_ID: &str = "veloren.core.pseudo_containers.loadout"; const OVERFLOW_ITEMS_PSEUDO_CONTAINER_DEF_ID: &str = "veloren.core.pseudo_containers.overflow_items"; +const RECIPE_BOOK_PSEUDO_CONTAINER_DEF_ID: &str = "veloren.core.pseudo_containers.recipe_book"; const INVENTORY_PSEUDO_CONTAINER_POSITION: &str = "inventory"; const LOADOUT_PSEUDO_CONTAINER_POSITION: &str = "loadout"; const OVERFLOW_ITEMS_PSEUDO_CONTAINER_POSITION: &str = "overflow_items"; +const RECIPE_BOOK_PSEUDO_CONTAINER_POSITION: &str = "recipe_book"; const WORLD_PSEUDO_CONTAINER_ID: EntityId = 1; #[derive(Clone, Copy)] @@ -58,6 +60,7 @@ struct CharacterContainers { inventory_container_id: EntityId, loadout_container_id: EntityId, overflow_items_container_id: EntityId, + recipe_book_container_id: EntityId, } /// Load the inventory/loadout @@ -132,6 +135,7 @@ pub fn load_character_data( let loadout_items = load_items(connection, character_containers.loadout_container_id)?; let overflow_items_items = load_items(connection, character_containers.overflow_items_container_id)?; + let recipe_book_items = load_items(connection, character_containers.recipe_book_container_id)?; let mut stmt = connection.prepare_cached( " @@ -284,6 +288,7 @@ pub fn load_character_data( &loadout_items, character_containers.overflow_items_container_id, &overflow_items_items, + &recipe_book_items, )?, waypoint: char_waypoint, pets, @@ -362,10 +367,21 @@ pub fn load_character_list(player_uuid_: &str, connection: &Connection) -> Chara let loadout = convert_loadout_from_database_items(loadout_container_id, &loadout_items)?; + let recipe_book_container_id = get_pseudo_container_id( + connection, + CharacterId(character_data.character_id), + RECIPE_BOOK_PSEUDO_CONTAINER_POSITION, + )?; + + let recipe_book_items = load_items(connection, recipe_book_container_id)?; + + let recipe_book = convert_recipe_book_from_database_items(&recipe_book_items)?; + Ok(CharacterItem { character: char, body: char_body, - inventory: Inventory::with_loadout(loadout, char_body), + inventory: Inventory::with_loadout(loadout, char_body) + .with_recipe_book(recipe_book), location: character_data.waypoint.as_ref().cloned(), }) }) @@ -391,14 +407,16 @@ pub fn create_character( map_marker, } = persisted_components; - // Fetch new entity IDs for character, inventory, loadout, and overflow items - let mut new_entity_ids = get_new_entity_ids(transaction, |next_id| next_id + 4)?; + // Fetch new entity IDs for character, inventory, loadout, overflow items, and + // recipe book + let mut new_entity_ids = get_new_entity_ids(transaction, |next_id| next_id + 5)?; // Create pseudo-container items for character let character_id = new_entity_ids.next().unwrap(); let inventory_container_id = new_entity_ids.next().unwrap(); let loadout_container_id = new_entity_ids.next().unwrap(); let overflow_items_container_id = new_entity_ids.next().unwrap(); + let recipe_book_container_id = new_entity_ids.next().unwrap(); let pseudo_containers = vec![ Item { @@ -433,6 +451,14 @@ pub fn create_character( position: OVERFLOW_ITEMS_PSEUDO_CONTAINER_POSITION.to_owned(), properties: String::new(), }, + Item { + stack_size: 1, + item_id: recipe_book_container_id, + parent_container_item_id: character_id, + item_definition_id: RECIPE_BOOK_PSEUDO_CONTAINER_DEF_ID.to_owned(), + position: RECIPE_BOOK_PSEUDO_CONTAINER_POSITION.to_owned(), + properties: String::new(), + }, ]; let mut stmt = transaction.prepare_cached( @@ -542,6 +568,7 @@ pub fn create_character( &inventory, inventory_container_id, overflow_items_container_id, + recipe_book_container_id, &mut next_id, ); inserts = inserts_; @@ -830,6 +857,11 @@ fn get_pseudo_containers( character_id, OVERFLOW_ITEMS_PSEUDO_CONTAINER_POSITION, )?, + recipe_book_container_id: get_pseudo_container_id( + connection, + character_id, + RECIPE_BOOK_PSEUDO_CONTAINER_POSITION, + )?, }; Ok(character_containers) @@ -1025,6 +1057,7 @@ pub fn update( &inventory, pseudo_containers.inventory_container_id, pseudo_containers.overflow_items_container_id, + pseudo_containers.recipe_book_container_id, &mut next_id, ); upserts = upserts_; @@ -1037,6 +1070,7 @@ pub fn update( Value::from(pseudo_containers.inventory_container_id), Value::from(pseudo_containers.loadout_container_id), Value::from(pseudo_containers.overflow_items_container_id), + Value::from(pseudo_containers.recipe_book_container_id), ]; for it in load_items(transaction, pseudo_containers.inventory_container_id)? { existing_item_ids.push(Value::from(it.item_id)); @@ -1047,6 +1081,9 @@ pub fn update( for it in load_items(transaction, pseudo_containers.overflow_items_container_id)? { existing_item_ids.push(Value::from(it.item_id)); } + for it in load_items(transaction, pseudo_containers.recipe_book_container_id)? { + existing_item_ids.push(Value::from(it.item_id)); + } let non_upserted_items = upserts .iter() diff --git a/server/src/persistence/character/conversions.rs b/server/src/persistence/character/conversions.rs index e4b4c7acaf..cd78197be3 100644 --- a/server/src/persistence/character/conversions.rs +++ b/server/src/persistence/character/conversions.rs @@ -1,25 +1,26 @@ use crate::persistence::{ character::EntityId, - models::{AbilitySets, Character, Item, SkillGroup}, -}; - -use crate::persistence::{ error::PersistenceError, json_models::{ self, CharacterPosition, DatabaseAbilitySet, DatabaseItemProperties, GenericBody, HumanoidBody, }, + models::{AbilitySets, Character, Item, SkillGroup}, }; use common::{ character::CharacterId, comp::{ + body, inventory::{ item::{tool::AbilityMap, Item as VelorenItem, MaterialStatManifest}, loadout::{Loadout, LoadoutError}, loadout_builder::LoadoutBuilder, + recipe_book::RecipeBook, slot::InvSlotId, }, - skillset, Body as CompBody, Waypoint, *, + item, + skillset::{self, skills::Skill, SkillGroupKind, SkillSet}, + ActiveAbilities, Body as CompBody, Inventory, MapMarker, Stats, Waypoint, }, resources::Time, }; @@ -57,6 +58,7 @@ pub fn convert_items_to_database_items( inventory: &Inventory, inventory_container_id: EntityId, overflow_items_container_id: EntityId, + recipe_book_container_id: EntityId, next_id: &mut i64, ) -> Vec { let loadout = inventory @@ -71,6 +73,16 @@ pub fn convert_items_to_database_items( ) }); + let recipe_book = inventory + .persistence_recipes_iter_with_index() + .map(|(i, item)| { + ( + serde_json::to_string(&i) + .expect("failed to serialize index of recipe from recipe book"), + Some(item), + recipe_book_container_id, + ) + }); // Inventory slots. let inventory = inventory.slots_with_id().map(|(pos, item)| { ( @@ -82,12 +94,17 @@ pub fn convert_items_to_database_items( // Use Breadth-first search to recurse into containers/modular weapons to store // their parts - let mut bfs_queue: VecDeque<_> = inventory.chain(loadout).chain(overflow_items).collect(); + let mut bfs_queue: VecDeque<_> = inventory + .chain(loadout) + .chain(overflow_items) + .chain(recipe_book) + .collect(); let mut upserts = Vec::new(); let mut depth = HashMap::new(); depth.insert(inventory_container_id, 0); depth.insert(loadout_container_id, 0); depth.insert(overflow_items_container_id, 0); + depth.insert(recipe_book_container_id, 0); while let Some((position, item, parent_container_item_id)) = bfs_queue.pop_front() { // Construct new items. if let Some(item) = item { @@ -182,7 +199,7 @@ pub fn convert_items_to_database_items( let upsert = ItemModelPair { model: Item { - item_definition_id: String::from(item.persistence_item_id()), + item_definition_id: item.persistence_item_id(), position, parent_container_item_id, item_id, @@ -210,27 +227,27 @@ pub fn convert_body_to_database_json( comp_body: &CompBody, ) -> Result<(&str, String), PersistenceError> { Ok(match comp_body { - Body::Humanoid(body) => ( + CompBody::Humanoid(body) => ( "humanoid", serde_json::to_string(&HumanoidBody::from(body))?, ), - Body::QuadrupedLow(body) => ( + CompBody::QuadrupedLow(body) => ( "quadruped_low", serde_json::to_string(&GenericBody::from(body))?, ), - Body::QuadrupedMedium(body) => ( + CompBody::QuadrupedMedium(body) => ( "quadruped_medium", serde_json::to_string(&GenericBody::from(body))?, ), - Body::QuadrupedSmall(body) => ( + CompBody::QuadrupedSmall(body) => ( "quadruped_small", serde_json::to_string(&GenericBody::from(body))?, ), - Body::BirdMedium(body) => ( + CompBody::BirdMedium(body) => ( "bird_medium", serde_json::to_string(&GenericBody::from(body))?, ), - Body::Crustacean(body) => ( + CompBody::Crustacean(body) => ( "crustacean", serde_json::to_string(&GenericBody::from(body))?, ), @@ -364,6 +381,7 @@ pub fn convert_inventory_from_database_items( loadout_items: &[Item], overflow_items_container_id: i64, overflow_items: &[Item], + recipe_book_items: &[Item], ) -> Result { // Loadout items must be loaded before inventory items since loadout items // provide inventory slots. Since items stored inside loadout items actually @@ -374,7 +392,8 @@ pub fn convert_inventory_from_database_items( let loadout = convert_loadout_from_database_items(loadout_container_id, loadout_items)?; let overflow_items = convert_overflow_items_from_database_items(overflow_items_container_id, overflow_items)?; - let mut inventory = Inventory::with_loadout_humanoid(loadout); + let recipe_book = convert_recipe_book_from_database_items(recipe_book_items)?; + let mut inventory = Inventory::with_loadout_humanoid(loadout).with_recipe_book(recipe_book); let mut item_indices = HashMap::new(); let mut failed_inserts = HashMap::new(); @@ -681,8 +700,8 @@ pub fn convert_body_from_database( // extra fields on its body struct "humanoid" => { let json_model = serde_json::de::from_str::(body_data)?; - CompBody::Humanoid(humanoid::Body { - species: humanoid::ALL_SPECIES + CompBody::Humanoid(body::humanoid::Body { + species: body::humanoid::ALL_SPECIES .get(json_model.species as usize) .ok_or_else(|| { PersistenceError::ConversionError(format!( @@ -691,7 +710,7 @@ pub fn convert_body_from_database( )) })? .to_owned(), - body_type: humanoid::ALL_BODY_TYPES + body_type: body::humanoid::ALL_BODY_TYPES .get(json_model.body_type as usize) .ok_or_else(|| { PersistenceError::ConversionError(format!( @@ -740,7 +759,7 @@ pub fn convert_character_from_database(character: &Character) -> common::charact } } -pub fn convert_stats_from_database(alias: String, body: Body) -> Stats { +pub fn convert_stats_from_database(alias: String, body: CompBody) -> Stats { let mut new_stats = Stats::empty(body); new_stats.name = alias; new_stats @@ -867,3 +886,25 @@ pub fn convert_active_abilities_from_database(ability_sets: &AbilitySets) -> Act }); json_models::active_abilities_from_db_model(ability_sets) } + +pub fn convert_recipe_book_from_database_items( + database_items: &[Item], +) -> Result { + let mut recipes_groups = Vec::new(); + + for db_item in database_items.iter() { + let item = get_item_from_asset(db_item.item_definition_id.as_str())?; + + // NOTE: item id is currently *unique*, so we can store the ID safely. + let comp = item.get_item_id_for_database(); + comp.store(Some(NonZeroU64::try_from(db_item.item_id as u64).map_err( + |_| PersistenceError::ConversionError("Item with zero item_id".to_owned()), + )?)); + + recipes_groups.push(item); + } + + let recipe_book = RecipeBook::recipe_book_from_persistence(recipes_groups); + + Ok(recipe_book) +} diff --git a/server/src/sys/msg/register.rs b/server/src/sys/msg/register.rs index 024920ef01..446c1d7303 100644 --- a/server/src/sys/msg/register.rs +++ b/server/src/sys/msg/register.rs @@ -8,7 +8,7 @@ use crate::{ use common::{ comp::{self, Admin, Player, Stats}, event::{ClientDisconnectEvent, EventBus, MakeAdminEvent}, - recipe::{default_component_recipe_book, default_recipe_book, default_repair_recipe_book}, + recipe::{default_component_recipe_book, default_repair_recipe_book}, resources::TimeOfDay, shared_server_config::ServerConstants, uid::Uid, @@ -50,6 +50,7 @@ pub struct ReadData<'a> { time_of_day: Read<'a, TimeOfDay>, material_stats: ReadExpect<'a, comp::item::MaterialStatManifest>, ability_map: ReadExpect<'a, comp::item::tool::AbilityMap>, + recipe_book: ReadExpect<'a, common::recipe::RecipeBookManifest>, map: ReadExpect<'a, WorldMapMsg>, trackers: TrackedStorages<'a>, #[allow(dead_code)] @@ -352,7 +353,7 @@ impl<'a> System<'a> for Sys { max_group_size: read_data.settings.max_player_group_size, client_timeout: read_data.settings.client_timeout, world_map: (*read_data.map).clone(), - recipe_book: default_recipe_book().cloned(), + recipe_book: (*read_data.recipe_book).clone(), component_recipe_book: default_component_recipe_book().cloned(), repair_recipe_book: default_repair_recipe_book().cloned(), material_stats: (*read_data.material_stats).clone(), diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index a59b472ce5..c418b4d692 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -26,7 +26,7 @@ use common::{ Inventory, }, mounting::VolumePos, - recipe::{ComponentKey, Recipe, RecipeInput}, + recipe::{ComponentKey, Recipe, RecipeBookManifest, RecipeInput}, terrain::SpriteKind, }; use conrod_core::{ @@ -158,6 +158,7 @@ pub struct Crafting<'a> { slot_manager: &'a mut SlotManager, item_imgs: &'a ItemImgs, inventory: &'a Inventory, + rbm: &'a RecipeBookManifest, msm: &'a MaterialStatManifest, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -166,6 +167,7 @@ pub struct Crafting<'a> { } impl<'a> Crafting<'a> { + #[allow(clippy::too_many_arguments)] pub fn new( client: &'a Client, info: &'a HudInfo, @@ -179,6 +181,7 @@ impl<'a> Crafting<'a> { slot_manager: &'a mut SlotManager, item_imgs: &'a ItemImgs, inventory: &'a Inventory, + rbm: &'a RecipeBookManifest, msm: &'a MaterialStatManifest, tooltip_manager: &'a mut TooltipManager, show: &'a mut Show, @@ -197,6 +200,7 @@ impl<'a> Crafting<'a> { tooltip_manager, item_imgs, inventory, + rbm, msm, show, common: widget::CommonBuilder::default(), @@ -617,9 +621,8 @@ impl<'a> Widget for Crafting<'a> { }) }; let mut ordered_recipes: Vec<_> = self - .client - .recipe_book() - .iter() + .inventory + .available_recipes_iter(self.rbm) .filter(|(_, recipe)| match search_filter { SearchFilter::None => { search(&recipe.output.0) @@ -696,7 +699,7 @@ impl<'a> Widget for Crafting<'a> { }); // Recipe list - let recipe_list_length = self.client.recipe_book().iter().len() + pseudo_entries.len(); + let recipe_list_length = self.inventory.recipe_book_len() + pseudo_entries.len(); if state.ids.recipe_list_btns.len() < recipe_list_length { state.update(|state| { state @@ -869,9 +872,8 @@ impl<'a> Widget for Crafting<'a> { { Some((selected_recipe, *modular_recipe)) } else { - self.client - .recipe_book() - .get(selected_recipe) + self.inventory + .get_recipe(selected_recipe, self.rbm) .map(|r| (selected_recipe, r)) } }, @@ -955,7 +957,7 @@ impl<'a> Widget for Crafting<'a> { }; // Output slot, tags, and modular input slots - let (craft_slot_1, craft_slot_2, can_perform) = match recipe_kind { + let (craft_slot_1, craft_slot_2, can_perform, recipe_known) = match recipe_kind { RecipeKind::ModularWeapon | RecipeKind::Component(_) => { if state.ids.craft_slots.len() < 2 { state.update(|s| { @@ -1182,9 +1184,9 @@ impl<'a> Widget for Crafting<'a> { let ability_map = &AbilityMap::load().read(); let msm = &MaterialStatManifest::load().read(); - let output_item = match recipe_kind { + let (output_item, recipe_known) = match recipe_kind { RecipeKind::ModularWeapon => { - if let Some((primary_comp, toolkind, hand_restriction)) = + let item = if let Some((primary_comp, toolkind, hand_restriction)) = primary_slot.item(self.inventory).and_then(|item| { if let ItemKind::ModularComponent( ModularComponent::ToolPrimaryComponent { @@ -1198,8 +1200,7 @@ impl<'a> Widget for Crafting<'a> { } else { None } - }) - { + }) { secondary_slot .item(self.inventory) .filter(|item| { @@ -1223,7 +1224,8 @@ impl<'a> Widget for Crafting<'a> { }) } else { None - } + }; + (item, true) }, RecipeKind::Component(toolkind) => { if let Some(material) = @@ -1240,16 +1242,22 @@ impl<'a> Widget for Crafting<'a> { }, ), }; - self.client.component_recipe_book().get(&component_key).map( - |component_recipe| { - component_recipe.item_output(ability_map, msm) - }, - ) + self.client + .component_recipe_book() + .get(&component_key) + .map(|component_recipe| { + let item = component_recipe.item_output(ability_map, msm); + let learned = self + .inventory + .recipe_is_known(&component_recipe.recipe_book_key); + (item, learned) + }) + .map_or((None, true), |(item, known)| (Some(item), known)) } else { - None + (None, true) } }, - RecipeKind::Simple | RecipeKind::Repair => None, + RecipeKind::Simple | RecipeKind::Repair => (None, true), }; if let Some(output_item) = output_item { @@ -1281,6 +1289,7 @@ impl<'a> Widget for Crafting<'a> { secondary_slot.slot, self.show.crafting_fields.craft_sprite.map(|(_, s)| s) == recipe.craft_sprite, + recipe_known, ) } else { Text::new(&self.localized_strings.get_msg("hud-crafting-modular_desc")) @@ -1295,7 +1304,7 @@ impl<'a> Widget for Crafting<'a> { .w_h(70.0, 70.0) .graphics_for(state.ids.output_img) .set(state.ids.modular_wep_empty_bg, ui); - (primary_slot.slot, secondary_slot.slot, false) + (primary_slot.slot, secondary_slot.slot, false, recipe_known) } }, RecipeKind::Simple => { @@ -1398,6 +1407,7 @@ impl<'a> Widget for Crafting<'a> { == self.show.crafting_fields.craft_sprite.map(|(_, s)| s) }) }), + true, ) }, RecipeKind::Repair => { @@ -1530,12 +1540,16 @@ impl<'a> Widget for Crafting<'a> { let can_perform = repair_slot.item(self.inventory).map_or(false, can_repair); - (repair_slot.slot, None, can_perform) + (repair_slot.slot, None, can_perform, true) }, }; // Craft button - if Button::image(self.imgs.button) + let label = &match recipe_kind { + RecipeKind::Repair => self.localized_strings.get_msg("hud-crafting-repair"), + _ => self.localized_strings.get_msg("hud-crafting-craft"), + }; + let craft_button_init = Button::image(self.imgs.button) .w_h(105.0, 25.0) .hover_image(if can_perform { self.imgs.button_hover @@ -1547,10 +1561,7 @@ impl<'a> Widget for Crafting<'a> { } else { self.imgs.button }) - .label(&match recipe_kind { - RecipeKind::Repair => self.localized_strings.get_msg("hud-crafting-repair"), - _ => self.localized_strings.get_msg("hud-crafting-craft"), - }) + .label(label) .label_y(conrod_core::position::Relative::Scalar(1.0)) .label_color(if can_perform { TEXT_COLOR @@ -1565,11 +1576,27 @@ impl<'a> Widget for Crafting<'a> { TEXT_GRAY_COLOR }) .bottom_left_with_margins_on(state.ids.align_ing, -31.0, 15.0) - .parent(state.ids.window_frame) - .set(state.ids.btn_craft, ui) - .was_clicked() - && can_perform - { + .parent(state.ids.window_frame); + + let craft_button = if !recipe_known { + craft_button_init + .with_tooltip( + self.tooltip_manager, + &self + .localized_strings + .get_msg("hud-crafting-recipe-uncraftable"), + &self + .localized_strings + .get_msg("hud-crafting-recipe-unlearned"), + &tabs_tooltip, + TEXT_COLOR, + ) + .set(state.ids.btn_craft, ui) + } else { + craft_button_init.set(state.ids.btn_craft, ui) + }; + + if craft_button.was_clicked() && can_perform { match recipe_kind { RecipeKind::ModularWeapon => { if let ( diff --git a/voxygen/src/hud/item_imgs.rs b/voxygen/src/hud/item_imgs.rs index 8bd1ffc061..eb0b4caf96 100644 --- a/voxygen/src/hud/item_imgs.rs +++ b/voxygen/src/hud/item_imgs.rs @@ -1,7 +1,7 @@ use crate::ui::{Graphic, SampleStrat, Transform, Ui}; use common::{ assets::{self, AssetCombined, AssetExt, AssetHandle, Concatenate, DotVoxAsset, ReloadWatcher}, - comp::item::item_key::ItemKey, + comp::item::{item_key::ItemKey, modular}, figure::Segment, }; use conrod_core::image::Id; @@ -17,7 +17,7 @@ pub fn animate_by_pulse(ids: &[Id], pulse: f32) -> Id { ids[animation_frame % ids.len()] } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub enum ImageSpec { Png(String), Vox(String, #[serde(default)] u32), @@ -56,7 +56,7 @@ impl ImageSpec { } #[derive(Serialize, Deserialize)] -pub struct ItemImagesSpec(pub HashMap); +pub struct ItemImagesSpec(pub HashMap); impl assets::Asset for ItemImagesSpec { type Loader = assets::RonLoader; @@ -68,7 +68,7 @@ impl Concatenate for ItemImagesSpec { // TODO: when there are more images don't load them all into memory pub struct ItemImgs { - map: HashMap, + map: HashMap, manifest: AssetHandle, watcher: ReloadWatcher, not_found: Id, @@ -118,11 +118,11 @@ impl ItemImgs { if let ItemKey::TagExamples(keys, _) = item_key { return keys .iter() - .filter_map(|k| self.map.get(k)) + .filter_map(|k| self.map.get(&ItemVisualKey::from(k.clone()))) .cloned() .collect(); }; - match self.map.get(&item_key) { + match self.map.get(&ItemVisualKey::from(item_key.clone())) { Some(id) => vec![*id], // There was no specification in the ron None => { @@ -183,3 +183,25 @@ fn graceful_load_segment_no_skin(specifier: &str, model_index: u32) -> Arc, String), + Recipe(String), + Empty, +} + +impl From for ItemVisualKey { + fn from(item_key: ItemKey) -> Self { + match item_key { + ItemKey::Simple(key) => Self::Simple(key), + ItemKey::ModularWeapon(key) => Self::ModularWeapon(key), + ItemKey::ModularWeaponComponent(key) => Self::ModularWeaponComponent(key), + ItemKey::TagExamples(keys, key) => Self::TagExamples(keys, key), + ItemKey::Empty => Self::Empty, + } + } +} diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 8ebccbff26..62291ba323 100755 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -109,6 +109,7 @@ use common::{ link::Is, mounting::{Mount, Rider, VolumePos}, outcome::Outcome, + recipe::RecipeBookManifest, resources::{ProgramTime, Secs, Time}, slowjob::SlowJobPool, terrain::{SpriteKind, TerrainChunk, UnlockKind}, @@ -3142,6 +3143,7 @@ impl Hud { let entity = info.viewpoint_entity; let healths = ecs.read_storage::(); let inventories = ecs.read_storage::(); + let rbm = ecs.read_resource::(); let energies = ecs.read_storage::(); let skillsets = ecs.read_storage::(); let active_abilities = ecs.read_storage::(); @@ -3381,6 +3383,7 @@ impl Hud { &mut self.slot_manager, &self.item_imgs, inventory, + &rbm, &msm, tooltip_manager, &mut self.show, diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 09c3900b1d..41b4a021b9 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -112,6 +112,7 @@ pub fn kind_text<'a>(kind: &ItemKind, i18n: &'a Localization) -> Cow<'a, str> { ItemKind::Ingredient { .. } => i18n.get_msg("common-kind-ingredient"), ItemKind::Lantern { .. } => i18n.get_msg("common-kind-lantern"), ItemKind::TagExamples { .. } => Cow::Borrowed(""), + ItemKind::RecipeGroup { .. } => i18n.get_msg("common-kind-recipegroup"), } } diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 549e0c06af..70c76cbffe 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -216,11 +216,10 @@ impl CharacterCacheKey { .map(|id| match id { // TODO: Properly handle items with components here. Probably wait until modular // armor? - ItemDefinitionId::Simple(id) => id, - ItemDefinitionId::Compound { simple_base, .. } => simple_base, - ItemDefinitionId::Modular { pseudo_base, .. } => pseudo_base, + ItemDefinitionId::Simple(id) => String::from(id), + ItemDefinitionId::Compound { simple_base, .. } => String::from(simple_base), + ItemDefinitionId::Modular { pseudo_base, .. } => String::from(pseudo_base), }) - .map(String::from) }; // Third person tools are only modeled when the camera is either not first diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index a0e6fe70e7..1db0080b49 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -1,6 +1,7 @@ use super::cache::{ FigureKey, FigureModelEntryFuture, ModelEntryFuture, TerrainModelEntryFuture, ToolKey, }; +use crate::hud::item_imgs::ItemVisualKey; use common::{ assets::{self, AssetExt, AssetHandle, Concatenate, DotVoxAsset, MultiRon, ReloadWatcher}, comp::{ @@ -5609,7 +5610,7 @@ impl<'de> Deserialize<'de> for ModelWithOptionalIndex { } #[derive(Deserialize)] -struct ItemDropCentralSpec(HashMap); +struct ItemDropCentralSpec(HashMap); impl_concatenate_for_wrapper!(ItemDropCentralSpec); make_vox_spec!( @@ -5645,7 +5646,7 @@ impl ItemDropCentralSpec { if let Some(spec) = match item_drop { item_drop::Body::CoinPouch => Some(&coin_pouch), - _ => self.0.get(item_key), + _ => self.0.get(&ItemVisualKey::from(item_key.clone())), } { let full_spec: String = ["voxygen.", spec.0.as_str()].concat(); let segment = match item_drop { diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 79bd31b06d..0cef377265 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -27,7 +27,7 @@ use common::{ link::Is, mounting::{Mount, VolumePos}, outcome::Outcome, - recipe, + recipe::{self, RecipeBookManifest}, states::utils::can_perform_pet, terrain::{Block, BlockKind}, trade::TradeResult, @@ -1978,13 +1978,24 @@ impl PlayState for SessionState { } => { let slots = { let client = self.client.borrow(); - if let Some(recipe) = client.recipe_book().get(&recipe) { - client.inventories().get(client.entity()).and_then(|inv| { - recipe.inventory_contains_ingredients(inv, 1).ok() - }) + + let s = if let Some(inventory) = client + .state() + .ecs() + .read_storage::() + .get(client.entity()) + { + let rbm = + client.state().ecs().read_resource::(); + if let Some(recipe) = inventory.get_recipe(&recipe, &rbm) { + recipe.inventory_contains_ingredients(inventory, 1).ok() + } else { + None + } } else { None - } + }; + s }; if let Some(slots) = slots { self.client.borrow_mut().craft_recipe( diff --git a/world/src/site/economy/map_types.rs b/world/src/site/economy/map_types.rs index 7e0502514b..9ef120d7df 100644 --- a/world/src/site/economy/map_types.rs +++ b/world/src/site/economy/map_types.rs @@ -20,7 +20,7 @@ pub struct GoodIndex { idx: usize, } -impl GenericIndex for GoodIndex { +impl GenericIndex for GoodIndex { // static list of all Goods traded const VALUES: [Good; GoodIndex::LENGTH] = [ // controlled resources @@ -41,6 +41,7 @@ impl GenericIndex for GoodIndex { Armor, Potions, Transportation, + Recipe, // exchange currency Coin, // uncontrolled resources From c2604ef5274c945f90362dc3e04317afe838c7c3 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 2 Jun 2024 20:36:49 -0400 Subject: [PATCH 2/5] Added test for each recipe being reachable. Made new recipes reachable. --- assets/common/item_i18n_manifest.ron | 2 + .../common/items/recipes/armor/orichalcum.ron | 1 + assets/common/items/recipes/default.ron | 1 + .../items/recipes/unique/abyssal_ring.ron | 11 ++++ .../items/recipes/unique/delvers_lamp.ron | 11 ++++ .../dungeon/dwarven_quarry/miner.ron | 1 + .../dungeon/sea_chapel/chest_coral.ron | 1 + assets/voxygen/i18n/en/item/recipe.ftl | 4 ++ common/src/comp/inventory/recipe_book.rs | 61 ++++++++++++++++--- 9 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 assets/common/items/recipes/unique/abyssal_ring.ron create mode 100644 assets/common/items/recipes/unique/delvers_lamp.ron diff --git a/assets/common/item_i18n_manifest.ron b/assets/common/item_i18n_manifest.ron index 159ee79682..bd5491166d 100644 --- a/assets/common/item_i18n_manifest.ron +++ b/assets/common/item_i18n_manifest.ron @@ -7094,11 +7094,13 @@ Simple("common.items.recipes.equipment.moderate"): "recipe-equipment-moderate", Simple("common.items.recipes.equipment.advanced"): "recipe-equipment-advanced", Simple("common.items.recipes.unique.abyssal_gorget"): "recipe-unique-abyssal_gorget", + Simple("common.items.recipes.unique.abyssal_ring"): "recipe-unique-abyssal_ring", Simple("common.items.recipes.unique.mindflayer_spellbag"): "recipe-unique-mindflayer_spellbag", Simple("common.items.recipes.unique.polaris"): "recipe-unique-polaris", Simple("common.items.recipes.unique.seashell_necklace"): "recipe-unique-seashell_necklace", Simple("common.items.recipes.unique.troll_hide_pack"): "recipe-unique-troll_hide_pack", Simple("common.items.recipes.unique.winged_coronet"): "recipe-unique-winged_coronet", + Simple("common.items.recipes.unique.delvers_lamp"): "recipe-unique-delvers_lamp", Simple("common.items.recipes.charms"): "recipe-charms", Simple("common.items.recipes.explosives"): "recipe-explosives", Simple("common.items.recipes.food"): "recipe-food", diff --git a/assets/common/items/recipes/armor/orichalcum.ron b/assets/common/items/recipes/armor/orichalcum.ron index d6b6d04b50..3e32c69010 100644 --- a/assets/common/items/recipes/armor/orichalcum.ron +++ b/assets/common/items/recipes/armor/orichalcum.ron @@ -11,6 +11,7 @@ ItemDef( "orichalcum_hands", "orichalcum_pants", "orichalcum_shoulder", + "orichalcum_head", ], ), quality: Common, diff --git a/assets/common/items/recipes/default.ron b/assets/common/items/recipes/default.ron index 0d9dc4d447..6eff6d0088 100644 --- a/assets/common/items/recipes/default.ron +++ b/assets/common/items/recipes/default.ron @@ -56,6 +56,7 @@ ItemDef( "healing_sceptre", "bronze_weapons", "wood_weapons", + "shield", // Materials "tin_ingot", "copper_ingot", diff --git a/assets/common/items/recipes/unique/abyssal_ring.ron b/assets/common/items/recipes/unique/abyssal_ring.ron new file mode 100644 index 0000000000..02439d48e1 --- /dev/null +++ b/assets/common/items/recipes/unique/abyssal_ring.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "abyssal_ring", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/items/recipes/unique/delvers_lamp.ron b/assets/common/items/recipes/unique/delvers_lamp.ron new file mode 100644 index 0000000000..03d7ed6379 --- /dev/null +++ b/assets/common/items/recipes/unique/delvers_lamp.ron @@ -0,0 +1,11 @@ +ItemDef( + legacy_name: "", + legacy_description: "", + kind: RecipeGroup( + recipes: [ + "delvers_lamp", + ], + ), + quality: Common, + tags: [], +) diff --git a/assets/common/loot_tables/dungeon/dwarven_quarry/miner.ron b/assets/common/loot_tables/dungeon/dwarven_quarry/miner.ron index 81f8c1a976..5dce705ce1 100644 --- a/assets/common/loot_tables/dungeon/dwarven_quarry/miner.ron +++ b/assets/common/loot_tables/dungeon/dwarven_quarry/miner.ron @@ -5,4 +5,5 @@ (2.0, Item("common.items.consumable.potion_minor")), // Gold (2.0, MultiDrop(Item("common.items.utility.coins"), 25, 50)), + (0.5, Item("common.items.recipes.unique.delvers_lamp")), ] \ No newline at end of file diff --git a/assets/common/loot_tables/dungeon/sea_chapel/chest_coral.ron b/assets/common/loot_tables/dungeon/sea_chapel/chest_coral.ron index a8cb2c2b23..ec0dba3cca 100644 --- a/assets/common/loot_tables/dungeon/sea_chapel/chest_coral.ron +++ b/assets/common/loot_tables/dungeon/sea_chapel/chest_coral.ron @@ -6,6 +6,7 @@ (0.5, Item("common.items.crafting_ing.pearl")), (0.25, Item("common.items.recipes.unique.seashell_necklace")), (0.25, Item("common.items.recipes.unique.winged_coronet")), + (0.25, Item("common.items.recipes.unique.abyssal_ring")), ]), Lottery([ (0.6, Nothing), diff --git a/assets/voxygen/i18n/en/item/recipe.ftl b/assets/voxygen/i18n/en/item/recipe.ftl index f31f7d513b..918f8df277 100644 --- a/assets/voxygen/i18n/en/item/recipe.ftl +++ b/assets/voxygen/i18n/en/item/recipe.ftl @@ -38,6 +38,8 @@ recipe-equipment-advanced = Advanced Equipment Recipes .desc = Advanced Equipment Recipes recipe-unique-abyssal_gorget = Abyssal Gorget Recipe .desc = Abyssal Gorget Recipe +recipe-unique-abyssal_ring = Abyssal Ring Recipe + .desc = Abyssal Ring Recipe recipe-unique-mindflayer_spellbag = Mindflayer Spellbag Recipe .desc = Mindflayer Spellbag Recipe recipe-unique-polaris = Polaris Recipe @@ -48,6 +50,8 @@ recipe-unique-troll_hide_pack = Troll Hide Pack Recipe .desc = Troll Hide Pack Recipe recipe-unique-winged_coronet = Winged Coronet Recipe .desc = Winged Coronet Recipe +recipe-unique-delvers_lamp = Delver's Lamp Recipe + .desc = Delver's Lamp Recipe recipe-charms = Charms Recipes .desc = Charms Recipes recipe-explosives = Explosives Recipes diff --git a/common/src/comp/inventory/recipe_book.rs b/common/src/comp/inventory/recipe_book.rs index 38b72a6a90..95d6395c07 100644 --- a/common/src/comp/inventory/recipe_book.rs +++ b/common/src/comp/inventory/recipe_book.rs @@ -89,28 +89,69 @@ mod tests { comp::item::{Item, ItemKind}, recipe::{complete_recipe_book, default_component_recipe_book}, }; + use hashbrown::HashSet; - fn valid_recipe(recipe: &str) -> bool { + fn load_recipe_items() -> Vec { + Item::new_from_asset_glob("common.items.recipes.*").expect("The directory should exist") + } + + fn load_recipe_list() -> HashSet { let recipe_book = complete_recipe_book(); let component_recipe_book = default_component_recipe_book(); - recipe_book.read().keys().any(|key| key == recipe) - || component_recipe_book - .read() - .iter() - .any(|(_, cr)| cr.recipe_book_key == recipe) + recipe_book + .read() + .keys() + .cloned() + .chain( + component_recipe_book + .read() + .iter() + .map(|(_, cr)| &cr.recipe_book_key) + .cloned(), + ) + .collect::>() + } + + fn valid_recipe(recipe: &str) -> bool { + let recipe_list = load_recipe_list(); + recipe_list.contains(recipe) } /// Verify that all recipes in recipe items point to a valid recipe #[test] fn validate_recipes() { - let groups = Item::new_from_asset_glob("common.items.recipes.*") - .expect("The directory should exist"); - for group in groups { - let ItemKind::RecipeGroup { recipes } = &*group.kind() else { + let recipe_items = load_recipe_items(); + for item in recipe_items { + let ItemKind::RecipeGroup { recipes } = &*item.kind() else { panic!("Expected item to be of kind RecipeGroup") }; assert!(recipes.iter().all(|r| valid_recipe(r))); } } + + /// Verify that all recipes are contained in a recipe item + #[test] + fn recipes_reachable() { + let recipe_items = load_recipe_items(); + let reachable_recipes = recipe_items + .iter() + .flat_map(|i| { + if let ItemKind::RecipeGroup { recipes } = &*i.kind() { + recipes.to_vec() + } else { + Vec::new() + } + }) + .collect::>(); + + let recipe_list = load_recipe_list(); + + for recipe in recipe_list.iter() { + assert!( + reachable_recipes.contains(recipe), + "{recipe} was not found in a recipe item" + ); + } + } } From d7f5c013ad79ad0edf2b4cb21f311fdecf5a8a8b Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 3 Jun 2024 20:37:56 -0400 Subject: [PATCH 3/5] Remove ItemVisualKey --- assets/voxygen/item_image_manifest.ron | 8 ++++ assets/voxygen/voxel/item_drop_manifest.ron | 52 +++++++++++++++++---- voxygen/src/hud/item_imgs.rs | 32 ++----------- voxygen/src/scene/figure/load.rs | 5 +- 4 files changed, 59 insertions(+), 38 deletions(-) diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 359c50b284..83010c3453 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -1744,6 +1744,10 @@ "voxel.object.recipe_blacksmithing", (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, ), + Simple("common.items.recipes.unique.delvers_lamp"): VoxTrans( + "voxel.object.recipe_lapidary", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), Simple("common.items.recipes.weapons.iron"): VoxTrans( "voxel.object.recipe_blacksmithing", (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, @@ -1804,6 +1808,10 @@ "voxel.object.recipe_lapidary", (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, ), + Simple("common.items.recipes.unique.abyssal_ring"): VoxTrans( + "voxel.object.recipe_lapidary", + (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, + ), Simple("common.items.recipes.unique.seashell_necklace"): VoxTrans( "voxel.object.recipe_lapidary", (1.0, 0.0, 20.0), (30.0, 45.0, 120.0), 1.0, diff --git a/assets/voxygen/voxel/item_drop_manifest.ron b/assets/voxygen/voxel/item_drop_manifest.ron index 3940ef308d..8481ac76c5 100644 --- a/assets/voxygen/voxel/item_drop_manifest.ron +++ b/assets/voxygen/voxel/item_drop_manifest.ron @@ -418,14 +418,50 @@ // Other Simple("common.items.utility.coins"): "voxel.object.v-coin", Simple("common.items.utility.collar"): "voxel.object.collar", - Recipe("recipe_alchemy"): "voxel.object.recipe_alchemy", - Recipe("recipe_blacksmithing"): "voxel.object.recipe_blacksmithing", - Recipe("recipe_carpentry"): "voxel.object.recipe_carpentry", - Recipe("recipe_cooking"): "voxel.object.recipe_cooking", - Recipe("recipe_lapidary"): "voxel.object.recipe_lapidary", - Recipe("recipe_leatherworking"): "voxel.object.recipe_leatherworking", - Recipe("recipe_weaving"): "voxel.object.recipe_weaving", - Recipe("scroll"): "voxel.object.scroll", + Simple("common.items.recipes.potions"): "voxel.object.recipe_alchemy", + Simple("common.items.recipes.explosives"): "voxel.object.recipe_alchemy", + Simple("common.items.recipes.charms"): "voxel.object.recipe_alchemy", + Simple("common.items.recipes.armor.iron"): "voxel.object.recipe_blacksmithing", + Simple("common.items.recipes.armor.steel"): "voxel.object.recipe_blacksmithing", + Simple("common.items.recipes.armor.cobalt"): "voxel.object.recipe_blacksmithing", + Simple("common.items.recipes.armor.bloodsteel"): "voxel.object.recipe_blacksmithing", + Simple("common.items.recipes.armor.orichalcum"): "voxel.object.recipe_blacksmithing", + Simple("common.items.recipes.unique.polaris"): "voxel.object.recipe_blacksmithing", + Simple("common.items.recipes.unique.delvers_lamp"): "voxel.object.recipe_lapidary", + Simple("common.items.recipes.weapons.iron"): "voxel.object.recipe_blacksmithing", + Simple("common.items.recipes.weapons.steel"): "voxel.object.recipe_blacksmithing", + Simple("common.items.recipes.weapons.cobalt"): "voxel.object.recipe_blacksmithing", + Simple("common.items.recipes.weapons.bloodsteel"): "voxel.object.recipe_blacksmithing", + Simple("common.items.recipes.weapons.orichalcum"): "voxel.object.recipe_blacksmithing", + Simple("common.items.recipes.gliders"): "voxel.object.recipe_carpentry", + Simple("common.items.recipes.instruments"): "voxel.object.recipe_carpentry", + Simple("common.items.recipes.weapons.bamboo"): "voxel.object.recipe_carpentry", + Simple("common.items.recipes.weapons.hardwood"): "voxel.object.recipe_carpentry", + Simple("common.items.recipes.weapons.ironwood"): "voxel.object.recipe_carpentry", + Simple("common.items.recipes.weapons.frostwood"): "voxel.object.recipe_carpentry", + Simple("common.items.recipes.weapons.eldwood"): "voxel.object.recipe_carpentry", + Simple("common.items.recipes.food"): "voxel.object.recipe_cooking", + Simple("common.items.recipes.armor.brinestone"): "voxel.object.recipe_lapidary", + Simple("common.items.recipes.unique.abyssal_gorget"): "voxel.object.recipe_lapidary", + Simple("common.items.recipes.unique.abyssal_ring"): "voxel.object.recipe_lapidary", + Simple("common.items.recipes.unique.seashell_necklace"): "voxel.object.recipe_lapidary", + Simple("common.items.recipes.armor.leather"): "voxel.object.recipe_leatherworking", + Simple("common.items.recipes.armor.scale"): "voxel.object.recipe_leatherworking", + Simple("common.items.recipes.armor.carapace"): "voxel.object.recipe_leatherworking", + Simple("common.items.recipes.armor.primal"): "voxel.object.recipe_leatherworking", + Simple("common.items.recipes.armor.dragonscale"): "voxel.object.recipe_leatherworking", + Simple("common.items.recipes.unique.mindflayer_spellbag"): "voxel.object.recipe_leatherworking", + Simple("common.items.recipes.unique.troll_hide_pack"): "voxel.object.recipe_leatherworking", + Simple("common.items.recipes.unique.winged_coronet"): "voxel.object.recipe_leatherworking", + Simple("common.items.recipes.armor.woolen"): "voxel.object.recipe_weaving", + Simple("common.items.recipes.armor.silken"): "voxel.object.recipe_weaving", + Simple("common.items.recipes.armor.druid"): "voxel.object.recipe_weaving", + Simple("common.items.recipes.armor.moonweave"): "voxel.object.recipe_weaving", + Simple("common.items.recipes.armor.sunsilk"): "voxel.object.recipe_weaving", + Simple("common.items.recipes.utility"): "voxel.object.scroll", + Simple("common.items.recipes.equipment.basic"): "voxel.object.scroll", + Simple("common.items.recipes.equipment.moderate"): "voxel.object.scroll", + Simple("common.items.recipes.equipment.advanced"): "voxel.object.scroll", // Armor // Starter Parts Simple("common.items.armor.misc.foot.sandals"): "voxel.armor.misc.foot.cloth_sandal", diff --git a/voxygen/src/hud/item_imgs.rs b/voxygen/src/hud/item_imgs.rs index eb0b4caf96..7d7234f80c 100644 --- a/voxygen/src/hud/item_imgs.rs +++ b/voxygen/src/hud/item_imgs.rs @@ -1,7 +1,7 @@ use crate::ui::{Graphic, SampleStrat, Transform, Ui}; use common::{ assets::{self, AssetCombined, AssetExt, AssetHandle, Concatenate, DotVoxAsset, ReloadWatcher}, - comp::item::{item_key::ItemKey, modular}, + comp::item::item_key::ItemKey, figure::Segment, }; use conrod_core::image::Id; @@ -56,7 +56,7 @@ impl ImageSpec { } #[derive(Serialize, Deserialize)] -pub struct ItemImagesSpec(pub HashMap); +pub struct ItemImagesSpec(pub HashMap); impl assets::Asset for ItemImagesSpec { type Loader = assets::RonLoader; @@ -68,7 +68,7 @@ impl Concatenate for ItemImagesSpec { // TODO: when there are more images don't load them all into memory pub struct ItemImgs { - map: HashMap, + map: HashMap, manifest: AssetHandle, watcher: ReloadWatcher, not_found: Id, @@ -118,11 +118,11 @@ impl ItemImgs { if let ItemKey::TagExamples(keys, _) = item_key { return keys .iter() - .filter_map(|k| self.map.get(&ItemVisualKey::from(k.clone()))) + .filter_map(|k| self.map.get(k)) .cloned() .collect(); }; - match self.map.get(&ItemVisualKey::from(item_key.clone())) { + match self.map.get(&item_key) { Some(id) => vec![*id], // There was no specification in the ron None => { @@ -183,25 +183,3 @@ fn graceful_load_segment_no_skin(specifier: &str, model_index: u32) -> Arc, String), - Recipe(String), - Empty, -} - -impl From for ItemVisualKey { - fn from(item_key: ItemKey) -> Self { - match item_key { - ItemKey::Simple(key) => Self::Simple(key), - ItemKey::ModularWeapon(key) => Self::ModularWeapon(key), - ItemKey::ModularWeaponComponent(key) => Self::ModularWeaponComponent(key), - ItemKey::TagExamples(keys, key) => Self::TagExamples(keys, key), - ItemKey::Empty => Self::Empty, - } - } -} diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index 1db0080b49..a0e6fe70e7 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -1,7 +1,6 @@ use super::cache::{ FigureKey, FigureModelEntryFuture, ModelEntryFuture, TerrainModelEntryFuture, ToolKey, }; -use crate::hud::item_imgs::ItemVisualKey; use common::{ assets::{self, AssetExt, AssetHandle, Concatenate, DotVoxAsset, MultiRon, ReloadWatcher}, comp::{ @@ -5610,7 +5609,7 @@ impl<'de> Deserialize<'de> for ModelWithOptionalIndex { } #[derive(Deserialize)] -struct ItemDropCentralSpec(HashMap); +struct ItemDropCentralSpec(HashMap); impl_concatenate_for_wrapper!(ItemDropCentralSpec); make_vox_spec!( @@ -5646,7 +5645,7 @@ impl ItemDropCentralSpec { if let Some(spec) = match item_drop { item_drop::Body::CoinPouch => Some(&coin_pouch), - _ => self.0.get(&ItemVisualKey::from(item_key.clone())), + _ => self.0.get(item_key), } { let full_spec: String = ["voxygen.", spec.0.as_str()].concat(); let segment = match item_drop { From b6d08a8d245b2dd30768f0fd30911944ca91472a Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 3 Jun 2024 20:55:35 -0400 Subject: [PATCH 4/5] Fixed migration --- server/src/migrations/V60__recipe_book.sql | 26 ++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/server/src/migrations/V60__recipe_book.sql b/server/src/migrations/V60__recipe_book.sql index d34e9364e0..67125007fc 100644 --- a/server/src/migrations/V60__recipe_book.sql +++ b/server/src/migrations/V60__recipe_book.sql @@ -2,13 +2,15 @@ CREATE TEMP TABLE _temp_character_recipe_pairings ( temp_recipe_book_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, character_id INT NOT NULL, - recipe_book_id INT + recipe_book_id INT, + recipe_item_id INT ); INSERT INTO _temp_character_recipe_pairings SELECT NULL, i.item_id, + NULL, NULL FROM item i WHERE i.item_definition_id = 'veloren.core.pseudo_containers.character'; @@ -16,11 +18,19 @@ WHERE i.item_definition_id = 'veloren.core.pseudo_containers.character'; UPDATE _temp_character_recipe_pairings SET recipe_book_id = ((SELECT MAX(entity_id) FROM entity) + temp_recipe_book_id); +UPDATE _temp_character_recipe_pairings +SET recipe_item_id = ((SELECT MAX(entity_id) FROM entity) + (SELECT MAX(temp_recipe_book_id) FROM _temp_character_recipe_pairings) + temp_recipe_book_id); + INSERT INTO entity SELECT t.recipe_book_id FROM _temp_character_recipe_pairings t; +-- Insert the new recipe book items, temporarily disabling foreign key constraints +-- due to the parent_container_item_id foreign key constraint not being satisfied +-- until the end of the query. +PRAGMA defer_foreign_keys = true; + INSERT INTO item SELECT t.recipe_book_id, @@ -29,4 +39,16 @@ SELECT t.recipe_book_id, 1, 'recipe_book', '' -FROM _temp_character_recipe_pairings t; \ No newline at end of file +FROM _temp_character_recipe_pairings t; + +INSERT +INTO item +SELECT t.recipe_item_id, + t.recipe_book_id, + 'common.items.recipes.default', + 1, + '0', + '{}' +FROM _temp_character_recipe_pairings t; + +PRAGMA defer_foreign_keys = false; \ No newline at end of file From d1b54b7da4e0bb2740469f7600ca845719b8c12d Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 3 Jun 2024 21:24:41 -0400 Subject: [PATCH 5/5] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e9bfe7556..8cbb46f37c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Dwarven-Mine update and activation. - Craftable orichalcum helmet - Protocol to query game server information (player count, version, etc.) and make ping tests. +- Unlockable recipes ### Changed