Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/net/rs-buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ export class RsBuffer {
this.writerIndex += 2;
}

public writeUnsignedShortLE(value: number): void {
this.buffer.writeUInt16LE(value, this.writerIndex);
this.writerIndex += 2;
}

public writeOffsetShortBE(value: number): void {
this.writeUnsignedByte(value >> 8);
this.writeUnsignedByte(value + 128 & 0xff);
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/item-on-item/firemaking/firemaking-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const lightFire = (player: Player, position: Position, worldItemLog: WorldItem,
world.chunkManager.spawnWorldItem({ itemId: 592, amount: 1 }, position, null, 300);
});
player.packetSender.playSound(240, 7);
player.playAnimation(-1);
player.playAnimation(null);
player.packetSender.chatboxMessage(`The fire catches and the logs begin to burn.`);
player.skills.addExp(Skill.FIREMAKING, burnExp);

Expand Down
5 changes: 5 additions & 0 deletions src/plugins/npc/hans/hans-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ const action: npcAction = (details) => {
.then(d => {
d.close();

npc.clearFaceMob();
player.clearFaceMob();

if(d.action === 1) {
npc.updateFlags.animation = { id: 860 };
npc.updateFlags.addChatMessage({ message: 'Jerk!' });
player.packetSender.chatboxMessage('Hans wanders off rather dejectedly.');
} else {
player.packetSender.chatboxMessage('Hans wanders off aimlessly through the courtyard.');
Expand Down
49 changes: 38 additions & 11 deletions src/world/mob/mob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export abstract class Mob extends Entity {
private _faceDirection: number;
private readonly _inventory: ItemContainer;
public readonly skills: Skills;
public readonly metadata: { [key: string]: any } = {};

protected constructor() {
super();
Expand All @@ -32,9 +33,29 @@ export abstract class Mob extends Entity {
this.skills = new Skills(this);
}

// @TODO facing other mobs
public face(position: Position): void {
this.updateFlags.facePosition = position;
public face(face: Position | Mob, autoClear: boolean = true): void {
if(face instanceof Position) {
this.updateFlags.facePosition = face;
} else if(face instanceof Mob) {
this.updateFlags.faceMob = face;
this.metadata['faceMob'] = face;

if(autoClear) {
setTimeout(() => {
this.clearFaceMob();
}, 20000);
}
}

this.walkingQueue.clear();
this.walkingQueue.valid = false;
}

public clearFaceMob(): void {
if(this.metadata['faceMob']) {
this.updateFlags.faceMob = null;
this.metadata['faceMob'] = undefined;
}
}

public playAnimation(animation: number | Animation): void {
Expand Down Expand Up @@ -69,8 +90,16 @@ export abstract class Mob extends Entity {
return this.hasItemInInventory(item);
}

public canMove(): boolean {
return true;
}

public initiateRandomMovement(): void {
setInterval(() => {
if(!this.canMove()) {
return;
}

const movementChance = Math.floor(Math.random() * 10);

if(movementChance < 7) {
Expand Down Expand Up @@ -113,11 +142,9 @@ export abstract class Mob extends Entity {

let valid = true;

if(this instanceof Npc && (this as Npc).movementRadius) {
const npc = this as Npc;

if(px > npc.initialPosition.x + npc.movementRadius || px < npc.initialPosition.x - npc.movementRadius
|| py > npc.initialPosition.y + npc.movementRadius || py < npc.initialPosition.y - npc.movementRadius) {
if(this instanceof Npc) {
if(px > this.initialPosition.x + this.movementRadius || px < this.initialPosition.x - this.movementRadius
|| py > this.initialPosition.y + this.movementRadius || py < this.initialPosition.y - this.movementRadius) {
valid = false;
}
}
Expand All @@ -126,9 +153,9 @@ export abstract class Mob extends Entity {
}

if(px !== this.position.x || py !== this.position.y) {
this._walkingQueue.clear();
this._walkingQueue.valid = true;
this._walkingQueue.add(px, py);
this.walkingQueue.clear();
this.walkingQueue.valid = true;
this.walkingQueue.add(px, py);
}
}, 1000);
}
Expand Down
4 changes: 4 additions & 0 deletions src/world/mob/npc/npc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ export class Npc extends Mob {
});
}

public canMove(): boolean {
return this.updateFlags.faceMob === undefined && this.updateFlags.animation === undefined;
}

public equals(other: Npc): boolean {
if(!other) {
return false;
Expand Down
6 changes: 5 additions & 1 deletion src/world/mob/player/action/npc-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ export const npcAction = (player: Player, npc: Npc, position: Position, option:
// Make sure we walk to the NPC before running any of the walk-to plugins
if(walkToPlugins.length !== 0) {
walkToAction(player, position)
.then(() => walkToPlugins.forEach(plugin => plugin.action({ player, npc, position })))
.then(() => {
player.face(npc);
npc.face(player);
walkToPlugins.forEach(plugin => plugin.action({ player, npc, position }));
})
.catch(() => logger.warn(`Unable to complete walk-to action.`));
}

Expand Down
22 changes: 13 additions & 9 deletions src/world/mob/player/action/object-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,19 @@ export const objectAction = (player: Player, landscapeObject: LandscapeObject, l
// Make sure we walk to the object before running any of the walk-to plugins
if(walkToPlugins.length !== 0) {
walkToAction(player, position, { interactingObject: landscapeObject })
.then(() => walkToPlugins.forEach(plugin =>
plugin.action({
player,
object: landscapeObject,
objectDefinition: landscapeObjectDefinition,
option,
position,
cacheOriginal
})))
.then(() => {
player.face(position);

walkToPlugins.forEach(plugin =>
plugin.action({
player,
object: landscapeObject,
objectDefinition: landscapeObjectDefinition,
option,
position,
cacheOriginal
}))
})
.catch(() => logger.warn(`Unable to complete walk-to action.`));
}

Expand Down
2 changes: 1 addition & 1 deletion src/world/mob/player/packet/packet-sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ export class PacketSender {
public sendMembershipStatusAndWorldIndex(): void {
const packet = new Packet(126);
packet.writeUnsignedByte(1); // @TODO member status
packet.writeShortLE(this.player.worldIndex + 1);
packet.writeShortLE(this.player.worldIndex);

this.send(packet);
}
Expand Down
10 changes: 9 additions & 1 deletion src/world/mob/player/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ export class Player extends Mob {
private _walkingTo: Position;
private _nearbyChunks: Chunk[];
public readonly actionsCancelled: Subject<boolean>;
public readonly metadata: { [key: string]: any } = {};

public constructor(socket: Socket, inCipher: Isaac, outCipher: Isaac, clientUuid: number, username: string, password: string, isLowDetail: boolean) {
super();
Expand Down Expand Up @@ -269,6 +268,10 @@ export class Player extends Mob {
this.metadata['updateChunk'] = null;
}

if(this.metadata['teleporting']) {
this.metadata['teleporting'] = null;
}

resolve();
});
}
Expand All @@ -282,12 +285,17 @@ export class Player extends Mob {

this.updateFlags.mapRegionUpdateRequired = true;
this.lastMapRegionUpdatePosition = newPosition;
this.metadata['teleporting'] = true;

if(!oldChunk.equals(newChunk)) {
this.metadata['updateChunk'] = { newChunk, oldChunk };
}
}

public canMove(): boolean {
return true;
}

public removeFirstItem(item: number | Item): number {
const slot = this.inventory.removeFirst(item);

Expand Down
65 changes: 64 additions & 1 deletion src/world/mob/player/updating/npc-update-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,72 @@ export class NpcUpdateTask extends Task<void> {
return;
}

const mask = 0;
let mask = 0;

if(updateFlags.faceMob !== undefined) {
mask |= 0x40;
}
if(updateFlags.chatMessages.length !== 0) {
mask |= 0x20;
}
if(updateFlags.facePosition) {
mask |= 0x8;
}
if(updateFlags.animation) {
mask |= 0x2;
}

updateMaskData.writeUnsignedByte(mask);

if(updateFlags.faceMob !== undefined) {
const mob = updateFlags.faceMob;

if(mob === null) {
// Reset faced mob
updateMaskData.writeUnsignedShortLE(65535);
} else {
let mobIndex = mob.worldIndex;

if(mob instanceof Player) {
// Client checks if index is less than 32768.
// If it is, it looks for an NPC.
// If it isn't, it looks for a player (subtracting 32768 to find the index).
mobIndex += 32768;
}

updateMaskData.writeUnsignedShortLE(mobIndex);
}
}

if(updateFlags.chatMessages.length !== 0) {
const message = updateFlags.chatMessages[0];

if(message.message) {
updateMaskData.writeString(message.message);
} else {
updateMaskData.writeString('Undefined Message');
}
}

if(updateFlags.facePosition) {
const position = updateFlags.facePosition;
updateMaskData.writeOffsetShortLE(position.x * 2 + 1);
updateMaskData.writeShortLE(position.y * 2 + 1);
}

if(updateFlags.animation) {
const animation = updateFlags.animation;

if(animation === null || animation.id === -1) {
// Reset animation
updateMaskData.writeShortBE(-1);
updateMaskData.writeNegativeOffsetByte(0);
} else {
const delay = updateFlags.animation.delay || 0;
updateMaskData.writeShortBE(animation.id);
updateMaskData.writeNegativeOffsetByte(delay);
}
}
}

}
57 changes: 47 additions & 10 deletions src/world/mob/player/updating/player-update-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class PlayerUpdateTask extends Task<void> {
if(updateFlags.mapRegionUpdateRequired) {
playerUpdatePacket.writeBits(1, 1); // Update Required
playerUpdatePacket.writeBits(2, 3); // Map Region changed
playerUpdatePacket.writeBits(1, 0); // Whether or not the client should discard the current walking queue (1 if teleporting, 0 if not)
playerUpdatePacket.writeBits(1, this.player.metadata['teleporting'] ? 1 : 0); // Whether or not the client should discard the current walking queue (1 if teleporting, 0 if not)
playerUpdatePacket.writeBits(2, this.player.position.level); // Player Height
playerUpdatePacket.writeBits(7, this.player.position.chunkLocalY); // Player Local Chunk Y
playerUpdatePacket.writeBits(7, this.player.position.chunkLocalX); // Player Local Chunk X
Expand All @@ -57,7 +57,7 @@ export class PlayerUpdateTask extends Task<void> {
this.player.trackedPlayers.push(newPlayer);

// Notify the client of the new player and their worldIndex
playerUpdatePacket.writeBits(11, newPlayer.worldIndex + 1);
playerUpdatePacket.writeBits(11, newPlayer.worldIndex);

playerUpdatePacket.writeBits(5, positionOffsetX); // World Position X axis offset relative to the main player
playerUpdatePacket.writeBits(1, 1); // Update is required
Expand Down Expand Up @@ -94,6 +94,9 @@ export class PlayerUpdateTask extends Task<void> {
if(updateFlags.appearanceUpdateRequired || forceUpdate) {
mask |= 0x4;
}
if(updateFlags.faceMob !== undefined) {
mask |= 0x1;
}
if(updateFlags.facePosition || forceUpdate) {
mask |= 0x2;
}
Expand All @@ -103,7 +106,7 @@ export class PlayerUpdateTask extends Task<void> {
if(updateFlags.graphics) {
mask |= 0x200;
}
if(updateFlags.animation) {
if(updateFlags.animation !== undefined) {
mask |= 0x8;
}

Expand All @@ -115,10 +118,18 @@ export class PlayerUpdateTask extends Task<void> {
updateMaskData.writeByte(mask);
}

if(updateFlags.animation) {
const delay = updateFlags.animation.delay || 0;
updateMaskData.writeShortBE(updateFlags.animation.id);
updateMaskData.writeNegativeOffsetByte(delay);
if(updateFlags.animation !== undefined) {
const animation = updateFlags.animation;

if(animation === null || animation.id === -1) {
// Reset animation
updateMaskData.writeShortBE(-1);
updateMaskData.writeNegativeOffsetByte(0);
} else {
const delay = updateFlags.animation.delay || 0;
updateMaskData.writeShortBE(updateFlags.animation.id);
updateMaskData.writeNegativeOffsetByte(delay);
}
}

if(updateFlags.chatMessages.length !== 0 && !currentPlayer) {
Expand All @@ -131,10 +142,36 @@ export class PlayerUpdateTask extends Task<void> {
}
}

if(updateFlags.faceMob !== undefined) {
const mob = updateFlags.faceMob;

if(mob === null) {
// Reset faced mob
updateMaskData.writeOffsetShortBE(65535);
} else {
let mobIndex = mob.worldIndex;

if(mob instanceof Player) {
// Client checks if index is less than 32768.
// If it is, it looks for an NPC.
// If it isn't, it looks for a player (subtracting 32768 to find the index).
mobIndex += 32768;
}

updateMaskData.writeOffsetShortBE(mobIndex);
}
}

if(updateFlags.facePosition || forceUpdate) {
const position = updateFlags.facePosition ? updateFlags.facePosition : player.position.fromDirection(player.faceDirection);
updateMaskData.writeShortBE(position.x * 2 + 1);
updateMaskData.writeShortBE(position.y * 2 + 1);
if(forceUpdate) {
const position = player.position.fromDirection(player.faceDirection);
updateMaskData.writeShortBE(position.x * 2 + 1);
updateMaskData.writeShortBE(position.y * 2 + 1);
} else {
const position = updateFlags.facePosition;
updateMaskData.writeShortBE(position.x * 2 + 1);
updateMaskData.writeShortBE(position.y * 2 + 1);
}
}

if(updateFlags.graphics) {
Expand Down
Loading