Files
mc/server/block/model/stair.go
T

119 lines
4.3 KiB
Go
Raw Normal View History

2026-07-09 08:33:57 +08:00
package model
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl64"
)
// Stair is a model for stair-like blocks. These have different solid sides depending on the direction the
// stairs are facing, the surrounding blocks and whether it is upside down or not.
type Stair struct {
// Facing specifies the direction that the full side of the Stair faces.
Facing cube.Direction
// UpsideDown turns the Stair upside-down, meaning the full side of the Stair is turned to the top side of the
// block.
UpsideDown bool
}
// BBox returns a slice of physics.BBox depending on if the Stair is upside down and which direction it is facing.
// Additionally, these BBoxs depend on the Stair blocks surrounding this one, which can influence the model.
func (s Stair) BBox(pos cube.Pos, bs world.BlockSource) []cube.BBox {
b := []cube.BBox{cube.Box(0, 0, 0, 1, 0.5, 1)}
if s.UpsideDown {
b[0] = cube.Box(0, 0.5, 0, 1, 1, 1)
}
t := s.cornerType(pos, bs)
face, oppositeFace := s.Facing.Face(), s.Facing.Opposite().Face()
switch t {
case noCorner, cornerRightInner, cornerLeftInner:
b = append(b, cube.Box(0.5, 0.5, 0.5, 0.5, 1, 0.5).
ExtendTowards(face, 0.5).
Stretch(s.Facing.RotateRight().Face().Axis(), 0.5))
}
switch t {
case cornerRightOuter:
b = append(b, cube.Box(0.5, 0.5, 0.5, 0.5, 1, 0.5).
ExtendTowards(face, 0.5).
ExtendTowards(s.Facing.RotateLeft().Face(), 0.5))
case cornerLeftOuter:
b = append(b, cube.Box(0.5, 0.5, 0.5, 0.5, 1, 0.5).
ExtendTowards(face, 0.5).
ExtendTowards(s.Facing.RotateRight().Face(), 0.5))
case cornerRightInner:
b = append(b, cube.Box(0.5, 0.5, 0.5, 0.5, 1, 0.5).
ExtendTowards(oppositeFace, 0.5).
ExtendTowards(s.Facing.RotateRight().Face(), 0.5))
case cornerLeftInner:
b = append(b, cube.Box(0.5, 0.5, 0.5, 0.5, 1, 0.5).
ExtendTowards(oppositeFace, 0.5).
ExtendTowards(s.Facing.RotateLeft().Face(), 0.5))
}
if s.UpsideDown {
for i := range b[1:] {
b[i+1] = b[i+1].Translate(mgl64.Vec3{0, -0.5})
}
}
return b
}
// FaceSolid returns true for all faces of the Stair that are completely filled.
func (s Stair) FaceSolid(pos cube.Pos, face cube.Face, bs world.BlockSource) bool {
// Stairs always have a closed side at the top or bottom based on their orientation.
if (face == cube.FaceUp && s.UpsideDown) || (face == cube.FaceDown && !s.UpsideDown) {
return true
}
switch t := s.cornerType(pos, bs); t {
case cornerRightOuter, cornerLeftOuter:
// Small corner blocks, they do not block water flowing out horizontally.
return false
case noCorner:
// Not a corner, so only block directly behind the stairs.
return s.Facing.Face() == face
case cornerRightInner:
return face == s.Facing.RotateRight().Face() || face == s.Facing.Face()
default:
return face == s.Facing.RotateLeft().Face() || face == s.Facing.Face()
}
}
const (
noCorner = iota
cornerRightInner
cornerLeftInner
cornerRightOuter
cornerLeftOuter
)
// cornerType returns the type of the corner that the stairs form, or 0 if it does not form a corner with any
// other stairs.
func (s Stair) cornerType(pos cube.Pos, bs world.BlockSource) uint8 {
rotatedFacing := s.Facing.RotateRight()
if closedSide, ok := bs.Block(pos.Side(s.Facing.Face())).Model().(Stair); ok && closedSide.UpsideDown == s.UpsideDown {
if closedSide.Facing == rotatedFacing {
return cornerLeftOuter
} else if closedSide.Facing == rotatedFacing.Opposite() {
// This will only form a corner if there is not a stair on the right of this one with the same
// direction.
if side, ok := bs.Block(pos.Side(s.Facing.RotateRight().Face())).Model().(Stair); !ok || side.Facing != s.Facing || side.UpsideDown != s.UpsideDown {
return cornerRightOuter
}
return noCorner
}
}
if openSide, ok := bs.Block(pos.Side(s.Facing.Opposite().Face())).Model().(Stair); ok && openSide.UpsideDown == s.UpsideDown {
if openSide.Facing == rotatedFacing {
// This will only form a corner if there is not a stair on the right of this one with the same
// direction.
if side, ok := bs.Block(pos.Side(s.Facing.RotateRight().Face())).Model().(Stair); !ok || side.Facing != s.Facing || side.UpsideDown != s.UpsideDown {
return cornerRightInner
}
} else if openSide.Facing == rotatedFacing.Opposite() {
return cornerLeftInner
}
}
return noCorner
}