github.com/df-mc/dragonfly@v0.9.13/server/block/farmland.go (about) 1 package block 2 3 import ( 4 "github.com/df-mc/dragonfly/server/block/cube" 5 "github.com/df-mc/dragonfly/server/world" 6 "math/rand" 7 ) 8 9 // Farmland is a block that crops are grown on. Farmland is created by interacting with a grass or dirt block using a 10 // hoe. Farmland can be hydrated by nearby water, with no hydration resulting in it turning into a dirt block. 11 type Farmland struct { 12 tilledGrass 13 14 // Hydration is how much moisture the farmland block has. Hydration starts at 0 & caps at 7. During a random tick 15 // update, if there is water within 4 blocks from the farmland block, hydration is set to 7. Otherwise, it 16 // decrements until it turns into dirt. 17 Hydration int 18 } 19 20 // SoilFor ... 21 func (f Farmland) SoilFor(block world.Block) bool { 22 switch block.(type) { 23 case TallGrass, DoubleTallGrass, Flower, DoubleFlower, NetherSprouts: 24 return true 25 } 26 return false 27 } 28 29 // NeighbourUpdateTick ... 30 func (f Farmland) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { 31 if solid := w.Block(pos.Side(cube.FaceUp)).Model().FaceSolid(pos.Side(cube.FaceUp), cube.FaceDown, w); solid { 32 w.SetBlock(pos, Dirt{}, nil) 33 } 34 } 35 36 // RandomTick ... 37 func (f Farmland) RandomTick(pos cube.Pos, w *world.World, _ *rand.Rand) { 38 if !f.hydrated(pos, w) { 39 if f.Hydration > 0 { 40 f.Hydration-- 41 w.SetBlock(pos, f, nil) 42 } else { 43 blockAbove := w.Block(pos.Side(cube.FaceUp)) 44 if _, cropAbove := blockAbove.(Crop); !cropAbove { 45 w.SetBlock(pos, Dirt{}, nil) 46 } 47 } 48 } else { 49 f.Hydration = 7 50 w.SetBlock(pos, f, nil) 51 } 52 } 53 54 // hydrated checks for water within 4 blocks in each direction from the farmland. 55 func (f Farmland) hydrated(pos cube.Pos, w *world.World) bool { 56 posX, posY, posZ := pos.X(), pos.Y(), pos.Z() 57 for y := 0; y <= 1; y++ { 58 for x := -4; x <= 4; x++ { 59 for z := -4; z <= 4; z++ { 60 if liquid, ok := w.Liquid(cube.Pos{posX + x, posY + y, posZ + z}); ok { 61 if _, ok := liquid.(Water); ok { 62 return true 63 } 64 } 65 } 66 } 67 } 68 return false 69 } 70 71 // EntityLand ... 72 func (f Farmland) EntityLand(pos cube.Pos, w *world.World, e world.Entity, distance *float64) { 73 if living, ok := e.(livingEntity); ok { 74 if fall, ok := living.(fallDistanceEntity); ok && rand.Float64() < fall.FallDistance()-0.5 { 75 w.SetBlock(pos, Dirt{}, nil) 76 } 77 } 78 } 79 80 // fallDistanceEntity is an entity that has a fall distance. 81 type fallDistanceEntity interface { 82 // ResetFallDistance resets the entities fall distance. 83 ResetFallDistance() 84 // FallDistance returns the entities fall distance. 85 FallDistance() float64 86 } 87 88 // BreakInfo ... 89 func (f Farmland) BreakInfo() BreakInfo { 90 return newBreakInfo(0.6, alwaysHarvestable, shovelEffective, oneOf(Dirt{})) 91 } 92 93 // EncodeBlock ... 94 func (f Farmland) EncodeBlock() (name string, properties map[string]any) { 95 return "minecraft:farmland", map[string]any{"moisturized_amount": int32(f.Hydration)} 96 } 97 98 // EncodeItem ... 99 func (f Farmland) EncodeItem() (name string, meta int16) { 100 return "minecraft:farmland", 0 101 } 102 103 // allFarmland returns all possible states that a block of farmland can be in. 104 func allFarmland() (b []world.Block) { 105 for i := 0; i <= 7; i++ { 106 b = append(b, Farmland{Hydration: i}) 107 } 108 return 109 }