From 36eab1b3237dbeeaaf5b48808bf0d47eb4bd32e9 Mon Sep 17 00:00:00 2001 From: Tobias Wilken Date: Tue, 14 Jul 2020 18:56:42 +0200 Subject: [PATCH] Introduce recipe book functionality (#4493) * Introduce recipe book functionality The recipe book helps especially new players. Missing it gives the impression that cuberite is not as advanced as it is. The handling of the recipe book uses the following functions: - Unlock Recipes (https://wiki.vg/index.php?title=Protocol&oldid=14204#Unlock_Recipes) to make recipes available and show the notification for new recipes. Initialization is done on player login for known ones, the update is done when new items are discovered. - Craft Recipe Request (https://wiki.vg/index.php?title=Protocol&oldid=14204#Craft_Recipe_Request) when the user selects a recipe from the recipe book to fill the slots. Known recipes are initialized on player login via `Unlock Recipes` with `Action` 0. As soon as a new recipe is discovered this is added via `Unlock Recipes` with `Action` 1. To be able to know and recognize new recipes the player class is extended with `KnownItems` and `KnownRecipes`. As soon as a player touches an item this is compared to the list of `KnownItems`, if the item is unknown the recipes are checked for this item and the other ingredients are checked with the list of `KnownItems`. If a full match is discovered the recipe is unlocked with the client and stored in the `KnownRecipes`. To unlock recipes the recipe ID is sent to the client. A mapping file (for protocol 1.12.2) translated the minecraft recipe names to ids. The crafting.txt is extended with and minecraft recipe names is possible. Limitations: Only a single recipe is added to the crafting area. Multiple clicks or shift click does not increase the number of builds. Co-authored-by: peterbell10 * Address first issues mentioned by @peterbell10 - Some linting - Extract loading of recipe specific protocol mapping into a function - Build `RecipeNameMap` only once - Use `std::optional` - Extract `LoadRecipe` from `Window` * Start to implement new suggestions * Update with suggestions from @peterbell10 * Some minor cleanup * Update protocol packet IDs * Remove unused include * Include header in cmake * Change a vector to integer counter * Change dromedaryCase method names to PascalCase * Address suggestions from @madmaxoft * Read Protocol subdirectories to load recipe books To load all recipebooks iterate over the `Protocol` subdirectories to find mapping files. Co-authored-by: peterbell10 --- CONTRIBUTORS | 1 + Server/Plugins/APIDump/APIDesc.lua | 34 ++ Server/Protocol/1.12.2/base.recipes.txt | 434 +++++++++++++ Server/crafting.txt | 768 ++++++++++++------------ src/ClientHandle.cpp | 44 +- src/ClientHandle.h | 10 + src/CraftingRecipes.cpp | 95 ++- src/CraftingRecipes.h | 52 +- src/Entities/Player.cpp | 102 +++- src/Entities/Player.h | 14 + src/Inventory.cpp | 26 +- src/Inventory.h | 7 +- src/Item.h | 25 + src/ItemGrid.cpp | 29 +- src/ItemGrid.h | 6 +- src/Protocol/CMakeLists.txt | 2 + src/Protocol/Packetizer.cpp | 5 +- src/Protocol/Protocol.h | 3 + src/Protocol/ProtocolRecognizer.cpp | 21 + src/Protocol/ProtocolRecognizer.h | 2 + src/Protocol/Protocol_1_12.cpp | 76 ++- src/Protocol/Protocol_1_12.h | 9 +- src/Protocol/Protocol_1_13.cpp | 1 + src/Protocol/Protocol_1_8.cpp | 20 + src/Protocol/Protocol_1_8.h | 2 + src/Protocol/Protocol_1_9.cpp | 6 + src/Protocol/RecipeMapper.cpp | 128 ++++ src/Protocol/RecipeMapper.h | 35 ++ src/Root.cpp | 2 + src/Root.h | 8 +- src/UI/CraftingWindow.cpp | 6 + src/UI/CraftingWindow.h | 7 +- src/UI/InventoryWindow.cpp | 6 + src/UI/InventoryWindow.h | 6 +- src/UI/SlotArea.cpp | 70 ++- src/UI/SlotArea.h | 9 +- 36 files changed, 1614 insertions(+), 457 deletions(-) create mode 100644 Server/Protocol/1.12.2/base.recipes.txt create mode 100644 src/Protocol/RecipeMapper.cpp create mode 100644 src/Protocol/RecipeMapper.h diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 281ca6f74..c1e8c171c 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -63,6 +63,7 @@ Sxw1212 Taugeshtu tigerw (Tiger Wang) tonibm19 +TooAngel UltraCoderRU Warmist WebFreak001 diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index ab31ad6b2..a55c2cade 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -6345,6 +6345,23 @@ These ItemGrids are available in the API and can be manipulated by the plugins, }, Notes = "Adds the specified damage (1 by default) to the specified item. Removes the item and returns true if the item reached its max damage and was destroyed.", }, + FindItem = + { + Params = + { + { + Name = "RecipeItem", + Type = "cItem", + }, + }, + Returns = + { + { + Type = "cItem", + }, + }, + Notes = "Finds an item in the shield, hotbar and inventory slots matching `ItemType` and `ItemDamage`. The actual item is returned, if none is found `nullptr`. This can be used to validate that the player has a specific type of item.", + }, GetArmorGrid = { Returns = @@ -7496,6 +7513,23 @@ This class represents a 2D array of items. It is used as the underlying storage Notes = "Destroys the item in the specified slot", }, }, + FindItem = + { + Params = + { + { + Name = "RecipeItem", + Type = "cItem", + }, + }, + Returns = + { + { + Type = "cItem", + }, + }, + Notes = "Finds an item within the grid matching `ItemType` and `ItemDamage`. The actual item is returned, if none is found `nullptr`.", + }, GetFirstEmptySlot = { Returns = diff --git a/Server/Protocol/1.12.2/base.recipes.txt b/Server/Protocol/1.12.2/base.recipes.txt new file mode 100644 index 000000000..e5c52fa8b --- /dev/null +++ b/Server/Protocol/1.12.2/base.recipes.txt @@ -0,0 +1,434 @@ +# recipeId minecraftName + +11 yellow_wool +12 yellow_terracotta +13 yellow_stained_glass_pane +14 yellow_stained_glass +15 yellow_dye_from_sunflower +16 yellow_dye_from_dandelion +17 yellow_concrete_powder +18 yellow_carpet +19 yellow_bed_from_white_bed +20 yellow_bed +21 yellow_banner +22 writable_book +23 wooden_sword +24 wooden_shovel +25 wooden_pressure_plate +26 wooden_pickaxe +27 wooden_hoe +28 oak_door +29 oak_button +30 wooden_axe +31 white_terracotta +32 white_stained_glass_pane +33 white_stained_glass +34 white_concrete_powder +35 white_carpet +36 white_bed +37 white_banner +38 wheat +39 tripwire_hook +40 trapped_chest +41 oak_trapdoor +42 torch +43 tnt_minecart +44 tnt +45 sugar +46 white_wool_from_string +47 stone_bricks +48 stone_sword +49 stone_stairs +50 stone_slab +51 stone_shovel +52 stone_pressure_plate +53 stone_pickaxe +54 stone_hoe +55 stone_button +56 stone_brick_stairs +57 stone_brick_slab +58 stone_axe +59 sticky_piston +60 stick +61 spruce_stairs +62 spruce_slab +63 spruce_planks +64 spruce_fence_gate +65 spruce_fence +66 spruce_door +67 spruce_boat +68 spectral_arrow +69 glistering_melon_slice +70 snow +71 snow_block +72 smooth_sandstone +73 smooth_red_sandstone +74 slime_ball +75 slime_block +76 oak_sign +77 shield +78 shears +79 sea_lantern +80 sandstone_stairs +81 sandstone_slab +82 sandstone +83 repeater +84 redstone_torch +85 redstone_lamp +86 redstone_block +87 redstone +88 red_wool +89 red_terracotta +90 red_stained_glass_pane +91 red_stained_glass +92 red_sandstone_stairs +93 red_sandstone_slab +94 red_sandstone +95 red_nether_bricks +96 red_dye_from_tulip +97 red_dye_from_rose_bush +98 red_dye_from_poppy +99 red_dye_from_beetroot +100 red_concrete_powder +101 red_carpet +102 red_bed_from_white_bed +103 red_bed +104 red_banner +105 rail +106 rabbit_stew_from_red_mushroom +107 rabbit_stew_from_brown_mushroom +108 quartz_stairs +109 quartz_slab +110 quartz_block +111 purpur_stairs +112 purpur_slab +113 purpur_pillar +114 purpur_block +115 purple_wool +116 purple_terracotta +117 purple_stained_glass_pane +118 purple_stained_glass +119 purpur_shulker_box +120 purple_dye +121 purple_concrete_powder +122 purple_carpet +123 purple_bed_from_white_bed +124 purple_bed +125 purple_banner +126 pumpkin_seeds +127 pumpkin_pie +128 prismarine_bricks +129 prismarine +130 polished_granite +131 polished_diorite +132 polished_andesite +133 piston +134 pink_wool +135 pink_terracotta +136 pink_stained_glass_pane +137 pink_stained_glass +138 pink_dye_from_red_white_dye +139 pink_tulip +140 peony +141 pink_concrete_powder +142 pink_carpet +143 pink_bed_from_white_bed +144 pink_bed +145 pink_banner +146 quartz_pillar +147 paper +148 painting +149 orange_wool +150 orange_terracotta +151 orange_stained_glass_pane +152 orange_stained_glass +153 orange_dye_from_red_yellow +154 orange_dye_from_orange_tulip +155 orange_concrete_powder +156 orange_carpet +157 orange_bed_from_white_bed +158 orange_bed +159 orange_banner +160 observer +161 oak_stairs +162 oak_slab +163 oak_planks +164 note_block +165 nether_wart_block +166 nether_brick_stairs +167 nether_brick_slab +168 nether_brick_fence +169 nether_bricks +170 mushroom_stew +171 mossy_stone_bricks +172 mossy_cobblestone_wall +173 mossy_cobblestone +174 minecart +175 melon_seeds +176 melon +177 map +178 magma_cream +179 magma_block +180 magenta_wool +181 magenta_terracotta +182 magenta_stained_glass_pane +183 magenta_stained_glass +184 magenta_dye_from_purple_and_pink +185 magenta_dye_from_lilac +186 magenta_dye_from_blue_red_pink +187 magenta_dye_from_blue_red_white_dye +188 magenta_dye_from_allium +189 magenta_concrete_powder +190 magenta_carpet +191 magenta_bed_from_white_bed +192 magenta_bed +193 magenta_banner +194 jack_o_lantern +195 lime_wool +196 lime_terracotta +197 lime_stained_glass_pane +198 lime_stained_glass +199 lime_dye +200 lime_concrete_powder +201 lime_carpet +202 lime_bed_from_white_bed +203 lime_bed +204 lime_banner +205 light_weighted_pressure_plate +206 light_gray_wool +207 light_gray_terracotta +208 light_gray_stained_glass_pane +209 light_gray_stained_glass +210 light_gray_dye_from_white_tulip +211 light_gray_dye_from_oxeye_daisy +212 light_gray_dye_from_gray_white_dye +213 light_gray_dye_from_black_white_dye +214 light_gray_dye_from_azure_bluet +215 light_gray_concrete_powder +216 light_gray_carpet +217 light_gray_bed_from_white_bed +218 light_gray_bed +219 light_gray_banner +220 light_blue_wool +221 light_blue_terracotta +222 light_blue_stained_glass_pane +223 light_blue_stained_glass +224 light_blue_dye_from_blue_white_dye +225 light_blue_dye_from_blue_orchid +226 light_blue_concrete_powder +227 light_blue_carpet +228 light_blue_bed_from_white_bed +229 light_blue_bed +230 light_blue_banner +231 lever +232 leather_leggings +233 leather_helmet +234 leather_chestplate +235 leather_boots +236 leather +237 lead +238 lapis_lazuli +239 lapis_block +240 ladder +241 jungle_slab +242 jungle_stairs +243 jungle_planks +244 jungle_fence_gate +245 jungle_fence +246 jungle_door +247 jungle_boat +248 jukebox +249 item_frame +250 iron_trapdoor +251 iron_sword +252 iron_shovel +253 iron_pickaxe +254 iron_nugget +255 iron_leggings +256 iron_ingot_from_iron_block +257 iron_ingot_from_nuggets +258 iron_hoe +259 iron_helmet +260 iron_door +261 iron_chestplate +262 iron_boots +263 iron_block +264 iron_bars +265 iron_axe +266 hopper_minecart +267 hopper +268 heavy_weighted_pressure_plate +269 hay_block +270 green_wool +271 green_terracotta +272 green_stained_glass_pane +273 green_stained_glass +274 green_concrete_powder +275 green_carpet +276 green_bed_from_white_bed +277 green_bed +278 green_banner +279 gray_wool +280 gray_terracotta +281 gray_stained_glass_pane +282 gray_stained_glass +283 gray_dye +284 gray_concrete_powder +285 gray_carpet +286 gray_bed_from_white_bed +287 gray_bed +288 gray_banner +289 granite +290 golden_sword +291 golden_shovel +292 powered_rail +293 golden_pickaxe +294 golden_leggings +295 golden_hoe +296 golden_helmet +297 golden_chestplate +298 golden_carrot +299 golden_boots +300 golden_axe +301 golden_apple +302 gold_nugget +303 gold_ingot_from_gold_block +304 gold_ingot +305 gold_block +306 glowstone +307 glass_pane +308 glass_bottle +309 furnace_minecart +310 furnace +311 flower_pot +312 flint_and_steel +313 fishing_rod +314 fire_charge +315 fermented_spider_eye +316 oak_fence_gate +317 oak_fence +318 ender_eye +319 ender_chest +320 end_rod +321 end_crystal +322 end_stone_bricks +323 enchanting_table +324 emerald_block +325 emerald +326 dropper +327 dispenser +328 diorite +329 diamond_sword +330 diamond_shovel +331 diamond_pickaxe +332 diamond_leggings +333 diamond_hoe +334 diamond_helmet +335 diamond_chestplate +336 diamond_boots +337 diamond_block +338 diamond_axe +339 diamond +340 detector_rail +341 daylight_detector +342 dark_prismarine +343 dark_oak_slab +344 dark_oak_stairs +345 dark_oak_planks +346 dark_oak_fence_gate +347 dark_oak_fence +348 dark_oak_door +349 dark_oak_boat +350 cyan_wool +351 cyan_terracotta +352 cyan_stained_glass_pane +353 cyan_stained_glass +354 cyan_dye +355 cyan_concrete_powder +356 cyan_carpet +357 cyan_bed_from_white_bed +358 cyan_bed +359 cyan_banner +360 crafting_table +361 cookie +362 compass +363 comparator +364 cobblestone_wall +365 cobblestone_slab +366 coarse_dirt +367 coal_block +368 coal +369 clock +370 clay +371 chiseled_stone_bricks +372 chiseled_sandstone +373 chiseled_red_sandstone +374 chiseled_quartz_block +375 chest_minecart +376 chest +377 cauldron +378 carrot_on_a_stick +379 cake +380 bucket +381 brown_wool +382 brown_terracotta +383 brown_stained_glass_pane +384 brown_stained_glass +385 brown_concrete_powder +386 brown_carpet +387 brown_bed_from_white_bed +388 brown_bed +389 brown_banner +390 brick_stairs +391 brick_slab +392 bricks +393 brewing_stand +394 bread +395 bowl +396 bow +397 bookshelf +398 book +399 bone_meal +400 bone_meal_from_bone_block +401 bone_block +402 oak_boat +403 blue_wool +404 blue_terracotta +405 blue_stained_glass_pane +406 blue_stained_glass +407 blue_concrete_powder +408 blue_carpet +409 blue_bed_from_white_bed +410 blue_bed +411 blue_banner +412 blaze_powder +413 black_wool +414 black_terracotta +415 black_stained_glass_pane +416 black_stained_glass +417 black_concrete_powder +418 black_carpet +419 black_bed_from_white_bed +420 black_bed +421 black_banner +422 birch_slab +423 birch_stairs +424 birch_planks +425 birch_fence_gate +426 birch_fence +427 birch_door +428 birch_boat +429 beetroot_soup +430 beacon +431 arrow +432 armor_stand +433 anvil +434 andesite +435 activator_rail +436 acacia_slab +437 acacia_stairs +438 acacia_planks +439 acacia_fence_gate +440 acacia_fence +441 acacia_door +442 acacia_boat diff --git a/Server/crafting.txt b/Server/crafting.txt index c85943996..e1a07d96b 100644 --- a/Server/crafting.txt +++ b/Server/crafting.txt @@ -43,19 +43,19 @@ # Need to list each of the four log types, otherwise all logs would get converted into apple planks (^0) -AcaciaPlanks, 4 = AcaciaLog, * -BirchPlanks, 4 = BirchLog, * -Chest = Planks^-1, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 -DarkOakPlanks, 4 = DarkOakLog, * -EnderChest = EyeOfEnder, 2:2 | Obsidian, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 -Furnace = Cobblestone, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 -JunglePlanks, 4 = JungleLog, * -OakPlanks, 4 = OakLog, * -SprucePlanks, 4 = SpruceLog, * -Stick, 4 = Planks^-1, 2:2, 2:3 -Torch, 4 = Stick, 1:2 | Coal^-1, 1:1 -TrappedChest = TripWireHook, * | Chest, * -Workbench = Planks^-1, 1:1, 1:2, 2:1, 2:2 +acacia_planks: AcaciaPlanks, 4 = AcaciaLog, * +birch_planks: BirchPlanks, 4 = BirchLog, * +chest: Chest = Planks^-1, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 +dark_oak_planks: DarkOakPlanks, 4 = DarkOakLog, * +ender_chest: EnderChest = EyeOfEnder, 2:2 | Obsidian, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 +furnace: Furnace = Cobblestone, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 +jungle_planks: JunglePlanks, 4 = JungleLog, * +oak_planks: OakPlanks, 4 = OakLog, * +spruce_planks: SprucePlanks, 4 = SpruceLog, * +stick: Stick, 4 = Planks^-1, 2:2, 2:3 +torch: Torch, 4 = Stick, 1:2 | Coal^-1, 1:1 +trapped_chest: TrappedChest = TripWireHook, * | Chest, * +crafting_table: Workbench = Planks^-1, 1:1, 1:2, 2:1, 2:2 @@ -64,110 +64,110 @@ Workbench = Planks^-1, 1:1, 1:2, 2:1, 2:2 #******************************************************# # Blocks # -Andesite, 2 = Diorite, * | Cobblestone, * -BoneBlock = BoneMeal, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -BookShelf = Planks^-1, 1:1, 2:1, 3:1, 1:3, 2:3, 3:3 | Book, 1:2, 2:2, 3:2 -BrickBlock = Brick, 1:1, 1:2, 2:1, 2:2 -ChiseledQuartzBlock = QuartzSlab, 1:1, 1:2 -ChiseledRedSandstone = RedSandstoneSlab, 1:1, 1:2 -ChiseledStoneBrick = StoneBrickSlab, 1:1, 1:2 -ClayBlock = Clay, 1:1, 1:2, 2:1, 2:2 -CoalBlock = Coal, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -CoarsedDirt, 4 = Dirt, 1:1, 2:2 | Gravel, 1:2, 2:1 +andesite: Andesite, 2 = Diorite, * | Cobblestone, * +bone_block: BoneBlock = BoneMeal, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +bookshelf: BookShelf = Planks^-1, 1:1, 2:1, 3:1, 1:3, 2:3, 3:3 | Book, 1:2, 2:2, 3:2 +bricks: BrickBlock = Brick, 1:1, 1:2, 2:1, 2:2 +chiseled_quartz_block: ChiseledQuartzBlock = QuartzSlab, 1:1, 1:2 +chiseled_red_sandstone: ChiseledRedSandstone = RedSandstoneSlab, 1:1, 1:2 +chiseled_stone_bricks: ChiseledStoneBrick = StoneBrickSlab, 1:1, 1:2 +clay: ClayBlock = Clay, 1:1, 1:2, 2:1, 2:2 +coal_block: CoalBlock = Coal, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +coarse_dirt: CoarsedDirt, 4 = Dirt, 1:1, 2:2 | Gravel, 1:2, 2:1 CoarsedDirt, 4 = Gravel, 1:1, 2:2 | Dirt, 1:2, 2:1 -DarkPrismarine = PrismarineShard, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Inksac, 2:2 -DiamondBlock = Diamond, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -Diorite, 2 = Cobblestone, * | NetherQuartz, * -EmeraldBlock = Emerald, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -EndstoneBrick, 4 = Endstone, 1:1, 1:2, 2:1, 2:2 -Glowstone = GlowstoneDust, 1:1, 1:2, 2:1, 2:2 -GoldBlock = GoldIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -Granite, 2 = Diorite, * | NetherQuartz, * -HayBale = Wheat, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -IronBlock = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -JackOLantern = Pumpkin, 1:1 | Torch, 1:2 -LapisBlock = LapisLazuli, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -Leather = RabbitHide, 1:1, 1:2, 2:1, 2:2 -MossyCobblestone = Cobblestone, * | Vines, * -MossyStoneBrick = Stonebrick, * | Vines, * -NetherBrickBlock = NetherBrick, 1:1, 1:2, 2:1, 2:2 -NetherWartBlock = NetherWart, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -OrnamentSandstone = SandstoneSlab, 1:1, 1:2 -PillarQuartzBlock, 2 = QuartzBlock, 1:1, 1:2 -PolishedAndesite, 4 = Andesite, 1:1, 1:2, 2:1, 2:2 -PolishedDiorite, 4 = Diorite, 1:1, 1:2, 2:1, 2:2 -PolishedGranite, 4 = Granite, 1:1, 1:2, 2:1, 2:2 -Prismarine = PrismarineShard, 1:1, 1:2, 2:1, 2:2 -PrismarineBricks = PrismarineShard, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -PurpurBlock, 4 = PoppedChorusFruit, 1:1, 1:2, 2:1, 2:2 -PurpurPillar, 1 = PurpurSlab, 1:1, 1:2 -QuartzBlock = NetherQuartz, 1:1, 1:2, 2:1, 2:2 -RedNetherBrick = NetherBrick, 1:1, 2:2 | NetherWart, 1:2, 2:1 +dark_prismarine: DarkPrismarine = PrismarineShard, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Inksac, 2:2 +diamond_block: DiamondBlock = Diamond, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +diorite: Diorite, 2 = Cobblestone, * | NetherQuartz, * +emerald_block: EmeraldBlock = Emerald, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +end_stone_bricks: EndstoneBrick, 4 = Endstone, 1:1, 1:2, 2:1, 2:2 +glowstone: Glowstone = GlowstoneDust, 1:1, 1:2, 2:1, 2:2 +gold_block: GoldBlock = GoldIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +granite: Granite, 2 = Diorite, * | NetherQuartz, * +hay_block: HayBale = Wheat, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +iron_block: IronBlock = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +jack_o_lantern: JackOLantern = Pumpkin, 1:1 | Torch, 1:2 +lapis_block: LapisBlock = LapisLazuli, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +leather: Leather = RabbitHide, 1:1, 1:2, 2:1, 2:2 +mossy_cobblestone: MossyCobblestone = Cobblestone, * | Vines, * +mossy_stone_bricks: MossyStoneBrick = Stonebrick, * | Vines, * +nether_bricks: NetherBrickBlock = NetherBrick, 1:1, 1:2, 2:1, 2:2 +nether_wart_block: NetherWartBlock = NetherWart, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +chiseled_sandstone: OrnamentSandstone = SandstoneSlab, 1:1, 1:2 +quartz_pillar: PillarQuartzBlock, 2 = QuartzBlock, 1:1, 1:2 +polished_andesite: PolishedAndesite, 4 = Andesite, 1:1, 1:2, 2:1, 2:2 +polished_diorite: PolishedDiorite, 4 = Diorite, 1:1, 1:2, 2:1, 2:2 +polished_granite: PolishedGranite, 4 = Granite, 1:1, 1:2, 2:1, 2:2 +prismarine: Prismarine = PrismarineShard, 1:1, 1:2, 2:1, 2:2 +prismarine_bricks: PrismarineBricks = PrismarineShard, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +purpur_block: PurpurBlock, 4 = PoppedChorusFruit, 1:1, 1:2, 2:1, 2:2 +purpur_pillar: PurpurPillar, 1 = PurpurSlab, 1:1, 1:2 +quartz_block: QuartzBlock = NetherQuartz, 1:1, 1:2, 2:1, 2:2 +red_nether_bricks: RedNetherBrick = NetherBrick, 1:1, 2:2 | NetherWart, 1:2, 2:1 RedNetherBrick = NetherWart, 1:1, 2:2 | NetherBrick, 1:2, 2:1 -RedSandstone = RedSand, 1:1, 1:2, 2:1, 2:2 -RedstoneBlock = RedstoneDust, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -Sandstone = Sand, 1:1, 1:2, 2:1, 2:2 -SeaLantern = PrismarineShard, 1:1, 1:3, 3:1, 3:3 | PrismarineCrystals, 1:2, 2:1, 2:2, 2:3, 3:2 -SlimeBlock = Slimeball, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -SmoothRedSandstone, 4 = RedSandstone, 1:1, 1:2, 2:1, 2:2 -SmoothSandstone, 4 = Sandstone, 1:1, 1:2, 2:1, 2:2 -SnowBlock = SnowBall, 1:1, 1:2, 2:1, 2:2 -StoneBrick, 4 = Stone, 1:1, 1:2, 2:1, 2:2 -TNT = Gunpowder, 1:1, 3:1, 2:2, 1:3, 3:3 | Sand, 2:1, 1:2, 3:2, 2:3 +red_sandstone: RedSandstone = RedSand, 1:1, 1:2, 2:1, 2:2 +redstone_block: RedstoneBlock = RedstoneDust, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +sandstone: Sandstone = Sand, 1:1, 1:2, 2:1, 2:2 +sea_lantern: SeaLantern = PrismarineShard, 1:1, 1:3, 3:1, 3:3 | PrismarineCrystals, 1:2, 2:1, 2:2, 2:3, 3:2 +slime_block: SlimeBlock = Slimeball, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +smooth_red_sandstone: SmoothRedSandstone, 4 = RedSandstone, 1:1, 1:2, 2:1, 2:2 +smooth_sandstone: SmoothSandstone, 4 = Sandstone, 1:1, 1:2, 2:1, 2:2 +snow_block: SnowBlock = SnowBall, 1:1, 1:2, 2:1, 2:2 +stone_bricks: StoneBrick, 4 = Stone, 1:1, 1:2, 2:1, 2:2 +tnt: TNT = Gunpowder, 1:1, 3:1, 2:2, 1:3, 3:3 | Sand, 2:1, 1:2, 3:2, 2:3 Wool = String, 1:1, 1:2, 2:1, 2:2 # Slabs: -AcaciaWoodSlab, 6 = AcaciaPlanks, 1:1, 2:1, 3:1 -BirchWoodSlab, 6 = BirchPlanks, 1:1, 2:1, 3:1 -BrickSlab, 6 = BrickBlock, 1:1, 2:1, 3:1 -CobblestoneSlab, 6 = Cobblestone, 1:1, 2:1, 3:1 -DarkOakWoodSlab, 6 = DarkOakPlanks, 1:1, 2:1, 3:1 -JungleWoodSlab, 6 = JunglePlanks, 1:1, 2:1, 3:1 -NetherBrickSlab, 6 = NetherBrickBlock, 1:1, 2:1, 3:1 -OakWoodSlab, 6 = OakPlanks, 1:1, 2:1, 3:1 -PurpurSlab, 6 = PurpurBlock, 1:1, 2:1, 3:1 -Quartzslab, 6 = QuartzBlock, 1:1, 2:1, 3:1 -RedSandstoneSlab, 6 = RedSandstone^-1, 1:1, 2:1, 3:1 -SandstoneSlab, 6 = OrnamentSandstone, 1:1, 2:1, 3:1 +acacia_slab: AcaciaWoodSlab, 6 = AcaciaPlanks, 1:1, 2:1, 3:1 +birch_slab: BirchWoodSlab, 6 = BirchPlanks, 1:1, 2:1, 3:1 +brick_slab: BrickSlab, 6 = BrickBlock, 1:1, 2:1, 3:1 +cobblestone_slab: CobblestoneSlab, 6 = Cobblestone, 1:1, 2:1, 3:1 +dark_oak_slab: DarkOakWoodSlab, 6 = DarkOakPlanks, 1:1, 2:1, 3:1 +jungle_slab: JungleWoodSlab, 6 = JunglePlanks, 1:1, 2:1, 3:1 +nether_brick_slab: NetherBrickSlab, 6 = NetherBrickBlock, 1:1, 2:1, 3:1 +oak_slab: OakWoodSlab, 6 = OakPlanks, 1:1, 2:1, 3:1 +purpur_slab: PurpurSlab, 6 = PurpurBlock, 1:1, 2:1, 3:1 +quartz_slab: Quartzslab, 6 = QuartzBlock, 1:1, 2:1, 3:1 +red_sandstone_slab: RedSandstoneSlab, 6 = RedSandstone^-1, 1:1, 2:1, 3:1 +sandstone_slab: SandstoneSlab, 6 = OrnamentSandstone, 1:1, 2:1, 3:1 SandstoneSlab, 6 = Sandstone, 1:1, 2:1, 3:1 SandstoneSlab, 6 = SmoothSandstone, 1:1, 2:1, 3:1 -SnowLayer, 6 = SnowBlock, 1:1, 2:1, 3:1 -SpruceWoodSlab, 6 = SprucePlanks, 1:1, 2:1, 3:1 -StonebrickSlab, 6 = StoneBrick, 1:1, 2:1, 3:1 -StoneSlab, 6 = Stone, 1:1, 2:1, 3:1 +snow: SnowLayer, 6 = SnowBlock, 1:1, 2:1, 3:1 +spruce_slab: SpruceWoodSlab, 6 = SprucePlanks, 1:1, 2:1, 3:1 +stone_brick_slab: StonebrickSlab, 6 = StoneBrick, 1:1, 2:1, 3:1 +stone_slab: StoneSlab, 6 = Stone, 1:1, 2:1, 3:1 # Stairs: -AcaciaWoodStairs, 4 = AcaciaPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +acacia_stairs: AcaciaWoodStairs, 4 = AcaciaPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 AcaciaWoodStairs, 4 = AcaciaPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -BirchWoodStairs, 4 = BirchPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +birch_stairs: BirchWoodStairs, 4 = BirchPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 BirchWoodStairs, 4 = BirchPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -BrickStairs, 4 = BrickBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +brick_stairs: BrickStairs, 4 = BrickBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 BrickStairs, 4 = BrickBlock, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -cobblestoneStairs, 4 = Cobblestone, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +stone_stairs: cobblestoneStairs, 4 = Cobblestone, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 cobblestoneStairs, 4 = Cobblestone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -DarkOakWoodStairs, 4 = DarkOakPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +dark_oak_stairs: DarkOakWoodStairs, 4 = DarkOakPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 DarkOakWoodStairs, 4 = DarkOakPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -JungleWoodStairs, 4 = JunglePlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +jungle_stairs: JungleWoodStairs, 4 = JunglePlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 JungleWoodStairs, 4 = JunglePlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -NetherBrickStairs, 4 = NetherBrickBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +nether_brick_stairs: NetherBrickStairs, 4 = NetherBrickBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 NetherBrickStairs, 4 = NetherBrickBlock, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -PurpurStairs, 4 = PurpurBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +purpur_stairs: PurpurStairs, 4 = PurpurBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 PurpurStairs, 4 = PurpurBlock, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -QuartzStairs, 4 = QuartzBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +quartz_stairs: QuartzStairs, 4 = QuartzBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 QuartzStairs, 4 = QuartzBlock, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -RedSandstoneStairs, 4 = RedSandstone^-1, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +red_sandstone_stairs: RedSandstoneStairs, 4 = RedSandstone^-1, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 RedSandstoneStairs, 4 = RedSandstone^-1, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -SandstoneStairs, 4 = OrnamentSandstone, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +sandstone_stairs: SandstoneStairs, 4 = OrnamentSandstone, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 SandstoneStairs, 4 = OrnamentSandstone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 SandstoneStairs, 4 = Sandstone, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 SandstoneStairs, 4 = Sandstone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 SandstoneStairs, 4 = SmoothSandstone, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 SandstoneStairs, 4 = SmoothSandstone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -SpruceWoodStairs, 4 = SprucePlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +spruce_stairs: SpruceWoodStairs, 4 = SprucePlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 SpruceWoodStairs, 4 = SprucePlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -StoneBrickStairs, 4 = StoneBrick, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +stone_brick_stairs: StoneBrickStairs, 4 = StoneBrick, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 StoneBrickStairs, 4 = StoneBrick, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 -WoodStairs, 4 = OakPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 +oak_stairs: WoodStairs, 4 = OakPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 WoodStairs, 4 = OakPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 @@ -177,49 +177,49 @@ WoodStairs, 4 = OakPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3 # # Axes: -DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 1:1, 1:2 +diamond_axe: DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 1:1, 1:2 DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 3:1, 3:2 -GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 1:1, 1:2 +golden_axe: GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 1:1, 1:2 GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 3:1, 3:2 -IronAxe = Stick, 2:2, 2:3 | IronIngot, 2:1, 1:1, 1:2 +iron_axe: IronAxe = Stick, 2:2, 2:3 | IronIngot, 2:1, 1:1, 1:2 IronAxe = Stick, 2:2, 2:3 | IronIngot, 2:1, 3:1, 3:2 -StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 1:1, 1:2 +stone_axe: StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 1:1, 1:2 StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 3:1, 3:2 -WoodenAxe = Stick, 2:2, 2:3 | Planks^-1, 2:1, 1:1, 1:2 +wooden_axe: WoodenAxe = Stick, 2:2, 2:3 | Planks^-1, 2:1, 1:1, 1:2 WoodenAxe = Stick, 2:2, 2:3 | Planks^-1, 2:1, 3:1, 3:2 # Pickaxes: -DiamondPickaxe = Stick, 2:2, 2:3 | Diamond, 1:1, 2:1, 3:1 -GoldenPickaxe = Stick, 2:2, 2:3 | GoldIngot, 1:1, 2:1, 3:1 -IronPickaxe = Stick, 2:2, 2:3 | IronIngot, 1:1, 2:1, 3:1 -StonePickaxe = Stick, 2:2, 2:3 | Cobblestone, 1:1, 2:1, 3:1 -WoodenPickaxe = Stick, 2:2, 2:3 | Planks^-1, 1:1, 2:1, 3:1 +diamond_pickaxe: DiamondPickaxe = Stick, 2:2, 2:3 | Diamond, 1:1, 2:1, 3:1 +golden_pickaxe: GoldenPickaxe = Stick, 2:2, 2:3 | GoldIngot, 1:1, 2:1, 3:1 +iron_pickaxe: IronPickaxe = Stick, 2:2, 2:3 | IronIngot, 1:1, 2:1, 3:1 +stone_pickaxe: StonePickaxe = Stick, 2:2, 2:3 | Cobblestone, 1:1, 2:1, 3:1 +wooden_pickaxe: WoodenPickaxe = Stick, 2:2, 2:3 | Planks^-1, 1:1, 2:1, 3:1 # Shovels: -DiamondShovel = Stick, 2:2, 2:3 | Diamond, 2:1 -GoldenShovel = Stick, 2:2, 2:3 | GoldIngot, 2:1 -IronShovel = Stick, 2:2, 2:3 | IronIngot, 2:1 -StoneShovel = Stick, 2:2, 2:3 | Cobblestone, 2:1 -WoodenShovel = Stick, 2:2, 2:3 | Planks^-1, 2:1 +diamond_shovel: DiamondShovel = Stick, 2:2, 2:3 | Diamond, 2:1 +golden_shovel: GoldenShovel = Stick, 2:2, 2:3 | GoldIngot, 2:1 +iron_shovel: IronShovel = Stick, 2:2, 2:3 | IronIngot, 2:1 +stone_shovel: StoneShovel = Stick, 2:2, 2:3 | Cobblestone, 2:1 +wooden_shovel: WoodenShovel = Stick, 2:2, 2:3 | Planks^-1, 2:1 # Hoes: -DiamondHoe = Stick, 2:2, 2:3 | Diamond, 2:1, *:1 -GoldenHoe = Stick, 2:2, 2:3 | GoldIngot, 2:1, *:1 -IronHoe = Stick, 2:2, 2:3 | IronIngot, 2:1, *:1 -StoneHoe = Stick, 2:2, 2:3 | Cobblestone, 2:1, *:1 -WoodenHoe = Stick, 2:2, 2:3 | Planks^-1, 2:1, *:1 +diamond_hoe: DiamondHoe = Stick, 2:2, 2:3 | Diamond, 2:1, *:1 +golden_hoe: GoldenHoe = Stick, 2:2, 2:3 | GoldIngot, 2:1, *:1 +iron_hoe: IronHoe = Stick, 2:2, 2:3 | IronIngot, 2:1, *:1 +stone_hoe: StoneHoe = Stick, 2:2, 2:3 | Cobblestone, 2:1, *:1 +wooden_hoe: WoodenHoe = Stick, 2:2, 2:3 | Planks^-1, 2:1, *:1 -Bucket = IronIngot, 1:1, 2:2, 3:1 -Compass = IronIngot, 2:1, 1:2, 3:2, 2:3 | RedstoneDust, 2:2 -EmptyMap = Paper, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Compass, 2:2 -FireCharge, 3 = BlazePowder, * | Coal, * | Gunpowder, * -FishingRod = Stick, 1:3, 2:2, 3:1 | String, 3:2, 3:3 +bucket: Bucket = IronIngot, 1:1, 2:2, 3:1 +compass: Compass = IronIngot, 2:1, 1:2, 3:2, 2:3 | RedstoneDust, 2:2 +map: EmptyMap = Paper, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Compass, 2:2 +fire_charge: FireCharge, 3 = BlazePowder, * | Coal, * | Gunpowder, * +fishing_rod: FishingRod = Stick, 1:3, 2:2, 3:1 | String, 3:2, 3:3 FishingRod = Stick, 3:3, 2:2, 1:1 | String, 1:2, 1:3 -Lead, 2 = String, 1:1, 1:2, 2:1, 3:3 | Slimeball, 2:2 -Lighter = IronIngot, * | Flint, * -Shears = IronIngot, 1:1, 2:2 +lead: Lead, 2 = String, 1:1, 1:2, 2:1, 3:3 | Slimeball, 2:2 +flint_and_steel: Lighter = IronIngot, * | Flint, * +shears: Shears = IronIngot, 1:1, 2:2 Shears = IronIngot, 2:1, 1:2 -Watch = GoldIngot, 2:1, 1:2, 3:2, 2:3 | RedstoneDust, 2:2 +clock: Watch = GoldIngot, 2:1, 1:2, 3:2, 2:3 | RedstoneDust, 2:2 @@ -228,15 +228,15 @@ Watch = GoldIngot, 2:1, 1:2, 3:2, 2:3 | RedstoneDust, 2:2 #******************************************************# # Weapons # -Arrow, 4 = Flint, 1:1 | Stick, 1:2 | Feather, 1:3 -Bow = Stick, 2:1, 1:2, 2:3 | String, 3:1, 3:2, 3:3 +arrow: Arrow, 4 = Flint, 1:1 | Stick, 1:2 | Feather, 1:3 +bow: Bow = Stick, 2:1, 1:2, 2:3 | String, 3:1, 3:2, 3:3 Bow = Stick, 2:1, 3:2, 2:3 | String, 1:1, 1:2, 1:3 -DiamondSword = Stick, 2:3 | Diamond, 2:1, 2:2 -GoldenSword = Stick, 2:3 | GoldIngot, 2:1, 2:2 -IronSword = Stick, 2:3 | IronIngot, 2:1, 2:2 -SpectralArrow, 2 = Arrow, 2:2 | GlowstoneDust, 1:2, 2:1, 2:3, 3:2 -StoneSword = Stick, 2:3 | Cobblestone, 2:1, 2:2 -WoodenSword = Stick, 2:3 | Planks^-1, 2:1, 2:2 +diamond_sword: DiamondSword = Stick, 2:3 | Diamond, 2:1, 2:2 +golden_sword: GoldenSword = Stick, 2:3 | GoldIngot, 2:1, 2:2 +iron_sword: IronSword = Stick, 2:3 | IronIngot, 2:1, 2:2 +spectral_arrow: SpectralArrow, 2 = Arrow, 2:2 | GlowstoneDust, 1:2, 2:1, 2:3, 3:2 +stone_sword: StoneSword = Stick, 2:3 | Cobblestone, 2:1, 2:2 +wooden_sword: WoodenSword = Stick, 2:3 | Planks^-1, 2:1, 2:2 @@ -248,31 +248,31 @@ WoodenSword = Stick, 2:3 | Planks^-1, 2:1, 2:2 # # Helmets: -DiamondHelmet = Diamond, 1:1, 2:1, 3:1, 1:2, 3:2 -GoldenHelmet = GoldIngot, 1:1, 2:1, 3:1, 1:2, 3:2 -IronHelmet = IronIngot, 1:1, 2:1, 3:1, 1:2, 3:2 -LeatherHelmet = Leather, 1:1, 2:1, 3:1, 1:2, 3:2 +diamond_helmet: DiamondHelmet = Diamond, 1:1, 2:1, 3:1, 1:2, 3:2 +golden_helmet: GoldenHelmet = GoldIngot, 1:1, 2:1, 3:1, 1:2, 3:2 +iron_helmet: IronHelmet = IronIngot, 1:1, 2:1, 3:1, 1:2, 3:2 +leather_helmet: LeatherHelmet = Leather, 1:1, 2:1, 3:1, 1:2, 3:2 # Chestplates: -DiamondChestplate = Diamond, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 2:3, 3:3 -GoldenChestplate = GoldIngot, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 2:3, 3:3 -IronChestplate = IronIngot, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 2:3, 3:3 -LeatherChestplate = Leather, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 2:3, 3:3 +diamond_chestplate: DiamondChestplate = Diamond, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 2:3, 3:3 +golden_chestplate: GoldenChestplate = GoldIngot, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 2:3, 3:3 +iron_chestplate: IronChestplate = IronIngot, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 2:3, 3:3 +leather_chestplate: LeatherChestplate = Leather, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 2:3, 3:3 # Leggings: -DiamondLeggings = Diamond, 1:1, 2:1, 3:1, 1:2, 3:2, 1:3, 3:3 -GoldenLeggings = GoldIngot, 1:1, 2:1, 3:1, 1:2, 3:2, 1:3, 3:3 -IronLeggings = IronIngot, 1:1, 2:1, 3:1, 1:2, 3:2, 1:3, 3:3 -LeatherPants = Leather, 1:1, 2:1, 3:1, 1:2, 3:2, 1:3, 3:3 +diamond_leggings: DiamondLeggings = Diamond, 1:1, 2:1, 3:1, 1:2, 3:2, 1:3, 3:3 +golden_leggings: GoldenLeggings = GoldIngot, 1:1, 2:1, 3:1, 1:2, 3:2, 1:3, 3:3 +iron_leggings: IronLeggings = IronIngot, 1:1, 2:1, 3:1, 1:2, 3:2, 1:3, 3:3 +leather_leggings: LeatherPants = Leather, 1:1, 2:1, 3:1, 1:2, 3:2, 1:3, 3:3 # Boots: -DiamondBoots = Diamond, 1:1, 3:1, 1:2, 3:2 -GoldenBoots = GoldIngot, 1:1, 3:1, 1:2, 3:2 -IronBoots = IronIngot, 1:1, 3:1, 1:2, 3:2 -LeatherBoots = Leather, 1:1, 3:1, 1:2, 3:2 +diamond_boots: DiamondBoots = Diamond, 1:1, 3:1, 1:2, 3:2 +golden_boots: GoldenBoots = GoldIngot, 1:1, 3:1, 1:2, 3:2 +iron_boots: IronBoots = IronIngot, 1:1, 3:1, 1:2, 3:2 +leather_boots: LeatherBoots = Leather, 1:1, 3:1, 1:2, 3:2 # Shield: -Shield = IronIngot, 2:1 | Planks^-1, 1:1, 3:1, 1:2, 2:2, 3:2, 2:3 +shield: Shield = IronIngot, 2:1 | Planks^-1, 1:1, 3:1, 1:2, 2:2, 3:2, 2:3 @@ -280,22 +280,22 @@ Shield = IronIngot, 2:1 | Planks^-1, 1:1, 3:1, 1:2, 2:2, 3:2, 2:3 #******************************************************# # Transportation # -AcaciaBoat = AcaciaPlanks, 1:1, 3:1, 1:2, 2:2, 3:2 -ActivatorRail, 6 = IronIngot, 1:1, 1:2, 1:3, 3:1, 3:2, 3:3 | Stick, 2:1, 2:3 | RedstoneTorchon, 2:2 -BirchBoat = BirchPlanks, 1:1, 3:1, 1:2, 2:2, 3:2 -CarrotOnAStick = FishingRod, 1:2 | Carrot, 2:3 -DarkOakBoat = DarkOakPlanks, 1:1, 3:1, 1:2, 2:2, 3:2 -DetectorRail, 6 = IronIngot, 1:1, 3:1, 1:2, 3:2, 1:3, 3:3 | StonePlate, 2:2 | RedstoneDust, 2:3 -hopperminecart = Minecart, * | Hopper, * -JungleBoat = JunglePlanks, 1:1, 3:1, 1:2, 2:2, 3:2 -Minecart = IronIngot, 1:1, 3:1, 1:2, 2:2, 3:2 -OakBoat = OakPlanks, 1:1, 3:1, 1:2, 2:2, 3:2 -PoweredMinecart = Minecart, * | Furnace, * -PoweredRail, 6 = GoldIngot, 1:1, 3:1, 1:2, 3:2, 1:3, 3:3 | Stick, 2:2 | RedstoneDust, 2:3 -Rails, 16 = IronIngot, 1:1, 3:1, 1:2, 3:2, 1:3, 3:3 | Stick, 2:2 -SpruceBoat = SprucePlanks, 1:1, 3:1, 1:2, 2:2, 3:2 -StorageMinecart = Minecart, * | Chest, * -TNTMinecart = Minecart, * | TNT, * +acacia_boat: AcaciaBoat = AcaciaPlanks, 1:1, 3:1, 1:2, 2:2, 3:2 +activator_rail: ActivatorRail, 6 = IronIngot, 1:1, 1:2, 1:3, 3:1, 3:2, 3:3 | Stick, 2:1, 2:3 | RedstoneTorchon, 2:2 +birch_boat: BirchBoat = BirchPlanks, 1:1, 3:1, 1:2, 2:2, 3:2 +carrot_on_a_stick: CarrotOnAStick = FishingRod, 1:2 | Carrot, 2:3 +dark_oak_boat: DarkOakBoat = DarkOakPlanks, 1:1, 3:1, 1:2, 2:2, 3:2 +detector_rail: DetectorRail, 6 = IronIngot, 1:1, 3:1, 1:2, 3:2, 1:3, 3:3 | StonePlate, 2:2 | RedstoneDust, 2:3 +hopper_minecart: hopperminecart = Minecart, * | Hopper, * +jungle_boat: JungleBoat = JunglePlanks, 1:1, 3:1, 1:2, 2:2, 3:2 +minecart: Minecart = IronIngot, 1:1, 3:1, 1:2, 2:2, 3:2 +oak_boat: OakBoat = OakPlanks, 1:1, 3:1, 1:2, 2:2, 3:2 +furnace_minecart: PoweredMinecart = Minecart, * | Furnace, * +powered_rail: PoweredRail, 6 = GoldIngot, 1:1, 3:1, 1:2, 3:2, 1:3, 3:3 | Stick, 2:2 | RedstoneDust, 2:3 +rail: Rails, 16 = IronIngot, 1:1, 3:1, 1:2, 3:2, 1:3, 3:3 | Stick, 2:2 +spruce_boat: SpruceBoat = SprucePlanks, 1:1, 3:1, 1:2, 2:2, 3:2 +chest_minecart: StorageMinecart = Minecart, * | Chest, * +tnt_minecart: TNTMinecart = Minecart, * | TNT, * @@ -303,35 +303,35 @@ TNTMinecart = Minecart, * | TNT, * #******************************************************# # Mechanisms # -AcaciaDoor, 3 = AcaciaPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 -BirchDoor, 3 = BirchPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 -Comparator = RedstoneTorchOn, 2:1, 1:2, 3:2 | NetherQuartz, 2:2 | Stone, 1:3, 2:3, 3:3 -DarkOakDoor, 3 = DarkOakPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 -DaylightSensor = Glass, 1:1, 2:1, 3:1 | NetherQuartz, 1:2, 2:2, 3:2 | WoodenSlab^-1, 1:3, 2:3, 3:3 -Dispenser = Cobblestone, 1:1, 1:2, 1:3, 2:1, 3:1, 3:2, 3:3 | RedstoneDust, 2:3 | Bow, 2:2 -Dropper = Cobblestone, 1:1, 2:1, 3:1, 1:2, 1:3, 3:2, 3:3 | RedstoneDust, 2:3 -heavyweightedpressureplate = IronIngot, 1:1, 2:1 -Hopper = IronIngot, 1:1, 3:1, 1:2, 3:2, 2:3 | Chest, 2:2 -IronDoor, 3 = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 -IronTrapDoor = IronIngot, 1:1, 1:2, 2:1, 2:2 -Jukebox = Planks^-1, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Diamond, 2:2 -JungleDoor, 3 = JunglePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 -Lever = Cobblestone, 1:2 | Stick, 1:1 -lightweightedpressureplate = GoldIngot, 1:1, 2:1 -NoteBlock = Planks^-1, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | RedstoneDust, 2:2 -Piston = Planks^-1, 1:1, 2:1, 3:1 | RedstoneDust, 2:3 | Cobblestone, 1:2, 3:2, 1:3, 3:3 | IronIngot, 2:2 -RedstoneLamp = RedstoneDust, 2:1, 1:2, 3:2, 2:3 | Glowstone, 2:2 -RedstoneTorchOn = Stick, 1:2 | RedstoneDust, 1:1 -Repeater = Stone, 1:2, 2:2, 3:2 | RedstoneTorchOn, 1:1, 3:1 | RedstoneDust, 2:1 -PurpleShulkerBox = ShulkerShell, 2:1, 2:3 | Chest, 2:2 -SpruceDoor, 3 = SprucePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 -StickyPiston = Piston, * | SlimeBall, * -StoneButton = Stone, 1:1 -StonePlate = Stone, 1:1, 2:1 -TrapDoor, 2 = Planks^-1, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 -TripwireHook, 2 = Planks^-1, 2:3 | stick, 2:2 | IronIngot, 2:1 +acacia_door: AcaciaDoor, 3 = AcaciaPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 +birch_door: BirchDoor, 3 = BirchPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 +comparator: Comparator = RedstoneTorchOn, 2:1, 1:2, 3:2 | NetherQuartz, 2:2 | Stone, 1:3, 2:3, 3:3 +dark_oak_door: DarkOakDoor, 3 = DarkOakPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 +daylight_detector: DaylightSensor = Glass, 1:1, 2:1, 3:1 | NetherQuartz, 1:2, 2:2, 3:2 | WoodenSlab^-1, 1:3, 2:3, 3:3 +dispenser: Dispenser = Cobblestone, 1:1, 1:2, 1:3, 2:1, 3:1, 3:2, 3:3 | RedstoneDust, 2:3 | Bow, 2:2 +dropper: Dropper = Cobblestone, 1:1, 2:1, 3:1, 1:2, 1:3, 3:2, 3:3 | RedstoneDust, 2:3 +heavy_weighted_pressure_plate: heavyweightedpressureplate = IronIngot, 1:1, 2:1 +hopper: Hopper = IronIngot, 1:1, 3:1, 1:2, 3:2, 2:3 | Chest, 2:2 +iron_door: IronDoor, 3 = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 +iron_trapdoor: IronTrapDoor = IronIngot, 1:1, 1:2, 2:1, 2:2 +jukebox: Jukebox = Planks^-1, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Diamond, 2:2 +jungle_door: JungleDoor, 3 = JunglePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 +lever: Lever = Cobblestone, 1:2 | Stick, 1:1 +light_weighted_pressure_plate: lightweightedpressureplate = GoldIngot, 1:1, 2:1 +note_block: NoteBlock = Planks^-1, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | RedstoneDust, 2:2 +piston: Piston = Planks^-1, 1:1, 2:1, 3:1 | RedstoneDust, 2:3 | Cobblestone, 1:2, 3:2, 1:3, 3:3 | IronIngot, 2:2 +redstone_lamp: RedstoneLamp = RedstoneDust, 2:1, 1:2, 3:2, 2:3 | Glowstone, 2:2 +redstone_torch: RedstoneTorchOn = Stick, 1:2 | RedstoneDust, 1:1 +repeater: Repeater = Stone, 1:2, 2:2, 3:2 | RedstoneTorchOn, 1:1, 3:1 | RedstoneDust, 2:1 +purpur_shulker_box: PurpleShulkerBox = ShulkerShell, 2:1, 2:3 | Chest, 2:2 +spruce_door: SpruceDoor, 3 = SprucePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 +sticky_piston: StickyPiston = Piston, * | SlimeBall, * +stone_button: StoneButton = Stone, 1:1 +stone_pressure_plate: StonePlate = Stone, 1:1, 2:1 +oak_trapdoor: TrapDoor, 2 = Planks^-1, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 +tripwire_hook: TripwireHook, 2 = Planks^-1, 2:3 | stick, 2:2 | IronIngot, 2:1 WoodenButton = Planks^-1, 1:1 -WoodenDoor, 3 = OakPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 +oak_button: WoodenDoor, 3 = OakPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3 WoodPlate = Planks^-1, 1:1, 2:1 @@ -341,21 +341,21 @@ WoodPlate = Planks^-1, 1:1, 2:1 #******************************************************# # Food # -Bowl, 4 = Planks^-1, 1:1, 2:2, 3:1 -Bread = Wheat, 1:1, 2:1, 3:1 -Cake = MilkBucket, 1:1, 2:1, 3:1 | Sugar, 1:2, 3:2 | Egg, 2:2 | Wheat, 1:3, 2:3, 3:3 -Cookie, 8 = Wheat, *, * | CocoaBeans, * -GoldenApple = RedApple, 2:2 | GoldIngot, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 -MelonBlock = MelonSlice, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -MelonSeeds = MelonSlice, * -MushroomStew = Bowl, * | BrownMushroom, * | RedMushroom, * -BeetrootSoup = Bowl, 2:3 | Beetroot, 1:1, 1:2, 2:1, 2:2, 3:1, 3:2 -PumpkinPie = Pumpkin, * | Sugar, * | egg, * -PumpkinSeeds, 4 = Pumpkin, * -RabbitStew = Cooked Rabbit, 2:1 | Carrot, 1:2 | BakedPotato, 2:2 | BrownMushroom, 3:2 | Bowl, 2:3 -RabbitStew = Cooked Rabbit, 2:1 | Carrot, 1:2 | BakedPotato, 2:2 | RedMushroom, 3:2 | Bowl, 2:3 -Sugar = Sugarcane, * -Wheat, 9 = Haybale, * +bowl: Bowl, 4 = Planks^-1, 1:1, 2:2, 3:1 +bread: Bread = Wheat, 1:1, 2:1, 3:1 +cake: Cake = MilkBucket, 1:1, 2:1, 3:1 | Sugar, 1:2, 3:2 | Egg, 2:2 | Wheat, 1:3, 2:3, 3:3 +cookie: Cookie, 8 = Wheat, *, * | CocoaBeans, * +golden_apple: GoldenApple = RedApple, 2:2 | GoldIngot, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 +melon: MelonBlock = MelonSlice, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +melon_seeds: MelonSeeds = MelonSlice, * +mushroom_stew: MushroomStew = Bowl, * | BrownMushroom, * | RedMushroom, * +beetroot_soup: BeetrootSoup = Bowl, 2:3 | Beetroot, 1:1, 1:2, 2:1, 2:2, 3:1, 3:2 +pumpkin_pie: PumpkinPie = Pumpkin, * | Sugar, * | egg, * +pumpkin_seeds: PumpkinSeeds, 4 = Pumpkin, * +rabbit_stew_from_brown_mushroom: RabbitStew = Cooked Rabbit, 2:1 | Carrot, 1:2 | BakedPotato, 2:2 | BrownMushroom, 3:2 | Bowl, 2:3 +rabbit_stew_from_red_mushroom: RabbitStew = Cooked Rabbit, 2:1 | Carrot, 1:2 | BakedPotato, 2:2 | RedMushroom, 3:2 | Bowl, 2:3 +sugar: Sugar = Sugarcane, * +wheat: Wheat, 9 = Haybale, * @@ -366,71 +366,71 @@ Wheat, 9 = Haybale, * # # Minerals: -Clay, 4 = ClayBlock, * -Coal, 9 = CoalBlock, * -Diamond, 9 = DiamondBlock, * -Emerald, 9 = EmeraldBlock, * -GoldIngot, 9 = GoldBlock, * -IronIngot = IronNugget, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -IronIngot, 9 = IronBlock, * -LapisLazuli, 9 = LapisBlock, * -RedstoneDust, 9 = RedstoneBlock, * -SlimeBall, 9 = SlimeBlock, * +terracotta: Clay, 4 = ClayBlock, * +coal: Coal, 9 = CoalBlock, * +diamond: Diamond, 9 = DiamondBlock, * +emerald: Emerald, 9 = EmeraldBlock, * +gold_ingot_from_gold_block: GoldIngot, 9 = GoldBlock, * +iron_ingot_from_nuggets: IronIngot = IronNugget, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +iron_ingot_from_iron_block: IronIngot, 9 = IronBlock, * +lapis_lazuli: LapisLazuli, 9 = LapisBlock, * +redstone: RedstoneDust, 9 = RedstoneBlock, * +slime_ball: SlimeBall, 9 = SlimeBlock, * -AcaciaFence, 3 = AcaciaPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 -AcaciaFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | AcaciaPlanks, 2:1, 2:2 -Anvil = IronBlock, 1:1, 2:1, 3:1 | IronIngot, 2:2, 1:3, 2:3, 3:3 -ArmorStand = Stick, 1:1, 1:3, 2:1, 2:2, 3:1, 3:3 | StoneSlab, 2:3 -Beacon = Glass, 1:1, 1:2, 2:1, 3:1, 3:2 | Obsidian, 1:3, 2:3, 3:3 | NetherStar, 2:2 -BirchFence, 3 = BirchPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 -BirchFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | BirchPlanks, 2:1, 2:2 -Bookandquill = Book, * | feather, * | inksac, * -Book = Paper, *, *, * | leather, * -Cobblestonewall, 6 = cobblestone, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -DarkOakFence, 3 = DarkOakPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 -DarkOakFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | DarkOakPlanks, 2:1, 2:2 -EndCrystal = Glass, 1:1, 1:2, 1:3, 2:1, 3:1, 3:2, 3:3 | EyeOfEnder, 2:2 | GhastTear, 2:3 -EndRod, 4 = BlazeRod, 1:1 | PoppedChorusFruit, 1:2 -EyeOfEnder = EnderPearl, * | BlazePowder, * -Fence, 3 = OakPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 -FenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | OakPlanks, 2:1, 2:2 -FlowerPot = Brick, 1:2, 2:3, 3:2 -GlassPane, 16 = Glass, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 -GoldIngot = GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 -IronBars, 16 = IronIngot, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 -IronNugget, 9 = IronIngot, * -ItemFrame = Stick, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Leather, 2:2 -JungleFence, 3 = JunglePlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 -JungleFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | JunglePlanks, 2:1, 2:2 -Ladder, 3 = Stick, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 3:3 -mossycobblestonewall, 6 = mossycobblestone, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -NetherBrickFence, 6 = NetherBrickBlock, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 -Painting = Stick, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Wool^-1, 2:2 -Paper, 3 = Sugarcane, 1:1, 2:1, 3:1 -Sign, 3 = Planks^-1, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 | Stick, 2:3 -SpruceFence, 3 = SprucePlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 -SpruceFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | SprucePlanks, 2:1, 2:2 +acacia_fence: AcaciaFence, 3 = AcaciaPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 +acacia_fence_gate: AcaciaFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | AcaciaPlanks, 2:1, 2:2 +anvil: Anvil = IronBlock, 1:1, 2:1, 3:1 | IronIngot, 2:2, 1:3, 2:3, 3:3 +armor_stand: ArmorStand = Stick, 1:1, 1:3, 2:1, 2:2, 3:1, 3:3 | StoneSlab, 2:3 +beacon: Beacon = Glass, 1:1, 1:2, 2:1, 3:1, 3:2 | Obsidian, 1:3, 2:3, 3:3 | NetherStar, 2:2 +birch_fence: BirchFence, 3 = BirchPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 +birch_fence_gate: BirchFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | BirchPlanks, 2:1, 2:2 +writable_book: Bookandquill = Book, * | feather, * | inksac, * +book: Book = Paper, *, *, * | leather, * +cobblestone_wall: Cobblestonewall, 6 = cobblestone, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +dark_oak_fence: DarkOakFence, 3 = DarkOakPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 +dark_oak_fence_gate: DarkOakFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | DarkOakPlanks, 2:1, 2:2 +end_crystal: EndCrystal = Glass, 1:1, 1:2, 1:3, 2:1, 3:1, 3:2, 3:3 | EyeOfEnder, 2:2 | GhastTear, 2:3 +end_rod: EndRod, 4 = BlazeRod, 1:1 | PoppedChorusFruit, 1:2 +ender_eye: EyeOfEnder = EnderPearl, * | BlazePowder, * +oak_fence: Fence, 3 = OakPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 +oak_fence_gate: FenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | OakPlanks, 2:1, 2:2 +flower_pot: FlowerPot = Brick, 1:2, 2:3, 3:2 +glass_pane: GlassPane, 16 = Glass, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 +gold_ingot: GoldIngot = GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3 +iron_bars: IronBars, 16 = IronIngot, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 +iron_nugget: IronNugget, 9 = IronIngot, * +item_frame: ItemFrame = Stick, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Leather, 2:2 +jungle_fence: JungleFence, 3 = JunglePlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 +jungle_fence_gate: JungleFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | JunglePlanks, 2:1, 2:2 +ladder: Ladder, 3 = Stick, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 3:3 +mossy_cobblestone_wall: mossycobblestonewall, 6 = mossycobblestone, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +nether_brick_fence: NetherBrickFence, 6 = NetherBrickBlock, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 +painting: Painting = Stick, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Wool^-1, 2:2 +paper: Paper, 3 = Sugarcane, 1:1, 2:1, 3:1 +oak_sign: Sign, 3 = Planks^-1, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2 | Stick, 2:3 +spruce_fence: SpruceFence, 3 = SprucePlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2 +spruce_fence_gate: SpruceFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | SprucePlanks, 2:1, 2:2 # These are just the basic ones, you can add various shapes and stuff to each of them # ToDo: Add the various shapes (saved in NBT-Tags, not in meta) # Banners: -BlackBanner = Stick, 2:3 | BlackWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -BlueBanner = Stick, 2:3 | BlueWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -BrownBanner = Stick, 2:3 | BrownWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -CyanBanner = Stick, 2:3 | CyanWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -GrayBanner = Stick, 2:3 | GrayWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -GreenBanner = Stick, 2:3 | GreenWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -LightBlueBanner = Stick, 2:3 | LightBlueWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -LightGrayBanner = Stick, 2:3 | LightGrayWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -LimeBanner = Stick, 2:3 | LimeWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -MagentaBanner = Stick, 2:3 | MagentaWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -OrangeBanner = Stick, 2:3 | OrangeWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -PinkBanner = Stick, 2:3 | PinkWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -PurpleBanner = Stick, 2:3 | PurpleWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -RedBanner = Stick, 2:3 | RedWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -WhiteBanner = Stick, 2:3 | WhiteWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 -YellowBanner = Stick, 2:3 | YellowWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +black_banner: BlackBanner = Stick, 2:3 | BlackWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +blue_banner: BlueBanner = Stick, 2:3 | BlueWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +brown_banner: BrownBanner = Stick, 2:3 | BrownWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +cyan_banner: CyanBanner = Stick, 2:3 | CyanWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +gray_banner: GrayBanner = Stick, 2:3 | GrayWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +green_banner: GreenBanner = Stick, 2:3 | GreenWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +light_blue_banner: LightBlueBanner = Stick, 2:3 | LightBlueWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +light_gray_banner: LightGrayBanner = Stick, 2:3 | LightGrayWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +lime_banner: LimeBanner = Stick, 2:3 | LimeWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +magenta_banner: MagentaBanner = Stick, 2:3 | MagentaWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +orange_banner: OrangeBanner = Stick, 2:3 | OrangeWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +pink_banner: PinkBanner = Stick, 2:3 | PinkWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +purple_banner: PurpleBanner = Stick, 2:3 | PurpleWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +red_banner: RedBanner = Stick, 2:3 | RedWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +white_banner: WhiteBanner = Stick, 2:3 | WhiteWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 +yellow_banner: YellowBanner = Stick, 2:3 | YellowWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 @@ -440,28 +440,28 @@ YellowBanner = Stick, 2:3 | YellowWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2 # Dyes # -RedDye, 1 = Beetroot, * -RedDye, 2 = Rose, * +red_dye_from_beetroot: RedDye, 1 = Beetroot, * +red_dye_from_rose_bush: RedDye, 2 = Rose, * WhiteDye, 3 = Bone, * -YellowDye, 2 = Dandelion, * +yellow_dye_from_dandelion: YellowDye, 2 = Dandelion, * # Color mixing, duals: -CyanDye, 2 = GreenDye, * | BlueDye, * -GrayDye, 2 = BlackDye, * | WhiteDye, * -LimeDye, 2 = GreenDye, * | WhiteDye, * -LtBlueDye, 2 = BlueDye, * | WhiteDye, * -LtGrayDye, 2 = GrayDye, * | WhiteDye, * -MagentaDye, 2 = PurpleDye, * | PinkDye, * -OrangeDye, 2 = YellowDye, * | RedDye, * -PinkDye, 2 = RedDye, * | WhiteDye, * -PurpleDye, 2 = RedDye, * | BlueDye, * +cyan_dye: CyanDye, 2 = GreenDye, * | BlueDye, * +gray_dye: GrayDye, 2 = BlackDye, * | WhiteDye, * +lime_dye: LimeDye, 2 = GreenDye, * | WhiteDye, * +light_blue_dye_from_blue_white_dye: LtBlueDye, 2 = BlueDye, * | WhiteDye, * +light_gray_dye_from_gray_white_dye: LtGrayDye, 2 = GrayDye, * | WhiteDye, * +magenta_dye_from_purple_and_pink: MagentaDye, 2 = PurpleDye, * | PinkDye, * +orange_dye_from_red_yellow: OrangeDye, 2 = YellowDye, * | RedDye, * +pink_dye_from_red_white_dye: PinkDye, 2 = RedDye, * | WhiteDye, * +purple_dye: PurpleDye, 2 = RedDye, * | BlueDye, * # triplets: -LtGrayDye, 3 = BlackDye, * | WhiteDye, *, * -MagentaDye, 3 = BlueDye, * | PinkDye, * | RedDye, * +light_gray_dye_from_black_white_dye: LtGrayDye, 3 = BlackDye, * | WhiteDye, *, * +magenta_dye_from_blue_red_pink: MagentaDye, 3 = BlueDye, * | PinkDye, * | RedDye, * # quads: -MagentaDye, 4 = BlueDye, * | WhiteDye, * | RedDye, *, * +magenta_dye_from_blue_red_white_dye: MagentaDye, 4 = BlueDye, * | WhiteDye, * | RedDye, *, * @@ -469,21 +469,21 @@ MagentaDye, 4 = BlueDye, * | WhiteDye, * | RedDye, *, * #******************************************************# # Concrete Powder: # -White_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | BoneMeal, 2:2 -Orange_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | OrangeDye, 2:2 -Magenta_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | MagentaDye, 2:2 -Light_Blue_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | LightBlueDye, 2:2 -Yellow_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | YellowDye, 2:2 -Lime_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | LimeDye, 2:2 -Pink_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | PinkDye, 2:2 -Gray_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | GrayDye, 2:2 -Light_Gray_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | LightGrayDye, 2:2 -Cyan_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | CyanDye, 2:2 -Blue_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | BlueDye, 2:2 -Brown_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | BrownDye, 2:2 -Green_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | GreenDye, 2:2 -Red_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | RedDye, 2:2 -Black_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | BlackDye, 2:2 +white_concrete_powder: White_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | BoneMeal, 2:2 +orange_concrete_powder: Orange_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | OrangeDye, 2:2 +magenta_concrete_powder: Magenta_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | MagentaDye, 2:2 +light_blue_concrete_powder: Light_Blue_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | LightBlueDye, 2:2 +yellow_concrete_powder: Yellow_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | YellowDye, 2:2 +lime_concrete_powder: Lime_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | LimeDye, 2:2 +pink_concrete_powder: Pink_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | PinkDye, 2:2 +gray_concrete_powder: Gray_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | GrayDye, 2:2 +light_gray_concrete_powder: Light_Gray_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | LightGrayDye, 2:2 +cyan_concrete_powder: Cyan_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | CyanDye, 2:2 +blue_concrete_powder: Blue_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | BlueDye, 2:2 +brown_concrete_powder: Brown_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | BrownDye, 2:2 +green_concrete_powder: Green_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | GreenDye, 2:2 +red_concrete_powder: Red_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | RedDye, 2:2 +black_concrete_powder: Black_Concrete_Powder = Sand, 1:1, 3:1, 1:3, 3:3 | Gravel, 2:1, 1:2, 3:2, 2:3 | BlackDye, 2:2 #******************************************************# # Colored shulker boxes: @@ -748,118 +748,118 @@ YellowShulkerBox = YellowShulkerBox, * | YellowDye, * #******************************************************# # Colored wool: # -BlackWool = WhiteWool, * | BlackDye, * -BlueWool = WhiteWool, * | BlueDye, * -BrownWool = WhiteWool, * | BrownDye, * -CyanWool = WhiteWool, * | CyanDye, * -GrayWool = WhiteWool, * | GrayDye, * -GreenWool = WhiteWool, * | GreenDye, * -LightBlueWool = WhiteWool, * | LightBlueDye, * -LightGrayWool = WhiteWool, * | LightGrayDye, * -LimeWool = WhiteWool, * | LimeDye, * -MagentaWool = WhiteWool, * | MagentaDye, * -OrangeWool = WhiteWool, * | OrangeDye, * -PinkWool = WhiteWool, * | PinkDye, * -PurpleWool = WhiteWool, * | PurpleDye, * -RedWool = WhiteWool, * | RedDye, * +black_wool: BlackWool = WhiteWool, * | BlackDye, * +blue_wool: BlueWool = WhiteWool, * | BlueDye, * +brown_wool: BrownWool = WhiteWool, * | BrownDye, * +cyan_wool: CyanWool = WhiteWool, * | CyanDye, * +gray_wool: GrayWool = WhiteWool, * | GrayDye, * +green_wool: GreenWool = WhiteWool, * | GreenDye, * +light_blue_wool: LightBlueWool = WhiteWool, * | LightBlueDye, * +light_gray_wool: LightGrayWool = WhiteWool, * | LightGrayDye, * +lime_wool: LimeWool = WhiteWool, * | LimeDye, * +magenta_wool: MagentaWool = WhiteWool, * | MagentaDye, * +orange_wool: OrangeWool = WhiteWool, * | OrangeDye, * +pink_wool: PinkWool = WhiteWool, * | PinkDye, * +purple_wool: PurpleWool = WhiteWool, * | PurpleDye, * +red_wool: RedWool = WhiteWool, * | RedDye, * WhiteWool = Wool^-1, * | BoneMeal, * -YellowWool = WhiteWool, * | YellowDye, * +yellow_wool: YellowWool = WhiteWool, * | YellowDye, * -BlackCarpet, 3 = BlackWool, 1:1, 2:1 -BlueCarpet, 3 = BlueWool, 1:1, 2:1 -BrownCarpet, 3 = BrownWool, 1:1, 2:1 -CyanCarpet, 3 = CyanWool, 1:1, 2:1 -GrayCarpet, 3 = GrayWool, 1:1, 2:1 -GreenCarpet, 3 = GreenWool, 1:1, 2:1 -LightBlueCarpet, 3 = LightBlueWool, 1:1, 2:1 -LightGrayCarpet, 3 = LightGrayWool, 1:1, 2:1 -LimeCarpet, 3 = LimeWool, 1:1, 2:1 -MagentaCarpet, 3 = MagentaWool, 1:1, 2:1 -OrangeCarpet, 3 = OrangeWool, 1:1, 2:1 -PinkCarpet, 3 = PinkWool, 1:1, 2:1 -PurpleCarpet, 3 = PurpleWool, 1:1, 2:1 -RedCarpet, 3 = RedWool, 1:1, 2:1 -WhiteCarpet, 3 = WhiteWool, 1:1, 2:1 -YellowCarpet, 3 = YellowWool, 1:1, 2:1 +black_carpet: BlackCarpet, 3 = BlackWool, 1:1, 2:1 +blue_carpet: BlueCarpet, 3 = BlueWool, 1:1, 2:1 +brown_carpet: BrownCarpet, 3 = BrownWool, 1:1, 2:1 +cyan_carpet: CyanCarpet, 3 = CyanWool, 1:1, 2:1 +gray_carpet: GrayCarpet, 3 = GrayWool, 1:1, 2:1 +green_carpet: GreenCarpet, 3 = GreenWool, 1:1, 2:1 +light_blue_carpet: LightBlueCarpet, 3 = LightBlueWool, 1:1, 2:1 +light_gray_carpet: LightGrayCarpet, 3 = LightGrayWool, 1:1, 2:1 +lime_carpet: LimeCarpet, 3 = LimeWool, 1:1, 2:1 +magenta_carpet: MagentaCarpet, 3 = MagentaWool, 1:1, 2:1 +orange_carpet: OrangeCarpet, 3 = OrangeWool, 1:1, 2:1 +pink_carpet: PinkCarpet, 3 = PinkWool, 1:1, 2:1 +purple_carpet: PurpleCarpet, 3 = PurpleWool, 1:1, 2:1 +red_carpet: RedCarpet, 3 = RedWool, 1:1, 2:1 +white_carpet: WhiteCarpet, 3 = WhiteWool, 1:1, 2:1 +yellow_carpet: YellowCarpet, 3 = YellowWool, 1:1, 2:1 #******************************************************# # Stained Glass: # -BlackStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BlackDye, 2:2 -BlueStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BlueDye, 2:2 -BrownStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BrownDye, 2:2 -CyanStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | CyanDye, 2:2 -GrayStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | GrayDye, 2:2 -GreenStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | GreenDye, 2:2 -LightBlueStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LightBlueDye, 2:2 -LightGrayStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LightGrayDye, 2:2 -LimeStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LimeDye, 2:2 -MagentaStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | MagentaDye, 2:2 -OrangeStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | OrangeDye, 2:2 -PinkStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | PinkDye, 2:2 -RedStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | RedDye, 2:2 -VioletStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | VioletDye, 2:2 -WhiteStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BoneMeal, 2:2 -YellowStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | YellowDye, 2:2 +black_stained_glass: BlackStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BlackDye, 2:2 +blue_stained_glass: BlueStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BlueDye, 2:2 +brown_stained_glass: BrownStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BrownDye, 2:2 +cyan_stained_glass: CyanStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | CyanDye, 2:2 +gray_stained_glass: GrayStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | GrayDye, 2:2 +green_stained_glass: GreenStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | GreenDye, 2:2 +light_blue_stained_glass: LightBlueStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LightBlueDye, 2:2 +light_gray_stained_glass: LightGrayStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LightGrayDye, 2:2 +lime_stained_glass: LimeStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LimeDye, 2:2 +magenta_stained_glass: MagentaStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | MagentaDye, 2:2 +orange_stained_glass: OrangeStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | OrangeDye, 2:2 +pink_stained_glass: PinkStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | PinkDye, 2:2 +red_stained_glass: RedStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | RedDye, 2:2 +purple_stained_glass: VioletStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | VioletDye, 2:2 +white_stained_glass: WhiteStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BoneMeal, 2:2 +yellow_stained_glass: YellowStainedGlass, 8 = Glass, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | YellowDye, 2:2 #******************************************************# # Stained Glass Pane: # -BlackStainedGlassPane , 16 = BlackStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -BlueStainedGlassPane, 16 = BlueStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -BrownStainedGlassPane, 16 = BrownStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -CyanStainedGlassPane, 16 = CyanStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -GrayStainedGlassPane, 16 = GrayStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -GreenStainedGlassPane, 16 = GreenStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -LightBlueStainedGlassPane, 16 = LightBlueStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -LightGrayStainedGlassPane, 16 = LightGrayStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -LimeStainedGlassPane, 16 = LimeStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -MagentaStainedGlassPane, 16 = MagentaStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -OrangeStainedGlassPane, 16 = OrangeStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -PinkStainedGlassPane, 16 = PinkStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -RedStainedGlassPane, 16 = RedStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -VioletStainedGlassPane, 16 = VioletStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -WhiteStainedGlassPane, 16 = WhiteStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 -YellowStainedGlassPane, 16 = YellowStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +black_stained_glass_pane: BlackStainedGlassPane , 16 = BlackStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +blue_stained_glass_pane: BlueStainedGlassPane, 16 = BlueStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +brown_stained_glass_pane: BrownStainedGlassPane, 16 = BrownStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +cyan_stained_glass_pane: CyanStainedGlassPane, 16 = CyanStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +gray_stained_glass_pane: GrayStainedGlassPane, 16 = GrayStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +green_stained_glass_pane: GreenStainedGlassPane, 16 = GreenStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +light_blue_stained_glass_pane: LightBlueStainedGlassPane, 16 = LightBlueStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +light_gray_stained_glass_pane: LightGrayStainedGlassPane, 16 = LightGrayStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +lime_stained_glass_pane: LimeStainedGlassPane, 16 = LimeStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +magenta_stained_glass_pane: MagentaStainedGlassPane, 16 = MagentaStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +orange_stained_glass_pane: OrangeStainedGlassPane, 16 = OrangeStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +pink_stained_glass_pane: PinkStainedGlassPane, 16 = PinkStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +red_stained_glass_pane: RedStainedGlassPane, 16 = RedStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +purple_stained_glass_pane: VioletStainedGlassPane, 16 = VioletStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +white_stained_glass_pane: WhiteStainedGlassPane, 16 = WhiteStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 +yellow_stained_glass_pane: YellowStainedGlassPane, 16 = YellowStainedGlass, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3 #******************************************************# # Stained Clay: # -BlackStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BlackDye, 2:2 -BlueStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BlueDye, 2:2 -BrownStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BrownDye, 2:2 -CyanStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | CyanDye, 2:2 -GrayStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | GrayDye, 2:2 -GreenStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | GreenDye, 2:2 -LightBlueStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LightBlueDye, 2:2 -LightGrayStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LightGrayDye, 2:2 -LimeStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LimeDye, 2:2 -MagentaStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | MagentaDye, 2:2 -OrangeStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | OrangeDye, 2:2 -PinkStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | PinkDye, 2:2 +black_terracotta: BlackStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BlackDye, 2:2 +blue_terracotta: BlueStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BlueDye, 2:2 +brown_terracotta: BrownStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BrownDye, 2:2 +cyan_terracotta: CyanStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | CyanDye, 2:2 +gray_terracotta: GrayStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | GrayDye, 2:2 +green_terracotta: GreenStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | GreenDye, 2:2 +light_blue_terracotta: LightBlueStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LightBlueDye, 2:2 +light_gray_terracotta: LightGrayStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LightGrayDye, 2:2 +lime_terracotta: LimeStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | LimeDye, 2:2 +magenta_terracotta: MagentaStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | MagentaDye, 2:2 +orange_terracotta: OrangeStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | OrangeDye, 2:2 +pink_terracotta: PinkStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | PinkDye, 2:2 RedStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | RedDye, 2:2 -VioletStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | VioletDye, 2:2 +purple_terracotta: VioletStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | VioletDye, 2:2 WhiteStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | BoneMeal, 2:2 -YellowStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | YellowDye, 2:2 +yellow_terracotta: YellowStainedClay, 8 = HardenedClay, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | YellowDye, 2:2 #******************************************************# # Enchantment & Brewing # -BlazePowder, 2 = BlazeRod, * -BrewingStand = Cobblestone, 1:2, 2:2, 3:2 | BlazeRod, 2:1 -Cauldron = IronIngot, 1:1, 3:1, 1:2, 3:2, 1:3, 2:3, 3:3 -EnchantmentTable = Obsidian, 1:3, 2:3, 3:3, 2:2 | Diamond, 1:2, 3:2 | Book, 2:1 -FermentedSpiderEye = SpiderEye, * | Sugar, * | BrownMushroom, * -GlassBottle, 3 = Glass, 1:1, 2:2, 3:1 -GlisteringMelon = MelonSlice, 2:2 | GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 -GoldenCarrot = Carrot, 2:2 | GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 -GoldNugget, 9 = GoldIngot, * -MagmaCream = SlimeBall, * | BlazePowder, * +blaze_powder: BlazePowder, 2 = BlazeRod, * +brewing_stand: BrewingStand = Cobblestone, 1:2, 2:2, 3:2 | BlazeRod, 2:1 +cauldron: Cauldron = IronIngot, 1:1, 3:1, 1:2, 3:2, 1:3, 2:3, 3:3 +enchanting_table: EnchantmentTable = Obsidian, 1:3, 2:3, 3:3, 2:2 | Diamond, 1:2, 3:2 | Book, 2:1 +fermented_spider_eye: FermentedSpiderEye = SpiderEye, * | Sugar, * | BrownMushroom, * +glass_bottle: GlassBottle, 3 = Glass, 1:1, 2:2, 3:1 +glistering_melon_slice: GlisteringMelon = MelonSlice, 2:2 | GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 +golden_carrot: GoldenCarrot = Carrot, 2:2 | GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 +gold_nugget: GoldNugget, 9 = GoldIngot, * +magma_cream: MagmaCream = SlimeBall, * | BlazePowder, * #******************************************************# # Dyed Armor # Do not modify -LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * +leather_helmet: LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * @@ -868,7 +868,7 @@ LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1 LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * -LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * +leather_chestplate: LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * @@ -877,7 +877,7 @@ LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * -LeatherPants = LeatherPants^-1, * | Dye^-1, * +leather_leggings: LeatherPants = LeatherPants^-1, * | Dye^-1, * LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * @@ -886,7 +886,7 @@ LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * -LeatherBoots = LeatherBoots^-1, * | Dye^-1, * +leather_boots: LeatherBoots = LeatherBoots^-1, * | Dye^-1, * LeatherBoots = LeatherBoots^-1, * | Dye^-1, * | Dye^-1, * LeatherBoots = LeatherBoots^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * LeatherBoots = LeatherBoots^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 3cad17849..9fbc9f89d 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -14,6 +14,8 @@ #include "BlockEntities/ChestEntity.h" #include "BlockEntities/CommandBlockEntity.h" #include "BlockEntities/SignEntity.h" +#include "UI/InventoryWindow.h" +#include "UI/CraftingWindow.h" #include "UI/Window.h" #include "UI/AnvilWindow.h" #include "UI/BeaconWindow.h" @@ -1692,7 +1694,7 @@ void cClientHandle::HandleWindowClick(UInt8 a_WindowID, Int16 a_SlotNum, eClickA LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str()); return; } - + m_Player->AddKnownItem(a_HeldItem); Window->Clicked(*m_Player, a_WindowID, a_SlotNum, a_ClickAction, a_HeldItem); } @@ -3129,6 +3131,46 @@ void cClientHandle::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_Blo +void cClientHandle::SendUnlockRecipe(UInt32 a_RecipeId) +{ + m_Protocol->SendUnlockRecipe(a_RecipeId); +} + + + + + +void cClientHandle::SendInitRecipes(UInt32 a_RecipeId) +{ + m_Protocol->SendInitRecipes(a_RecipeId); +} + + + + + +void cClientHandle::HandleCraftRecipe(UInt32 a_RecipeId) +{ + auto * Window = m_Player->GetWindow(); + if (Window == nullptr) + { + return; + } + + if (Window->GetWindowType() == cWindow::wtInventory) + { + static_cast(Window)->LoadRecipe(*m_Player, a_RecipeId); + } + else if (Window->GetWindowType() == cWindow::wtWorkbench) + { + static_cast(Window)->LoadRecipe(*m_Player, a_RecipeId); + } +} + + + + + void cClientHandle::SendWeather(eWeather a_Weather) { m_Protocol->SendWeather(a_Weather); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 8495fb239..1d988b137 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -221,6 +221,13 @@ public: // tolua_export void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity); void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ); + + /** Send a newly discovered recipe to show the notification and unlock in the recipe book */ + void SendUnlockRecipe (UInt32 a_RecipeId); + + /** Send already known recipes without notification but visible in the recipe book */ + void SendInitRecipes (UInt32 a_RecipeId); + void SendWeather (eWeather a_Weather); void SendWholeInventory (const cWindow & a_Window); void SendWindowClose (const cWindow & a_Window); @@ -371,6 +378,9 @@ public: // tolua_export void HandleWindowClick (UInt8 a_WindowID, Int16 a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem); void HandleWindowClose (UInt8 a_WindowID); + /** Called when a recipe from the recipe book is selected */ + void HandleCraftRecipe (UInt32 a_RecipeId); + /** Called when the protocol has finished logging the user in. Return true to allow the user in; false to kick them. */ diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp index bdc98e151..7cd41ec97 100644 --- a/src/CraftingRecipes.cpp +++ b/src/CraftingRecipes.cpp @@ -268,6 +268,7 @@ void cCraftingRecipe::Dump(void) cCraftingRecipes::cCraftingRecipes(void) { LoadRecipes(); + PopulateRecipeNameMap(); } @@ -283,6 +284,72 @@ cCraftingRecipes::~cCraftingRecipes() +bool cCraftingRecipes::IsNewCraftableRecipe(const cRecipe * a_Recipe, const cItem & a_Item, const std::set & a_KnownItems) +{ + bool ContainsNewItem = false; + for (const auto & Ingredient : a_Recipe->m_Ingredients) + { + if ( + (Ingredient.m_Item.m_ItemType == a_Item.m_ItemType) && + ( + (Ingredient.m_Item.m_ItemDamage == a_Item.m_ItemDamage) || + (Ingredient.m_Item.m_ItemDamage == -1) + ) + ) + { + ContainsNewItem = true; + } + if (a_KnownItems.find(Ingredient.m_Item) == a_KnownItems.end()) + { + return false; + } + } + return ContainsNewItem; +} + + + + + +std::vector cCraftingRecipes::FindNewRecipesForItem(const cItem & a_Item, const std::set & a_KnownItems) +{ + std::vector Recipes; + for (UInt32 i = 0; i < m_Recipes.size(); i++) + { + if (m_Recipes[i]->m_RecipeName.empty()) + { + continue; + } + if (IsNewCraftableRecipe(m_Recipes[i], a_Item, a_KnownItems)) + { + Recipes.push_back(i); + } + } + return Recipes; +} + + + + + +const std::map & cCraftingRecipes::GetRecipeNameMap() +{ + return m_RecipeNameMap; +} + + + + + +cCraftingRecipes::cRecipe * cCraftingRecipes::GetRecipeById(UInt32 a_RecipeId) +{ + return m_Recipes[a_RecipeId]; +} + + + + + void cCraftingRecipes::GetRecipe(cPlayer & a_Player, cCraftingGrid & a_CraftingGrid, cCraftingRecipe & a_Recipe) { // Allow plugins to intercept recipes using a pre-craft hook: @@ -355,6 +422,21 @@ void cCraftingRecipes::LoadRecipes(void) +void cCraftingRecipes::PopulateRecipeNameMap(void) +{ + for (UInt32 i = 0; i < m_Recipes.size(); i++) + { + if (!m_Recipes[i]->m_RecipeName.empty()) + { + m_RecipeNameMap.emplace(m_Recipes[i]->m_RecipeName, i); + } + } +} + + + + + void cCraftingRecipes::ClearRecipes(void) { for (cRecipes::iterator itr = m_Recipes.begin(); itr != m_Recipes.end(); ++itr) @@ -384,8 +466,15 @@ void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine std::unique_ptr Recipe = cpp14::make_unique(); + AStringVector RecipeSplit = StringSplit(Sides[0], ":"); + const auto * resultPart = &RecipeSplit[0]; + if (RecipeSplit.size() > 1) + { + resultPart = &RecipeSplit[1]; + Recipe->m_RecipeName = RecipeSplit[0]; + } // Parse the result: - AStringVector ResultSplit = StringSplit(Sides[0], ","); + AStringVector ResultSplit = StringSplit(*resultPart, ","); if (ResultSplit.empty()) { LOGWARNING("crafting.txt: line %d: Result is empty, ignoring the recipe.", a_LineNum); @@ -1059,7 +1148,3 @@ void cCraftingRecipes::HandleDyedLeather(const cItem * a_CraftingGrid, cCrafting a_Recipe->m_Result.m_ItemColor.SetColor(result_red, result_green, result_blue); } } - - - - diff --git a/src/CraftingRecipes.h b/src/CraftingRecipes.h index 9659e53fc..7e4ac86ae 100644 --- a/src/CraftingRecipes.h +++ b/src/CraftingRecipes.h @@ -10,10 +10,6 @@ #include "Item.h" - - - - // fwd: cPlayer.h class cPlayer; @@ -104,7 +100,20 @@ protected: +/** +The crafting recipes are the configurations to build a result item out of a set +of ingredient items. +The recipes are configured in the `crafting.txt`. When populating the crafting +grid in game (inventory or crafting table), the items are compared to the +ingredients to find a matching recipe and show and craft the result. + +Each recipe is defined via the result, the ingredients and the minecraft recipe +name. + +To handle the crafting recipes internally efficient the vector index of the +`cRecipes` is used as `RecipeId`. +*/ class cCraftingRecipes { public: @@ -117,7 +126,8 @@ public: /** Returns the recipe for current crafting grid. Doesn't modify the grid. Clears a_Recipe if no recipe found. */ void GetRecipe(cPlayer & a_Player, cCraftingGrid & a_CraftingGrid, cCraftingRecipe & a_Recipe); -protected: + /** Find recipes and returns the RecipeIds which contain the new item and all ingredients are in the known items */ + std::vector FindNewRecipesForItem(const cItem & a_Item, const std::set & a_KnownItems); struct cRecipeSlot { @@ -132,11 +142,21 @@ protected: { cRecipeSlots m_Ingredients; cItem m_Result; + AString m_RecipeName; // Size of the regular items in the recipe; "anywhere" items are excluded: int m_Width; int m_Height; } ; + + /** Returns the recipe by id */ + cRecipe * GetRecipeById(UInt32 a_RecipeId); + + /** Gets a map of all recipes with name and recipe id */ + const std::map & GetRecipeNameMap(); + +protected: + typedef std::vector cRecipes; cRecipes m_Recipes; @@ -170,8 +190,22 @@ protected: /** Searches for anything dye related for leather, calculates the appropriate color value, and sets the resulting value. */ void HandleDyedLeather(const cItem * a_CraftingGrid, cCraftingRecipes::cRecipe * a_Recipe, int a_GridStride, int a_GridWidth, int a_GridHeight); + +private: + /** Mapping the minecraft recipe names to the internal cuberite recipe Ids */ + std::map m_RecipeNameMap; + + /** + Checks if all ingredients of the a_Recipe are within the a_KnownItems list and + if the a_NewItem is part of the ingredients. + This makes sure to only find 'newly discovered' recipes. + */ + bool IsNewCraftableRecipe( + const cRecipe * a_Recipe, + const cItem & a_NewItem, + const std::set & a_KnownItems + ); + + /** Populates the RecipeNameMap */ + void PopulateRecipeNameMap(void); } ; - - - - diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index d5d9f49af..df410575e 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -31,6 +31,8 @@ #include "../JsonUtils.h" #include "json/json.h" +#include "../CraftingRecipes.h" + // 6000 ticks or 5 minutes #define PLAYER_INVENTORY_SAVE_INTERVAL 6000 @@ -194,6 +196,18 @@ bool cPlayer::Initialize(OwnedEntity a_Self, cWorld & a_World) cPluginManager::Get()->CallHookSpawnedEntity(*GetWorld(), *this); + if (m_KnownRecipes.empty()) + { + m_ClientHandle->SendInitRecipes(0); + } + else + { + for (const auto KnownRecipe : m_KnownRecipes) + { + m_ClientHandle->SendInitRecipes(KnownRecipe); + } + } + return true; } @@ -201,6 +215,47 @@ bool cPlayer::Initialize(OwnedEntity a_Self, cWorld & a_World) +void cPlayer::AddKnownItem(const cItem & a_Item) +{ + if (a_Item.m_ItemType < 0) + { + return; + } + + auto Response = m_KnownItems.insert(a_Item.CopyOne()); + if (!Response.second) + { + // The item was already known, bail out: + return; + } + + // Process the recipes that got unlocked by this newly-known item: + auto Recipes = cRoot::Get()->GetCraftingRecipes()->FindNewRecipesForItem(a_Item, m_KnownItems); + for (const auto & RecipeId : Recipes) + { + AddKnownRecipe(RecipeId); + } +} + + + + + +void cPlayer::AddKnownRecipe(UInt32 a_RecipeId) +{ + auto Response = m_KnownRecipes.insert(a_RecipeId); + if (!Response.second) + { + // The recipe was already known, bail out: + return; + } + m_ClientHandle->SendUnlockRecipe(a_RecipeId); +} + + + + + cPlayer::~cPlayer(void) { if (!cRoot::Get()->GetPluginManager()->CallHookPlayerDestroyed(*this)) @@ -2229,6 +2284,26 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) m_CurrentXp = root.get("xpCurrent", 0).asInt(); m_IsFlying = root.get("isflying", 0).asBool(); + Json::Value & JSON_KnownItems = root["knownItems"]; + for (UInt32 i = 0; i < JSON_KnownItems.size(); i++) + { + cItem Item; + Item.FromJson(JSON_KnownItems[i]); + m_KnownItems.insert(Item); + } + + const auto & RecipeNameMap = cRoot::Get()->GetCraftingRecipes()->GetRecipeNameMap(); + + Json::Value & JSON_KnownRecipes = root["knownRecipes"]; + for (UInt32 i = 0; i < JSON_KnownRecipes.size(); i++) + { + auto RecipeId = RecipeNameMap.find(JSON_KnownRecipes[i].asString()); + if (RecipeId != RecipeNameMap.end()) + { + m_KnownRecipes.insert(RecipeId->second); + } + } + m_GameMode = static_cast(root.get("gamemode", eGameMode_NotSet).asInt()); if (m_GameMode == eGameMode_Creative) @@ -2327,10 +2402,27 @@ bool cPlayer::SaveToDisk() Json::Value JSON_EnderChestInventory; cEnderChestEntity::SaveToJson(JSON_EnderChestInventory, m_EnderChestContents); + Json::Value JSON_KnownItems; + for (const auto & KnownItem : m_KnownItems) + { + Json::Value JSON_Item; + KnownItem.GetJson(JSON_Item); + JSON_KnownItems.append(JSON_Item); + } + + Json::Value JSON_KnownRecipes; + for (auto KnownRecipe : m_KnownRecipes) + { + auto Recipe = cRoot::Get()->GetCraftingRecipes()->GetRecipeById(KnownRecipe); + JSON_KnownRecipes.append(Recipe->m_RecipeName); + } + Json::Value root; root["position"] = JSON_PlayerPosition; root["rotation"] = JSON_PlayerRotation; root["inventory"] = JSON_Inventory; + root["knownItems"] = JSON_KnownItems; + root["knownRecipes"] = JSON_KnownRecipes; root["equippedItemSlot"] = m_Inventory.GetEquippedSlotNum(); root["enderchestinventory"] = JSON_EnderChestInventory; root["health"] = m_Health; @@ -3095,13 +3187,3 @@ float cPlayer::GetExplosionExposureRate(Vector3d a_ExplosionPosition, float a_Ex return Super::GetExplosionExposureRate(a_ExplosionPosition, a_ExlosionPower) / 30.0f; } - - - - - - - - - - diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 11d448b11..c52d6bbdc 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -600,6 +600,10 @@ public: /** get player explosion exposure rate */ virtual float GetExplosionExposureRate(Vector3d a_ExplosionPosition, float a_ExlosionPower) override; + /** Adds an Item to the list of known items. + If the item is already known, does nothing. */ + void AddKnownItem(const cItem & a_Item); + protected: typedef std::vector > AStringVectorVector; @@ -753,6 +757,12 @@ protected: /** The main hand of the player */ eMainHand m_MainHand; + /** List on known recipes as Ids */ + std::set m_KnownRecipes; + + /** List of known items as Ids */ + std::set m_KnownItems; + virtual void DoMoveToWorld(const cEntity::sWorldChangeInfo & a_WorldChangeInfo) override; /** Sets the speed and sends it to the client, so that they are forced to move so. */ @@ -795,4 +805,8 @@ private: If he is not on ground it also gets divided by 5. */ float GetDigSpeed(BLOCKTYPE a_Block); + /** Add the recipe Id to the known recipes. + If the recipe is already known, does nothing. */ + void AddKnownRecipe(UInt32 RecipeId); + } ; // tolua_export diff --git a/src/Inventory.cpp b/src/Inventory.cpp index 42c243f17..6509dfe5d 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -105,6 +105,8 @@ int cInventory::HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int int cInventory::AddItem(const cItem & a_Item, bool a_AllowNewStacks) { + m_Owner.AddKnownItem(a_Item); + cItem ToAdd(a_Item); int res = 0; @@ -207,6 +209,26 @@ int cInventory::RemoveItem(const cItem & a_ItemStack) +cItem * cInventory::FindItem(const cItem & a_RecipeItem) +{ + cItem * Item = m_ShieldSlots.FindItem(a_RecipeItem); + if (Item != nullptr) + { + return Item; + } + Item = m_HotbarSlots.FindItem(a_RecipeItem); + if (Item != nullptr) + { + return Item; + } + + return m_InventorySlots.FindItem(a_RecipeItem); +} + + + + + bool cInventory::RemoveOneEquippedItem(void) { if (m_HotbarSlots.GetSlot(m_EquippedSlotNum).IsEmpty()) @@ -863,7 +885,3 @@ void cInventory::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) SendSlot(Base + a_SlotNum); } - - - - diff --git a/src/Inventory.h b/src/Inventory.h index 7436d7528..7221ded00 100644 --- a/src/Inventory.h +++ b/src/Inventory.h @@ -88,6 +88,9 @@ public: Returns the number of items that were removed. */ int RemoveItem(const cItem & a_ItemStack); + /** Finds an item based on ItemType and ItemDamage (<- defines the itemType, too) */ + cItem * FindItem(const cItem & a_RecipeItem); + /** Removes one item out of the currently equipped item stack, returns true if successful, false if empty-handed */ bool RemoveOneEquippedItem(void); @@ -210,7 +213,3 @@ protected: // cItemGrid::cListener override: virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; }; // tolua_export - - - - diff --git a/src/Item.h b/src/Item.h index 175f044b5..2f7b1a238 100644 --- a/src/Item.h +++ b/src/Item.h @@ -169,6 +169,31 @@ public: AStringVector m_LoreTable; // Exported in ManualBindings.cpp + /** + Compares two items for the same type or category. Type of item is defined + via `m_ItemType` and `m_ItemDamage`. Some items (e.g. planks) have the same + `m_ItemType` and the wood kind is defined via `m_ItemDamage`. `-1` is used + as placeholder for all kinds (e.g. all kind of planks). + + Items are different when the `ItemType` is different or the `ItemDamage` + is different and unequal -1. + */ + struct sItemCompare + { + bool operator() (const cItem & a_Lhs, const cItem & a_Rhs) const + { + if (a_Lhs.m_ItemType != a_Rhs.m_ItemType) + { + return (a_Lhs.m_ItemType < a_Rhs.m_ItemType); + } + if ((a_Lhs.m_ItemDamage == -1) || (a_Rhs.m_ItemDamage == -1)) + { + return false; // -1 is a wildcard, damage of -1 alway compares equal + } + return (a_Lhs.m_ItemDamage < a_Rhs.m_ItemDamage); + } + }; + // tolua_begin int m_RepairCost; diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp index 045f083c8..790f078fc 100644 --- a/src/ItemGrid.cpp +++ b/src/ItemGrid.cpp @@ -440,6 +440,31 @@ int cItemGrid::RemoveItem(const cItem & a_ItemStack) +cItem * cItemGrid::FindItem(const cItem & a_RecipeItem) +{ + if (!m_Slots.IsStorageAllocated()) + { + return nullptr; + } + + for (int i = 0; i < m_Slots.size(); i++) + { + // Items are equal if none is greater the other + auto compare = cItem::sItemCompare{}; + if (!compare(a_RecipeItem, m_Slots[i]) && + !compare(m_Slots[i], a_RecipeItem)) + { + return &m_Slots[i]; + } + } + + return nullptr; +} + + + + + int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount) { if (!IsValidSlotNum(a_SlotNum)) @@ -825,7 +850,3 @@ void cItemGrid::TriggerListeners(int a_SlotNum) } // for itr - m_Listeners[] m_IsInTriggerListeners = false; } - - - - diff --git a/src/ItemGrid.h b/src/ItemGrid.h index ee2dc79f3..52cfbc84a 100644 --- a/src/ItemGrid.h +++ b/src/ItemGrid.h @@ -102,6 +102,9 @@ public: Returns the number of items that were removed. */ int RemoveItem(const cItem & a_ItemStack); + /** Finds an item based on ItemType and ItemDamage (<- defines the itemType, too) */ + cItem * FindItem(const cItem & a_RecipeItem); + /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. If the slot is empty, ignores the call. Returns the new count. @@ -198,6 +201,3 @@ protected: int AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack); } ; // tolua_end - - - diff --git a/src/Protocol/CMakeLists.txt b/src/Protocol/CMakeLists.txt index e197853cb..40eecde07 100644 --- a/src/Protocol/CMakeLists.txt +++ b/src/Protocol/CMakeLists.txt @@ -14,6 +14,7 @@ target_sources( Protocol_1_13.cpp ProtocolPalettes.cpp ProtocolRecognizer.cpp + RecipeMapper.cpp Authenticator.h ChunkDataSerializer.h @@ -29,4 +30,5 @@ target_sources( Protocol_1_13.h ProtocolPalettes.h ProtocolRecognizer.h + RecipeMapper.h ) diff --git a/src/Protocol/Packetizer.cpp b/src/Protocol/Packetizer.cpp index 6afea8a36..12bfcc0dd 100644 --- a/src/Protocol/Packetizer.cpp +++ b/src/Protocol/Packetizer.cpp @@ -121,6 +121,7 @@ AString cPacketizer::PacketTypeToStr(cProtocol::ePacketType a_PacketType) case cProtocol::pktTimeUpdate: return "pktTimeUpdate"; case cProtocol::pktTitle: return "pktTitle"; case cProtocol::pktUnloadChunk: return "pktUnloadChunk"; + case cProtocol::pktUnlockRecipe: return "pktUnlockRecipe"; case cProtocol::pktUpdateBlockEntity: return "pktUpdateBlockEntity"; case cProtocol::pktUpdateHealth: return "pktUpdateHealth"; case cProtocol::pktUpdateScore: return "pktUpdateScore"; @@ -134,7 +135,3 @@ AString cPacketizer::PacketTypeToStr(cProtocol::ePacketType a_PacketType) } return Printf("Unknown packet type: 0x%02x", a_PacketType); } - - - - diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 12382b954..e1d901321 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -132,6 +132,7 @@ public: pktTimeUpdate, pktTitle, pktUnloadChunk, + pktUnlockRecipe, pktUpdateBlockEntity, pktUpdateHealth, pktUpdateScore, @@ -225,6 +226,8 @@ public: virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) = 0; virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0; virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) = 0; + virtual void SendUnlockRecipe (UInt32 a_RecipeID) = 0; + virtual void SendInitRecipes (UInt32 a_RecipeID) = 0; virtual void SendWeather (eWeather a_Weather) = 0; virtual void SendWholeInventory (const cWindow & a_Window) = 0; virtual void SendWindowClose (const cWindow & a_Window) = 0; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 73f8e0ff1..3f3982c90 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -51,6 +51,7 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) case PROTO_VERSION_1_11_1: return "1.11.1"; case PROTO_VERSION_1_12: return "1.12"; case PROTO_VERSION_1_12_1: return "1.12.1"; + case PROTO_VERSION_1_12_2: return "1.12.2"; case PROTO_VERSION_1_13: return "1.13"; } ASSERT(!"Unknown protocol version"); @@ -921,6 +922,26 @@ void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int +void cProtocolRecognizer::SendUnlockRecipe(UInt32 a_RecipeID) +{ + ASSERT(m_Protocol != nullptr); + m_Protocol->SendUnlockRecipe(a_RecipeID); +} + + + + + +void cProtocolRecognizer::SendInitRecipes(UInt32 a_RecipeID) +{ + ASSERT(m_Protocol != nullptr); + m_Protocol->SendInitRecipes(a_RecipeID); +} + + + + + void cProtocolRecognizer::SendWeather(eWeather a_Weather) { ASSERT(m_Protocol != nullptr); diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index f82dab08a..c5d180b44 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -127,6 +127,8 @@ public: virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override; virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendUnlockRecipe (UInt32 a_RecipeID) override; + virtual void SendInitRecipes (UInt32 a_RecipeID) override; virtual void SendWeather (eWeather a_Weather) override; virtual void SendWholeInventory (const cWindow & a_Window) override; virtual void SendWindowClose (const cWindow & a_Window) override; diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index dba85435b..6998f73bf 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -25,6 +25,7 @@ Implements the 1.12 protocol classes: #include "../Root.h" #include "../Server.h" #include "../ClientHandle.h" +#include "../CraftingRecipes.h" #include "../Bindings/PluginManager.h" #include "../JsonUtils.h" @@ -1007,6 +1008,7 @@ UInt32 cProtocol_1_12::GetPacketID(cProtocol::ePacketType a_Packet) case pktTeleportEntity: return 0x4b; case pktTimeUpdate: return 0x46; case pktTitle: return 0x47; + case pktUnlockRecipe: return 0x30; case pktUpdateBlockEntity: return 0x09; case pktUpdateHealth: return 0x40; case pktUpdateScore: return 0x44; @@ -1019,10 +1021,27 @@ UInt32 cProtocol_1_12::GetPacketID(cProtocol::ePacketType a_Packet) +void cProtocol_1_12::HandleCraftRecipe(cByteBuffer & a_ByteBuffer) +{ + HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, WindowID); + HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, RecipeID); + HANDLE_READ(a_ByteBuffer, ReadBool, bool, MakeAll); + auto CuberiteRecipeId = cRoot::Get()->GetRecipeMapper()->GetCuberiteRecipeId(RecipeID, m_Client->GetProtocolVersion()); + if (CuberiteRecipeId.has_value()) + { + m_Client->HandleCraftRecipe(CuberiteRecipeId.value()); + } +} + + + + + void cProtocol_1_12::HandlePacketCraftingBookData(cByteBuffer & a_ByteBuffer) { + // TODO not yet used, not sure if it is needed + // https://wiki.vg/index.php?title=Protocol&oldid=14204#Crafting_Book_Data a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); - m_Client->GetPlayer()->SendMessageInfo("The green crafting book feature is not implemented yet."); } @@ -1170,6 +1189,7 @@ UInt32 cProtocol_1_12_1::GetPacketID(ePacketType a_Packet) case pktRespawn: return 0x35; case pktScoreboardObjective: return 0x42; case pktSpawnPosition: return 0x46; + case pktUnlockRecipe: return 0x31; case pktUpdateHealth: return 0x41; case pktUpdateScore: return 0x45; case pktUseBed: return 0x30; @@ -1277,7 +1297,7 @@ bool cProtocol_1_12_1::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketT case 0x0f: HandlePacketPlayerLook(a_ByteBuffer); return true; case 0x10: HandlePacketVehicleMove(a_ByteBuffer); return true; case 0x11: HandlePacketBoatSteer(a_ByteBuffer); return true; - case 0x12: break; // Craft Recipe Request - not yet implemented + case 0x12: HandleCraftRecipe(a_ByteBuffer); return true; case 0x13: HandlePacketPlayerAbilities(a_ByteBuffer); return true; case 0x14: HandlePacketBlockDig(a_ByteBuffer); return true; case 0x15: HandlePacketEntityAction(a_ByteBuffer); return true; @@ -1397,3 +1417,55 @@ void cProtocol_1_12_2::SendKeepAlive(UInt32 a_PingID) cPacketizer Pkt(*this, pktKeepAlive); Pkt.WriteBEInt64(a_PingID); } + + + + + +void cProtocol_1_12_2::SendUnlockRecipe(UInt32 a_RecipeID) +{ + ASSERT(m_State == 3); // In game mode? + + auto ProtocolRecipeId = cRoot::Get()->GetRecipeMapper()->GetProtocolRecipeId(a_RecipeID, m_Client->GetProtocolVersion()); + if (ProtocolRecipeId.has_value()) + { + cPacketizer Pkt(*this, pktUnlockRecipe); + Pkt.WriteVarInt32(1); + Pkt.WriteBool(true); + Pkt.WriteBool(false); + Pkt.WriteVarInt32(1); + Pkt.WriteVarInt32(ProtocolRecipeId.value()); + } +} + + + + + +void cProtocol_1_12_2::SendInitRecipes(UInt32 a_RecipeID) +{ + ASSERT(m_State == 3); // In game mode? + + auto ProtocolRecipeId = cRoot::Get()->GetRecipeMapper()->GetProtocolRecipeId(a_RecipeID, m_Client->GetProtocolVersion()); + if (!ProtocolRecipeId.has_value()) + { + return; + } + + cPacketizer Pkt(*this, pktUnlockRecipe); + Pkt.WriteVarInt32(0); + Pkt.WriteBool(true); + Pkt.WriteBool(false); + if (a_RecipeID == 0) + { + Pkt.WriteVarInt32(0); + Pkt.WriteVarInt32(0); + } + else + { + Pkt.WriteVarInt32(1); + Pkt.WriteVarInt32(ProtocolRecipeId.value()); + Pkt.WriteVarInt32(1); + Pkt.WriteVarInt32(ProtocolRecipeId.value()); + } +} diff --git a/src/Protocol/Protocol_1_12.h b/src/Protocol/Protocol_1_12.h index 38c025e9e..c1b81955a 100644 --- a/src/Protocol/Protocol_1_12.h +++ b/src/Protocol/Protocol_1_12.h @@ -20,7 +20,7 @@ Declares the 1.12 protocol classes: #include "Protocol_1_11.h" - +#include "RecipeMapper.h" @@ -36,6 +36,7 @@ public: protected: virtual bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) override; virtual void HandlePacketAdvancementTab(cByteBuffer & a_ByteBuffer); + virtual void HandleCraftRecipe(cByteBuffer & a_ByteBuffer); virtual void HandlePacketCraftingBookData(cByteBuffer & a_ByteBuffer); virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) override; virtual void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity) override; @@ -86,8 +87,6 @@ protected: virtual void HandlePacketKeepAlive(cByteBuffer & a_ByteBuffer) override; virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) override; virtual void SendKeepAlive(UInt32 a_PingID) override; + virtual void SendUnlockRecipe(UInt32 a_RecipeID) override; + virtual void SendInitRecipes(UInt32 a_RecipeID) override; }; - - - - diff --git a/src/Protocol/Protocol_1_13.cpp b/src/Protocol/Protocol_1_13.cpp index fc048fe70..1dcecaa4b 100644 --- a/src/Protocol/Protocol_1_13.cpp +++ b/src/Protocol/Protocol_1_13.cpp @@ -140,6 +140,7 @@ UInt32 cProtocol_1_13::GetPacketID(ePacketType a_PacketType) case pktTimeUpdate: return 0x4a; case pktTitle: return 0x4b; case pktUnloadChunk: return 0x1f; + case pktUnlockRecipe: return 0x32; case pktUpdateHealth: return 0x44; case pktUpdateScore: return 0x48; case pktUpdateSign: return GetPacketID(pktUpdateBlockEntity); diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index b5d78e457..469f01c39 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -1587,6 +1587,26 @@ void cProtocol_1_8_0::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_B +void cProtocol_1_8_0::SendUnlockRecipe(UInt32 a_RecipeID) +{ + // Client doesn't support this feature + return; +} + + + + + +void cProtocol_1_8_0::SendInitRecipes(UInt32 a_RecipeID) +{ + // Client doesn't support this feature + return; +} + + + + + void cProtocol_1_8_0::SendWeather(eWeather a_Weather) { ASSERT(m_State == 3); // In game mode? diff --git a/src/Protocol/Protocol_1_8.h b/src/Protocol/Protocol_1_8.h index 42903a921..a8232104b 100644 --- a/src/Protocol/Protocol_1_8.h +++ b/src/Protocol/Protocol_1_8.h @@ -114,6 +114,8 @@ public: virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override; virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendUnlockRecipe (UInt32 a_RecipeID) override; + virtual void SendInitRecipes (UInt32 a_RecipeID) override; virtual void SendWeather (eWeather a_Weather) override; virtual void SendWholeInventory (const cWindow & a_Window) override; virtual void SendWindowClose (const cWindow & a_Window) override; diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 8327eaf40..784c26f34 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -567,6 +567,12 @@ UInt32 cProtocol_1_9_0::GetPacketID(cProtocol::ePacketType a_Packet) case pktWindowItems: return 0x14; case pktWindowOpen: return 0x13; case pktWindowProperty: return 0x15; + + // Unsupported packets + case pktUnlockRecipe: + { + break; + } } UNREACHABLE("Unsupported outgoing packet type"); } diff --git a/src/Protocol/RecipeMapper.cpp b/src/Protocol/RecipeMapper.cpp new file mode 100644 index 000000000..2757fdfd6 --- /dev/null +++ b/src/Protocol/RecipeMapper.cpp @@ -0,0 +1,128 @@ +#include "Globals.h" +#include "RecipeMapper.h" +#include "../Root.h" + +cRecipeMapper::cRecipeMapper(void) +{ + AString path = "Protocol"; + auto contents = cFile::GetFolderContents(path); + for (const auto & content: contents) + { + auto fullName = path + cFile::PathSeparator() + content; + if (cFile::IsFolder(fullName)) + { + loadRecipes(content); + } + } +} + + + + + +void cRecipeMapper::loadRecipes(const AString & a_ProtocolVersion) +{ + cFile f; + if (!f.Open("Protocol/" + a_ProtocolVersion + "/base.recipes.txt", cFile::fmRead)) + { + LOGWARNING("Cannot open file \"Protocol/%s/base.recipes.txt\", no recipe book recipes will be available!", a_ProtocolVersion); + return; + } + AString Everything; + if (!f.ReadRestOfFile(Everything)) + { + LOGWARNING("Cannot read file \"Protocol/%s/base.recipes.txt\", no recipe book recipes will be available!", a_ProtocolVersion); + return; + } + f.Close(); + + // Split it into lines, then process each line as a single recipe: + AStringVector Split = StringSplit(Everything, "\n"); + m_ProtocolVersionMap[a_ProtocolVersion] = {}; + const auto & RecipeNameMap = cRoot::Get()->GetCraftingRecipes()->GetRecipeNameMap(); + + int LineNum = 1; + for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr, ++LineNum) + { + // Remove anything after a '#' sign and trim away the whitespace: + AString Recipe = TrimString(itr->substr(0, itr->find('#'))); + if (Recipe.empty()) + { + // Empty recipe + continue; + } + AddRecipeLine(a_ProtocolVersion, LineNum, Recipe, RecipeNameMap); + } + LOG("Loaded %s %zu recipe book", a_ProtocolVersion, m_ProtocolVersionMap[a_ProtocolVersion].size()); +} + + + + + +cRecipeMapper::~cRecipeMapper() +{ +} + + + + + +void cRecipeMapper::AddRecipeLine(const AString & a_ProtocolVersion, int a_LineNum, const AString & a_RecipeLine, const std::map & a_RecipeNameMap) +{ + AStringVector Sides = StringSplit(a_RecipeLine, " "); + UInt32 Id; + if (Sides.size() != 2) + { + LOGINFO("Recipe incompletely configured %s", a_RecipeLine); + return; + } + StringToInteger(Sides[0], Id); + + auto RecipeIndex = a_RecipeNameMap.find(Sides[1]); + if (RecipeIndex == a_RecipeNameMap.end()) + { + return; + } + m_ProtocolVersionMap[a_ProtocolVersion].emplace(Id, RecipeIndex->second); +} + + + + + +std::optional cRecipeMapper::GetProtocolRecipeId(UInt32 a_RecipeId, UInt32 a_ProtocolVersion) +{ + auto ProtocolMap = m_ProtocolVersionMap.find(cRoot::Get()->GetProtocolVersionTextFromInt(static_cast(a_ProtocolVersion))); + if (ProtocolMap == m_ProtocolVersionMap.end()) + { + return {}; + } + for (const auto & item: ProtocolMap->second) + { + if (item.second == a_RecipeId) + { + return item.first; + } + } + return {}; +} + + + + + +std::optional cRecipeMapper::GetCuberiteRecipeId(UInt32 a_ProtocolRecipeId, UInt32 a_ProtocolVersion) +{ + auto ProtocolMap = m_ProtocolVersionMap.find(cRoot::Get()->GetProtocolVersionTextFromInt(static_cast(a_ProtocolVersion))); + if (ProtocolMap == m_ProtocolVersionMap.end()) + { + return {}; + } + auto Element = ProtocolMap->second.find(a_ProtocolRecipeId); + if (Element != ProtocolMap->second.end()) + { + return Element->second; + } + return {}; +} diff --git a/src/Protocol/RecipeMapper.h b/src/Protocol/RecipeMapper.h new file mode 100644 index 000000000..1cac62f92 --- /dev/null +++ b/src/Protocol/RecipeMapper.h @@ -0,0 +1,35 @@ +#pragma once + +#include "../CraftingRecipes.h" +#include + +/** +The RecipeMapper handles the translation of crafting recipes into protocol +specific recipe Ids. +The crafting recipes are identified by the RecipeId. +The actual configuration is stored in the protocol specific configuration +directory, e.g. `Server/Protocol/1.12.2/base.recipes.txt` +*/ +class cRecipeMapper +{ +public: + cRecipeMapper(void); + ~cRecipeMapper(); + + /** Translates the cuberite RecipeId to the protocol specific RecipeId */ + std::optional GetProtocolRecipeId(UInt32 a_RecipeId, UInt32 a_ProtocolVersion); + + /** Translates the protocol specific RecipeId to the cuberite RecipeId */ + std::optional GetCuberiteRecipeId(UInt32 a_ProtocolRecipeId, UInt32 a_ProtocolVersion); + +private: + /** A mapping for each protocol from the protocol specific RecipeId and the cuberite RecipeId */ + std::map> m_ProtocolVersionMap; + + /** Load Recipes from the protocol specific mapping file */ + void loadRecipes(const AString & a_ProtocolVersion); + + /** Handles a single line of the protocol specific mapping file */ + void AddRecipeLine(const AString & a_ProtocolVersion, int a_LineNum, const AString & a_RecipeLine, const std::map & a_RecipeNameMap); + +}; diff --git a/src/Root.cpp b/src/Root.cpp index 0cece9e6d..b3e7f61ee 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -24,6 +24,7 @@ #include "BrewingRecipes.h" #include "FurnaceRecipe.h" #include "CraftingRecipes.h" +#include "Protocol/RecipeMapper.h" #include "Bindings/PluginManager.h" #include "MonsterConfig.h" #include "Entities/Player.h" @@ -213,6 +214,7 @@ void cRoot::Start(std::unique_ptr a_OverridesRepo) m_RankManager.reset(new cRankManager()); m_RankManager->Initialize(*m_MojangAPI); m_CraftingRecipes = new cCraftingRecipes(); + m_RecipeMapper.reset(new cRecipeMapper()); m_FurnaceRecipe = new cFurnaceRecipe(); m_BrewingRecipes.reset(new cBrewingRecipes()); diff --git a/src/Root.h b/src/Root.h index 6c84e6bf7..2393871da 100644 --- a/src/Root.h +++ b/src/Root.h @@ -18,6 +18,7 @@ class cItem; class cMonsterConfig; class cBrewingRecipes; class cCraftingRecipes; +class cRecipeMapper; class cFurnaceRecipe; class cWebAdmin; class cPluginManager; @@ -89,6 +90,7 @@ public: cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; } cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export + cRecipeMapper * GetRecipeMapper(void) { return m_RecipeMapper.get(); } cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature cBrewingRecipes * GetBrewingRecipes (void) { return m_BrewingRecipes.get(); } // Exported in ManualBindings.cpp @@ -229,6 +231,7 @@ private: cMonsterConfig * m_MonsterConfig; cCraftingRecipes * m_CraftingRecipes; + std::unique_ptr m_RecipeMapper; cFurnaceRecipe * m_FurnaceRecipe; std::unique_ptr m_BrewingRecipes; cWebAdmin * m_WebAdmin; @@ -275,8 +278,3 @@ private: static void InputThread(cRoot & a_Params); }; // tolua_export - - - - - diff --git a/src/UI/CraftingWindow.cpp b/src/UI/CraftingWindow.cpp index 34599788c..d72e13729 100644 --- a/src/UI/CraftingWindow.cpp +++ b/src/UI/CraftingWindow.cpp @@ -59,3 +59,9 @@ void cCraftingWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & + +void cCraftingWindow::LoadRecipe(cPlayer & a_Player, UInt32 a_RecipeId) +{ + auto slotAreaCrafting = static_cast(m_SlotAreas[0]); + slotAreaCrafting->LoadRecipe(a_Player, a_RecipeId); +} diff --git a/src/UI/CraftingWindow.h b/src/UI/CraftingWindow.h index 75026dc67..b0de69704 100644 --- a/src/UI/CraftingWindow.h +++ b/src/UI/CraftingWindow.h @@ -25,8 +25,7 @@ public: cCraftingWindow(); virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; + + /** Loads the given Recipe into the crafting grid */ + void LoadRecipe(cPlayer & a_Player, UInt32 a_RecipeId); }; - - - - diff --git a/src/UI/InventoryWindow.cpp b/src/UI/InventoryWindow.cpp index 3c787ff7c..a3122d2d9 100644 --- a/src/UI/InventoryWindow.cpp +++ b/src/UI/InventoryWindow.cpp @@ -72,3 +72,9 @@ void cInventoryWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer + +void cInventoryWindow::LoadRecipe(cPlayer & a_Player, UInt32 a_RecipeId) +{ + auto slotAreaCrafting = static_cast(m_SlotAreas[0]); + slotAreaCrafting->LoadRecipe(a_Player, a_RecipeId); +} diff --git a/src/UI/InventoryWindow.h b/src/UI/InventoryWindow.h index 108f58fa0..052439f40 100644 --- a/src/UI/InventoryWindow.h +++ b/src/UI/InventoryWindow.h @@ -26,10 +26,8 @@ public: virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; + /** Loads the given Recipe into the crafting grid */ + void LoadRecipe(cPlayer & a_Player, UInt32 a_RecipeId); protected: cPlayer & m_Player; }; - - - - diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 0bbfb4b12..1552d4bfe 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -665,6 +665,7 @@ void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player) { return; } + a_Player.AddKnownItem(Result); cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1; for (;;) { @@ -780,6 +781,70 @@ void cSlotAreaCrafting::HandleCraftItem(const cItem & a_Result, cPlayer & a_Play +void cSlotAreaCrafting::LoadRecipe(cPlayer & a_Player, UInt32 a_RecipeId) +{ + if (a_RecipeId == 0) + { + return; + } + auto Recipe = cRoot::Get()->GetCraftingRecipes()->GetRecipeById(a_RecipeId); + + int NumItems = 0; + ClearCraftingGrid(a_Player); + + for (auto itrS = Recipe->m_Ingredients.begin(); itrS != Recipe->m_Ingredients.end(); ++itrS) + { + cItem * FoundItem = a_Player.GetInventory().FindItem(itrS->m_Item); + if (FoundItem == nullptr) + { + ClearCraftingGrid(a_Player); + break; + } + cItem Item = FoundItem->CopyOne(); + ++NumItems; + int pos = 1 + itrS->x + m_GridSize * itrS->y; + // Assuming there are ether shaped or unshaped recipes, no mixed ones + if ((itrS->x == -1) && (itrS->y == -1)) + { + pos = NumItems; + } + // Handle x wildcard + else if (itrS->x == -1) + { + for (int i = 0; i < m_GridSize; i++) + { + pos = 1 + i + m_GridSize * itrS->y; + auto itemCheck = GetSlot(pos, a_Player); + if (itemCheck->IsEmpty()) + { + break; + } + } + } + SetSlot(pos, a_Player, Item); + a_Player.GetInventory().RemoveItem(Item); + } +} + + + + + +void cSlotAreaCrafting::ClearCraftingGrid(cPlayer & a_Player) +{ + for (int pos = 1; pos <= m_GridSize * m_GridSize; pos++) + { + auto Item = GetSlot(pos, a_Player); + if (Item->m_ItemCount > 0) + { + a_Player.GetInventory().AddItem(*Item); + SetSlot(pos, a_Player, cItem()); + } + } +} + + + //////////////////////////////////////////////////////////////////////////////// // cSlotAreaAnvil: @@ -2749,8 +2814,3 @@ void cSlotAreaHorse::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bo --a_ItemStack.m_ItemCount; } } - - - - - diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index 86c0afd51..d363a72e6 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -273,6 +273,11 @@ public: // Distributing items into this area is completely disabled virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override; + /** Clear the crafting grid */ + void ClearCraftingGrid(cPlayer & a_Player); + + /** Loads the given Recipe into the crafting grid */ + void LoadRecipe(cPlayer & a_Player, UInt32 a_RecipeId); protected: /** Maps player's EntityID -> current recipe. @@ -555,7 +560,3 @@ public: private: cHorse & m_Horse; }; - - - -