up3
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/df-mc/dragonfly/server/world"
|
||||
"math"
|
||||
"math/rand/v2"
|
||||
|
||||
"github.com/df-mc/dragonfly/server/block"
|
||||
"github.com/df-mc/dragonfly/server/entity"
|
||||
"github.com/df-mc/dragonfly/server/item"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||
)
|
||||
|
||||
const (
|
||||
// grindstoneFirstInputSlot is the slot index of the first input item in the grindstone.
|
||||
grindstoneFirstInputSlot = 0x10
|
||||
// grindstoneSecondInputSlot is the slot index of the second input item in the grindstone.
|
||||
grindstoneSecondInputSlot = 0x11
|
||||
)
|
||||
|
||||
// handleGrindstoneCraft handles a CraftGrindstoneRecipe stack request action made using a grindstone.
|
||||
func (h *ItemStackRequestHandler) handleGrindstoneCraft(s *Session, tx *world.Tx, c Controllable) error {
|
||||
// First check if there actually is a grindstone opened.
|
||||
if !s.containerOpened.Load() {
|
||||
return fmt.Errorf("no grindstone container opened")
|
||||
}
|
||||
if _, ok := tx.Block(*s.openedPos.Load()).(block.Grindstone); !ok {
|
||||
return fmt.Errorf("no grindstone container opened")
|
||||
}
|
||||
|
||||
// Next, get both input items and ensure they are comparable.
|
||||
firstInput, _ := h.itemInSlot(protocol.StackRequestSlotInfo{
|
||||
Container: protocol.FullContainerName{ContainerID: protocol.ContainerGrindstoneInput},
|
||||
Slot: grindstoneFirstInputSlot,
|
||||
}, s, tx)
|
||||
secondInput, _ := h.itemInSlot(protocol.StackRequestSlotInfo{
|
||||
Container: protocol.FullContainerName{ContainerID: protocol.ContainerGrindstoneAdditional},
|
||||
Slot: grindstoneSecondInputSlot,
|
||||
}, s, tx)
|
||||
if firstInput.Empty() && secondInput.Empty() {
|
||||
return fmt.Errorf("input item(s) are empty")
|
||||
}
|
||||
if firstInput.Count() > 1 || secondInput.Count() > 1 {
|
||||
return fmt.Errorf("input item(s) are not single items")
|
||||
}
|
||||
|
||||
resultStack := nonZeroItem(firstInput, secondInput)
|
||||
if !firstInput.Empty() && !secondInput.Empty() {
|
||||
name, meta := firstInput.Item().EncodeItem()
|
||||
name2, meta2 := secondInput.Item().EncodeItem()
|
||||
if name != name2 || meta != meta2 {
|
||||
return fmt.Errorf("input items must be the same type")
|
||||
}
|
||||
if _, ok := firstInput.Item().(item.Durable); !ok {
|
||||
return fmt.Errorf("input items must be durable")
|
||||
}
|
||||
|
||||
// We add the enchantments to the result stack in order to calculate the gained experience. These enchantments
|
||||
// are stripped when creating the result.
|
||||
resultStack = firstInput.WithEnchantments(secondInput.Enchantments()...)
|
||||
|
||||
// Merge the durability of the two input items at 5%.
|
||||
maxDurability := firstInput.MaxDurability()
|
||||
firstDurability, secondDurability := firstInput.Durability(), secondInput.Durability()
|
||||
|
||||
resultStack = resultStack.WithDurability(firstDurability + secondDurability + maxDurability*5/100)
|
||||
}
|
||||
|
||||
for _, o := range entity.NewExperienceOrbs(entity.EyePosition(c), experienceFromEnchantments(resultStack)) {
|
||||
tx.AddEntity(o)
|
||||
}
|
||||
|
||||
h.setItemInSlot(protocol.StackRequestSlotInfo{
|
||||
Container: protocol.FullContainerName{ContainerID: protocol.ContainerGrindstoneInput},
|
||||
Slot: grindstoneFirstInputSlot,
|
||||
}, item.Stack{}, s, tx)
|
||||
h.setItemInSlot(protocol.StackRequestSlotInfo{
|
||||
Container: protocol.FullContainerName{ContainerID: protocol.ContainerGrindstoneAdditional},
|
||||
Slot: grindstoneSecondInputSlot,
|
||||
}, item.Stack{}, s, tx)
|
||||
return h.createResults(s, tx, stripPossibleEnchantments(resultStack))
|
||||
}
|
||||
|
||||
// curseEnchantment represents an enchantment that may be a curse enchantment.
|
||||
type curseEnchantment interface {
|
||||
Curse() bool
|
||||
}
|
||||
|
||||
// experienceFromEnchantments returns the amount of experience that is gained from the enchantments on the given stack.
|
||||
func experienceFromEnchantments(stack item.Stack) int {
|
||||
var totalCost int
|
||||
for _, enchant := range stack.Enchantments() {
|
||||
if _, ok := enchant.Type().(curseEnchantment); ok {
|
||||
continue
|
||||
}
|
||||
cost, _ := enchant.Type().Cost(enchant.Level())
|
||||
totalCost += cost
|
||||
}
|
||||
if totalCost == 0 {
|
||||
// No cost, no experience.
|
||||
return 0
|
||||
}
|
||||
|
||||
minExperience := int(math.Ceil(float64(totalCost) / 2))
|
||||
return minExperience + rand.IntN(minExperience)
|
||||
}
|
||||
|
||||
// stripPossibleEnchantments strips all enchantments possible, excluding curses.
|
||||
func stripPossibleEnchantments(stack item.Stack) item.Stack {
|
||||
for _, enchant := range stack.Enchantments() {
|
||||
if _, ok := enchant.Type().(curseEnchantment); ok {
|
||||
continue
|
||||
}
|
||||
stack = stack.WithoutEnchantments(enchant.Type())
|
||||
}
|
||||
return stack.WithAnvilCost(0)
|
||||
}
|
||||
|
||||
// nonZeroItem returns the item.Stack that exists out of two input items. The function expects at least one of the
|
||||
// items to be non-empty.
|
||||
func nonZeroItem(first, second item.Stack) item.Stack {
|
||||
if first.Empty() {
|
||||
return second
|
||||
}
|
||||
return first
|
||||
}
|
||||
Reference in New Issue
Block a user