163 lines
4.2 KiB
Go
163 lines
4.2 KiB
Go
|
|
package block
|
||
|
|
|
||
|
|
import (
|
||
|
|
"math/rand/v2"
|
||
|
|
|
||
|
|
"github.com/df-mc/dragonfly/server/block/cube"
|
||
|
|
"github.com/df-mc/dragonfly/server/event"
|
||
|
|
"github.com/df-mc/dragonfly/server/item"
|
||
|
|
"github.com/df-mc/dragonfly/server/world"
|
||
|
|
"github.com/go-gl/mathgl/mgl64"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Leaves are blocks that grow as part of trees which mainly drop saplings and sticks.
|
||
|
|
type Leaves struct {
|
||
|
|
leaves
|
||
|
|
sourceWaterDisplacer
|
||
|
|
|
||
|
|
// Type is the type of the leaves.
|
||
|
|
Type LeavesType
|
||
|
|
// Persistent specifies if the leaves are persistent, meaning they will not decay as a result of no wood
|
||
|
|
// being nearby.
|
||
|
|
Persistent bool
|
||
|
|
|
||
|
|
ShouldUpdate bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// UseOnBlock makes leaves persistent when they are placed so that they don't decay.
|
||
|
|
func (l Leaves) 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, l)
|
||
|
|
if !used {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
l.Persistent = true
|
||
|
|
|
||
|
|
place(tx, pos, l, user, ctx)
|
||
|
|
return placed(ctx)
|
||
|
|
}
|
||
|
|
|
||
|
|
// findLog ...
|
||
|
|
func findLog(pos cube.Pos, tx *world.Tx, visited *[]cube.Pos, distance int) bool {
|
||
|
|
for _, v := range *visited {
|
||
|
|
if v == pos {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
*visited = append(*visited, pos)
|
||
|
|
|
||
|
|
if log, ok := tx.Block(pos).(Log); ok && !log.Stripped {
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
if _, ok := tx.Block(pos).(Leaves); !ok || distance > 6 {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
logFound := false
|
||
|
|
pos.Neighbours(func(neighbour cube.Pos) {
|
||
|
|
if !logFound && findLog(neighbour, tx, visited, distance+1) {
|
||
|
|
logFound = true
|
||
|
|
}
|
||
|
|
}, tx.Range())
|
||
|
|
return logFound
|
||
|
|
}
|
||
|
|
|
||
|
|
// RandomTick ...
|
||
|
|
func (l Leaves) RandomTick(pos cube.Pos, tx *world.Tx, _ *rand.Rand) {
|
||
|
|
if !l.Persistent && l.ShouldUpdate {
|
||
|
|
if findLog(pos, tx, &[]cube.Pos{}, 0) {
|
||
|
|
l.ShouldUpdate = false
|
||
|
|
tx.SetBlock(pos, l, nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
ctx := event.C(tx)
|
||
|
|
if tx.World().Handler().HandleLeavesDecay(ctx, pos); ctx.Cancelled() {
|
||
|
|
// Prevent immediate re-updating.
|
||
|
|
l.ShouldUpdate = false
|
||
|
|
tx.SetBlock(pos, l, nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
tx.SetBlock(pos, nil, nil)
|
||
|
|
for _, drop := range l.BreakInfo().Drops(item.ToolNone{}, nil) {
|
||
|
|
dropItem(tx, drop, pos.Vec3Centre())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// NeighbourUpdateTick ...
|
||
|
|
func (l Leaves) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) {
|
||
|
|
if !l.Persistent && !l.ShouldUpdate {
|
||
|
|
l.ShouldUpdate = true
|
||
|
|
tx.SetBlock(pos, l, nil)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// FlammabilityInfo ...
|
||
|
|
func (l Leaves) FlammabilityInfo() FlammabilityInfo {
|
||
|
|
return newFlammabilityInfo(30, 60, true)
|
||
|
|
}
|
||
|
|
|
||
|
|
// BreakInfo ...
|
||
|
|
func (l Leaves) BreakInfo() BreakInfo {
|
||
|
|
return newBreakInfo(0.2, alwaysHarvestable, func(t item.Tool) bool {
|
||
|
|
return t.ToolType() == item.TypeShears || t.ToolType() == item.TypeHoe
|
||
|
|
}, func(t item.Tool, enchantments []item.Enchantment) []item.Stack {
|
||
|
|
if t.ToolType() == item.TypeShears || hasSilkTouch(enchantments) {
|
||
|
|
return []item.Stack{item.NewStack(l, 1)}
|
||
|
|
}
|
||
|
|
fortune := fortuneLevel(enchantments)
|
||
|
|
var drops []item.Stack
|
||
|
|
|
||
|
|
// TODO: Drop saplings.
|
||
|
|
|
||
|
|
stickChances := []float64{0.02, 0.022222222, 0.025, 0.033333333}
|
||
|
|
if rand.Float64() < stickChances[min(fortune, 3)] {
|
||
|
|
drops = append(drops, item.NewStack(item.Stick{}, rand.IntN(2)+1))
|
||
|
|
}
|
||
|
|
if wood, ok := l.Type.Wood(); ok && (wood == OakWood() || wood == DarkOakWood()) {
|
||
|
|
appleChances := []float64{0.005, 0.005555556, 0.00625, 0.008333333}
|
||
|
|
if rand.Float64() < appleChances[min(fortune, 3)] {
|
||
|
|
drops = append(drops, item.NewStack(item.Apple{}, 1))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return drops
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// CompostChance ...
|
||
|
|
func (Leaves) CompostChance() float64 {
|
||
|
|
return 0.3
|
||
|
|
}
|
||
|
|
|
||
|
|
// EncodeItem ...
|
||
|
|
func (l Leaves) EncodeItem() (name string, meta int16) {
|
||
|
|
return "minecraft:" + l.Type.String(), 0
|
||
|
|
}
|
||
|
|
|
||
|
|
// LightDiffusionLevel ...
|
||
|
|
func (Leaves) LightDiffusionLevel() uint8 {
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
|
||
|
|
// SideClosed ...
|
||
|
|
func (Leaves) SideClosed(cube.Pos, cube.Pos, *world.Tx) bool {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
// EncodeBlock ...
|
||
|
|
func (l Leaves) EncodeBlock() (name string, properties map[string]any) {
|
||
|
|
return "minecraft:" + l.Type.String(), map[string]any{"persistent_bit": l.Persistent, "update_bit": l.ShouldUpdate}
|
||
|
|
}
|
||
|
|
|
||
|
|
// allLeaves returns a list of all possible leaves states.
|
||
|
|
func allLeaves() (leaves []world.Block) {
|
||
|
|
f := func(persistent, update bool) {
|
||
|
|
for _, t := range LeavesTypes() {
|
||
|
|
leaves = append(leaves, Leaves{Type: t, Persistent: persistent, ShouldUpdate: update})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
f(true, true)
|
||
|
|
f(true, false)
|
||
|
|
f(false, true)
|
||
|
|
f(false, false)
|
||
|
|
return
|
||
|
|
}
|