github.com/df-mc/dragonfly@v0.9.13/server/item/bucket.go (about) 1 package item 2 3 import ( 4 "github.com/df-mc/dragonfly/server/block/cube" 5 "github.com/df-mc/dragonfly/server/world" 6 "github.com/df-mc/dragonfly/server/world/sound" 7 "github.com/go-gl/mathgl/mgl64" 8 "time" 9 ) 10 11 // BucketContent is the content of a bucket. 12 type BucketContent struct { 13 liquid world.Liquid 14 milk bool 15 } 16 17 // LiquidBucketContent returns a new BucketContent with the liquid passed in. 18 func LiquidBucketContent(l world.Liquid) BucketContent { 19 return BucketContent{liquid: l} 20 } 21 22 // MilkBucketContent returns a new BucketContent with the milk flag set. 23 func MilkBucketContent() BucketContent { 24 return BucketContent{milk: true} 25 } 26 27 // Liquid returns the world.Liquid that a Bucket with this BucketContent places. 28 // If this BucketContent does not place a liquid block, false is returned. 29 func (b BucketContent) Liquid() (world.Liquid, bool) { 30 return b.liquid, b.liquid != nil 31 } 32 33 // String converts the BucketContent to a string. 34 func (b BucketContent) String() string { 35 if b.milk { 36 return "milk" 37 } else if b.liquid != nil { 38 return b.liquid.LiquidType() 39 } 40 return "" 41 } 42 43 // LiquidType returns the type of liquid the bucket contains. 44 func (b BucketContent) LiquidType() string { 45 if b.liquid != nil { 46 return b.liquid.LiquidType() 47 } 48 return "milk" 49 } 50 51 // Bucket is a tool used to carry water, lava and fish. 52 type Bucket struct { 53 // Content is the content that the bucket has. By default, this value resolves to an empty bucket. 54 Content BucketContent 55 } 56 57 // MaxCount returns 16. 58 func (b Bucket) MaxCount() int { 59 if b.Empty() { 60 return 16 61 } 62 return 1 63 } 64 65 // AlwaysConsumable ... 66 func (b Bucket) AlwaysConsumable() bool { 67 return b.Content.milk 68 } 69 70 // CanConsume ... 71 func (b Bucket) CanConsume() bool { 72 return b.Content.milk 73 } 74 75 // ConsumeDuration ... 76 func (b Bucket) ConsumeDuration() time.Duration { 77 return DefaultConsumeDuration 78 } 79 80 // Consume ... 81 func (b Bucket) Consume(_ *world.World, c Consumer) Stack { 82 for _, effect := range c.Effects() { 83 c.RemoveEffect(effect.Type()) 84 } 85 return NewStack(Bucket{}, 1) 86 } 87 88 // Empty returns true if the bucket is empty. 89 func (b Bucket) Empty() bool { 90 return b.Content.liquid == nil && !b.Content.milk 91 } 92 93 // FuelInfo ... 94 func (b Bucket) FuelInfo() FuelInfo { 95 if liq := b.Content.liquid; liq != nil && liq.LiquidType() == "lava" { 96 return newFuelInfo(time.Second * 1000).WithResidue(NewStack(Bucket{}, 1)) 97 } 98 return FuelInfo{} 99 } 100 101 // UseOnBlock handles the bucket filling and emptying logic. 102 func (b Bucket) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, _ User, ctx *UseContext) bool { 103 if b.Content.milk { 104 return false 105 } 106 if b.Empty() { 107 return b.fillFrom(pos, w, ctx) 108 } 109 liq := b.Content.liquid.WithDepth(8, false) 110 if bl := w.Block(pos); canDisplace(bl, liq) || replaceableWith(bl, liq) { 111 w.SetLiquid(pos, liq) 112 } else if bl := w.Block(pos.Side(face)); canDisplace(bl, liq) || replaceableWith(bl, liq) { 113 w.SetLiquid(pos.Side(face), liq) 114 } else { 115 return false 116 } 117 118 w.PlaySound(pos.Vec3Centre(), sound.BucketEmpty{Liquid: b.Content.liquid}) 119 ctx.NewItem = NewStack(Bucket{}, 1) 120 ctx.NewItemSurvivalOnly = true 121 ctx.SubtractFromCount(1) 122 return true 123 } 124 125 // fillFrom fills a bucket from the liquid at the position passed in the world. If there is no liquid or if 126 // the liquid is no source, fillFrom returns false. 127 func (b Bucket) fillFrom(pos cube.Pos, w *world.World, ctx *UseContext) bool { 128 liquid, ok := w.Liquid(pos) 129 if !ok { 130 return false 131 } 132 if liquid.LiquidDepth() != 8 || liquid.LiquidFalling() { 133 // Only allow picking up liquid source blocks. 134 return false 135 } 136 w.SetLiquid(pos, nil) 137 w.PlaySound(pos.Vec3Centre(), sound.BucketFill{Liquid: liquid}) 138 139 ctx.NewItem = NewStack(Bucket{Content: LiquidBucketContent(liquid)}, 1) 140 ctx.NewItemSurvivalOnly = true 141 ctx.SubtractFromCount(1) 142 return true 143 } 144 145 // EncodeItem ... 146 func (b Bucket) EncodeItem() (name string, meta int16) { 147 if !b.Empty() { 148 return "minecraft:" + b.Content.String() + "_bucket", 0 149 } 150 return "minecraft:bucket", 0 151 } 152 153 type replaceable interface { 154 ReplaceableBy(b world.Block) bool 155 } 156 157 func replaceableWith(b world.Block, with world.Block) bool { 158 if r, ok := b.(replaceable); ok { 159 return r.ReplaceableBy(with) 160 } 161 return false 162 } 163 164 func canDisplace(b world.Block, liq world.Liquid) bool { 165 if d, ok := b.(world.LiquidDisplacer); ok { 166 return d.CanDisplace(liq) 167 } 168 return false 169 }