Skip to content

Commit 5155b7b

Browse files
Starting on a basic implementation of player following.
1 parent ef7fcd8 commit 5155b7b

File tree

14 files changed

+276
-19
lines changed

14 files changed

+276
-19
lines changed

src/game-server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { setItemOnNpcPlugins } from '@server/world/actor/player/action/item-on-n
2525
import { setPlayerInitPlugins } from '@server/world/actor/player/player';
2626
import { setNpcInitPlugins } from '@server/world/actor/npc/npc';
2727
import { setQuestPlugins } from '@server/world/config/quests';
28+
import { setPlayerPlugins } from '@server/world/actor/player/action/player-action';
2829

2930

3031
export let serverConfig: ServerConfig;
@@ -59,6 +60,7 @@ export async function injectPlugins(): Promise<void> {
5960
setWidgetPlugins(actionTypes[ActionType.WIDGET_ACTION]);
6061
setPlayerInitPlugins(actionTypes[ActionType.PLAYER_INIT]);
6162
setNpcInitPlugins(actionTypes[ActionType.NPC_INIT]);
63+
setPlayerPlugins(actionTypes[ActionType.PLAYER_ACTION]);
6264
}
6365

6466
function generateCrcTable(): void {

src/net/incoming-packet-directory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { itemInteractionPacket } from '@server/net/incoming-packets/item-interac
2121
import { itemOnObjectPacket } from '@server/net/incoming-packets/item-on-object-packet';
2222
import { numberInputPacket } from '@server/net/incoming-packets/number-input-packet';
2323
import { itemOnNpcPacket } from '@server/net/incoming-packets/item-on-npc-packet';
24+
import { playerInteractionPacket } from '@server/net/incoming-packets/player-interaction-packet';
2425

2526
const ignore = [ 234, 160, 216, 13, 58 /* camera move */ ];
2627

@@ -58,6 +59,9 @@ const packets: { [key: number]: incomingPacket } = {
5859
30: objectInteractionPacket,
5960
164: objectInteractionPacket,
6061
183: objectInteractionPacket,
62+
63+
68: playerInteractionPacket,
64+
211: playerInteractionPacket,
6165
};
6266

6367
export function handlePacket(player: Player, packetId: number, packetSize: number, buffer: Buffer): void {

src/net/incoming-packet-sizes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const incomingPacketSizes: number[] = [
55
6, -3, -3, -3, -3, -3, -3, -3, 8, -3, //30
66
16, -3, -3, -3, -3, -3, -3, -3, -3, -3, //40
77
-3, -3, -3, -3, -3, -3, -3, -3, 4, -3, //50
8-
-3, -3, -3, 2, 4, 6, -3, -3, -3, -3, //60
8+
-3, -3, -3, 2, 4, 6, -3, -3, 2, -3, //60
99
-3, -3, -3, -1, -3, -3, -3, -3, -3, -3, //70
1010
-3, -3, -3, 9, -3, 6, 8, -3, -3, -1, //80
1111
-3, -3, -3, -3, -3, -3, -3, -3, 8, -3, //90
@@ -20,7 +20,7 @@ export const incomingPacketSizes: number[] = [
2020
-3, -3, -3, 6, -3, -3, -3, -3, -3, -3, //180
2121
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, //190
2222
-3, -3, -3, -3, -3, -3, -3, -3, 10, -3, //200
23-
-3, -3, -3, -3, -3, -3, 0, -3, -3, -3, //210
23+
-3, 2, -3, -3, -3, -3, 0, -3, -3, -3, //210
2424
-3, -3, -3, -3, -3, -3, -3, -3, 8, -3, //220
2525
-3, 13, -3, -3, 4, -3, -1, -3, 4, -3, //230
2626
-3, -3, -3, -3, -3, -3, -3, -3, -1, -3, //240
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { incomingPacket } from '../incoming-packet';
2+
import { Player, playerOptions } from '../../world/actor/player/player';
3+
import { world } from '@server/game-server';
4+
import { World } from '@server/world/world';
5+
import { ByteBuffer } from '@runejs/byte-buffer';
6+
import { playerAction } from '@server/world/actor/player/action/player-action';
7+
import { logger } from '@runejs/logger/dist/logger';
8+
9+
export const playerInteractionPacket: incomingPacket = (player: Player, packetId: number, packetSize: number, packet: ByteBuffer): void => {
10+
const args = {
11+
68: [ 'SHORT', 'UNSIGNED', 'LITTLE_ENDIAN' ],
12+
211: [ 'SHORT', 'UNSIGNED', 'LITTLE_ENDIAN' ]
13+
};
14+
const playerIndex = packet.get(...args[packetId]) - 1;
15+
16+
if(playerIndex < 0 || playerIndex > World.MAX_PLAYERS - 1) {
17+
return;
18+
}
19+
20+
const otherPlayer = world.playerList[playerIndex];
21+
if(!otherPlayer) {
22+
return;
23+
}
24+
25+
const position = otherPlayer.position;
26+
const distance = Math.floor(position.distanceBetween(player.position));
27+
28+
// Too far away
29+
if(distance > 16) {
30+
return;
31+
}
32+
33+
const actions = {
34+
68: 0,
35+
211: 1
36+
};
37+
38+
const playerOption = playerOptions.find(playerOption => playerOption.index === actions[packetId]);
39+
40+
if(!playerOption) {
41+
logger.error(`Invalid player option ${actions[packetId]}!`);
42+
return;
43+
}
44+
45+
playerAction(player, otherPlayer, position, playerOption.option.toLowerCase());
46+
};

src/net/outgoing-packets.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,15 @@ export class OutgoingPackets {
472472
this.queue(packet);
473473
}
474474

475+
public updatePlayerOption(option: string, index: number = 0, placement: 'TOP' | 'BOTTOM' = 'BOTTOM'): void {
476+
const packet = new Packet(223, PacketType.DYNAMIC_SMALL);
477+
packet.putString(!option ? 'hidden' : option);
478+
packet.put(placement === 'TOP' ? 1 : 0);
479+
packet.put(index + 1);
480+
481+
this.queue(packet);
482+
}
483+
475484
public flushQueue(): void {
476485
if(!this.socket || this.socket.destroyed) {
477486
return;

src/plugins/commands/path-command.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ const action: commandAction = (details) => {
77

88
const x: number = args.x as number;
99
const y: number = args.y as number;
10-
const diameter: number = args.diameter as number;
10+
const pathingDiameter: number = args.diameter as number;
1111

12-
player.pathfinding.walkTo(new Position(x, y, player.position.level), diameter);
12+
player.pathfinding.walkTo(new Position(x, y, player.position.level), { pathingDiameter });
1313
};
1414

1515
export default new RunePlugin({
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { ActionType, RunePlugin } from '@server/plugins/plugin';
2+
import { playerAction } from '@server/world/actor/player/action/player-action';
3+
import { loopingAction } from '@server/world/actor/player/action/action';
4+
import { Position } from '@server/world/position';
5+
6+
export const action: playerAction = (details) => {
7+
const { player, otherPlayer } = details;
8+
9+
player.face(otherPlayer, false, false, false);
10+
11+
let followingPosition: Position = new Position(otherPlayer.position.x, otherPlayer.position.y, otherPlayer.position.level);
12+
13+
const loop = loopingAction();
14+
15+
loop.event.subscribe(async () => {
16+
if(player.metadata.pathfinding) {
17+
return;
18+
}
19+
20+
const distance = Math.floor(otherPlayer.position.distanceBetween(player.position));
21+
if(distance > 16) {
22+
loop.cancel();
23+
player.clearFaceActor();
24+
player.metadata.faceActorClearedByWalking = true;
25+
return;
26+
}
27+
28+
console.log(distance);
29+
30+
if(distance > 1 && !followingPosition.equals(otherPlayer.position)) {
31+
followingPosition = new Position(otherPlayer.position.x, otherPlayer.position.y, otherPlayer.position.level);
32+
33+
try {
34+
player.metadata.pathfinding = true;
35+
await player.pathfinding.walkTo(otherPlayer.position, { pathingDiameter: distance + 5, ignoreDestination: true });
36+
player.metadata.pathfinding = false;
37+
} catch(error) {
38+
}
39+
}
40+
});
41+
};
42+
43+
export default new RunePlugin({
44+
type: ActionType.PLAYER_ACTION,
45+
options: 'follow',
46+
action
47+
});

src/plugins/plugin.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ButtonActionPlugin } from '@server/world/actor/player/action/button-act
1111
import { WorldItemActionPlugin } from '@server/world/actor/player/action/world-item-action';
1212
import { ItemActionPlugin } from '@server/world/actor/player/action/item-action';
1313
import { QuestPlugin } from '@server/world/config/quests';
14+
import { PlayerActionPlugin } from '@server/world/actor/player/action/player-action';
1415

1516
export enum ActionType {
1617
BUTTON = 'button',
@@ -25,7 +26,8 @@ export enum ActionType {
2526
COMMAND = 'command',
2627
PLAYER_INIT = 'player_init',
2728
NPC_INIT = 'npc_init',
28-
QUEST = 'quest'
29+
QUEST = 'quest',
30+
PLAYER_ACTION = 'player_action',
2931
}
3032

3133
export interface QuestAction {
@@ -61,13 +63,18 @@ export function questFilter(player: Player, plugin: ActionPlugin): boolean {
6163

6264
export class RunePlugin {
6365

64-
public actions: (NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin | ItemOnObjectActionPlugin | ItemOnNpcActionPlugin |
65-
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin | PlayerInitPlugin | NpcInitPlugin | QuestPlugin)[];
66+
public actions: (NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin |
67+
ItemOnObjectActionPlugin | CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin |
68+
PlayerInitPlugin | NpcInitPlugin | QuestPlugin | PlayerActionPlugin | ItemOnNpcActionPlugin)[];
69+
70+
public constructor(actions: NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin |
71+
ItemOnObjectActionPlugin | CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin |
72+
PlayerInitPlugin | NpcInitPlugin | QuestPlugin | PlayerActionPlugin | ItemOnNpcActionPlugin |
73+
74+
(NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin | ItemOnObjectActionPlugin |
75+
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin | PlayerInitPlugin |
76+
NpcInitPlugin | QuestPlugin | PlayerActionPlugin | ItemOnNpcActionPlugin)[], quest?: QuestAction) {
6677

67-
public constructor(actions: NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin | ItemOnObjectActionPlugin |
68-
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin | PlayerInitPlugin | NpcInitPlugin | QuestPlugin | ItemOnNpcActionPlugin |
69-
(NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin | ItemOnObjectActionPlugin | ItemOnNpcActionPlugin |
70-
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin | PlayerInitPlugin | NpcInitPlugin | QuestPlugin)[], quest?: QuestAction) {
7178
if(!Array.isArray(actions)) {
7279
if(quest !== undefined && !actions.questAction) {
7380
actions.questAction = quest;

src/world/actor/actor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,13 @@ export abstract class Actor extends Entity {
4242
this.pathfinding = new Pathfinding(this);
4343
}
4444

45-
public face(face: Position | Actor, clearWalkingQueue: boolean = true, autoClear: boolean = true): void {
45+
public face(face: Position | Actor, clearWalkingQueue: boolean = true, autoClear: boolean = true, clearedByWalking: boolean = true): void {
4646
if(face instanceof Position) {
4747
this.updateFlags.facePosition = face;
4848
} else if(face instanceof Actor) {
4949
this.updateFlags.faceActor = face;
5050
this.metadata['faceActor'] = face;
51+
this.metadata['faceActorClearedByWalking'] = clearedByWalking;
5152

5253
if(autoClear) {
5354
setTimeout(() => {

src/world/actor/pathfinding.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ class Point {
5353
}
5454
}
5555

56+
export interface PathingOptions {
57+
pathingDiameter?: number;
58+
ignoreDestination?: boolean;
59+
}
60+
5661
export class Pathfinding {
5762

5863
private currentPoint: Point;
@@ -63,8 +68,12 @@ export class Pathfinding {
6368
public constructor(private actor: Actor) {
6469
}
6570

66-
public walkTo(position: Position, pathingDiameter: number = 16): void {
67-
const path = this.pathTo(position.x, position.y, pathingDiameter);
71+
public async walkTo(position: Position, options: PathingOptions): Promise<void> {
72+
if(!options.pathingDiameter) {
73+
options.pathingDiameter = 16;
74+
}
75+
76+
const path = await this.pathTo(position.x, position.y, options.pathingDiameter);
6877

6978
if(!path) {
7079
throw new Error(`Unable to find path.`);
@@ -79,17 +88,23 @@ export class Pathfinding {
7988
walkingQueue.clear();
8089
walkingQueue.valid = true;
8190

91+
if(options.ignoreDestination) {
92+
path.splice(path.length - 1, 1);
93+
}
94+
8295
for(const point of path) {
8396
walkingQueue.add(point.x, point.y);
8497
}
8598
}
8699

87-
public pathTo(destinationX: number, destinationY: number, diameter: number = 16): Point[] {
100+
public async pathTo(destinationX: number, destinationY: number, diameter: number = 16): Promise<Point[]> {
88101
// @TODO check if destination is too far away
89102

90103
const radius = Math.floor(diameter / 2);
91104
const pathingStartX = this.actor.position.x - radius;
92105
const pathingStartY = this.actor.position.y - radius;
106+
const destinationIndexX = destinationX - pathingStartX;
107+
const destinationIndexY = destinationY - pathingStartY;
93108

94109
if(destinationX < pathingStartX || destinationY < pathingStartY) {
95110
throw new Error(`Pathing diameter too small!`);
@@ -112,7 +127,7 @@ export class Pathfinding {
112127
while(this.openPoints.length > 0) {
113128
this.currentPoint = this.calculateBestPoint();
114129

115-
if(this.currentPoint === this.points[destinationX - pathingStartX][destinationY - pathingStartY]) {
130+
if(this.currentPoint === this.points[destinationIndexX][destinationIndexY]) {
116131
break;
117132
}
118133

@@ -121,32 +136,38 @@ export class Pathfinding {
121136

122137
let level = this.actor.position.level;
123138
let { x, y, indexX, indexY } = this.currentPoint;
139+
let canPath = false;
124140

125141
// West
126142
if(indexX > 0 && this.canPathNSEW(new Position(x - 1, y, level), 0x1280108)) {
127143
this.calculateCost(this.points[indexX - 1][indexY]);
144+
canPath = true;
128145
}
129146

130147
// East
131148
if(indexX < pointLen - 1 && this.canPathNSEW(new Position(x + 1, y, level), 0x1280180)) {
132149
this.calculateCost(this.points[indexX + 1][indexY]);
150+
canPath = true;
133151
}
134152

135153
// South
136154
if(indexY > 0 && this.canPathNSEW(new Position(x, y - 1, level), 0x1280102)) {
137155
this.calculateCost(this.points[indexX][indexY - 1]);
156+
canPath = true;
138157
}
139158

140159
// North
141160
if(indexY < pointLen - 1 && this.canPathNSEW(new Position(x, y + 1, level), 0x1280120)) {
142161
this.calculateCost(this.points[indexX][indexY + 1]);
162+
canPath = true;
143163
}
144164

145165
// South-West
146166
if(indexX > 0 && indexY > 0) {
147167
if(this.canPathDiagonally(this.currentPoint.x, this.currentPoint.y, new Position(x - 1, y - 1, level), -1, -1,
148168
0x128010e, 0x1280108, 0x1280102)) {
149169
this.calculateCost(this.points[indexX - 1][indexY - 1]);
170+
canPath = true;
150171
}
151172
}
152173

@@ -155,6 +176,7 @@ export class Pathfinding {
155176
if(this.canPathDiagonally(this.currentPoint.x, this.currentPoint.y, new Position(x + 1, y - 1, level), 1, -1,
156177
0x1280183, 0x1280180, 0x1280102)) {
157178
this.calculateCost(this.points[indexX + 1][indexY - 1]);
179+
canPath = true;
158180
}
159181
}
160182

@@ -163,6 +185,7 @@ export class Pathfinding {
163185
if(this.canPathDiagonally(this.currentPoint.x, this.currentPoint.y, new Position(x - 1, y + 1, level), -1, 1,
164186
0x1280138, 0x1280108, 0x1280120)) {
165187
this.calculateCost(this.points[indexX - 1][indexY + 1]);
188+
canPath = true;
166189
}
167190
}
168191

@@ -171,11 +194,16 @@ export class Pathfinding {
171194
if(this.canPathDiagonally(this.currentPoint.x, this.currentPoint.y, new Position(x + 1, y + 1, level), 1, 1,
172195
0x12801e0, 0x1280180, 0x1280120)) {
173196
this.calculateCost(this.points[indexX + 1][indexY + 1]);
197+
canPath = true;
174198
}
175199
}
200+
201+
if(!canPath) {
202+
break;
203+
}
176204
}
177205

178-
const destinationPoint = this.points[destinationX - pathingStartX][destinationY - pathingStartY];
206+
const destinationPoint = this.points[destinationIndexX][destinationIndexY];
179207

180208
if(destinationPoint === null || destinationPoint.parent === null) {
181209
return null;

0 commit comments

Comments
 (0)