Skip to content

Commit fa96799

Browse files
authored
Merge branch 'develop' into feature/replace-login-music
2 parents 829f71a + 1d07131 commit fa96799

28 files changed

+1420
-399
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ yarn-error.log*
2222
*.njsproj
2323
*.sln
2424
*.sw?
25+
/.vs

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"start:infra": "concurrently \"npm run start:update\" \"npm run start:login\"",
1313
"start:standalone": "concurrently \"npm run start:infra\" \"npm run start:game\"",
1414
"lint": "eslint -c .eslintrc.js --ext .ts src",
15+
"lint-fix": "eslint -c .eslintrc.js --ext .ts src --fix",
1516
"fake-players": "concurrently \"npm run build:watch\" \"node --max-old-space-size=4096 dist/index.js -fakePlayers\"",
1617
"fake-players-tick": "concurrently \"npm run build:watch\" \"node --max-old-space-size=4096 dist/index.js -fakePlayers -tickTime\"",
1718
"build:watch": "babel ./src --out-dir dist --extensions \".ts,.tsx,.js\" --source-maps --watch",
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Player, playerOptions } from '../../world/actor/player/player';
2+
import { world } from '../../game-server';
3+
import { World } from '../../world';
4+
import { logger } from '@runejs/core';
5+
import { PacketData } from '../inbound-packets';
6+
7+
8+
const magicAttackPacket = (player: Player, packet: PacketData) => {
9+
const { buffer } = packet;
10+
const npcWorldIndex = buffer.get('short', 'u'); // unsigned short BE
11+
const widgetId = buffer.get('short', 'u', 'le'); // unsigned short LE
12+
const widgetChildId = buffer.get(); // unsigned short LE
13+
14+
const npc = world.npcList[npcWorldIndex];
15+
16+
player.actionPipeline.call('magic_on_npc', npc, player, widgetId, widgetChildId);
17+
};
18+
19+
export default [{
20+
opcode: 253,
21+
size: 6,
22+
handler: magicAttackPacket
23+
}];

src/game-engine/net/inbound-packets/widget-interaction-packet.js renamed to src/game-engine/net/inbound-packets/widget-interaction-packet.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
const widgetInteractionPacket = (player, packet) => {
1+
import { Player } from '@engine/world/actor/player/player';
2+
import { PacketData } from '@engine/net/inbound-packets';
3+
4+
5+
const widgetInteractionPacket = (player: Player, packet: PacketData) => {
26
const { buffer } = packet;
37
const childId = buffer.get('short');
48
const widgetId = buffer.get('short');

src/game-engine/world/action/index.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ export type ActionType =
3636
| 'move_item'
3737
| 'spawned_item_interaction'
3838

39+
| 'magic_on_item'
40+
| 'magic_on_player'
41+
| 'magic_on_npc'
42+
3943
| 'player_init'
4044
| 'player_command'
4145
| 'player_interaction'
@@ -61,10 +65,15 @@ export type ActionCancelType =
6165
| 'widget';
6266

6367

68+
/**
69+
* The definition for the actual action pipe handler function.
70+
*/
71+
export type ActionPipeHandler = (...args: any[]) => RunnableHooks | void;
72+
6473
/**
6574
* Basic definition of a game engine action file (.action.ts exports).
6675
*/
67-
export type ActionPipe = [ ActionType, (...args: any[]) => void ];
76+
export type ActionPipe = [ ActionType, ActionPipeHandler ];
6877

6978

7079
/**
@@ -86,7 +95,7 @@ export interface RunnableHooks<T = any> {
8695
*/
8796
export class ActionPipeline {
8897

89-
private static pipes = new Map<string, any>();
98+
private static pipes = new Map<string, ActionPipeHandler>();
9099

91100
private runningTasks: TaskExecutor<any>[] = [];
92101
private canceling: boolean = false;
@@ -97,12 +106,12 @@ export class ActionPipeline {
97106
.subscribe(async () => this.cancelRunningTasks());
98107
}
99108

100-
public static getPipe(action: ActionType): Map<string, any> {
109+
public static getPipe(action: ActionType): ActionPipeHandler {
101110
return ActionPipeline.pipes.get(action);
102111
}
103112

104-
public static register(action: ActionType, actionPipe: (...args: any[]) => void): void {
105-
ActionPipeline.pipes.set(action.toString(), actionPipe);
113+
public static register(action: ActionType, actionPipeHandlerFn: ActionPipeHandler): void {
114+
ActionPipeline.pipes.set(action.toString(), actionPipeHandlerFn);
106115
}
107116

108117
public shutdown(): void {
@@ -113,7 +122,7 @@ export class ActionPipeline {
113122
const actionHandler = ActionPipeline.pipes.get(action.toString());
114123
if(actionHandler) {
115124
try {
116-
await this.runActionHandler(actionHandler, ...args);
125+
await this.runActionHandler(actionHandler, args);
117126
} catch(error) {
118127
if(error) {
119128
logger.error(`Error handling action ${action.toString()}`);
@@ -141,7 +150,7 @@ export class ActionPipeline {
141150
this.canceling = false;
142151
}
143152

144-
private async runActionHandler(actionHandler: any, ...args: any[]): Promise<void> {
153+
private async runActionHandler(actionHandler: any, args: any[]): Promise<void> {
145154
const runnableHooks: RunnableHooks | null | undefined = await actionHandler(...args);
146155

147156
if(!runnableHooks?.hooks || runnableHooks.hooks.length === 0) {
@@ -150,7 +159,6 @@ export class ActionPipeline {
150159

151160
for(let i = 0; i < runnableHooks.hooks.length; i++) {
152161
const hook = runnableHooks.hooks[i];
153-
154162
if(!hook) {
155163
continue;
156164
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Player } from '@engine/world/actor/player/player';
2+
import { ActionHook, getActionHooks } from '@engine/world/action/hooks';
3+
import { advancedNumberHookFilter, questHookFilter } from '@engine/world/action/hooks/hook-filters';
4+
import { ActionPipe, RunnableHooks } from '@engine/world/action/index';
5+
import { Npc } from '../actor/npc/npc';
6+
7+
8+
/**
9+
* Defines a button action hook.
10+
*/
11+
export interface MagicOnNPCActionHook extends ActionHook<MagicOnNPCAction, magiconnpcActionHandler> {
12+
// The npc world id that was clicked on after choosing the spell
13+
npcworldId?: number;
14+
// The IDs of the UI widgets that the buttons are on.
15+
widgetIds?: number[];
16+
// The child ID or list of child IDs of the button(s) within the UI widget.
17+
buttonIds?: number | number[];
18+
// Whether or not this item action should cancel other running or queued actions.
19+
cancelActions?: boolean;
20+
}
21+
22+
23+
/**
24+
* The button action hook handler function to be called when the hook's conditions are met.
25+
*/
26+
export type magiconnpcActionHandler = (buttonAction: MagicOnNPCAction) => void | Promise<void>;
27+
28+
29+
/**
30+
* Details about a button action being performed.
31+
*/
32+
export interface MagicOnNPCAction {
33+
// The npc world id that was clicked on after choosing the spell
34+
npc: Npc;
35+
// The player performing the action.
36+
player: Player;
37+
// The ID of the UI widget that the button is on.
38+
widgetId: number;
39+
// The child ID of the button within the UI widget.
40+
buttonId: number;
41+
}
42+
43+
44+
/**
45+
* The pipe that the game engine hands button actions off to.
46+
* @param npc
47+
* @param player
48+
* @param widgetId
49+
* @param buttonId
50+
*/
51+
const magicOnNpcActionPipe = (npc:Npc, player: Player, widgetId: number, buttonId: number): RunnableHooks<MagicOnNPCAction> => {
52+
//console.info(`pew pew you use magic on ${npc.name}!`);
53+
54+
// Find all object action plugins that reference this location object
55+
const matchingHooks = getActionHooks<MagicOnNPCActionHook>('magic_on_npc');
56+
57+
58+
return {
59+
hooks: matchingHooks,
60+
actionPosition: player.position,
61+
action: {
62+
npc,
63+
player,
64+
widgetId,
65+
buttonId
66+
}
67+
}
68+
69+
70+
};
71+
72+
73+
/**
74+
* Button action pipe definition.
75+
*/
76+
export default [ 'magic_on_npc', magicOnNpcActionPipe ] as ActionPipe;

src/game-engine/world/action/widget-interaction.action.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ export interface WidgetInteractionAction {
4848
* @param optionId The widget context option chosen by the player.
4949
*/
5050
const widgetActionPipe = (player: Player, widgetId: number, childId: number, optionId: number): RunnableHooks<WidgetInteractionAction> => {
51-
const playerWidget = Object.values(player.interfaceState.widgetSlots).find((widget) => widget && widget.widgetId === widgetId);
51+
const playerWidget = Object.values(player.interfaceState.widgetSlots)
52+
.find((widget) => widget && widget.widgetId === widgetId);
5253

53-
if(playerWidget && playerWidget.fakeWidget != undefined) {
54+
if(playerWidget?.fakeWidget) {
5455
widgetId = playerWidget.fakeWidget;
5556
}
5657

@@ -86,9 +87,11 @@ const widgetActionPipe = (player: Player, widgetId: number, childId: number, opt
8687
return null;
8788
}
8889

90+
const action: WidgetInteractionAction = { player, widgetId, childId, optionId };
91+
8992
return {
9093
hooks: matchingHooks,
91-
action: { player, widgetId, childId, optionId }
94+
action
9295
};
9396
};
9497

src/game-engine/world/actor/actor.ts

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { WalkingQueue } from './walking-queue';
22
import { ItemContainer } from '../items/item-container';
33
import { Animation, DamageType, Graphic, UpdateFlags } from './update-flags';
44
import { Npc } from './npc/npc';
5-
import { Skills } from '@engine/world/actor/skills';
5+
import { Skill, Skills } from '@engine/world/actor/skills';
66
import { Item } from '@engine/world/items/item';
77
import { Position } from '@engine/world/position';
88
import { DirectionData, directionFromIndex } from '@engine/world/direction';
@@ -14,6 +14,11 @@ import { WorldInstance } from '@engine/world/instances';
1414
import { Player } from '@engine/world/actor/player/player';
1515
import { ActionCancelType, ActionPipeline } from '@engine/world/action';
1616
import { LandscapeObject } from '@runejs/filestore';
17+
import { Behavior } from './behaviors/behavior';
18+
import { soundIds } from '../config/sound-ids';
19+
import { animationIds } from '../config/animation-ids';
20+
import { findNpc } from '../../config';
21+
import { itemIds } from '../config/item-ids';
1722

1823

1924
/**
@@ -36,6 +41,25 @@ export abstract class Actor {
3641

3742
public pathfinding: Pathfinding = new Pathfinding(this);
3843
public lastMovementPosition: Position;
44+
// #region Behaviors and Combat flags/checks
45+
public inCombat: boolean = false;
46+
public meleeDistance: number = 1;
47+
public Behaviors: Behavior[] = [];
48+
public isDead: boolean = false;
49+
public combatTargets: Actor[] = [];
50+
public hitPoints = this.skills.hitpoints.level * 4;
51+
public maxHitPoints = this.skills.hitpoints.level * 4;
52+
public get highestCombatSkill(): Skill {
53+
const attack = this.skills.getLevel('attack');
54+
const magic = this.skills.getLevel('magic');
55+
const ranged = this.skills.getLevel('ranged');
56+
57+
if (ranged > magic && ranged > ranged) return ranged;
58+
else if (magic > attack && magic > ranged) return magic;
59+
else return attack;
60+
}
61+
62+
// #endregion
3963

4064
protected randomMovementInterval;
4165

@@ -59,20 +83,36 @@ export abstract class Actor {
5983
this._busy = false;
6084
}
6185

62-
public damage(amount: number, damageType: DamageType = DamageType.DAMAGE): 'alive' | 'dead' {
63-
let remainingHitpoints: number = this.skills.hitpoints.level - amount;
64-
const maximumHitpoints: number = this.skills.hitpoints.levelForExp;
65-
if(remainingHitpoints < 0) {
66-
remainingHitpoints = 0;
67-
}
68-
69-
this.skills.setHitpoints(remainingHitpoints);
86+
public damage(amount: number, damageType: DamageType = DamageType.DAMAGE) {
87+
const armorReduction = 0;
88+
const spellDamageReduction = 0;
89+
const poisonReistance = 0;
90+
amount -= armorReduction;
91+
this.hitPoints -= amount;
92+
this.skills.setHitpoints(this.hitPoints);
7093
this.updateFlags.addDamage(amount, amount === 0 ? DamageType.NO_DAMAGE : damageType,
71-
remainingHitpoints, maximumHitpoints);
72-
73-
return remainingHitpoints === 0 ? 'dead' : 'alive';
94+
this.hitPoints, this.maxHitPoints);
95+
//this actor should respond when hit
96+
world.playLocationSound(this.position, soundIds.npc.human.noArmorHitPlayer,5)
97+
this.playAnimation(this.getBlockAnimation());
7498
}
7599

100+
101+
102+
//public damage(amount: number, damageType: DamageType = DamageType.DAMAGE): 'alive' | 'dead' {
103+
// let remainingHitpoints: number = this.skills.hitpoints.level - amount;
104+
// const maximumHitpoints: number = this.skills.hitpoints.levelForExp;
105+
// if(remainingHitpoints < 0) {
106+
// remainingHitpoints = 0;
107+
// }
108+
109+
// this.skills.setHitpoints(remainingHitpoints);
110+
// this.updateFlags.addDamage(amount, amount === 0 ? DamageType.NO_DAMAGE : damageType,
111+
// remainingHitpoints, maximumHitpoints);
112+
113+
// return remainingHitpoints === 0 ? 'dead' : 'alive';
114+
//}
115+
76116
/**
77117
* Waits for the actor to reach the specified position before resolving it's promise.
78118
* The promise will be rejected if the actor's walking queue changes or their movement is otherwise canceled.
@@ -548,4 +588,5 @@ export abstract class Actor {
548588
};
549589
}
550590

591+
551592
}

0 commit comments

Comments
 (0)