package block import ( "math/rand/v2" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl64" ) // SugarCane is a plant block that generates naturally near water. type SugarCane struct { empty transparent // Age is the growth state of sugar cane. Values range from 0 to 15. Age int } // UseOnBlock ensures the placement of the block is OK. func (c SugarCane) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world.Tx, user item.User, ctx *item.UseContext) (used bool) { pos, _, used = firstReplaceable(tx, pos, face, c) if !used { return false } if !c.canGrowHere(pos, tx, true) { return false } place(tx, pos, c, user, ctx) return placed(ctx) } // NeighbourUpdateTick ... func (c SugarCane) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) { if !c.canGrowHere(pos, tx, true) { breakBlock(c, pos, tx) } } // RandomTick ... func (c SugarCane) RandomTick(pos cube.Pos, tx *world.Tx, _ *rand.Rand) { if !c.canGrowHere(pos, tx, true) { breakBlock(c, pos, tx) return } if c.Age < 15 { c.Age++ } else if c.Age == 15 { c.Age = 0 if c.canGrowHere(pos.Side(cube.FaceDown), tx, false) { for y := 1; y < 3; y++ { if _, ok := tx.Block(pos.Add(cube.Pos{0, y})).(Air); ok { tx.SetBlock(pos.Add(cube.Pos{0, y}), SugarCane{}, nil) break } else if _, ok := tx.Block(pos.Add(cube.Pos{0, y})).(SugarCane); !ok { break } } } } tx.SetBlock(pos, c, nil) } // BoneMeal ... func (c SugarCane) BoneMeal(pos cube.Pos, tx *world.Tx) item.BoneMealResult { for _, ok := tx.Block(pos.Side(cube.FaceDown)).(SugarCane); ok; _, ok = tx.Block(pos.Side(cube.FaceDown)).(SugarCane) { pos = pos.Side(cube.FaceDown) } if c.canGrowHere(pos.Side(cube.FaceDown), tx, false) { for y := 1; y < 3; y++ { if _, ok := tx.Block(pos.Add(cube.Pos{0, y})).(Air); ok { tx.SetBlock(pos.Add(cube.Pos{0, y}), SugarCane{}, nil) } } return item.BoneMealResultSmall } return item.BoneMealResultNone } // canGrowHere implements logic to check if sugar cane can live/grow here. func (c SugarCane) canGrowHere(pos cube.Pos, tx *world.Tx, recursive bool) bool { if _, ok := tx.Block(pos.Side(cube.FaceDown)).(SugarCane); ok && recursive { return c.canGrowHere(pos.Side(cube.FaceDown), tx, recursive) } if supportsVegetation(c, tx.Block(pos.Sub(cube.Pos{0, 1}))) { for _, face := range cube.HorizontalFaces() { if liquid, ok := tx.Liquid(pos.Side(face).Side(cube.FaceDown)); ok { if _, ok := liquid.(Water); ok { return true } } } } return false } // HasLiquidDrops ... func (c SugarCane) HasLiquidDrops() bool { return true } // BreakInfo ... func (c SugarCane) BreakInfo() BreakInfo { return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(c)) } // EncodeItem ... func (c SugarCane) EncodeItem() (name string, meta int16) { return "minecraft:sugar_cane", 0 } // EncodeBlock ... func (c SugarCane) EncodeBlock() (name string, properties map[string]any) { return "minecraft:reeds", map[string]any{"age": int32(c.Age)} } // allSugarCane returns all possible states of a sugar cane block. func allSugarCane() (b []world.Block) { for i := 0; i < 16; i++ { b = append(b, SugarCane{Age: i}) } return }