Skip to content

Commit cfacc35

Browse files
Fixing bridge tile path validation completely.
1 parent bf0593d commit cfacc35

File tree

2 files changed

+181
-25
lines changed

2 files changed

+181
-25
lines changed

src/world/actor/pathfinding.ts

Lines changed: 137 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,135 @@ import { Position } from '../position';
44
import { Chunk } from '@server/world/map/chunk';
55
import { Tile } from '@runejs/cache-parser';
66

7+
class Point {
8+
9+
private _parent: Point = null;
10+
private _cost: number;
11+
private _heuristic: number;
12+
private _depth: number;
13+
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;
19+
}
20+
21+
public equals(point: Point): boolean {
22+
if(this._cost === point._cost && this._heuristic === point._heuristic && this._depth === point._depth) {
23+
if(this._parent === null && point._parent !== null) {
24+
return false;
25+
} else if(this._parent !== null && !this._parent.equals(point._parent)) {
26+
return false;
27+
}
28+
29+
return this._x === point._x && this._y === point._y;
30+
}
31+
32+
return false;
33+
}
34+
35+
public get x(): number {
36+
return this._x;
37+
}
38+
39+
public get y(): number {
40+
return this._y;
41+
}
42+
43+
public get parent(): Point {
44+
return this._parent;
45+
}
46+
47+
public set parent(value: Point) {
48+
this._parent = value;
49+
}
50+
51+
public get cost(): number {
52+
return this._cost;
53+
}
54+
55+
public set cost(value: number) {
56+
this._cost = value;
57+
}
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+
}
76+
777
export class Pathfinding {
878

79+
private currentPoint: Point;
80+
private points: Point[][];
81+
private closedPoints: Point[] = [];
82+
private openPoints: Point[] = [];
83+
984
public constructor(private actor: Actor) {
1085
}
1186

12-
public pathTo(destinationX: number, destinationY: number): void {
13-
const path: Position[][] = new Array(16).fill(new Array(16));
14-
}
87+
public pathTo(destinationX: number, destinationY: number, diameter: number = 32): void {
88+
// @TODO check if destination is too far away
1589

16-
public canMoveTo(origin: Position, destination: Position): boolean {
17-
let destinationChunk: Chunk = world.chunkManager.getChunkForWorldPosition(destination);
18-
const positionAbove: Position = new Position(destination.x, destination.y, destination.level + 1);
19-
const chunkAbove: Chunk = world.chunkManager.getChunkForWorldPosition(positionAbove);
20-
let tile: Tile = chunkAbove.getTile(positionAbove);
21-
22-
if(!tile || !tile.bridge) {
23-
tile = destinationChunk.getTile(destination);
24-
} else {
25-
// Destination is a bridge, so we need to check the chunk above to get the bridge tiles instead of the level we're currently on
26-
destinationChunk = chunkAbove;
90+
const currentPos = this.actor.position;
91+
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;
96+
97+
this.points = new Array(diameter).fill(new Array(diameter));
98+
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);
102+
}
27103
}
28104

29-
if(tile) {
30-
if(tile.nonWalkable) {
31-
return false;
105+
// Center point
106+
this.openPoints.push(this.points[radius + 1][radius + 1]);
107+
108+
while(this.openPoints.length > 0) {
109+
this.currentPoint = this.calculateBestPoint();
110+
111+
if(this.currentPoint === this.points[destinationX - pathingStartX][destinationY - pathingStartY]) {
112+
break;
32113
}
114+
115+
this.openPoints.splice(this.openPoints.indexOf(this.currentPoint), 1);
116+
this.closedPoints.push(this.currentPoint);
117+
118+
let x = this.currentPoint.x;
119+
let y = this.currentPoint.y;
120+
let level = this.actor.position.level;
121+
let currentPosition = new Position(x, y, level);
122+
123+
let testPosition = new Position(x - 1, y, level);
124+
if(this.canMoveTo(currentPosition, testPosition)) {
125+
const point = this.points[x - 1][y];
126+
}
127+
}
128+
}
129+
130+
public canMoveTo(origin: Position, destination: Position): boolean {
131+
const destinationChunk: Chunk = world.chunkManager.getChunkForWorldPosition(destination);
132+
const tile: Tile = destinationChunk.getTile(destination);
133+
134+
if(tile && tile.nonWalkable) {
135+
return false;
33136
}
34137

35138
const initialX: number = origin.x;
@@ -38,10 +141,6 @@ export class Pathfinding {
38141
const destinationLocalX: number = destination.x - destinationChunk.collisionMap.insetX;
39142
const destinationLocalY: number = destination.y - destinationChunk.collisionMap.insetY;
40143

41-
// @TODO check objects moving from bridge tile to non bridge tile
42-
// ^ currently possible to clip through some bridge walls thanks to this issue
43-
// not the most important thing since you still can't walk on water or anything
44-
45144
// West
46145
if(destination.x < initialX && destination.y == initialY) {
47146
if((destinationAdjacency[destinationLocalX][destinationLocalY] & 0x1280108) != 0) {
@@ -133,4 +232,21 @@ export class Pathfinding {
133232
return { localX, localY, chunk: cornerChunk };
134233
}
135234

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+
136252
}

src/world/map/chunk.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Position } from '../position';
22
import { Player } from '../actor/player/player';
33
import { CollisionMap } from './collision-map';
4-
import { cache } from '../../game-server';
4+
import { cache, world } from '../../game-server';
55
import { LocationObject, LocationObjectDefinition, Tile } from '@runejs/cache-parser';
66
import { Npc } from '../actor/npc/npc';
77
import { WorldItem } from '@server/world/items/world-item';
@@ -83,12 +83,22 @@ export class Chunk {
8383

8484
if(!tile) {
8585
tile = new Tile(objectPosition.x, objectPosition.y, objectPosition.level);
86-
tile.flags = 0;
86+
tile.bridge = false;
87+
tile.nonWalkable = false;
8788
this.addTile(tile, objectPosition);
8889
}
8990

90-
this.markOnCollisionMap(locationObject, objectPosition, true);
91-
this._cacheLocationObjects.set(`${objectPosition.x},${objectPosition.y},${locationObject.objectId}`, locationObject);
91+
if(tile.bridge) {
92+
// Move this marker down one level if it's on a bridge tile
93+
objectPosition.level = objectPosition.level - 1;
94+
const lowerChunk = world.chunkManager.getChunkForWorldPosition(objectPosition);
95+
locationObject.level -= 1;
96+
lowerChunk.markOnCollisionMap(locationObject, objectPosition, true);
97+
lowerChunk.cacheLocationObjects.set(`${ objectPosition.x },${ objectPosition.y },${ locationObject.objectId }`, locationObject);
98+
} else if(tile.bridge !== null) {
99+
this.markOnCollisionMap(locationObject, objectPosition, true);
100+
this._cacheLocationObjects.set(`${ objectPosition.x },${ objectPosition.y },${ locationObject.objectId }`, locationObject);
101+
}
92102
}
93103

94104
public addTile(tile: Tile, tilePosition: Position): void {
@@ -97,6 +107,16 @@ export class Chunk {
97107
return;
98108
}
99109

110+
if(tile.bridge) {
111+
// Move this tile down one level if it's a bridge tile
112+
const newTilePosition = new Position(tilePosition.x, tilePosition.y, tilePosition.level - 1);
113+
const lowerChunk = world.chunkManager.getChunkForWorldPosition(newTilePosition);
114+
const newTile = new Tile(tilePosition.x, tilePosition.y, tilePosition.level - 1);
115+
newTile.nonWalkable = tile.nonWalkable;
116+
newTile.bridge = null;
117+
lowerChunk.setTile(newTile, newTilePosition);
118+
}
119+
100120
this._tileList.push(tile);
101121
}
102122

@@ -110,6 +130,26 @@ export class Chunk {
110130
return null;
111131
}
112132

133+
public findTile(position: Position): number {
134+
for(let i = 0; i < this._tileList.length; i++) {
135+
if(position.equalsIgnoreLevel({ x: this._tileList[i].x, y: this._tileList[i].y })) {
136+
return i;
137+
}
138+
}
139+
140+
return -1;
141+
}
142+
143+
public setTile(tile: Tile, tilePosition: Position): void {
144+
const existingTileIndex = this.findTile(tilePosition);
145+
146+
if(existingTileIndex !== -1) {
147+
this._tileList.splice(existingTileIndex, 1);
148+
}
149+
150+
this._tileList.push(tile);
151+
}
152+
113153
public addPlayer(player: Player): void {
114154
if(this._players.findIndex(p => p.equals(player)) === -1) {
115155
this._players.push(player);

0 commit comments

Comments
 (0)