Files
go-mc/bot/path/movement.go
2021-02-18 13:38:43 +08:00

363 lines
8.6 KiB
Go

package path
import (
"fmt"
"github.com/Tnze/go-mc/bot/world"
"github.com/Tnze/go-mc/data/block"
)
// Cardinal directions.
type Direction uint8
func (d Direction) Offset() (x, y, z int) {
switch d {
case North:
return 0, 0, -1
case South:
return 0, 0, 1
case East:
return 1, 0, 0
case West:
return -1, 0, 0
}
panic(fmt.Sprintf("unknown direction value: %v", d))
}
func (d Direction) Offset2x() (x, y, z int) {
x, y, z = d.Offset()
return x + x, y + y, z + z
}
func (d Direction) String() string {
switch d {
case North:
return "north"
case South:
return "south"
case East:
return "east"
case West:
return "west"
}
return fmt.Sprintf("Direction?<%d>", int(d))
}
// Valid direction values.
const (
North Direction = iota
South
West
East
)
func LadderDirection(bStateID world.BlockStatus) Direction {
return Direction(((uint32(bStateID) - block.Ladder.MinStateID) & 0xE) >> 1)
}
func ChestDirection(bStateID world.BlockStatus) Direction {
return Direction(((uint32(bStateID) - block.Chest.MinStateID) / 6) & 0x3)
}
func StairsDirection(bStateID world.BlockStatus) Direction {
b := block.StateID[uint32(bStateID)]
return Direction(((uint32(bStateID) - block.ByID[b].MinStateID) / 20) & 0x3)
}
func SlabIsBottom(bStateID world.BlockStatus) bool {
b := block.StateID[uint32(bStateID)]
return ((uint32(bStateID)-block.ByID[b].MinStateID)&0xE)>>1 == 1
}
// Movement represents a single type of movement in a path.
type Movement uint8
var allMovements = []Movement{TraverseNorth, TraverseSouth, TraverseEast, TraverseWest,
TraverseNorthWest, TraverseNorthEast, TraverseSouthWest, TraverseSouthEast,
DropNorth, DropSouth, DropEast, DropWest,
Drop2North, Drop2South, Drop2East, Drop2West,
AscendNorth, AscendSouth, AscendEast, AscendWest,
DescendLadder, DescendLadderNorth, DescendLadderSouth, DescendLadderEast, DescendLadderWest,
AscendLadder,
JumpCrossNorth, JumpCrossSouth, JumpCrossEast, JumpCrossWest,
}
// Valid movement values.
const (
Waypoint Movement = iota
TraverseNorth
TraverseSouth
TraverseEast
TraverseWest
TraverseNorthEast
TraverseNorthWest
TraverseSouthEast
TraverseSouthWest
DropNorth
DropSouth
DropEast
DropWest
Drop2North
Drop2South
Drop2East
Drop2West
AscendNorth
AscendSouth
AscendEast
AscendWest
DescendLadder
DescendLadderNorth
DescendLadderSouth
DescendLadderEast
DescendLadderWest
AscendLadder
JumpCrossEast
JumpCrossWest
JumpCrossNorth
JumpCrossSouth
)
func (m Movement) Possible(nav *Nav, x, y, z int, from V3, previous Movement) bool {
switch m {
case Waypoint, TraverseNorth, TraverseSouth, TraverseEast, TraverseWest:
if !SteppableBlock(nav.World.GetBlockStatus(x, y, z)) {
return false
}
b1, b2 := nav.World.GetBlockStatus(x, y+1, z), nav.World.GetBlockStatus(x, y+2, z)
u1, u2 := AirLikeBlock(b1) || IsLadder(b1), AirLikeBlock(b2) || IsLadder(b2)
return u1 && u2
case TraverseNorthWest, TraverseNorthEast, TraverseSouthWest, TraverseSouthEast:
if !SteppableBlock(nav.World.GetBlockStatus(x, y, z)) {
return false
}
if !AirLikeBlock(nav.World.GetBlockStatus(x, y+1, z)) || !AirLikeBlock(nav.World.GetBlockStatus(x, y+2, z)) {
return false
}
if !AirLikeBlock(nav.World.GetBlockStatus(from.X, y+1, z)) || !AirLikeBlock(nav.World.GetBlockStatus(from.X, y+2, z)) {
return false
}
return AirLikeBlock(nav.World.GetBlockStatus(x, y+1, from.Z)) && AirLikeBlock(nav.World.GetBlockStatus(x, y+2, from.Z))
case Drop2North, Drop2South, Drop2East, Drop2West:
if !AirLikeBlock(nav.World.GetBlockStatus(x, y+4, z)) {
return false
}
fallthrough
case DropNorth, DropSouth, DropEast, DropWest:
for amt := 0; amt < 3; amt++ {
if !AirLikeBlock(nav.World.GetBlockStatus(x, y+amt+1, z)) {
return false
}
}
return SteppableBlock(nav.World.GetBlockStatus(x, y, z))
case AscendNorth, AscendSouth, AscendEast, AscendWest:
if !AirLikeBlock(nav.World.GetBlockStatus(x, y+1, z)) || !AirLikeBlock(nav.World.GetBlockStatus(x, y+2, z)) {
return false
}
return SteppableBlock(nav.World.GetBlockStatus(x, y, z)) &&
AirLikeBlock(nav.World.GetBlockStatus(from.X, from.Y+1, from.Z)) &&
AirLikeBlock(nav.World.GetBlockStatus(from.X, from.Y+2, from.Z))
case DescendLadder, AscendLadder:
if bID := nav.World.GetBlockStatus(x, y+1, z); !AirLikeBlock(bID) && !IsLadder(bID) {
return false
}
return IsLadder(nav.World.GetBlockStatus(x, y, z))
case DescendLadderNorth, DescendLadderSouth, DescendLadderEast, DescendLadderWest:
for amt := 0; amt < 2; amt++ {
if bID := nav.World.GetBlockStatus(x, y+amt+1, z); !AirLikeBlock(bID) && !IsLadder(bID) {
return false
}
}
return IsLadder(nav.World.GetBlockStatus(x, y, z))
case JumpCrossEast, JumpCrossWest, JumpCrossNorth, JumpCrossSouth:
if !AirLikeBlock(nav.World.GetBlockStatus(x, y+1, z)) || !AirLikeBlock(nav.World.GetBlockStatus(x, y+2, z)) || !AirLikeBlock(nav.World.GetBlockStatus(x, y+3, z)) {
return false
}
midX, midZ := (from.X+x)/2, (from.Z+z)/2
if !AirLikeBlock(nav.World.GetBlockStatus(midX, y+1, midZ)) || !AirLikeBlock(nav.World.GetBlockStatus(midX, y+2, midZ)) || !AirLikeBlock(nav.World.GetBlockStatus(midX, y+3, midZ)) {
return false
}
if !AirLikeBlock(nav.World.GetBlockStatus(from.X, from.Y+3, from.Z)) {
return false
}
return SteppableBlock(nav.World.GetBlockStatus(x, y, z))
default:
panic(m)
}
}
func (m Movement) Offset() (x, y, z int) {
switch m {
case Waypoint:
return 0, 0, 0
case TraverseNorth:
return North.Offset()
case TraverseSouth:
return South.Offset()
case TraverseEast:
return East.Offset()
case TraverseWest:
return West.Offset()
case JumpCrossEast:
return East.Offset2x()
case JumpCrossWest:
return West.Offset2x()
case JumpCrossNorth:
return North.Offset2x()
case JumpCrossSouth:
return South.Offset2x()
case AscendLadder:
return 0, 1, 0
case DescendLadder:
return 0, -1, 0
case DropNorth, DescendLadderNorth:
return 0, -1, -1
case DropSouth, DescendLadderSouth:
return 0, -1, 1
case DropEast, DescendLadderEast:
return 1, -1, 0
case DropWest, DescendLadderWest:
return -1, -1, 0
case AscendNorth:
return 0, 1, -1
case AscendSouth:
return 0, 1, 1
case AscendEast:
return 1, 1, 0
case AscendWest:
return -1, 1, 0
case TraverseNorthWest:
return -1, 0, -1
case TraverseNorthEast:
return 1, 0, -1
case TraverseSouthWest:
return -1, 0, 1
case TraverseSouthEast:
return 1, 0, 1
case Drop2North:
return 0, -2, -1
case Drop2South:
return 0, -2, 1
case Drop2East:
return 1, -2, 0
case Drop2West:
return -1, -2, 0
default:
panic(m)
}
}
func (m Movement) BaseCost() float64 {
switch m {
case Waypoint:
return 0
case TraverseNorth, TraverseSouth, TraverseEast, TraverseWest:
return 2
case TraverseNorthWest, TraverseNorthEast, TraverseSouthWest, TraverseSouthEast:
return 2.5
case DropNorth, DropSouth, DropEast, DropWest, Drop2North, Drop2South, Drop2East, Drop2West:
return 4
case AscendNorth, AscendSouth, AscendEast, AscendWest:
return 4
case DescendLadderNorth, DescendLadderSouth, DescendLadderEast, DescendLadderWest:
return 1.5
case DescendLadder:
return 1
case AscendLadder:
return 3
case JumpCrossEast, JumpCrossWest, JumpCrossNorth, JumpCrossSouth:
return 5.5
default:
panic(m)
}
}
func (m Movement) String() string {
switch m {
case Waypoint:
return "waypoint"
case TraverseNorth:
return "traverse-north"
case TraverseSouth:
return "traverse-south"
case TraverseEast:
return "traverse-east"
case TraverseWest:
return "traverse-west"
case DropNorth:
return "drop-north"
case DropSouth:
return "drop-south"
case DropEast:
return "drop-east"
case DropWest:
return "drop-west"
case Drop2North:
return "drop-2-north"
case Drop2South:
return "drop-2-south"
case Drop2East:
return "drop-2-east"
case Drop2West:
return "drop-2-west"
case AscendNorth:
return "jump-north"
case AscendSouth:
return "jump-south"
case AscendEast:
return "jump-east"
case AscendWest:
return "jump-west"
case TraverseNorthWest:
return "traverse-northwest"
case TraverseNorthEast:
return "traverse-northeast"
case TraverseSouthWest:
return "traverse-southwest"
case TraverseSouthEast:
return "traverse-southeast"
case DescendLadder:
return "descend-ladder"
case DescendLadderNorth:
return "descend-ladder-north"
case DescendLadderSouth:
return "descend-ladder-south"
case DescendLadderEast:
return "descend-ladder-east"
case DescendLadderWest:
return "descend-ladder-west"
case AscendLadder:
return "ascend-ladder"
case JumpCrossEast:
return "jump-crossing-east"
case JumpCrossWest:
return "jump-crossing-west"
case JumpCrossNorth:
return "jump-crossing-north"
case JumpCrossSouth:
return "jump-crossing-south"
default:
panic(m)
}
}