26ed99fda6
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
170 lines
4.3 KiB
Go
170 lines
4.3 KiB
Go
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
|
|
}
|