-
Notifications
You must be signed in to change notification settings - Fork 143
/
bucket.go
169 lines (146 loc) · 4.25 KB
/
bucket.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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.World, 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, w *world.World, _ User, ctx *UseContext) bool {
if b.Content.milk {
return false
}
if b.Empty() {
return b.fillFrom(pos, w, ctx)
}
liq := b.Content.liquid.WithDepth(8, false)
if bl := w.Block(pos); canDisplace(bl, liq) || replaceableWith(bl, liq) {
w.SetLiquid(pos, liq)
} else if bl := w.Block(pos.Side(face)); canDisplace(bl, liq) || replaceableWith(bl, liq) {
w.SetLiquid(pos.Side(face), liq)
} else {
return false
}
w.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, w *world.World, ctx *UseContext) bool {
liquid, ok := w.Liquid(pos)
if !ok {
return false
}
if liquid.LiquidDepth() != 8 || liquid.LiquidFalling() {
// Only allow picking up liquid source blocks.
return false
}
w.SetLiquid(pos, nil)
w.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
}