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

This commit is contained in:
2026-07-09 08:33:57 +08:00
commit 26ed99fda6
845 changed files with 75419 additions and 0 deletions
+169
View File
@@ -0,0 +1,169 @@
package item
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
"github.com/go-gl/mathgl/mgl64"
"time"
)
// BucketContent is the content of a bucket.
type BucketContent struct {
liquid world.Liquid
milk bool
}
// LiquidBucketContent returns a new BucketContent with the liquid passed in.
func LiquidBucketContent(l world.Liquid) BucketContent {
return BucketContent{liquid: l}
}
// MilkBucketContent returns a new BucketContent with the milk flag set.
func MilkBucketContent() BucketContent {
return BucketContent{milk: true}
}
// Liquid returns the world.Liquid that a Bucket with this BucketContent places.
// If this BucketContent does not place a liquid block, false is returned.
func (b BucketContent) Liquid() (world.Liquid, bool) {
return b.liquid, b.liquid != nil
}
// String converts the BucketContent to a string.
func (b BucketContent) String() string {
if b.milk {
return "milk"
} else if b.liquid != nil {
return b.liquid.LiquidType()
}
return ""
}
// LiquidType returns the type of liquid the bucket contains.
func (b BucketContent) LiquidType() string {
if b.liquid != nil {
return b.liquid.LiquidType()
}
return "milk"
}
// Bucket is a tool used to carry water, lava and fish.
type Bucket struct {
// Content is the content that the bucket has. By default, this value resolves to an empty bucket.
Content BucketContent
}
// MaxCount returns 16.
func (b Bucket) MaxCount() int {
if b.Empty() {
return 16
}
return 1
}
// AlwaysConsumable ...
func (b Bucket) AlwaysConsumable() bool {
return b.Content.milk
}
// CanConsume ...
func (b Bucket) CanConsume() bool {
return b.Content.milk
}
// ConsumeDuration ...
func (b Bucket) ConsumeDuration() time.Duration {
return DefaultConsumeDuration
}
// Consume ...
func (b Bucket) Consume(_ *world.Tx, c Consumer) Stack {
for _, effect := range c.Effects() {
c.RemoveEffect(effect.Type())
}
return NewStack(Bucket{}, 1)
}
// Empty returns true if the bucket is empty.
func (b Bucket) Empty() bool {
return b.Content.liquid == nil && !b.Content.milk
}
// FuelInfo ...
func (b Bucket) FuelInfo() FuelInfo {
if liq := b.Content.liquid; liq != nil && liq.LiquidType() == "lava" {
return newFuelInfo(time.Second * 1000).WithResidue(NewStack(Bucket{}, 1))
}
return FuelInfo{}
}
// UseOnBlock handles the bucket filling and emptying logic.
func (b Bucket) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world.Tx, _ User, ctx *UseContext) bool {
if b.Content.milk {
return false
}
if b.Empty() {
return b.fillFrom(pos, tx, ctx)
}
liq := b.Content.liquid.WithDepth(8, false)
if bl := tx.Block(pos); canDisplace(bl, liq) || replaceableWith(bl, liq) {
tx.SetLiquid(pos, liq)
} else if bl := tx.Block(pos.Side(face)); canDisplace(bl, liq) || replaceableWith(bl, liq) {
tx.SetLiquid(pos.Side(face), liq)
} else {
return false
}
tx.PlaySound(pos.Vec3Centre(), sound.BucketEmpty{Liquid: b.Content.liquid})
ctx.NewItem = NewStack(Bucket{}, 1)
ctx.NewItemSurvivalOnly = true
ctx.SubtractFromCount(1)
return true
}
// fillFrom fills a bucket from the liquid at the position passed in the world. If there is no liquid or if
// the liquid is no source, fillFrom returns false.
func (b Bucket) fillFrom(pos cube.Pos, tx *world.Tx, ctx *UseContext) bool {
liquid, ok := tx.Liquid(pos)
if !ok {
return false
}
if liquid.LiquidDepth() != 8 || liquid.LiquidFalling() {
// Only allow picking up liquid source blocks.
return false
}
tx.SetLiquid(pos, nil)
tx.PlaySound(pos.Vec3Centre(), sound.BucketFill{Liquid: liquid})
ctx.NewItem = NewStack(Bucket{Content: LiquidBucketContent(liquid)}, 1)
ctx.NewItemSurvivalOnly = true
ctx.SubtractFromCount(1)
return true
}
// EncodeItem ...
func (b Bucket) EncodeItem() (name string, meta int16) {
if !b.Empty() {
return "minecraft:" + b.Content.String() + "_bucket", 0
}
return "minecraft:bucket", 0
}
type replaceable interface {
ReplaceableBy(b world.Block) bool
}
func replaceableWith(b world.Block, with world.Block) bool {
if r, ok := b.(replaceable); ok {
return r.ReplaceableBy(with)
}
return false
}
func canDisplace(b world.Block, liq world.Liquid) bool {
if d, ok := b.(world.LiquidDisplacer); ok {
return d.CanDisplace(liq)
}
return false
}