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
This commit is contained in:
James Melkonian 2022-11-28 19:50:44 -08:00 committed by Sam
parent 9036baa479
commit b40a14ae62
122 changed files with 2187 additions and 258 deletions

File diff suppressed because it is too large Load Diff

View File

@ -40,6 +40,9 @@
Ingredients: ( Ingredients: (
decay_rate: 0.1, // revisit decay_rate: 0.1, // revisit
), ),
Recipe: (
decay_rate: 0.1, // revisit
),
Tools: ( Tools: (
// TODO: Separate stone, metal, bone, wood // TODO: Separate stone, metal, bone, wood
// decay_rate: 0.05, // 14 years half-life // decay_rate: 0.05, // 14 years half-life

View File

@ -19,6 +19,7 @@
items: [ items: [
(10, "common.items.consumable.potion_big"), (10, "common.items.consumable.potion_big"),
(10, "common.items.food.sunflower_icetea"), (10, "common.items.food.sunflower_icetea"),
(1, "common.items.recipes.potions"),
], ],
), ),
meta: [ meta: [

View File

@ -18,7 +18,10 @@
)), )),
items: [ items: [
(10, "common.items.consumable.potion_big"), (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: [ meta: [

View File

@ -19,6 +19,7 @@
items: [ items: [
(10, "common.items.consumable.potion_big"), (10, "common.items.consumable.potion_big"),
(10, "common.items.food.sunflower_icetea"), (10, "common.items.food.sunflower_icetea"),
(1, "common.items.recipes.food"),
], ],
), ),
meta: [ meta: [

View File

@ -15,6 +15,8 @@
items: [ items: [
(10, "common.items.food.cheese"), (10, "common.items.food.cheese"),
(10, "common.items.food.plainsalad"), (10, "common.items.food.plainsalad"),
(1, "common.items.recipes.food"),
(1, "common.items.recipes.armor.woolen"),
], ],
), ),
meta: [], meta: [],

View File

@ -14,6 +14,11 @@
(2, ModularWeapon(tool: Staff, material: Ironwood, hands: None)), (2, ModularWeapon(tool: Staff, material: Ironwood, hands: None)),
]), None)), ]), None)),
)), )),
items: [
(1, "common.items.recipes.equipment.basic"),
(1, "common.items.recipes.armor.leather"),
(1, "common.items.recipes.weapons.bamboo"),
],
), ),
meta: [ meta: [
SkillSetAsset("common.skillset.preset.rank3.fullskill"), SkillSetAsset("common.skillset.preset.rank3.fullskill"),

View File

@ -7074,5 +7074,48 @@
One, One,
), ),
): "weapon-hammer-hammer-cobalt-1h", ): "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",
}, },
) )

View File

@ -6,6 +6,5 @@ ItemDef(
stats: FromSet("Pirate"), stats: FromSet("Pirate"),
)), )),
quality: Epic, quality: Epic,
tags: [ tags: [],
],
) )

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -0,0 +1,13 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"burning_charm",
"frozen_charm",
"lifesteal_charm",
],
),
quality: Common,
tags: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -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: [],
)

View File

@ -0,0 +1,14 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"potion_combustion",
"potion_agility",
"potion_minor",
"potion_medium",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,11 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"abyssal_gorget",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,11 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"mindflayer_spellbag",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,11 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"polaris",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,11 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"seashell_necklace",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,11 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"troll_hide_pack",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,11 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"winged_coronet",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,15 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"collar_basic",
"velorite_frag",
"lockpick",
"gold_ingot",
"silver_ingot",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,11 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"bamboo_weapons",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,12 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"bloodsteel_ingot",
"bloodsteel_weapons",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,12 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"cobalt_ingot",
"cobalt_weapons",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,11 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"eldwood_weapons",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,11 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"frostwood_weapons",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,11 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"hardwood_weapons",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,12 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"iron_ingot",
"iron_weapons",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,11 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"ironwood_weapons",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,12 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"orichalcum_ingot",
"orichalcum_weapons",
],
),
quality: Common,
tags: [],
)

View File

@ -0,0 +1,12 @@
ItemDef(
legacy_name: "",
legacy_description: "",
kind: RecipeGroup(
recipes: [
"steel_ingot",
"steel_weapons",
],
),
quality: Common,
tags: [],
)

View File

@ -14,4 +14,14 @@
(2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)), (2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)),
// Food // Food
(1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 1, 3)), (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")),
] ]

View File

@ -14,6 +14,10 @@
(1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 3, 6)), (1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 3, 6)),
// Ingredients // Ingredients
(0.75, MultiDrop(Item("common.items.crafting_ing.alkahest"), 1, 3)), (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([ Lottery([
(0.6, Nothing), (0.6, Nothing),

View File

@ -12,4 +12,12 @@
(2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)), (2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)),
// Food // Food
(1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 1, 2)), (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")),
] ]

View File

@ -11,6 +11,17 @@
(2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)), (2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)),
// Food // Food
(1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 2, 4)), (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([ Lottery([
(0.7, Nothing), (0.7, Nothing),

View File

@ -12,6 +12,18 @@
(2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 4, 8)), (2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 4, 8)),
// Food // Food
(1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 2, 5)), (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([ Lottery([
(0.6, Nothing), (0.6, Nothing),

View File

@ -11,6 +11,19 @@
(2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)), (2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)),
// Food // Food
(1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 1, 4)), (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([ Lottery([
(0.8, Nothing), (0.8, Nothing),

View File

@ -4,6 +4,8 @@
// Ingredients // Ingredients
(2.0, Item("common.items.crafting_ing.coral_branch")), (2.0, Item("common.items.crafting_ing.coral_branch")),
(0.5, Item("common.items.crafting_ing.pearl")), (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([ Lottery([
(0.6, Nothing), (0.6, Nothing),

View File

@ -1,4 +1,3 @@
[ [
// Nothing (1.0, Item("common.items.recipes.armor.brinestone")),
(1.0, Nothing),
] ]

View File

@ -14,4 +14,7 @@
(0.4, Item("common.items.armor.misc.head.bandana.red")), (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")),
(0.4, Item("common.items.armor.misc.head.hood_dark")), (0.4, Item("common.items.armor.misc.head.hood_dark")),
// Recipes
(2.0, Item("common.items.recipes.equipment.basic")),
(1.0, Item("common.items.recipes.equipment.moderate")),
] ]

View File

@ -17,4 +17,8 @@
// Food // Food
(1.0, LootTable("common.loot_tables.food.wild_ingredients")), (1.0, LootTable("common.loot_tables.food.wild_ingredients")),
(0.5, Item("common.items.food.meat.fish_raw")), (0.5, Item("common.items.food.meat.fish_raw")),
// 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")),
] ]

View File

@ -5,4 +5,5 @@
(0.5, LootTable("common.loot_tables.weapons.tier-3")), (0.5, LootTable("common.loot_tables.weapons.tier-3")),
(0.1, LootTable("common.loot_tables.weapons.tier-4")), (0.1, LootTable("common.loot_tables.weapons.tier-4")),
(0.05, Item("common.items.armor.witch.hat")), (0.05, Item("common.items.armor.witch.hat")),
(0.1, Item("common.items.recipes.unique.troll_hide_pack")),
] ]

View File

@ -1,4 +1,5 @@
[ [
(1.0, LootTable("common.loot_tables.weapons.components.tier-1")), (1.0, LootTable("common.loot_tables.weapons.components.tier-1")),
(1.0, LootTable("common.loot_tables.armor.cloth")), (1.0, LootTable("common.loot_tables.armor.cloth")),
(0.5, Item("common.items.recipes.explosives")),
] ]

View File

@ -2,4 +2,12 @@
(1.0, LootTable("common.loot_tables.weapons.components.tier-0")), (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.weapons.components.tier-1")),
(1.0, LootTable("common.loot_tables.armor.cloth")), (1.0, LootTable("common.loot_tables.armor.cloth")),
(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")),
] ]

View File

@ -2,4 +2,7 @@
(2.0, LootTable("common.loot_tables.materials.common")), (2.0, LootTable("common.loot_tables.materials.common")),
(3.0, LootTable("common.loot_tables.food.prepared")), (3.0, LootTable("common.loot_tables.food.prepared")),
(1.0, Item("common.items.armor.misc.head.straw")), (1.0, Item("common.items.armor.misc.head.straw")),
(0.5, Item("common.items.recipes.instruments")),
(1.0, Item("common.items.recipes.food")),
(1.0, Item("common.items.recipes.utility")),
] ]

View File

@ -1,4 +1,5 @@
[ [
(1.0, LootTable("common.loot_tables.armor.boreal")), (1.0, LootTable("common.loot_tables.armor.boreal")),
(1.0, Item("common.items.crafting_ing.glacial_crystal")), (1.0, Item("common.items.crafting_ing.glacial_crystal")),
(1.0, Item("common.items.recipes.unique.polaris")),
] ]

View File

@ -1,5 +1,5 @@
{ {
"crafting_hammer": ( "craftsman_hammer": (
output: ("common.items.tool.craftsman_hammer", 1), output: ("common.items.tool.craftsman_hammer", 1),
inputs: [ inputs: [
(Item("common.items.log.wood"), 1, false), (Item("common.items.log.wood"), 1, false),
@ -50,7 +50,7 @@
], ],
craft_sprite: Some(Cauldron), craft_sprite: Some(Cauldron),
), ),
"potion_s": ( "potion_minor": (
output: ("common.items.consumable.potion_minor", 1), output: ("common.items.consumable.potion_minor", 1),
inputs: [ inputs: [
(Item("common.items.crafting_ing.empty_vial"), 1, false), (Item("common.items.crafting_ing.empty_vial"), 1, false),
@ -59,7 +59,7 @@
], ],
craft_sprite: Some(Cauldron), craft_sprite: Some(Cauldron),
), ),
"potion_m": ( "potion_medium": (
output: ("common.items.consumable.potion_med", 1), output: ("common.items.consumable.potion_med", 1),
inputs: [ inputs: [
(Item("common.items.consumable.potion_minor"), 2, false), (Item("common.items.consumable.potion_minor"), 2, false),
@ -329,14 +329,6 @@
], ],
craft_sprite: Some(Forge), 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": ( "cotton": (
output: ("common.items.crafting_ing.cloth.cotton", 1), output: ("common.items.crafting_ing.cloth.cotton", 1),
inputs: [ inputs: [

View File

@ -23,6 +23,10 @@ loot_tables: [
// Food Ingredients // Food Ingredients
(20.375, true, "common.trading.food"), (20.375, true, "common.trading.food"),
// Recipes
(1.0, true, "common.trading.sellable_recipe"),
(1.0, false, "common.trading.unsellable_recipe"),
// Potions // Potions
// //
// crafted from food, no need to duplicate it here. // 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 (Armor, 0.025), // common.items.armor.misc.pants.worker_blue
(Tools, 0.015487), // common.items.weapons.staff.starter_staff (Tools, 0.015487), // common.items.weapons.staff.starter_staff
(Ingredients, 0.034626), // common.items.crafting_ing.leather_scraps (Ingredients, 0.034626), // common.items.crafting_ing.leather_scraps
(Recipe, 0.01), // common.items.recipes
]) ])

View File

@ -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")),
]

View File

@ -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")),
]

View File

@ -86,6 +86,7 @@ common-kind-throwable = Can be thrown
common-kind-utility = Utility common-kind-utility = Utility
common-kind-ingredient = Ingredient common-kind-ingredient = Ingredient
common-kind-lantern = Lantern common-kind-lantern = Lantern
common-kind-recipegroup = Recipes
common-hands-one = One-Handed common-hands-one = One-Handed
common-hands-two = Two-Handed common-hands-two = Two-Handed
common-rand_appearance = Random appearance common-rand_appearance = Random appearance

View File

@ -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-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_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-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.

View File

@ -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

View File

@ -1708,6 +1708,174 @@
"voxel.object.collar", "voxel.object.collar",
(0.1, 0.0, 0.0), (-60.0, 20.0, 10.0), 0.9, (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 // Armor
// Starter Parts // Starter Parts
Simple("common.items.armor.misc.foot.sandals"): VoxTrans( Simple("common.items.armor.misc.foot.sandals"): VoxTrans(

View File

@ -418,6 +418,14 @@
// Other // Other
Simple("common.items.utility.coins"): "voxel.object.v-coin", Simple("common.items.utility.coins"): "voxel.object.v-coin",
Simple("common.items.utility.collar"): "voxel.object.collar", 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 // Armor
// Starter Parts // Starter Parts
Simple("common.items.armor.misc.foot.sandals"): "voxel.armor.misc.foot.cloth_sandal", Simple("common.items.armor.misc.foot.sandals"): "voxel.armor.misc.foot.cloth_sandal",

BIN
assets/voxygen/voxel/object/recipe_alchemy.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/object/recipe_blacksmithing.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/object/recipe_carpentry.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/object/recipe_cooking.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/object/recipe_lapidary.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/object/recipe_leatherworking.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/object/recipe_weaving.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/voxel/object/scroll.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -1131,4 +1131,14 @@
central: ("armor.empty"), 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"),
)
),
}) })

View File

@ -37,7 +37,7 @@ use common::{
lod, lod,
mounting::{Rider, VolumePos, VolumeRider}, mounting::{Rider, VolumePos, VolumeRider},
outcome::Outcome, outcome::Outcome,
recipe::{ComponentRecipeBook, RecipeBook, RepairRecipeBook}, recipe::{ComponentRecipeBook, RecipeBookManifest, RepairRecipeBook},
resources::{GameMode, PlayerEntity, Time, TimeOfDay}, resources::{GameMode, PlayerEntity, Time, TimeOfDay},
shared_server_config::ServerConstants, shared_server_config::ServerConstants,
spiral::Spiral2d, spiral::Spiral2d,
@ -278,7 +278,6 @@ pub struct Client {
possible_starting_sites: Vec<SiteId>, possible_starting_sites: Vec<SiteId>,
pois: Vec<PoiInfo>, pois: Vec<PoiInfo>,
pub chat_mode: ChatMode, pub chat_mode: ChatMode,
recipe_book: RecipeBook,
component_recipe_book: ComponentRecipeBook, component_recipe_book: ComponentRecipeBook,
repair_recipe_book: RepairRecipeBook, repair_recipe_book: RepairRecipeBook,
available_recipes: HashMap<String, Option<SpriteKind>>, available_recipes: HashMap<String, Option<SpriteKind>>,
@ -692,6 +691,7 @@ impl Client {
*state.ecs_mut().write_resource() = PlayerEntity(Some(entity)); *state.ecs_mut().write_resource() = PlayerEntity(Some(entity));
state.ecs_mut().insert(material_stats); state.ecs_mut().insert(material_stats);
state.ecs_mut().insert(ability_map); state.ecs_mut().insert(ability_map);
state.ecs_mut().insert(recipe_book);
let map_size = map_size_lg.chunks(); let map_size = map_size_lg.chunks();
let max_height = world_map.max_height; let max_height = world_map.max_height;
@ -953,7 +953,6 @@ impl Client {
world_map.sites, world_map.sites,
world_map.possible_starting_sites, world_map.possible_starting_sites,
world_map.pois, world_map.pois,
recipe_book,
component_recipe_book, component_recipe_book,
repair_recipe_book, repair_recipe_book,
max_group_size, max_group_size,
@ -972,7 +971,6 @@ impl Client {
sites, sites,
possible_starting_sites, possible_starting_sites,
pois, pois,
recipe_book,
component_recipe_book, component_recipe_book,
repair_recipe_book, repair_recipe_book,
max_group_size, max_group_size,
@ -1019,7 +1017,6 @@ impl Client {
.collect(), .collect(),
possible_starting_sites, possible_starting_sites,
pois, pois,
recipe_book,
component_recipe_book, component_recipe_book,
repair_recipe_book, repair_recipe_book,
available_recipes: HashMap::default(), available_recipes: HashMap::default(),
@ -1492,8 +1489,6 @@ impl Client {
pub fn world_data(&self) -> &WorldData { &self.world_data } 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 component_recipe_book(&self) -> &ComponentRecipeBook { &self.component_recipe_book }
pub fn repair_recipe_book(&self) -> &RepairRecipeBook { &self.repair_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. /// entity does not have a position.
pub fn set_lod_pos_fallback(&mut self, pos: Vec2<f32>) { self.lod_pos_fallback = Some(pos); } pub fn set_lod_pos_fallback(&mut self, pos: Vec2<f32>) { 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<SpriteKind>) {
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( pub fn craft_recipe(
&mut self, &mut self,
recipe: &str, recipe: &str,
@ -1530,8 +1510,20 @@ impl Client {
craft_sprite: Option<(VolumePos, SpriteKind)>, craft_sprite: Option<(VolumePos, SpriteKind)>,
amount: u32, amount: u32,
) -> bool { ) -> bool {
let (can_craft, required_sprite) = self.can_craft_recipe(recipe, amount); let (can_craft, has_sprite) = if let Some(inventory) = self
let has_sprite = required_sprite.map_or(true, |s| Some(s) == craft_sprite.map(|(_, s)| s)); .state
.ecs()
.read_storage::<comp::Inventory>()
.get(self.entity())
{
let rbm = self.state.ecs().read_resource::<RecipeBookManifest>();
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 { if can_craft && has_sprite {
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent( self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent(
InventoryEvent::CraftRecipe { InventoryEvent::CraftRecipe {
@ -1680,12 +1672,14 @@ impl Client {
} }
fn update_available_recipes(&mut self) { fn update_available_recipes(&mut self) {
self.available_recipes = self let rbm = self.state.ecs().read_resource::<RecipeBookManifest>();
.recipe_book let inventories = self.state.ecs().read_storage::<comp::Inventory>();
.iter() if let Some(inventory) = inventories.get(self.entity()) {
.map(|(name, _)| name.clone()) self.available_recipes = inventory
.recipes_iter()
.cloned()
.filter_map(|name| { .filter_map(|name| {
let (can_craft, required_sprite) = self.can_craft_recipe(&name, 1); let (can_craft, required_sprite) = inventory.can_craft_recipe(&name, 1, &rbm);
if can_craft { if can_craft {
Some((name, required_sprite)) Some((name, required_sprite))
} else { } else {
@ -1694,6 +1688,7 @@ impl Client {
}) })
.collect(); .collect();
} }
}
/// Unstable, likely to be removed in a future release /// Unstable, likely to be removed in a future release
pub fn sites(&self) -> &HashMap<SiteId, SiteInfoRich> { &self.sites } pub fn sites(&self) -> &HashMap<SiteId, SiteInfoRich> { &self.sites }
@ -2844,6 +2839,9 @@ impl Client {
ServerGeneral::SpectatePosition(pos) => { ServerGeneral::SpectatePosition(pos) => {
frontend_events.push(Event::SpectatePosition(pos)); frontend_events.push(Event::SpectatePosition(pos));
}, },
ServerGeneral::UpdateRecipes => {
self.update_available_recipes();
},
_ => unreachable!("Not a in_game message"), _ => unreachable!("Not a in_game message"),
} }
Ok(()) Ok(())

View File

@ -10,6 +10,7 @@ hot-reloading = ["common-assets/hot-reloading"]
simd = ["vek/platform_intrinsics"] simd = ["vek/platform_intrinsics"]
bin_csv = ["ron", "csv", "clap"] bin_csv = ["ron", "csv", "clap"]
bin_graphviz = ["petgraph", "clap"] bin_graphviz = ["petgraph", "clap"]
bin_recipe_gen = ["ron"]
bin_cmd_doc_gen = [] bin_cmd_doc_gen = []
bin_asset_migrate = ["ron"] bin_asset_migrate = ["ron"]
rrt_pathfinding = ["kiddo"] rrt_pathfinding = ["kiddo"]

View File

@ -10,7 +10,7 @@ use common::{
event::{PluginHash, UpdateCharacterMetadata}, event::{PluginHash, UpdateCharacterMetadata},
lod, lod,
outcome::Outcome, outcome::Outcome,
recipe::{ComponentRecipeBook, RecipeBook, RepairRecipeBook}, recipe::{ComponentRecipeBook, RecipeBookManifest, RepairRecipeBook},
resources::{Time, TimeOfDay, TimeScale}, resources::{Time, TimeOfDay, TimeScale},
shared_server_config::ServerConstants, shared_server_config::ServerConstants,
terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
@ -68,7 +68,7 @@ pub enum ServerInit {
max_group_size: u32, max_group_size: u32,
client_timeout: Duration, client_timeout: Duration,
world_map: crate::msg::world_msg::WorldMapMsg, world_map: crate::msg::world_msg::WorldMapMsg,
recipe_book: RecipeBook, recipe_book: RecipeBookManifest,
component_recipe_book: ComponentRecipeBook, component_recipe_book: ComponentRecipeBook,
repair_recipe_book: RepairRecipeBook, repair_recipe_book: RepairRecipeBook,
material_stats: MaterialStatManifest, material_stats: MaterialStatManifest,
@ -222,6 +222,9 @@ pub enum ServerGeneral {
SpectatePosition(Vec3<f32>), SpectatePosition(Vec3<f32>),
/// Plugin data requested from the server /// Plugin data requested from the server
PluginData(Vec<u8>), PluginData(Vec<u8>),
/// Update the list of available recipes. Usually called after a new recipe
/// is acquired
UpdateRecipes,
} }
impl ServerGeneral { impl ServerGeneral {
@ -345,7 +348,8 @@ impl ServerMsg {
| ServerGeneral::MapMarker(_) | ServerGeneral::MapMarker(_)
| ServerGeneral::WeatherUpdate(_) | ServerGeneral::WeatherUpdate(_)
| ServerGeneral::LocalWindUpdate(_) | ServerGeneral::LocalWindUpdate(_)
| ServerGeneral::SpectatePosition(_) => { | ServerGeneral::SpectatePosition(_)
| ServerGeneral::UpdateRecipes => {
c_type == ClientType::Game && presence.is_some() c_type == ClientType::Game && presence.is_some()
}, },
// Always possible // Always possible

View File

@ -6,7 +6,7 @@ use clap::Parser;
use hashbrown::HashMap; use hashbrown::HashMap;
use ron::ser::{to_string_pretty, PrettyConfig}; use ron::ser::{to_string_pretty, PrettyConfig};
use serde::Serialize; use serde::Serialize;
use std::{error::Error, fs::File, io::Write}; use std::{borrow::Cow, error::Error, fs::File, io::Write};
use veloren_common::{ use veloren_common::{
assets::ASSETS_PATH, assets::ASSETS_PATH,
@ -79,9 +79,9 @@ fn armor_stats() -> Result<(), Box<dyn Error>> {
if let Ok(ref record) = record { if let Ok(ref record) = record {
if item.item_definition_id() if item.item_definition_id()
== ItemDefinitionId::Simple( == ItemDefinitionId::Simple(Cow::Borrowed(
record.get(headers["Path"]).expect("No file path in csv?"), record.get(headers["Path"]).expect("No file path in csv?"),
) ))
{ {
let protection = let protection =
if let Some(protection_raw) = record.get(headers["Protection"]) { if let Some(protection_raw) = record.get(headers["Protection"]) {
@ -287,9 +287,9 @@ fn weapon_stats() -> Result<(), Box<dyn Error>> {
if let ItemKind::Tool(tool) = &*item.kind() { if let ItemKind::Tool(tool) = &*item.kind() {
if let Ok(ref record) = record { if let Ok(ref record) = record {
if item.item_definition_id() if item.item_definition_id()
== ItemDefinitionId::Simple( == ItemDefinitionId::Simple(Cow::Borrowed(
record.get(headers["Path"]).expect("No file path in csv?"), record.get(headers["Path"]).expect("No file path in csv?"),
) ))
{ {
let kind = tool.kind; let kind = tool.kind;
let equip_time_secs: f32 = record let equip_time_secs: f32 = record

View File

@ -5,13 +5,12 @@ use petgraph::{
}; };
use std::{fs::File, io::Write}; use std::{fs::File, io::Write};
use veloren_common::{ use veloren_common::{
assets::AssetExt,
comp::item::ItemDesc, comp::item::ItemDesc,
recipe::{RecipeBook, RecipeInput}, recipe::{RecipeBookManifest, RecipeInput},
}; };
fn main() { fn main() {
let recipes = RecipeBook::load_expect_cloned("common.recipe_book"); let recipes = RecipeBookManifest::load().read();
let mut graph = Graph::new(); let mut graph = Graph::new();
let mut nodes = HashMap::new(); let mut nodes = HashMap::new();
let mut add_node = |graph: &mut Graph<_, _>, node: &str| { let mut add_node = |graph: &mut Graph<_, _>, node: &str| {

View File

@ -6,7 +6,9 @@ use crate::{
AdminRole as Role, Skill, AdminRole as Role, Skill,
}, },
generation::try_all_entity_configs, generation::try_all_entity_configs,
npc, terrain, npc,
recipe::RecipeBookManifest,
terrain,
}; };
use hashbrown::HashMap; use hashbrown::HashMap;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -137,6 +139,10 @@ lazy_static! {
.iter() .iter()
.map(|o| o.to_string().to_string()) .map(|o| o.to_string().to_string())
.collect(); .collect();
static ref RECIPES: Vec<String> = {
let rbm = RecipeBookManifest::load().cloned();
rbm.keys().cloned().collect::<Vec<String>>()
};
static ref TIMES: Vec<String> = [ static ref TIMES: Vec<String> = [
"midnight", "night", "dawn", "morning", "day", "noon", "dusk" "midnight", "night", "dawn", "morning", "day", "noon", "dusk"
] ]
@ -378,6 +384,7 @@ pub enum ServerChatCommand {
ReloadChunks, ReloadChunks,
RemoveLights, RemoveLights,
RepairEquipment, RepairEquipment,
ResetRecipes,
Respawn, Respawn,
RevokeBuild, RevokeBuild,
RevokeBuildAll, RevokeBuildAll,
@ -734,6 +741,7 @@ impl ServerChatCommand {
"Reloads chunks loaded on the server", "Reloads chunks loaded on the server",
Some(Admin), Some(Admin),
), ),
ServerChatCommand::ResetRecipes => cmd(vec![], "Resets your recipe book", Some(Admin)),
ServerChatCommand::RemoveLights => cmd( ServerChatCommand::RemoveLights => cmd(
vec![Float("radius", 20.0, Optional)], vec![Float("radius", 20.0, Optional)],
"Removes all lights spawned by players", "Removes all lights spawned by players",
@ -1030,6 +1038,7 @@ impl ServerChatCommand {
ServerChatCommand::PermitBuild => "permit_build", ServerChatCommand::PermitBuild => "permit_build",
ServerChatCommand::Players => "players", ServerChatCommand::Players => "players",
ServerChatCommand::Portal => "portal", ServerChatCommand::Portal => "portal",
ServerChatCommand::ResetRecipes => "reset_recipes",
ServerChatCommand::Region => "region", ServerChatCommand::Region => "region",
ServerChatCommand::ReloadChunks => "reload_chunks", ServerChatCommand::ReloadChunks => "reload_chunks",
ServerChatCommand::RemoveLights => "remove_lights", ServerChatCommand::RemoveLights => "remove_lights",

View File

@ -125,6 +125,7 @@ make_case_elim!(
BubbleBomb = 110, BubbleBomb = 110,
IronPikeBomb = 111, IronPikeBomb = 111,
Lavathrower = 112, 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::Arrow,
Body::Bomb, Body::Bomb,
Body::Scarecrow, Body::Scarecrow,
@ -249,6 +250,7 @@ pub const ALL_OBJECTS: [Body; 113] = [
Body::BubbleBomb, Body::BubbleBomb,
Body::IronPikeBomb, Body::IronPikeBomb,
Body::Lavathrower, Body::Lavathrower,
Body::Scroll,
]; ];
impl From<Body> for super::Body { impl From<Body> for super::Body {
@ -371,6 +373,7 @@ impl Body {
Body::SurpriseEgg => "surprise_egg", Body::SurpriseEgg => "surprise_egg",
Body::BubbleBomb => "bubble_bomb", Body::BubbleBomb => "bubble_bomb",
Body::IronPikeBomb => "iron_pike_bomb", Body::IronPikeBomb => "iron_pike_bomb",
Body::Scroll => "recipe",
} }
} }
@ -411,6 +414,7 @@ impl Body {
Body::TrainingDummy => 2000.0, Body::TrainingDummy => 2000.0,
Body::Snowball => 0.9 * WATER_DENSITY, Body::Snowball => 0.9 * WATER_DENSITY,
Body::Pebble => 1000.0, Body::Pebble => 1000.0,
Body::Scroll => 0.5 * WATER_DENSITY,
// let them sink // let them sink
_ => 1.1 * WATER_DENSITY, _ => 1.1 * WATER_DENSITY,
}; };
@ -462,7 +466,7 @@ impl Body {
| Body::ChestOpen | Body::ChestOpen
| Body::ChestSkull | Body::ChestSkull
| Body::ChestVines => 100.0, | Body::ChestVines => 100.0,
Body::Coins => 1.0, Body::Coins | Body::Scroll => 1.0,
Body::CraftingBench => 100.0, Body::CraftingBench => 100.0,
Body::Crate => 50.0, Body::Crate => 50.0,
Body::Crossbow => 200.0, Body::Crossbow => 200.0,

View File

@ -5,6 +5,7 @@ use crate::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
/// ItemKey should only be used for front-end identification purposes
#[derive(Clone, Debug, Serialize, Deserialize, Hash, Eq, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub enum ItemKey { pub enum ItemKey {
Simple(String), Simple(String),

View File

@ -366,6 +366,9 @@ pub enum ItemKind {
/// through /// through
item_ids: Vec<String>, item_ids: Vec<String>,
}, },
RecipeGroup {
recipes: Vec<String>,
},
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
@ -374,6 +377,7 @@ pub enum ConsumableKind {
Food, Food,
ComplexFood, ComplexFood,
Charm, Charm,
Recipe,
} }
impl ItemKind { impl ItemKind {
@ -404,6 +408,7 @@ impl ItemKind {
#[allow(deprecated)] #[allow(deprecated)]
ItemKind::Ingredient { descriptor } => format!("Ingredient: {}", descriptor), ItemKind::Ingredient { descriptor } => format!("Ingredient: {}", descriptor),
ItemKind::TagExamples { item_ids } => format!("TagExamples: {:?}", item_ids), ItemKind::TagExamples { item_ids } => format!("TagExamples: {:?}", item_ids),
ItemKind::RecipeGroup { .. } => String::from("Recipes:"),
} }
} }
@ -418,7 +423,8 @@ impl ItemKind {
| ItemKind::Throwable { .. } | ItemKind::Throwable { .. }
| ItemKind::Utility { .. } | ItemKind::Utility { .. }
| ItemKind::Ingredient { .. } | ItemKind::Ingredient { .. }
| ItemKind::TagExamples { .. } => false, | ItemKind::TagExamples { .. }
| ItemKind::RecipeGroup { .. } => false,
} }
} }
} }
@ -582,10 +588,7 @@ impl Serialize for ItemBase {
where where
S: Serializer, S: Serializer,
{ {
serializer.serialize_str(match self { serializer.serialize_str(&self.serialization_item_id())
ItemBase::Simple(item_def) => &item_def.item_definition_id,
ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(),
})
} }
} }
@ -609,13 +612,7 @@ impl<'de> Deserialize<'de> for ItemBase {
where where
E: de::Error, E: de::Error,
{ {
Ok( Ok(ItemBase::from_item_id_string(serialized_item_base))
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::<ItemDef>::load_expect_cloned(serialized_item_base))
},
)
} }
} }
@ -630,6 +627,23 @@ impl ItemBase {
ItemBase::Modular(_) => 0, 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::<ItemDef>::load_expect_cloned(item_id_string))
}
}
} }
// TODO: could this theorectically hold a ref to the actual components and // TODO: could this theorectically hold a ref to the actual components and
@ -637,7 +651,7 @@ impl ItemBase {
// `Vec`s) // `Vec`s)
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ItemDefinitionId<'a> { pub enum ItemDefinitionId<'a> {
Simple(&'a str), Simple(Cow<'a, str>),
Modular { Modular {
pseudo_base: &'a str, pseudo_base: &'a str,
components: Vec<ItemDefinitionId<'a>>, components: Vec<ItemDefinitionId<'a>>,
@ -664,7 +678,7 @@ pub enum ItemDefinitionIdOwned {
impl ItemDefinitionIdOwned { impl ItemDefinitionIdOwned {
pub fn as_ref(&self) -> ItemDefinitionId<'_> { pub fn as_ref(&self) -> ItemDefinitionId<'_> {
match *self { match *self {
Self::Simple(ref id) => ItemDefinitionId::Simple(id), Self::Simple(ref id) => ItemDefinitionId::Simple(Cow::Borrowed(id)),
Self::Modular { Self::Modular {
ref pseudo_base, ref pseudo_base,
ref components, ref components,
@ -694,7 +708,7 @@ impl<'a> ItemDefinitionId<'a> {
pub fn to_owned(&self) -> ItemDefinitionIdOwned { pub fn to_owned(&self) -> ItemDefinitionIdOwned {
match self { match self {
Self::Simple(id) => ItemDefinitionIdOwned::Simple(String::from(*id)), Self::Simple(id) => ItemDefinitionIdOwned::Simple(String::from(&**id)),
Self::Modular { Self::Modular {
pseudo_base, pseudo_base,
components, components,
@ -969,7 +983,7 @@ impl Item {
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let (base, components) = match item_definition_id { let (base, components) = match item_definition_id {
ItemDefinitionId::Simple(spec) => { ItemDefinitionId::Simple(spec) => {
let base = ItemBase::Simple(Arc::<ItemDef>::load_cloned(spec)?); let base = ItemBase::Simple(Arc::<ItemDef>::load_cloned(&spec)?);
(base, Vec::new()) (base, Vec::new())
}, },
ItemDefinitionId::Modular { ItemDefinitionId::Modular {
@ -1023,11 +1037,7 @@ impl Item {
/// Creates a new instance of an `Item from the provided asset identifier if /// Creates a new instance of an `Item from the provided asset identifier if
/// it exists /// it exists
pub fn new_from_asset(asset: &str) -> Result<Self, Error> { pub fn new_from_asset(asset: &str) -> Result<Self, Error> {
let inner_item = if asset.starts_with("veloren.core.pseudo_items.modular") { let inner_item = ItemBase::from_item_id_string(asset);
ItemBase::Modular(ModularBase::load_from_pseudo_id(asset))
} else {
ItemBase::Simple(Arc::<ItemDef>::load_cloned(asset)?)
};
// TODO: Get msm and ability_map less hackily // TODO: Get msm and ability_map less hackily
let msm = &MaterialStatManifest::load().read(); let msm = &MaterialStatManifest::load().read();
let ability_map = &AbilityMap::load().read(); let ability_map = &AbilityMap::load().read();
@ -1206,7 +1216,7 @@ impl Item {
match &self.item_base { match &self.item_base {
ItemBase::Simple(item_def) => { ItemBase::Simple(item_def) => {
if self.components.is_empty() { if self.components.is_empty() {
ItemDefinitionId::Simple(&item_def.item_definition_id) ItemDefinitionId::Simple(Cow::Borrowed(&item_def.item_definition_id))
} else { } else {
ItemDefinitionId::Compound { ItemDefinitionId::Compound {
simple_base: &item_def.item_definition_id, simple_base: &item_def.item_definition_id,
@ -1388,10 +1398,10 @@ impl Item {
pub fn item_hash(&self) -> u64 { self.hash } 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 { match &self.item_base {
ItemBase::Simple(item_def) => &item_def.item_definition_id, ItemBase::Simple(item_def) => item_def.item_definition_id.clone(),
ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(), ItemBase::Modular(mod_base) => String::from(mod_base.pseudo_item_id()),
} }
} }
@ -1545,6 +1555,10 @@ impl Item {
Err(other) 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 { impl FrontendItem {
@ -1816,7 +1830,7 @@ impl ItemDesc for ItemDef {
fn num_slots(&self) -> u16 { self.slots } fn num_slots(&self) -> u16 { self.slots }
fn item_definition_id(&self) -> ItemDefinitionId<'_> { fn item_definition_id(&self) -> ItemDefinitionId<'_> {
ItemDefinitionId::Simple(&self.item_definition_id) ItemDefinitionId::Simple(Cow::Borrowed(&self.item_definition_id))
} }
fn tags(&self) -> Vec<ItemTag> { self.tags.to_vec() } fn tags(&self) -> Vec<ItemTag> { self.tags.to_vec() }

View File

@ -15,13 +15,16 @@ use crate::{
MaterialStatManifest, TagExampleInfo, MaterialStatManifest, TagExampleInfo,
}, },
loadout::Loadout, loadout::Loadout,
recipe_book::RecipeBook,
slot::{EquipSlot, Slot, SlotError}, slot::{EquipSlot, Slot, SlotError},
}, },
loot_owner::LootOwnerKind, loot_owner::LootOwnerKind,
slot::{InvSlotId, SlotId}, slot::{InvSlotId, SlotId},
Item, Item,
}, },
recipe::{Recipe, RecipeBookManifest},
resources::Time, resources::Time,
terrain::SpriteKind,
uid::Uid, uid::Uid,
LoadoutBuilder, LoadoutBuilder,
}; };
@ -31,6 +34,7 @@ use super::FrontendItem;
pub mod item; pub mod item;
pub mod loadout; pub mod loadout;
pub mod loadout_builder; pub mod loadout_builder;
pub mod recipe_book;
pub mod slot; pub mod slot;
#[cfg(test)] mod test; #[cfg(test)] mod test;
#[cfg(test)] mod test_helpers; #[cfg(test)] mod test_helpers;
@ -52,6 +56,8 @@ pub struct Inventory {
/// These slots are "remove-only" meaning that during normal gameplay items /// These slots are "remove-only" meaning that during normal gameplay items
/// can only be removed from these slots and never entered. /// can only be removed from these slots and never entered.
overflow_items: Vec<Item>, overflow_items: Vec<Item>,
/// Recipes that are available for use
recipe_book: RecipeBook,
} }
/// Errors which the methods on `Inventory` produce /// Errors which the methods on `Inventory` produce
@ -131,6 +137,7 @@ impl Inventory {
loadout, loadout,
slots: vec![None; DEFAULT_INVENTORY_SLOTS], slots: vec![None; DEFAULT_INVENTORY_SLOTS],
overflow_items: Vec::new(), overflow_items: Vec::new(),
recipe_book: RecipeBook::default(),
} }
} }
@ -140,10 +147,16 @@ impl Inventory {
loadout, loadout,
slots: vec![None; 1], slots: vec![None; 1],
overflow_items: Vec::new(), 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() } pub fn capacity(&self) -> usize { self.slots().count() }
/// An iterator of all inventory slots /// An iterator of all inventory slots
@ -1085,6 +1098,57 @@ impl Inventory {
pub fn persistence_push_overflow_items<I: Iterator<Item = Item>>(&mut self, overflow_items: I) { pub fn persistence_push_overflow_items<I: Iterator<Item = Item>>(&mut self, overflow_items: I) {
self.overflow_items.extend(overflow_items); self.overflow_items.extend(overflow_items);
} }
pub fn recipes_iter(&self) -> impl ExactSizeIterator<Item = &String> { self.recipe_book.iter() }
pub fn available_recipes_iter<'a>(
&'a self,
rbm: &'a RecipeBookManifest,
) -> impl Iterator<Item = (&String, &Recipe)> + '_ {
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<SpriteKind>) {
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<Item = (usize, &Item)> {
self.recipe_book.persistence_recipes_iter_with_index()
}
} }
impl Component for Inventory { impl Component for Inventory {

View File

@ -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<Item>,
recipes: HashSet<String>,
}
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<Item = &String> { self.recipes.iter() }
pub(super) fn get_available_iter<'a>(
&'a self,
rbm: &'a RecipeBookManifest,
) -> impl Iterator<Item = (&String, &Recipe)> + '_ {
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<Item>) -> Self {
let mut book = Self {
recipe_groups,
recipes: HashSet::new(),
};
book.update();
book
}
pub fn persistence_recipes_iter_with_index(&self) -> impl Iterator<Item = (usize, &Item)> {
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)));
}
}
}

View File

@ -5,6 +5,7 @@ use crate::comp::{
Item, Item,
}; };
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::borrow::Cow;
lazy_static! { lazy_static! {
static ref TEST_ITEMS: Vec<Item> = vec![Item::new_from_asset_expect( static ref TEST_ITEMS: Vec<Item> = vec![Item::new_from_asset_expect(
"common.items.debug.admin_stick" "common.items.debug.admin_stick"
@ -24,6 +25,7 @@ fn push_full() {
.collect(), .collect(),
loadout: LoadoutBuilder::empty().build(), loadout: LoadoutBuilder::empty().build(),
overflow_items: vec![], overflow_items: vec![],
recipe_book: RecipeBook::default(),
}; };
assert_eq!( assert_eq!(
inv.push(TEST_ITEMS[0].duplicate(ability_map, msm)) inv.push(TEST_ITEMS[0].duplicate(ability_map, msm))
@ -45,6 +47,7 @@ fn push_all_full() {
.collect(), .collect(),
loadout: LoadoutBuilder::empty().build(), loadout: LoadoutBuilder::empty().build(),
overflow_items: vec![], overflow_items: vec![],
recipe_book: RecipeBook::default(),
}; };
let Error::Full(leftovers) = inv let Error::Full(leftovers) = inv
.push_all( .push_all(
@ -76,6 +79,7 @@ fn push_unique_all_full() {
.collect(), .collect(),
loadout: LoadoutBuilder::empty().build(), loadout: LoadoutBuilder::empty().build(),
overflow_items: vec![], overflow_items: vec![],
recipe_book: RecipeBook::default(),
}; };
inv.push_all_unique( inv.push_all_unique(
TEST_ITEMS TEST_ITEMS
@ -96,6 +100,7 @@ fn push_all_empty() {
slots: vec![None, None], slots: vec![None, None],
loadout: LoadoutBuilder::empty().build(), loadout: LoadoutBuilder::empty().build(),
overflow_items: vec![], overflow_items: vec![],
recipe_book: RecipeBook::default(),
}; };
inv.push_all( inv.push_all(
TEST_ITEMS TEST_ITEMS
@ -116,6 +121,7 @@ fn push_all_unique_empty() {
slots: vec![None, None], slots: vec![None, None],
loadout: LoadoutBuilder::empty().build(), loadout: LoadoutBuilder::empty().build(),
overflow_items: vec![], overflow_items: vec![],
recipe_book: RecipeBook::default(),
}; };
inv.push_all_unique( inv.push_all_unique(
TEST_ITEMS TEST_ITEMS
@ -339,7 +345,7 @@ fn equip_equipping_smaller_bag_from_last_slot_of_big_bag() {
assert_eq!( assert_eq!(
inv.get(InvSlotId::new(0, 0)).unwrap().item_definition_id(), 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()); 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 // by itself, the bag is returned to the caller
assert_eq!( assert_eq!(
result[0].item_definition_id(), 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!(18, returned_items.len());
assert_eq!( 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() returned_items[0].item_definition_id()
); );
} }

View File

@ -9,7 +9,7 @@ use crate::{
tool::AbilityMap, tool::AbilityMap,
}, },
lottery::LootSpec, lottery::LootSpec,
recipe::{default_component_recipe_book, default_recipe_book, RecipeInput}, recipe::{complete_recipe_book, default_component_recipe_book, RecipeInput},
trade::Good, trade::Good,
}; };
use assets::AssetReadGuard; use assets::AssetReadGuard;
@ -294,7 +294,7 @@ lazy_static! {
// Load recipe book (done to check that material is valid for a particular component) // Load recipe book (done to check that material is valid for a particular component)
//use crate::recipe::ComponentKey; //use crate::recipe::ComponentKey;
let recipes = default_recipe_book().read(); let recipes = complete_recipe_book().read();
recipes recipes
.iter() .iter()
@ -463,8 +463,9 @@ impl From<Vec<(f32, LootSpec<String>)>> for ProbabilityFile {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct TradingPriceFile { struct TradingPriceFile {
/// Tuple format: (frequency, can_sell, asset_path)
pub loot_tables: Vec<(f32, bool, String)>, 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)>, pub good_scaling: Vec<(Good, f32)>,
} }
@ -628,17 +629,16 @@ impl TradePricing {
ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.flowers.") => { ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.flowers.") => {
Good::Ingredients Good::Ingredients
}, },
ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.consumable.") => { ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.consumable.") => {
Good::Potions Good::Potions
}, },
ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.food.") => { ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.food.") => {
Good::Food Good::Food
}, },
ItemDefinitionIdOwned::Simple(name) if name.as_str() == Self::COIN_ITEM => Good::Coin, 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.") => { ItemDefinitionIdOwned::Simple(name) if name.starts_with("common.items.glider.") => {
Good::default() Good::default()
}, },
@ -809,7 +809,7 @@ impl TradePricing {
// Apply recipe book // Apply recipe book
let mut secondaries: HashMap<ToolKind, Vec<ItemDefinitionIdOwned>> = HashMap::new(); let mut secondaries: HashMap<ToolKind, Vec<ItemDefinitionIdOwned>> = HashMap::new();
let book = default_recipe_book().read(); let book = complete_recipe_book().read();
let mut ordered_recipes: Vec<RememberedRecipe> = Vec::new(); let mut ordered_recipes: Vec<RememberedRecipe> = Vec::new();
for (_, recipe) in book.iter() { for (_, recipe) in book.iter() {
let (ref asset_path, amount) = recipe.output; let (ref asset_path, amount) = recipe.output;

View File

@ -80,6 +80,7 @@ pub use self::{
tool::{self, AbilityItem}, tool::{self, AbilityItem},
FrontendItem, Item, ItemConfig, ItemDrops, PickupItem, FrontendItem, Item, ItemConfig, ItemDrops, PickupItem,
}, },
recipe_book::RecipeBook,
slot, CollectFailedReason, Inventory, InventoryUpdate, InventoryUpdateEvent, slot, CollectFailedReason, Inventory, InventoryUpdate, InventoryUpdateEvent,
}, },
last::Last, last::Last,

View File

@ -428,15 +428,19 @@ pub fn modular_weapon(
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RecipeBook { pub struct RecipeBookManifest {
recipes: HashMap<String, Recipe>, recipes: HashMap<String, Recipe>,
} }
impl RecipeBook { impl RecipeBookManifest {
pub fn load() -> AssetHandle<Self> { Self::load_expect("common.recipe_book_manifest") }
pub fn get(&self, recipe: &str) -> Option<&Recipe> { self.recipes.get(recipe) } pub fn get(&self, recipe: &str) -> Option<&Recipe> { self.recipes.get(recipe) }
pub fn iter(&self) -> impl ExactSizeIterator<Item = (&String, &Recipe)> { self.recipes.iter() } pub fn iter(&self) -> impl ExactSizeIterator<Item = (&String, &Recipe)> { self.recipes.iter() }
pub fn keys(&self) -> impl ExactSizeIterator<Item = &String> { self.recipes.keys() }
pub fn get_available(&self, inv: &Inventory) -> Vec<(String, Recipe)> { pub fn get_available(&self, inv: &Inventory) -> Vec<(String, Recipe)> {
self.recipes self.recipes
.iter() .iter()
@ -451,8 +455,8 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn default_recipe_valid_key_check() { fn complete_recipe_book_valid_key_check() {
let recipe_book = default_recipe_book().read(); let recipe_book = complete_recipe_book().read();
let is_invalid_key = let is_invalid_key =
|input: &str| input.chars().any(|c| c.is_uppercase() || c.is_whitespace()); |input: &str| input.chars().any(|c| c.is_uppercase() || c.is_whitespace());
assert!(!recipe_book.iter().any(|(k, _)| is_invalid_key(k))); assert!(!recipe_book.iter().any(|(k, _)| is_invalid_key(k)));
@ -517,7 +521,7 @@ impl assets::Asset for ItemList {
const EXTENSION: &'static str = "ron"; const EXTENSION: &'static str = "ron";
} }
impl assets::Compound for RecipeBook { impl assets::Compound for RecipeBookManifest {
fn load( fn load(
cache: assets::AnyCache, cache: assets::AnyCache,
specifier: &assets::SharedString, specifier: &assets::SharedString,
@ -562,7 +566,7 @@ impl assets::Compound for RecipeBook {
) )
.collect::<Result<_, assets::Error>>()?; .collect::<Result<_, assets::Error>>()?;
Ok(RecipeBook { recipes }) Ok(RecipeBookManifest { recipes })
} }
} }
@ -617,6 +621,7 @@ pub struct ComponentKey {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ComponentRecipe { pub struct ComponentRecipe {
pub recipe_book_key: String,
output: ComponentOutput, output: ComponentOutput,
material: (RecipeInput, u32), material: (RecipeInput, u32),
modifier: Option<(RecipeInput, u32)>, modifier: Option<(RecipeInput, u32)>,
@ -810,6 +815,7 @@ impl<'a> ExactSizeIterator for ComponentRecipeInputsIterator<'a> {
#[derive(Clone, Deserialize)] #[derive(Clone, Deserialize)]
struct RawComponentRecipe { struct RawComponentRecipe {
recipe_book_key: String,
output: RawComponentOutput, output: RawComponentOutput,
/// String refers to an item definition id /// String refers to an item definition id
material: (String, u32), material: (String, u32),
@ -881,7 +887,9 @@ impl assets::Compound for ComponentRecipeBook {
.iter() .iter()
.map(|(input, amount)| input.load_recipe_input().map(|input| (input, *amount))) .map(|(input, amount)| input.load_recipe_input().map(|input| (input, *amount)))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let recipe_book_key = String::from(&raw_recipe.recipe_book_key);
Ok(ComponentRecipe { Ok(ComponentRecipe {
recipe_book_key,
output, output,
material, material,
modifier, modifier,
@ -1112,8 +1120,8 @@ impl assets::Compound for RepairRecipeBook {
} }
} }
pub fn default_recipe_book() -> AssetHandle<RecipeBook> { pub fn complete_recipe_book() -> AssetHandle<RecipeBookManifest> {
RecipeBook::load_expect("common.recipe_book") RecipeBookManifest::load_expect("common.recipe_book_manifest")
} }
pub fn default_component_recipe_book() -> AssetHandle<ComponentRecipeBook> { pub fn default_component_recipe_book() -> AssetHandle<ComponentRecipeBook> {

Some files were not shown because too many files have changed in this diff Show More