github.com/df-mc/dragonfly@v0.9.13/server/session/handler_grindstone.go (about) 1 package session 2 3 import ( 4 "fmt" 5 "math" 6 "math/rand" 7 8 "github.com/df-mc/dragonfly/server/block" 9 "github.com/df-mc/dragonfly/server/entity" 10 "github.com/df-mc/dragonfly/server/item" 11 "github.com/go-gl/mathgl/mgl64" 12 "github.com/sandertv/gophertunnel/minecraft/protocol" 13 ) 14 15 const ( 16 // grindstoneFirstInputSlot is the slot index of the first input item in the grindstone. 17 grindstoneFirstInputSlot = 0x10 18 // grindstoneSecondInputSlot is the slot index of the second input item in the grindstone. 19 grindstoneSecondInputSlot = 0x11 20 ) 21 22 // handleGrindstoneCraft handles a CraftGrindstoneRecipe stack request action made using a grindstone. 23 func (h *ItemStackRequestHandler) handleGrindstoneCraft(s *Session) error { 24 // First check if there actually is a grindstone opened. 25 if !s.containerOpened.Load() { 26 return fmt.Errorf("no grindstone container opened") 27 } 28 if _, ok := s.c.World().Block(s.openedPos.Load()).(block.Grindstone); !ok { 29 return fmt.Errorf("no grindstone container opened") 30 } 31 32 // Next, get both input items and ensure they are comparable. 33 firstInput, _ := h.itemInSlot(protocol.StackRequestSlotInfo{ 34 ContainerID: protocol.ContainerGrindstoneInput, 35 Slot: grindstoneFirstInputSlot, 36 }, s) 37 secondInput, _ := h.itemInSlot(protocol.StackRequestSlotInfo{ 38 ContainerID: protocol.ContainerGrindstoneAdditional, 39 Slot: grindstoneSecondInputSlot, 40 }, s) 41 if firstInput.Empty() && secondInput.Empty() { 42 return fmt.Errorf("input item(s) are empty") 43 } 44 if firstInput.Count() > 1 || secondInput.Count() > 1 { 45 return fmt.Errorf("input item(s) are not single items") 46 } 47 48 resultStack := nonZeroItem(firstInput, secondInput) 49 if !firstInput.Empty() && !secondInput.Empty() { 50 // We add the enchantments to the result stack in order to calculate the gained experience. These enchantments 51 // are stripped when creating the result. 52 resultStack = firstInput.WithEnchantments(secondInput.Enchantments()...) 53 54 // Merge the durability of the two input items at 5%. 55 maxDurability := firstInput.MaxDurability() 56 firstDurability, secondDurability := firstInput.Durability(), secondInput.Durability() 57 58 resultStack = resultStack.WithDurability(firstDurability + secondDurability + maxDurability*5/100) 59 } 60 61 w := s.c.World() 62 for _, o := range entity.NewExperienceOrbs(entity.EyePosition(s.c), experienceFromEnchantments(resultStack)) { 63 o.SetVelocity(mgl64.Vec3{(rand.Float64()*0.2 - 0.1) * 2, rand.Float64() * 0.4, (rand.Float64()*0.2 - 0.1) * 2}) 64 w.AddEntity(o) 65 } 66 67 h.setItemInSlot(protocol.StackRequestSlotInfo{ 68 ContainerID: protocol.ContainerGrindstoneInput, 69 Slot: grindstoneFirstInputSlot, 70 }, item.Stack{}, s) 71 h.setItemInSlot(protocol.StackRequestSlotInfo{ 72 ContainerID: protocol.ContainerGrindstoneAdditional, 73 Slot: grindstoneSecondInputSlot, 74 }, item.Stack{}, s) 75 return h.createResults(s, stripPossibleEnchantments(resultStack)) 76 } 77 78 // curseEnchantment represents an enchantment that may be a curse enchantment. 79 type curseEnchantment interface { 80 Curse() bool 81 } 82 83 // experienceFromEnchantments returns the amount of experience that is gained from the enchantments on the given stack. 84 func experienceFromEnchantments(stack item.Stack) int { 85 var totalCost int 86 for _, enchant := range stack.Enchantments() { 87 if _, ok := enchant.Type().(curseEnchantment); ok { 88 continue 89 } 90 cost, _ := enchant.Type().Cost(enchant.Level()) 91 totalCost += cost 92 } 93 if totalCost == 0 { 94 // No cost, no experience. 95 return 0 96 } 97 98 minExperience := int(math.Ceil(float64(totalCost) / 2)) 99 return minExperience + rand.Intn(minExperience) 100 } 101 102 // stripPossibleEnchantments strips all enchantments possible, excluding curses. 103 func stripPossibleEnchantments(stack item.Stack) item.Stack { 104 for _, enchant := range stack.Enchantments() { 105 if _, ok := enchant.Type().(curseEnchantment); ok { 106 continue 107 } 108 stack = stack.WithoutEnchantments(enchant.Type()) 109 } 110 return stack 111 } 112 113 // nonZeroItem returns the item.Stack that exists out of two input items. The function expects at least one of the 114 // items to be non-empty. 115 func nonZeroItem(first, second item.Stack) item.Stack { 116 if first.Empty() { 117 return second 118 } 119 return first 120 }