actor_pathfind.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. package mapPathfind
  2. import (
  3. "f1-game/internal/algorithm/astar6"
  4. "f1-game/internal/code"
  5. "f1-game/internal/constant"
  6. "f1-game/internal/data"
  7. "f1-game/internal/enum"
  8. nameActor "f1-game/internal/name/actor"
  9. nameRemote "f1-game/internal/name/remote"
  10. "f1-game/internal/pb"
  11. "f1-game/internal/types"
  12. mapCall "f1-game/nodes/map/internal/call"
  13. mapTypes "f1-game/nodes/map/internal/types"
  14. clog "github.com/cherry-game/cherry/logger"
  15. cactor "github.com/cherry-game/cherry/net/actor"
  16. )
  17. type (
  18. // actorPathfind 寻路子Actor,计划开启多个实例轮询调用
  19. actorPathfind struct {
  20. cactor.Base
  21. barrierData mapTypes.BarrierData // 动态障碍数据
  22. astar *astar6.AStar // a星寻路
  23. }
  24. )
  25. func newActorPathfind(barrierData mapTypes.BarrierData) *actorPathfind {
  26. var (
  27. width = data.MapInfo().Width
  28. height = data.MapInfo().Height
  29. )
  30. if width < 0 || height < 0 {
  31. clog.Errorf("map width or height is invalid. width = %v, height = %v", width, height)
  32. return nil
  33. }
  34. // 加载寻路数据
  35. tileData := make([][]int32, width)
  36. for x := range width {
  37. tileData[x] = make([]int32, height)
  38. for y := range height {
  39. tileID, ok := data.PointToTileID(int32(x), int32(y))
  40. if !ok {
  41. continue
  42. }
  43. tile := data.MapData().Tiles[tileID]
  44. if tile != nil && !tile.IsBlock() {
  45. tileData[x][y] = int32(tile.BlockType)
  46. }
  47. }
  48. }
  49. actor := &actorPathfind{
  50. barrierData: barrierData.Clone(),
  51. }
  52. actor.astar = astar6.New(astar6.Config{
  53. Min: types.Point{
  54. X: 0,
  55. Y: 0,
  56. },
  57. Max: types.Point{
  58. X: width - 1,
  59. Y: height - 1,
  60. },
  61. MapData: tileData,
  62. IsWalkableFunc: actor.isWalkable,
  63. })
  64. return actor
  65. }
  66. func (p *actorPathfind) AliasID() string {
  67. return nameActor.Map_Pathfind
  68. }
  69. func (p *actorPathfind) OnInit() {
  70. p.Remote().Register(nameRemote.MapPathFind_Find, p.findPath)
  71. p.Remote().Register(nameRemote.MapPathFind_SetBarriers, p.setBarriers)
  72. p.Remote().Register(nameRemote.MapPathFind_RemoveBarriers, p.removeBarriers)
  73. p.Remote().Register(nameRemote.MapPathFind_SiegePath, p.siegePath)
  74. }
  75. // siegePath 攻城路径
  76. func (p *actorPathfind) siegePath(req *mapTypes.SiegePathReq) {
  77. rsp := mapTypes.SiegePathRsp{
  78. SiegeBaseObjectID: req.SiegeBaseObjectID,
  79. MainForceDepartPath: make([]types.Points, req.MainForcePoints.Size()),
  80. MainForceArrivePath: make([]types.Points, req.MainForcePoints.Size()),
  81. SiegeSquadDepartPath: make([]types.Points, req.SiegeSquadPoints.Size()),
  82. SiegeSquadArrivePath: make([]types.Points, req.SiegeSquadPoints.Size()),
  83. DirectPath: make([]types.Points, req.SiegePoints.Size()),
  84. }
  85. // 主力出发路径
  86. for i, point := range req.MainForcePoints {
  87. path, errCode := p.doFindPath(req.StartPoint.X, req.StartPoint.Y, point.X, point.Y)
  88. if code.IsFail(errCode) {
  89. clog.Errorf("find main force path failed. start to middle: %v -> %v, errCode = %v",
  90. req.StartPoint, point, errCode)
  91. continue
  92. }
  93. points := types.Points{}
  94. for _, p := range path.List {
  95. points = append(points, &types.Point{X: p.X, Y: p.Y})
  96. }
  97. rsp.MainForceDepartPath[i] = points
  98. }
  99. // 搬迁队出发路径
  100. for i, point := range req.SiegeSquadPoints {
  101. path, errCode := p.doFindPath(req.StartPoint.X, req.StartPoint.Y, point.X, point.Y)
  102. if code.IsFail(errCode) {
  103. clog.Errorf("find siege squad path failed. start to middle: %v -> %v, errCode = %v",
  104. req.StartPoint, point, errCode)
  105. continue
  106. }
  107. points := types.Points{}
  108. for _, p := range path.List {
  109. points = append(points, &types.Point{X: p.X, Y: p.Y})
  110. }
  111. rsp.SiegeSquadDepartPath[i] = points
  112. }
  113. // 主力到达路径
  114. for i, point := range req.SiegePoints {
  115. startPoint := req.MainForcePoints[i]
  116. path, errCode := p.doFindPath(startPoint.X, startPoint.Y, point.X, point.Y)
  117. if code.IsFail(errCode) {
  118. clog.Errorf("find main force path failed. middle to siege: %v -> %v, errCode = %v",
  119. startPoint, point, errCode)
  120. continue
  121. }
  122. points := types.Points{}
  123. for _, p := range path.List {
  124. points = append(points, &types.Point{X: p.X, Y: p.Y})
  125. }
  126. rsp.MainForceArrivePath[i] = points
  127. }
  128. // 拆迁队到达路径
  129. for i, point := range req.SiegePoints {
  130. startPoint := req.SiegeSquadPoints[i]
  131. path, errCode := p.doFindPath(startPoint.X, startPoint.Y, point.X, point.Y)
  132. if code.IsFail(errCode) {
  133. clog.Errorf("find siege squad path failed. middle to siege: %v -> %v, errCode = %v",
  134. startPoint, point, errCode)
  135. continue
  136. }
  137. points := types.Points{}
  138. for _, p := range path.List {
  139. points = append(points, &types.Point{X: p.X, Y: p.Y})
  140. }
  141. rsp.SiegeSquadArrivePath[i] = points
  142. }
  143. // 直接到达路径
  144. for i, point := range req.SiegePoints {
  145. path, errCode := p.doFindPath(req.StartPoint.X, req.StartPoint.Y, point.X, point.Y)
  146. if code.IsFail(errCode) {
  147. clog.Errorf("find path failed. start to siege: %v -> %v, errCode = %v",
  148. req.StartPoint, point, errCode)
  149. continue
  150. }
  151. points := types.Points{}
  152. for _, p := range path.List {
  153. points = append(points, &types.Point{X: p.X, Y: p.Y})
  154. }
  155. rsp.DirectPath[i] = points
  156. }
  157. // 设置路径
  158. mapCall.Logic.SetSiegePath(&rsp)
  159. }
  160. // 计算寻路
  161. func (p *actorPathfind) findPath(req *pb.FindPathRequest) (*pb.Points, int32) {
  162. ctx := astar6.NewFindContext(req.StartX, req.StartY, req.EndX, req.EndY, req.PlayerID, req.LeagueID, req.MarchType)
  163. nodes, ok := p.astar.FindPath(ctx)
  164. if !ok {
  165. return nil, code.MapPathFindFail
  166. }
  167. path := &pb.Points{}
  168. for _, node := range nodes {
  169. path.List = append(path.List, &pb.Point{
  170. X: node.X(),
  171. Y: node.Y(),
  172. })
  173. }
  174. return path, code.OK
  175. }
  176. // findLocalPath 在指定区域内寻路
  177. func (p *actorPathfind) doFindPath(sx, sy, ex, ey int32) (*pb.Points, int32) {
  178. ctx := astar6.DefaultFindContext(sx, sy, ex, ey)
  179. nodes, ok := p.astar.FindPath(ctx)
  180. if !ok {
  181. return nil, code.MapPathFindFail
  182. }
  183. path := &pb.Points{}
  184. for _, node := range nodes {
  185. path.List = append(path.List, &pb.Point{
  186. X: node.X(),
  187. Y: node.Y(),
  188. })
  189. }
  190. return path, code.OK
  191. }
  192. func (p *actorPathfind) isWalkable(node *astar6.Node, ctx *astar6.FindContext) bool {
  193. // 当前坐标的障碍点
  194. barrier, found := p.barrierData.Get(node.X(), node.Y())
  195. // 没有障碍可直接通过
  196. if !found {
  197. return true
  198. }
  199. // 通用 marchType 执行逻辑
  200. marchType := enum.MarchType(ctx.MarchType())
  201. switch marchType {
  202. case enum.MarchType_Unknow: // 默认情况,所有障碍点不可通行
  203. return false
  204. case enum.MarchType_Retreat: // 撤退类型可以通过所有障碍点
  205. return true
  206. default:
  207. // 如果是攻城或者掠夺是可以上敌方的城墙, TODO: 仅限终点,现在先不限制,等配置了攻城点再打开
  208. if marchType == enum.MarchType_Siege || marchType == enum.MarchType_Snatch /*&& ctx.IsEndPoint(x, y)*/ {
  209. return true
  210. }
  211. // 其他行军指令,或者非攻城指令的终点
  212. // 1. 只能通过友军的城墙
  213. if barrier.PlayerID == ctx.PlayerID() || (barrier.LeagueID != 0 && barrier.LeagueID == ctx.LeagueID()) {
  214. return true
  215. }
  216. // 2. 中立的码头是可以通行的
  217. // 码头 + 通道
  218. if barrier.ConfigID == constant.MapCastle_Dock1 || barrier.ConfigID == constant.MapCastle_Dock2 {
  219. return barrier.LeagueID == 0
  220. }
  221. }
  222. return false
  223. }
  224. func (p *actorPathfind) setBarriers(req *mapTypes.BarrierRequest) {
  225. p.barrierData.Add(req)
  226. }
  227. func (p *actorPathfind) removeBarriers(req types.Points) {
  228. for _, point := range req {
  229. p.barrierData.Remove(point.X, point.Y)
  230. }
  231. }