@@ -3,23 +3,19 @@ import { Actor } from '@server/world/actor/actor';
3
3
import { Position } from '../position' ;
4
4
import { Chunk } from '@server/world/map/chunk' ;
5
5
import { Tile } from '@runejs/cache-parser' ;
6
+ import { Player } from '@server/world/actor/player/player' ;
6
7
7
8
class Point {
8
9
9
10
private _parent : Point = null ;
10
- private _cost : number ;
11
- private _heuristic : number ;
12
- private _depth : number ;
11
+ private _cost : number = 0 ;
13
12
14
- public constructor ( private readonly _x : number , private readonly _y : number ) {
15
- }
16
-
17
- public compare ( point : Point ) : number {
18
- return this . _cost - point . _cost ;
13
+ public constructor ( private readonly _x : number , private readonly _y : number ,
14
+ public readonly indexX : number , public readonly indexY : number ) {
19
15
}
20
16
21
17
public equals ( point : Point ) : boolean {
22
- if ( this . _cost === point . _cost && this . _heuristic === point . _heuristic && this . _depth === point . _depth ) {
18
+ if ( this . _cost === point . _cost ) {
23
19
if ( this . _parent === null && point . _parent !== null ) {
24
20
return false ;
25
21
} else if ( this . _parent !== null && ! this . _parent . equals ( point . _parent ) ) {
@@ -55,23 +51,6 @@ class Point {
55
51
public set cost ( value : number ) {
56
52
this . _cost = value ;
57
53
}
58
-
59
- public get heuristic ( ) : number {
60
- return this . _heuristic ;
61
- }
62
-
63
- public set heuristic ( value : number ) {
64
- this . _heuristic = value ;
65
- }
66
-
67
- public get depth ( ) : number {
68
- return this . _depth ;
69
- }
70
-
71
- public set depth ( value : number ) {
72
- this . _depth = value ;
73
- }
74
-
75
54
}
76
55
77
56
export class Pathfinding {
@@ -84,26 +63,51 @@ export class Pathfinding {
84
63
public constructor ( private actor : Actor ) {
85
64
}
86
65
87
- public pathTo ( destinationX : number , destinationY : number , diameter : number = 32 ) : void {
66
+ public walkTo ( position : Position , pathingDiameter : number = 16 ) : void {
67
+ const path = this . pathTo ( position . x , position . y , pathingDiameter ) ;
68
+
69
+ if ( ! path ) {
70
+ throw new Error ( `Unable to find path.` ) ;
71
+ }
72
+
73
+ const walkingQueue = this . actor . walkingQueue ;
74
+
75
+ if ( this . actor instanceof Player ) {
76
+ this . actor . walkingTo = null ;
77
+ }
78
+
79
+ walkingQueue . clear ( ) ;
80
+ walkingQueue . valid = true ;
81
+
82
+ for ( const point of path ) {
83
+ walkingQueue . add ( point . x , point . y ) ;
84
+ }
85
+ }
86
+
87
+ public pathTo ( destinationX : number , destinationY : number , diameter : number = 16 ) : Point [ ] {
88
88
// @TODO check if destination is too far away
89
89
90
- const currentPos = this . actor . position ;
91
90
const radius = Math . floor ( diameter / 2 ) ;
92
- const startX = currentPos . x ;
93
- const startY = currentPos . y ;
94
- const pathingStartX = startX - radius ;
95
- const pathingStartY = startY - radius ;
91
+ const pathingStartX = this . actor . position . x - radius ;
92
+ const pathingStartY = this . actor . position . y - radius ;
93
+
94
+ if ( destinationX < pathingStartX || destinationY < pathingStartY ) {
95
+ throw new Error ( `Pathing diameter too small!` ) ;
96
+ }
97
+
98
+ const pointLen = diameter + 1 ; // + 1 for the center row & column
99
+ this . points = [ ] ;
96
100
97
- this . points = new Array ( diameter ) . fill ( new Array ( diameter ) ) ;
101
+ for ( let x = 0 ; x < pointLen ; x ++ ) {
102
+ this . points . push ( [ ] ) ;
98
103
99
- for ( let x = 0 ; x < diameter ; x ++ ) {
100
- for ( let y = 0 ; y < diameter ; y ++ ) {
101
- this . points [ x ] [ y ] = new Point ( x + startX , y + startY ) ;
104
+ for ( let y = 0 ; y < pointLen ; y ++ ) {
105
+ this . points [ x ] . push ( new Point ( pathingStartX + x , pathingStartY + y , x , y ) ) ;
102
106
}
103
107
}
104
108
105
109
// Center point
106
- this . openPoints . push ( this . points [ radius + 1 ] [ radius + 1 ] ) ;
110
+ this . openPoints . push ( this . points [ radius ] [ radius ] ) ;
107
111
108
112
while ( this . openPoints . length > 0 ) {
109
113
this . currentPoint = this . calculateBestPoint ( ) ;
@@ -115,18 +119,137 @@ export class Pathfinding {
115
119
this . openPoints . splice ( this . openPoints . indexOf ( this . currentPoint ) , 1 ) ;
116
120
this . closedPoints . push ( this . currentPoint ) ;
117
121
118
- let x = this . currentPoint . x ;
119
- let y = this . currentPoint . y ;
120
122
let level = this . actor . position . level ;
121
- let currentPosition = new Position ( x , y , level ) ;
123
+ let { x, y, indexX, indexY } = this . currentPoint ;
124
+
125
+ // West
126
+ if ( indexX > 0 && this . canPathNSEW ( new Position ( x - 1 , y , level ) , 0x1280108 ) ) {
127
+ this . calculateCost ( this . points [ indexX - 1 ] [ indexY ] ) ;
128
+ }
129
+
130
+ // East
131
+ if ( indexX < pointLen - 1 && this . canPathNSEW ( new Position ( x + 1 , y , level ) , 0x1280180 ) ) {
132
+ this . calculateCost ( this . points [ indexX + 1 ] [ indexY ] ) ;
133
+ }
134
+
135
+ // South
136
+ if ( indexY > 0 && this . canPathNSEW ( new Position ( x , y - 1 , level ) , 0x1280102 ) ) {
137
+ this . calculateCost ( this . points [ indexX ] [ indexY - 1 ] ) ;
138
+ }
139
+
140
+ // North
141
+ if ( indexY < pointLen - 1 && this . canPathNSEW ( new Position ( x , y + 1 , level ) , 0x1280120 ) ) {
142
+ this . calculateCost ( this . points [ indexX ] [ indexY + 1 ] ) ;
143
+ }
144
+
145
+ // South-West
146
+ if ( indexX > 0 && indexY > 0 ) {
147
+ if ( this . canPathDiagonally ( this . currentPoint . x , this . currentPoint . y , new Position ( x - 1 , y - 1 , level ) , - 1 , - 1 ,
148
+ 0x128010e , 0x1280108 , 0x1280102 ) ) {
149
+ this . calculateCost ( this . points [ indexX - 1 ] [ indexY - 1 ] ) ;
150
+ }
151
+ }
152
+
153
+ // South-East
154
+ if ( indexX < pointLen - 1 && indexY > 0 ) {
155
+ if ( this . canPathDiagonally ( this . currentPoint . x , this . currentPoint . y , new Position ( x + 1 , y - 1 , level ) , 1 , - 1 ,
156
+ 0x1280183 , 0x1280180 , 0x1280102 ) ) {
157
+ this . calculateCost ( this . points [ indexX + 1 ] [ indexY - 1 ] ) ;
158
+ }
159
+ }
160
+
161
+ // North-West
162
+ if ( indexX > 0 && indexY < pointLen - 1 ) {
163
+ if ( this . canPathDiagonally ( this . currentPoint . x , this . currentPoint . y , new Position ( x - 1 , y + 1 , level ) , - 1 , 1 ,
164
+ 0x1280138 , 0x1280108 , 0x1280120 ) ) {
165
+ this . calculateCost ( this . points [ indexX - 1 ] [ indexY + 1 ] ) ;
166
+ }
167
+ }
168
+
169
+ // North-East
170
+ if ( indexX < pointLen - 1 && indexY < pointLen - 1 ) {
171
+ if ( this . canPathDiagonally ( this . currentPoint . x , this . currentPoint . y , new Position ( x + 1 , y + 1 , level ) , 1 , 1 ,
172
+ 0x12801e0 , 0x1280180 , 0x1280120 ) ) {
173
+ this . calculateCost ( this . points [ indexX + 1 ] [ indexY + 1 ] ) ;
174
+ }
175
+ }
176
+ }
177
+
178
+ const destinationPoint = this . points [ destinationX - pathingStartX ] [ destinationY - pathingStartY ] ;
179
+
180
+ if ( destinationPoint === null || destinationPoint . parent === null ) {
181
+ return null ;
182
+ }
183
+
184
+ // build path
185
+ const path : Point [ ] = [ ] ;
186
+ let point = destinationPoint ;
187
+
188
+ while ( ! point . equals ( this . points [ radius ] [ radius ] ) ) {
189
+ path . push ( point ) ;
190
+ point = point . parent ;
191
+
192
+ if ( point === null ) {
193
+ return null ;
194
+ }
195
+ }
196
+
197
+ return path . reverse ( ) ;
198
+ }
199
+
200
+ private calculateCost ( point : Point ) : void {
201
+ const differenceX = this . currentPoint . x - point . x ;
202
+ const differenceY = this . currentPoint . y - point . y ;
203
+ const nextStepCost = this . currentPoint . cost + ( ( Math . abs ( differenceX ) + Math . abs ( differenceY ) ) * 10 ) ;
204
+
205
+ if ( nextStepCost < point . cost ) {
206
+ this . openPoints . splice ( this . openPoints . indexOf ( point ) ) ;
207
+ this . closedPoints . splice ( this . closedPoints . indexOf ( point ) ) ;
208
+ }
209
+
210
+ if ( this . openPoints . indexOf ( point ) === - 1 && this . closedPoints . indexOf ( point ) === - 1 ) {
211
+ point . parent = this . currentPoint ;
212
+ point . cost = nextStepCost ;
213
+ this . openPoints . push ( point ) ;
214
+ }
215
+ }
216
+
217
+ private calculateBestPoint ( ) : Point {
218
+ let bestPoint : Point = null ;
219
+
220
+ for ( const point of this . openPoints ) {
221
+ if ( bestPoint === null ) {
222
+ bestPoint = point ;
223
+ continue ;
224
+ }
122
225
123
- let testPosition = new Position ( x - 1 , y , level ) ;
124
- if ( this . canMoveTo ( currentPosition , testPosition ) ) {
125
- const point = this . points [ x - 1 ] [ y ] ;
226
+ if ( point . cost < bestPoint . cost ) {
227
+ bestPoint = point ;
126
228
}
127
229
}
230
+
231
+ return bestPoint ;
128
232
}
129
233
234
+ private canPathNSEW ( position : Position , i : number ) : boolean {
235
+ const chunk = world . chunkManager . getChunkForWorldPosition ( position ) ;
236
+ const destinationAdjacency : number [ ] [ ] = chunk . collisionMap . adjacency ;
237
+ const destinationLocalX : number = position . x - chunk . collisionMap . insetX ;
238
+ const destinationLocalY : number = position . y - chunk . collisionMap . insetY ;
239
+ return Pathfinding . canMoveNSEW ( destinationAdjacency , destinationLocalX , destinationLocalY , i ) ;
240
+ }
241
+
242
+ private canPathDiagonally ( originX : number , originY : number , position : Position , offsetX : number , offsetY : number ,
243
+ destMask : number , cornerMask1 : number , cornerMask2 : number ) : boolean {
244
+ const chunk = world . chunkManager . getChunkForWorldPosition ( position ) ;
245
+ const destinationAdjacency : number [ ] [ ] = chunk . collisionMap . adjacency ;
246
+ const destinationLocalX : number = position . x - chunk . collisionMap . insetX ;
247
+ const destinationLocalY : number = position . y - chunk . collisionMap . insetY ;
248
+ return Pathfinding . canMoveDiagonally ( position , destinationAdjacency , destinationLocalX , destinationLocalY ,
249
+ originX , originY , offsetX , offsetY , destMask , cornerMask1 , cornerMask2 ) ;
250
+ }
251
+
252
+
130
253
public canMoveTo ( origin : Position , destination : Position ) : boolean {
131
254
const destinationChunk : Chunk = world . chunkManager . getChunkForWorldPosition ( destination ) ;
132
255
const tile : Tile = destinationChunk . getTile ( destination ) ;
@@ -143,28 +266,28 @@ export class Pathfinding {
143
266
144
267
// West
145
268
if ( destination . x < initialX && destination . y == initialY ) {
146
- if ( ( destinationAdjacency [ destinationLocalX ] [ destinationLocalY ] & 0x1280108 ) != 0 ) {
269
+ if ( ! Pathfinding . canMoveNSEW ( destinationAdjacency , destinationLocalX , destinationLocalY , 0x1280108 ) ) {
147
270
return false ;
148
271
}
149
272
}
150
273
151
274
// East
152
275
if ( destination . x > initialX && destination . y == initialY ) {
153
- if ( ( destinationAdjacency [ destinationLocalX ] [ destinationLocalY ] & 0x1280180 ) != 0 ) {
276
+ if ( ! Pathfinding . canMoveNSEW ( destinationAdjacency , destinationLocalX , destinationLocalY , 0x1280180 ) ) {
154
277
return false ;
155
278
}
156
279
}
157
280
158
281
// South
159
282
if ( destination . y < initialY && destination . x == initialX ) {
160
- if ( ( destinationAdjacency [ destinationLocalX ] [ destinationLocalY ] & 0x1280102 ) != 0 ) {
283
+ if ( ! Pathfinding . canMoveNSEW ( destinationAdjacency , destinationLocalX , destinationLocalY , 0x1280102 ) ) {
161
284
return false ;
162
285
}
163
286
}
164
287
165
288
// North
166
289
if ( destination . y > initialY && destination . x == initialX ) {
167
- if ( ( destinationAdjacency [ destinationLocalX ] [ destinationLocalY ] & 0x1280120 ) != 0 ) {
290
+ if ( ! Pathfinding . canMoveNSEW ( destinationAdjacency , destinationLocalX , destinationLocalY , 0x1280120 ) ) {
168
291
return false ;
169
292
}
170
293
}
@@ -204,6 +327,10 @@ export class Pathfinding {
204
327
return true ;
205
328
}
206
329
330
+ public static canMoveNSEW ( destinationAdjacency : number [ ] [ ] , destinationLocalX : number , destinationLocalY : number , i : number ) : boolean {
331
+ return ( destinationAdjacency [ destinationLocalX ] [ destinationLocalY ] & i ) === 0 ;
332
+ }
333
+
207
334
public static canMoveDiagonally ( origin : Position , destinationAdjacency : number [ ] [ ] , destinationLocalX : number , destinationLocalY : number ,
208
335
initialX : number , initialY : number , offsetX : number , offsetY : number , destMask : number , cornerMask1 : number , cornerMask2 : number ) : boolean {
209
336
const cornerX1 : number = initialX + offsetX ;
@@ -232,21 +359,4 @@ export class Pathfinding {
232
359
return { localX, localY, chunk : cornerChunk } ;
233
360
}
234
361
235
- private calculateBestPoint ( ) : Point {
236
- let bestPoint : Point = null ;
237
-
238
- for ( const point of this . openPoints ) {
239
- if ( bestPoint === null ) {
240
- bestPoint = point ;
241
- continue ;
242
- }
243
-
244
- if ( point . cost < bestPoint . cost ) {
245
- bestPoint = point ;
246
- }
247
- }
248
-
249
- return bestPoint ;
250
- }
251
-
252
362
}
0 commit comments