package session import ( "fmt" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/item/recipe" "github.com/df-mc/dragonfly/server/world" "github.com/sandertv/gophertunnel/minecraft/protocol" ) const ( // smithingInputSlot is the slot index of the input item in the smithing table. smithingInputSlot = 0x33 // smithingMaterialSlot is the slot index of the material in the smithing table. smithingMaterialSlot = 0x34 // smithingTemplateSlot is the slot index of the template item in the smithing table. smithingTemplateSlot = 0x35 ) // handleSmithing handles a CraftRecipe stack request action made using a smithing table. func (h *ItemStackRequestHandler) handleSmithing(a *protocol.CraftRecipeStackRequestAction, s *Session, tx *world.Tx) error { // First, check the recipe and ensure it is valid for the smithing table. craft, ok := s.recipes[a.RecipeNetworkID] if !ok { return fmt.Errorf("recipe with network id %v does not exist", a.RecipeNetworkID) } if craft.Block() != "smithing_table" { return fmt.Errorf("recipe with network id %v is not a smithing table recipe", a.RecipeNetworkID) } switch craft.(type) { case recipe.SmithingTransform, recipe.SmithingTrim: default: return fmt.Errorf("recipe with network id %v is not a smithing recipe", a.RecipeNetworkID) } // Check if the input item and material item match what the recipe requires. expectedInputs := craft.Input() input, _ := h.itemInSlot(protocol.StackRequestSlotInfo{ Container: protocol.FullContainerName{ContainerID: protocol.ContainerSmithingTableInput}, Slot: smithingInputSlot, }, s, tx) if !matchingStacks(input, expectedInputs[0]) { return fmt.Errorf("input item is not the same as expected input") } material, _ := h.itemInSlot(protocol.StackRequestSlotInfo{ Container: protocol.FullContainerName{ContainerID: protocol.ContainerSmithingTableMaterial}, Slot: smithingMaterialSlot, }, s, tx) if !matchingStacks(material, expectedInputs[1]) { return fmt.Errorf("material item is not the same as expected material") } template, _ := h.itemInSlot(protocol.StackRequestSlotInfo{ Container: protocol.FullContainerName{ContainerID: protocol.ContainerSmithingTableTemplate}, Slot: smithingTemplateSlot, }, s, tx) if !matchingStacks(template, expectedInputs[2]) { return fmt.Errorf("template item is not the same as expected template") } // Create the output using the input stack as reference and the recipe's output item type. h.setItemInSlot(protocol.StackRequestSlotInfo{ Container: protocol.FullContainerName{ContainerID: protocol.ContainerSmithingTableInput}, Slot: smithingInputSlot, }, input.Grow(-1), s, tx) h.setItemInSlot(protocol.StackRequestSlotInfo{ Container: protocol.FullContainerName{ContainerID: protocol.ContainerSmithingTableMaterial}, Slot: smithingMaterialSlot, }, material.Grow(-1), s, tx) h.setItemInSlot(protocol.StackRequestSlotInfo{ Container: protocol.FullContainerName{ContainerID: protocol.ContainerSmithingTableTemplate}, Slot: smithingTemplateSlot, }, template.Grow(-1), s, tx) if _, ok = craft.(recipe.SmithingTrim); ok { var trim item.ArmourTrim if t, ok := template.Item().(item.SmithingTemplate); ok { trim.Template = t.Template } else { return fmt.Errorf("template item is not a smithing template") } if trim.Material, ok = material.Item().(item.ArmourTrimMaterial); !ok { return fmt.Errorf("material item is not an armour trim material") } trimmable, ok := input.Item().(item.Trimmable) if !ok { return fmt.Errorf("input item is not trimmable") } return h.createResults(s, tx, input.WithItem(trimmable.WithTrim(trim))) } return h.createResults(s, tx, input.WithItem(craft.Output()[0].Item())) }