vRp.CD2g_test/utils/g2d/path/terrain.go

197 lines
6.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package path
import (
"container/heap"
"github.com/kercylan98/minotaur/utils/geometry"
)
// NewTerrain 返回一个大小为 width 和 height 的新的路径覆盖信息landformWidth 和 landformHeight 将对每
// 个路径地貌的尺寸进行描述,用于世界位置的转化,例如当 landformWidth 和 landformHeight 为 2, 2 时,路径
// 地貌位于 2, 3 的情况下,它的世界位置将是 4, 6
// - 地貌特征将默认统一为 PathLandformFeatureRoad
func NewTerrain(width, height, landformWidth, landformHeight int) *Terrain {
path := &Terrain{
features: map[*LandformFeature]map[int]struct{}{},
width: width,
height: height,
landformWidth: landformWidth,
landformHeight: landformHeight,
}
path.matrix = make([]*Landform, width*height)
for pos := 0; pos < len(path.matrix); pos++ {
path.matrix[pos] = NewLandform(pos, LandformFeatureRoad)
}
path.refreshFeatures()
return path
}
// NewPathWithMatrix 基于特定的 matrix 返回一个新的路径覆盖信息
// - 可参照于 NewTerrain
func NewPathWithMatrix(matrix []*Landform, width, height, landformWidth, landformHeight int) *Terrain {
path := &Terrain{
matrix: matrix,
width: width,
height: height,
landformWidth: landformWidth,
landformHeight: landformHeight,
}
path.refreshFeatures()
return path
}
type Terrain struct {
matrix []*Landform // 矩阵
width, height int // 矩阵宽高
landformWidth, landformHeight int // 地貌宽高
features map[*LandformFeature]map[int]struct{} // 标注了特定特征的路径地貌位置
}
// Get 返回 x 和 y 指向的地貌信息
// - 通常更建议使用 GetWithPos 进行获取,因为这样可以减少一次转换
func (slf *Terrain) Get(x, y int) *Landform {
return slf.matrix[geometry.CoordinateToPos(slf.width, x, y)]
}
// GetWithPos 返回 pos 指向的地貌信息
func (slf *Terrain) GetWithPos(pos int) *Landform {
return slf.matrix[pos]
}
// GetHeight 返回这个路径覆盖的范围高度
func (slf *Terrain) GetHeight() int {
return slf.height
}
// GetWidth 返回这个路径覆盖的范围宽度
func (slf *Terrain) GetWidth() int {
return slf.width
}
// GetAll 获取所有地貌信息
func (slf *Terrain) GetAll() []*Landform {
return slf.matrix
}
// GetPath 返回一个从起点位置到目标位置的路径
// - 可以通过参数 diagonals 控制是否支持沿对角线进行移动
// - 可以通过参数 wallsBlockDiagonals 控制在进行对角线移动时是否允许穿越不可通行的区域
func (slf *Terrain) GetPath(startPos, endPos int, diagonals, wallsBlockDiagonals bool) *Path {
start, end := slf.GetWithPos(startPos), slf.GetWithPos(endPos)
var nodes h
var checkedLandforms = make(map[int]struct{})
var path = new(Path)
heap.Push(&nodes, &Node{landform: end, cost: end.GetTotalCost()})
if !start.Walkable() || !end.Walkable() {
return nil
}
for {
if len(nodes) == 0 {
break
}
node := heap.Pop(&nodes).(*Node)
if node.landform == start {
var t = node
for true {
path.points = append(path.points, t.landform)
t = t.parent
if t == nil {
break
}
}
break
}
for _, adjacent := range geometry.GetAdjacentTranslatePos(slf.matrix, slf.width, node.landform.pos) {
landform := slf.GetWithPos(adjacent)
n := &Node{landform: landform, parent: node, cost: landform.GetTotalCost() + node.cost}
if _, exist := checkedLandforms[adjacent]; n.landform.Walkable() && !exist {
heap.Push(&nodes, n)
checkedLandforms[adjacent] = struct{}{}
}
}
if diagonals {
var up, down, left, right bool
if upPos := node.landform.pos - slf.width; upPos >= 0 {
up = slf.GetWithPos(upPos).Walkable()
}
if downPos := node.landform.pos + slf.width; downPos < len(slf.matrix) {
down = slf.GetWithPos(downPos).Walkable()
}
row := node.landform.pos / slf.width
if leftPos := node.landform.pos - 1; row == (leftPos / slf.width) {
left = slf.GetWithPos(leftPos).Walkable()
}
if rightPos := node.landform.pos + 1; row == (rightPos / slf.width) {
right = slf.GetWithPos(rightPos).Walkable()
}
diagonalCost := .414
size := len(slf.matrix)
currentRow := node.landform.pos / slf.width
if topLeft := node.landform.pos - slf.width - 1; topLeft >= 0 && currentRow-1 == (topLeft/slf.width) {
landform := slf.GetWithPos(topLeft)
n := &Node{landform: landform, parent: node, cost: landform.GetTotalCost() + node.cost + diagonalCost}
if _, exist := checkedLandforms[topLeft]; n.landform.Walkable() && !exist && (!wallsBlockDiagonals || (left && up)) {
heap.Push(&nodes, n)
checkedLandforms[topLeft] = struct{}{}
}
}
if topRight := node.landform.pos - slf.width + 1; topRight >= 0 && currentRow-1 == (topRight/slf.width) {
landform := slf.GetWithPos(topRight)
n := &Node{landform: landform, parent: node, cost: landform.GetTotalCost() + node.cost + diagonalCost}
if _, exist := checkedLandforms[topRight]; n.landform.Walkable() && !exist && (!wallsBlockDiagonals || (right && up)) {
heap.Push(&nodes, n)
checkedLandforms[topRight] = struct{}{}
}
}
if bottomLeft := node.landform.pos + slf.width - 1; bottomLeft < size && currentRow+1 == (bottomLeft/slf.width) {
landform := slf.GetWithPos(bottomLeft)
n := &Node{landform: landform, parent: node, cost: landform.GetTotalCost() + node.cost + diagonalCost}
if _, exist := checkedLandforms[bottomLeft]; n.landform.Walkable() && !exist && (!wallsBlockDiagonals || (left && down)) {
heap.Push(&nodes, n)
checkedLandforms[bottomLeft] = struct{}{}
}
}
if bottomRight := node.landform.pos + slf.width + 1; bottomRight < size && currentRow+1 == (bottomRight/slf.width) {
landform := slf.GetWithPos(bottomRight)
n := &Node{landform: landform, parent: node, cost: landform.GetTotalCost() + node.cost + diagonalCost}
if _, exist := checkedLandforms[bottomRight]; n.landform.Walkable() && !exist && (!wallsBlockDiagonals || (right && down)) {
heap.Push(&nodes, n)
checkedLandforms[bottomRight] = struct{}{}
}
}
}
}
return path
}
// 刷新地貌特征标注信息
// - 冗余:已使用过的地貌特征类型在未使用后不会被删除
func (slf *Terrain) refreshFeatures() {
for _, positions := range slf.features {
for pos := range positions {
delete(positions, pos)
}
}
for pos, landform := range slf.matrix {
for _, feature := range landform.GetFeatures() {
positions, exist := slf.features[feature]
if !exist {
positions = map[int]struct{}{}
slf.features[feature] = positions
}
positions[pos] = struct{}{}
}
}
}