Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented vines #578

Merged
merged 24 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
637aa6a
block: Implemented vines.
JustTalDevelops Jul 19, 2022
e0ea2fd
block/vine.go: Vines are replaceable.
JustTalDevelops Jul 19, 2022
a42ca29
block/vine.go: Reset fall distance.
JustTalDevelops Jul 19, 2022
b40c01d
world/tick.go: Revert changes.
JustTalDevelops Jul 19, 2022
54e00fe
block/vine.go: Corrected break info.
JustTalDevelops Jul 24, 2022
beb5e6f
Merge branch 'master' into feature/vines
JustTalDevelops Jul 24, 2022
85081a4
Merge branch 'master' into feature/vines
DaPigGuy Dec 19, 2024
1ed9b3f
update for world tx
DaPigGuy Dec 19, 2024
9c88378
block/vine.go: Do not drop with silk touch
DaPigGuy Dec 19, 2024
e787779
block/vine.go: Make liquid removable
DaPigGuy Dec 19, 2024
58e4f86
block/vine.go: Make waterloggable
DaPigGuy Dec 19, 2024
8883c74
block/vine.go: Actually make waterloggable
DaPigGuy Dec 19, 2024
6344c9a
block/vine.go: Make compostable
DaPigGuy Dec 19, 2024
97f3845
block/vine.go: Correct `canSpreadTo`
DaPigGuy Dec 19, 2024
40c17c4
block/vine.go: Fix incorrect spread logic
DaPigGuy Dec 20, 2024
77409b5
block/vine.go: Fix order of spread attempts/try down if all fails
DaPigGuy Dec 20, 2024
bea236f
block/vine.go: Check downward block is air or vines
DaPigGuy Dec 20, 2024
ec1a6f0
block/vine.go: Cleanup
DaPigGuy Dec 20, 2024
cf60e08
block/vine.go: Correctly break if no supporting blocks
DaPigGuy Dec 20, 2024
02bfd1b
block/vine.go: Do not allow overwriting existing vine blocks
DaPigGuy Dec 20, 2024
fa55fbf
block/vine.go: Cleanup
DaPigGuy Dec 20, 2024
5f44366
block/vine.go: Provide extensive docs on vine spread logic
DaPigGuy Dec 24, 2024
2e6f480
block/vine.go: Cleanup
DaPigGuy Dec 24, 2024
0d0cf29
block/vine.go: Rename SetAttachment to WithAttachment
DaPigGuy Dec 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions server/block/hash.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions server/block/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ func init() {
registerAll(allSugarCane())
registerAll(allTorches())
registerAll(allTrapdoors())
registerAll(allVines())
registerAll(allWalls())
registerAll(allWater())
registerAll(allWheat())
Expand Down Expand Up @@ -355,6 +356,7 @@ func init() {
world.RegisterItem(TuffBricks{})
world.RegisterItem(TuffBricks{Chiseled: true})
world.RegisterItem(PolishedTuff{})
world.RegisterItem(Vines{})
world.RegisterItem(WheatSeeds{})
world.RegisterItem(DecoratedPot{})
world.RegisterItem(ShortGrass{})
Expand Down
330 changes: 330 additions & 0 deletions server/block/vine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
package block

import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/block/model"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl64"
"math/rand"
)

// Vines are climbable non-solid vegetation blocks that grow on walls.
type Vines struct {
replaceable
transparent
empty
sourceWaterDisplacer

// NorthDirection is true if the vines are attached towards north.
NorthDirection bool
// EastDirection is true if the vines are attached towards east.
EastDirection bool
// SouthDirection is true if the vines are attached towards south.
SouthDirection bool
// WestDirection is true if the vines are attached towards west.
WestDirection bool
}

// CompostChance ...
func (Vines) CompostChance() float64 {
return 0.5
}

// SideClosed ...
func (Vines) SideClosed(cube.Pos, cube.Pos, *world.Tx) bool {
return false
}

// HasLiquidDrops ...
func (Vines) HasLiquidDrops() bool {
return false
}

// FlammabilityInfo ...
func (Vines) FlammabilityInfo() FlammabilityInfo {
return newFlammabilityInfo(15, 100, true)
}

// BreakInfo ...
func (v Vines) BreakInfo() BreakInfo {
return newBreakInfo(0.2, alwaysHarvestable, func(t item.Tool) bool {
return t.ToolType() == item.TypeShears || t.ToolType() == item.TypeAxe
}, func(t item.Tool, enchantments []item.Enchantment) []item.Stack {
if t.ToolType() == item.TypeShears {
return []item.Stack{item.NewStack(v, 1)}
}
return nil
})
}

// EntityInside ...
func (Vines) EntityInside(_ cube.Pos, _ *world.Tx, e world.Entity) {
if fallEntity, ok := e.(fallDistanceEntity); ok {
fallEntity.ResetFallDistance()
}
}

// WithAttachment returns a Vines block with an attachment on the given cube.Direction.
func (v Vines) WithAttachment(direction cube.Direction, attached bool) Vines {
switch direction {
case cube.North:
v.NorthDirection = attached
return v
case cube.East:
v.EastDirection = attached
return v
case cube.South:
v.SouthDirection = attached
return v
case cube.West:
v.WestDirection = attached
return v
}
panic("should never happen")
}

// Attachment returns the attachment of the vines at the given direction.
func (v Vines) Attachment(direction cube.Direction) bool {
switch direction {
case cube.North:
return v.NorthDirection
case cube.East:
return v.EastDirection
case cube.South:
return v.SouthDirection
case cube.West:
return v.WestDirection
}
panic("should never happen")
}

// Attachments returns all attachments of the vines.
func (v Vines) Attachments() (attachments []cube.Direction) {
for _, d := range cube.Directions() {
if v.Attachment(d) {
attachments = append(attachments, d)
}
}
return
}

// UseOnBlock ...
func (v Vines) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world.Tx, user item.User, ctx *item.UseContext) bool {
if _, ok := tx.Block(pos).Model().(model.Solid); !ok || face.Axis() == cube.Y {
return false
}
pos, face, used := firstReplaceable(tx, pos, face, v)
if !used {
return false
}
if _, ok := tx.Block(pos).(Vines); ok {
// Do not overwrite existing vine block.
return false
}
//noinspection GoAssignmentToReceiver
v = v.WithAttachment(face.Direction().Opposite(), true)

place(tx, pos, v, user, ctx)
return placed(ctx)
}

// NeighbourUpdateTick ...
func (v Vines) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) {
above, updated := tx.Block(pos.Side(cube.FaceUp)), false
for _, d := range v.Attachments() {
if !v.canSpreadTo(tx, pos.Side(d.Face())) {
if o, ok := above.(Vines); !ok || !o.Attachment(d) {
//noinspection GoAssignmentToReceiver
v = v.WithAttachment(d, false)
updated = true
}
}
}
if !updated {
return
}
if len(v.Attachments()) == 0 {
tx.SetBlock(pos, nil, nil)
return
}
tx.SetBlock(pos, v, nil)
}

// RandomTick ...
func (v Vines) RandomTick(pos cube.Pos, tx *world.Tx, r *rand.Rand) {
if r.Float64() > 0.25 {
// Vines have a 25% chance of spreading.
return
}

// Choose a random direction to spread.
face := cube.Face(r.Intn(len(cube.Faces())))
selectedPos := pos.Side(face)

DaPigGuy marked this conversation as resolved.
Show resolved Hide resolved
// If a horizontal direction was chosen and the vine block is not already
// attached in that direction, attempt to spread in that direction.
if face.Axis() != cube.Y && !v.Attachment(face.Direction()) {
if !v.canSpread(tx, pos) {
// No further attempt to spread vertically will be made.
return
}
// Attempt to create a new vine block if there is a neighbouring air block
// in the chosen direction.
if _, ok := tx.Block(selectedPos).(Air); ok {
rightRotatedFace := face.RotateRight()
leftRotatedFace := face.RotateLeft()

attachedOnRight := v.Attachment(rightRotatedFace.Direction())
attachedOnLeft := v.Attachment(leftRotatedFace.Direction())

rightSelectedPos := selectedPos.Side(rightRotatedFace)
leftSelectedPos := selectedPos.Side(leftRotatedFace)

// Four attempts to create a new vine block will be made, in the
// following order:
// 1) If the current vine block is attached in the direction towards
// the right ("clockwise") of the chosen direction, and a solid
// block can support a vine on that direction in the selected
// position, create a new vine block attached on that clockwise
// direction at the selected position.
// 2) If the clockwise direction fails, try again with the left
// ("counter-clockwise") direction.
// 3) If the current vine block is attached in the direction towards
// the right of the chosen direction, the current vine block is
// also backed by a solid block in that same direction, and the
// block neighbouring the selected position in that direction is
// air, spread into that air block onto the face opposite of the
// chosen direction. The vine jumps from one face of a block onto
// another as a result.
// 4) If the clockwise direction fails, try again with the left
// direction.
if attachedOnRight && v.canSpreadTo(tx, rightSelectedPos) {
tx.SetBlock(selectedPos, (Vines{}).WithAttachment(rightRotatedFace.Direction(), true), nil)
} else if attachedOnLeft && v.canSpreadTo(tx, leftSelectedPos) {
tx.SetBlock(selectedPos, (Vines{}).WithAttachment(leftRotatedFace.Direction(), true), nil)
} else if _, ok = tx.Block(rightSelectedPos).(Air); ok && attachedOnRight && v.canSpreadTo(tx, pos.Side(rightRotatedFace)) {
tx.SetBlock(rightSelectedPos, (Vines{}).WithAttachment(face.Opposite().Direction(), true), nil)
} else if _, ok = tx.Block(leftSelectedPos).(Air); ok && attachedOnLeft && v.canSpreadTo(tx, pos.Side(leftRotatedFace)) {
tx.SetBlock(leftSelectedPos, (Vines{}).WithAttachment(face.Opposite().Direction(), true), nil)
}
} else if v.canSpreadTo(tx, selectedPos) {
// If the neighbouring block is solid, update the vine to be attached in that direction.
tx.SetBlock(pos, v.WithAttachment(face.Direction(), true), nil)
}
return
}

// If the chosen direction is Up and the position above is within the height
// limit, attempt to spread upwards.
if face == cube.FaceUp && selectedPos.OutOfBounds(tx.Range()) {
// Vines can only spread upwards into an air block.
if _, ok := tx.Block(selectedPos).(Air); ok {
if !v.canSpread(tx, pos) {
// No further attempt to spread down will be made.
return
}
newVines := Vines{}
for _, f := range cube.HorizontalFaces() {
// For each direction the current vine block is attached on,
// there is a 50% chance for the new above vine block to
// attach onto the direction, if there is also a solid block
// in that direction to support the vine.
if r.Intn(2) == 0 && v.Attachment(f.Direction()) && v.canSpreadTo(tx, selectedPos.Side(f)) {
newVines = newVines.WithAttachment(f.Direction(), true)
}
}
if len(newVines.Attachments()) > 0 {
tx.SetBlock(selectedPos, newVines, nil)
}
return
}
}

// If an attempt to spread horizontally or upwards has failed but not exited
// early, attempt to spread downwards.
selectedPos = pos.Side(cube.FaceDown)
if selectedPos.OutOfBounds(tx.Range()) {
return
}
newVines, vines := tx.Block(selectedPos).(Vines)
if _, ok := tx.Block(selectedPos).(Air); !ok && !vines {
// The block under the current vine block must be air or a vine block.
return
}
var changed bool
for _, f := range cube.HorizontalFaces() {
// For each direction the current vine block is attached on, there is a
// 50% chance for the below vine block to attach onto the direction if
// it is not already attached in that direction.
if r.Intn(2) == 0 && v.Attachment(f.Direction()) && !newVines.Attachment(f.Direction()) {
newVines, changed = newVines.WithAttachment(f.Direction(), true), true
}
}
if changed {
tx.SetBlock(selectedPos, newVines, nil)
}
}

// EncodeItem ...
func (Vines) EncodeItem() (name string, meta int16) {
return "minecraft:vine", 0
}

// EncodeBlock ...
func (v Vines) EncodeBlock() (string, map[string]any) {
var bits int
for i, ok := range []bool{v.SouthDirection, v.WestDirection, v.NorthDirection, v.EastDirection} {
if ok {
bits |= 1 << i
}
}
return "minecraft:vine", map[string]any{"vine_direction_bits": int32(bits)}
}

// canSpreadTo returns true if the vines can spread onto the block at the
// given position. Vines may only spread onto fully solid blocks.
func (Vines) canSpreadTo(tx *world.Tx, pos cube.Pos) bool {
_, ok := tx.Block(pos).Model().(model.Solid)
return ok
}

// canSpread returns true if the vines can spread from the given position. Vines
// may only spread horizontally or upwards if there are fewer than 4 vines within
// a 9x9x3 area centered around the Vines.
func (v Vines) canSpread(tx *world.Tx, pos cube.Pos) bool {
var count int
for x := -4; x <= 4; x++ {
for z := -4; z <= 4; z++ {
for y := -1; y <= 1; y++ {
if _, ok := tx.Block(pos.Add(cube.Pos{x, y, z})).(Vines); ok {
count++
// The center vine is counted, for a max of 4+1=5.
if count >= 5 {
return false
}
}
}
}
}
return true
}

// allVines ...
func allVines() (b []world.Block) {
for _, north := range []bool{true, false} {
for _, east := range []bool{true, false} {
for _, south := range []bool{true, false} {
for _, west := range []bool{true, false} {
b = append(b, Vines{
NorthDirection: north,
EastDirection: east,
SouthDirection: south,
WestDirection: west,
})
}
}
}
}
return
}
Loading