github.com/df-mc/dragonfly@v0.9.13/server/block/grass.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 // Grass blocks generate abundantly across the surface of the world. 10 type Grass struct { 11 solid 12 } 13 14 // plantSelection are the plants that are picked from when a bone meal is attempted. 15 // TODO: Base plant selection on current biome. 16 var plantSelection = []world.Block{ 17 Flower{Type: OxeyeDaisy()}, 18 Flower{Type: PinkTulip()}, 19 Flower{Type: Cornflower()}, 20 Flower{Type: WhiteTulip()}, 21 Flower{Type: RedTulip()}, 22 Flower{Type: OrangeTulip()}, 23 Flower{Type: Dandelion()}, 24 Flower{Type: Poppy()}, 25 } 26 27 // init adds extra variants of TallGrass to the plant selection. 28 func init() { 29 for i := 0; i < 8; i++ { 30 plantSelection = append(plantSelection, TallGrass{Type: FernTallGrass()}) 31 } 32 for i := 0; i < 12; i++ { 33 plantSelection = append(plantSelection, TallGrass{Type: NormalTallGrass()}) 34 } 35 } 36 37 // SoilFor ... 38 func (g Grass) SoilFor(block world.Block) bool { 39 switch block.(type) { 40 case TallGrass, DoubleTallGrass, Flower, DoubleFlower, NetherSprouts, SugarCane: 41 return true 42 } 43 return false 44 } 45 46 // RandomTick handles the ticking of grass, which may or may not result in the spreading of grass onto dirt. 47 func (g Grass) RandomTick(pos cube.Pos, w *world.World, r *rand.Rand) { 48 aboveLight := w.Light(pos.Side(cube.FaceUp)) 49 if aboveLight < 4 { 50 // The light above the block is too low: The grass turns to dirt. 51 w.SetBlock(pos, Dirt{}, nil) 52 return 53 } 54 if aboveLight < 9 { 55 // Don't attempt to spread if the light level is lower than 9. 56 return 57 } 58 59 // Generate a single uint32 as we only need 28 bits (7 bits each iteration). 60 n := r.Uint32() 61 62 // Four attempts to spread to another block. 63 for i := 0; i < 4; i++ { 64 x, y, z := int(n)%3, int(n>>2)%5, int(n>>5)%3 65 n >>= 7 66 67 spreadPos := pos.Add(cube.Pos{x - 1, y - 3, z - 1}) 68 // Don't spread grass to locations where dirt is exposed to hardly any light. 69 if w.Light(spreadPos.Side(cube.FaceUp)) < 4 { 70 continue 71 } 72 b := w.Block(spreadPos) 73 if dirt, ok := b.(Dirt); !ok || dirt.Coarse { 74 continue 75 } 76 w.SetBlock(spreadPos, g, nil) 77 } 78 } 79 80 // BoneMeal ... 81 func (g Grass) BoneMeal(pos cube.Pos, w *world.World) bool { 82 for i := 0; i < 14; i++ { 83 c := pos.Add(cube.Pos{rand.Intn(6) - 3, 0, rand.Intn(6) - 3}) 84 above := c.Side(cube.FaceUp) 85 _, air := w.Block(above).(Air) 86 _, grass := w.Block(c).(Grass) 87 if air && grass { 88 w.SetBlock(above, plantSelection[rand.Intn(len(plantSelection))], nil) 89 } 90 } 91 92 return false 93 } 94 95 // BreakInfo ... 96 func (g Grass) BreakInfo() BreakInfo { 97 return newBreakInfo(0.6, alwaysHarvestable, shovelEffective, silkTouchOneOf(Dirt{}, g)) 98 } 99 100 // CompostChance ... 101 func (Grass) CompostChance() float64 { 102 return 0.3 103 } 104 105 // EncodeItem ... 106 func (Grass) EncodeItem() (name string, meta int16) { 107 return "minecraft:grass", 0 108 } 109 110 // EncodeBlock ... 111 func (Grass) EncodeBlock() (string, map[string]any) { 112 return "minecraft:grass", nil 113 } 114 115 // Till ... 116 func (g Grass) Till() (world.Block, bool) { 117 return Farmland{}, true 118 } 119 120 // Shovel ... 121 func (g Grass) Shovel() (world.Block, bool) { 122 return DirtPath{}, true 123 }