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 }