github.com/df-mc/dragonfly@v0.9.13/server/block/water.go (about) 1 package block 2 3 import ( 4 "github.com/df-mc/dragonfly/server/block/cube" 5 "github.com/df-mc/dragonfly/server/event" 6 "github.com/df-mc/dragonfly/server/item" 7 "github.com/df-mc/dragonfly/server/item/potion" 8 "github.com/df-mc/dragonfly/server/world" 9 "github.com/df-mc/dragonfly/server/world/sound" 10 "math/rand" 11 "time" 12 ) 13 14 // Water is a natural fluid that generates abundantly in the world. 15 type Water struct { 16 empty 17 replaceable 18 19 // Still makes the water appear as if it is not flowing. 20 Still bool 21 // Depth is the depth of the water. This is a number from 1-8, where 8 is a source block and 1 is the 22 // smallest possible water block. 23 Depth int 24 // Falling specifies if the water is falling. Falling water will always appear as a source block, but its 25 // behaviour differs when it starts spreading. 26 Falling bool 27 } 28 29 // EntityInside ... 30 func (w Water) EntityInside(_ cube.Pos, _ *world.World, e world.Entity) { 31 if fallEntity, ok := e.(fallDistanceEntity); ok { 32 fallEntity.ResetFallDistance() 33 } 34 if flammable, ok := e.(flammableEntity); ok { 35 flammable.Extinguish() 36 } 37 } 38 39 // FillBottle ... 40 func (w Water) FillBottle() (world.Block, item.Stack, bool) { 41 if w.Depth == 8 { 42 return w, item.NewStack(item.Potion{Type: potion.Water()}, 1), true 43 } 44 return nil, item.Stack{}, false 45 } 46 47 // LiquidDepth returns the depth of the water. 48 func (w Water) LiquidDepth() int { 49 return w.Depth 50 } 51 52 // SpreadDecay returns 1 - The amount of levels decreased upon spreading. 53 func (Water) SpreadDecay() int { 54 return 1 55 } 56 57 // WithDepth returns the water with the depth passed. 58 func (w Water) WithDepth(depth int, falling bool) world.Liquid { 59 w.Depth = depth 60 w.Falling = falling 61 w.Still = false 62 return w 63 } 64 65 // LiquidFalling returns Water.Falling. 66 func (w Water) LiquidFalling() bool { 67 return w.Falling 68 } 69 70 // BlastResistance always returns 500. 71 func (Water) BlastResistance() float64 { 72 return 500 73 } 74 75 // HasLiquidDrops ... 76 func (Water) HasLiquidDrops() bool { 77 return false 78 } 79 80 // LightDiffusionLevel ... 81 func (Water) LightDiffusionLevel() uint8 { 82 return 2 83 } 84 85 // ScheduledTick ... 86 func (w Water) ScheduledTick(pos cube.Pos, wo *world.World, _ *rand.Rand) { 87 if w.Depth == 7 { 88 // Attempt to form new water source blocks. 89 count := 0 90 pos.Neighbours(func(neighbour cube.Pos) { 91 if neighbour[1] == pos[1] { 92 if liquid, ok := wo.Liquid(neighbour); ok { 93 if water, ok := liquid.(Water); ok && water.Depth == 8 && !water.Falling { 94 count++ 95 } 96 } 97 } 98 }, wo.Range()) 99 if count >= 2 { 100 if !canFlowInto(w, wo, pos.Side(cube.FaceDown), true) { 101 // Only form a new source block if there either is no water below this block, or if the water 102 // below this is not falling (full source block). 103 res := Water{Depth: 8, Still: true} 104 ctx := event.C() 105 if wo.Handler().HandleLiquidFlow(ctx, pos, pos, res, w); ctx.Cancelled() { 106 return 107 } 108 wo.SetLiquid(pos, res) 109 } 110 } 111 } 112 tickLiquid(w, pos, wo) 113 } 114 115 // NeighbourUpdateTick ... 116 func (Water) NeighbourUpdateTick(pos, _ cube.Pos, wo *world.World) { 117 if wo.Dimension().WaterEvaporates() { 118 // Particles are spawned client-side. 119 wo.SetLiquid(pos, nil) 120 return 121 } 122 wo.ScheduleBlockUpdate(pos, time.Second/4) 123 } 124 125 // LiquidType ... 126 func (Water) LiquidType() string { 127 return "water" 128 } 129 130 // Harden hardens the water if lava flows into it. 131 func (w Water) Harden(pos cube.Pos, wo *world.World, flownIntoBy *cube.Pos) bool { 132 if flownIntoBy == nil { 133 return false 134 } 135 if lava, ok := wo.Block(pos.Side(cube.FaceUp)).(Lava); ok { 136 ctx := event.C() 137 if wo.Handler().HandleLiquidHarden(ctx, pos, w, lava, Stone{}); ctx.Cancelled() { 138 return false 139 } 140 wo.SetBlock(pos, Stone{}, nil) 141 wo.PlaySound(pos.Vec3Centre(), sound.Fizz{}) 142 return true 143 } else if lava, ok := wo.Block(*flownIntoBy).(Lava); ok { 144 ctx := event.C() 145 if wo.Handler().HandleLiquidHarden(ctx, pos, w, lava, Cobblestone{}); ctx.Cancelled() { 146 return false 147 } 148 wo.SetBlock(*flownIntoBy, Cobblestone{}, nil) 149 wo.PlaySound(pos.Vec3Centre(), sound.Fizz{}) 150 return true 151 } 152 return false 153 } 154 155 // EncodeBlock ... 156 func (w Water) EncodeBlock() (name string, properties map[string]any) { 157 if w.Depth < 1 || w.Depth > 8 { 158 panic("invalid water depth, must be between 1 and 8") 159 } 160 v := 8 - w.Depth 161 if w.Falling { 162 v += 8 163 } 164 if w.Still { 165 return "minecraft:water", map[string]any{"liquid_depth": int32(v)} 166 } 167 return "minecraft:flowing_water", map[string]any{"liquid_depth": int32(v)} 168 } 169 170 // allWater returns a list of all water states. 171 func allWater() (b []world.Block) { 172 f := func(still, falling bool) { 173 b = append(b, Water{Still: still, Falling: falling, Depth: 8}) 174 b = append(b, Water{Still: still, Falling: falling, Depth: 7}) 175 b = append(b, Water{Still: still, Falling: falling, Depth: 6}) 176 b = append(b, Water{Still: still, Falling: falling, Depth: 5}) 177 b = append(b, Water{Still: still, Falling: falling, Depth: 4}) 178 b = append(b, Water{Still: still, Falling: falling, Depth: 3}) 179 b = append(b, Water{Still: still, Falling: falling, Depth: 2}) 180 b = append(b, Water{Still: still, Falling: falling, Depth: 1}) 181 } 182 f(true, true) 183 f(true, false) 184 f(false, false) 185 f(false, true) 186 return 187 }