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  }