Skip to content

Commit

Permalink
Merge pull request #94 from rune-js/pickup-items
Browse files Browse the repository at this point in the history
Adding support for world item action plugins and a plugin for picking up world items.
  • Loading branch information
TheBlackParade committed Feb 28, 2020
2 parents d842877 + 8547d03 commit 5adb476
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 6 deletions.
2 changes: 2 additions & 0 deletions src/game-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { setButtonPlugins } from '@server/world/actor/player/action/button-actio
import { setCommandPlugins } from '@server/world/actor/player/action/input-command-action';
import { setWidgetPlugins } from '@server/world/actor/player/action/widget-action';
import { setItemPlugins } from '@server/world/actor/player/action/item-action';
import { setWorldItemPlugins } from '@server/world/actor/player/action/world-item-action';

export let serverConfig: ServerConfig;
export let gameCache377: EarlyFormatGameCache;
Expand All @@ -41,6 +42,7 @@ export async function injectPlugins(): Promise<void> {
setObjectPlugins(actionTypes[ActionType.OBJECT_ACTION]);
setItemOnItemPlugins(actionTypes[ActionType.ITEM_ON_ITEM]);
setItemPlugins(actionTypes[ActionType.ITEM_ACTION]);
setWorldItemPlugins(actionTypes[ActionType.WORLD_ITEM_ACTION]);
setCommandPlugins(actionTypes[ActionType.COMMAND]);
setWidgetPlugins(actionTypes[ActionType.WIDGET_ACTION]);
}
Expand Down
5 changes: 2 additions & 3 deletions src/net/incoming-packet-directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { logger } from '@runejs/logger';
import { incomingPacket } from './incoming-packet';
import { characterDesignPacket } from './incoming-packets/character-design-packet';
import { itemEquipPacket } from './incoming-packets/item-equip-packet';
import { interfaceClickPacket } from './incoming-packets/interface-click-packet';
import { cameraTurnPacket } from './incoming-packets/camera-turn-packet';
import { buttonClickPacket } from './incoming-packets/button-click-packet';
import { walkPacket } from './incoming-packets/walk-packet';
import { itemOption1Packet } from './incoming-packets/item-option-1-packet';
Expand All @@ -18,8 +16,8 @@ import { objectInteractionPacket } from '@server/net/incoming-packets/object-int
import { chatPacket } from '@server/net/incoming-packets/chat-packet';
import { dropItemPacket } from '@server/net/incoming-packets/drop-item-packet';
import { itemOnItemPacket } from '@server/net/incoming-packets/item-on-item-packet';
import { buyItemPacket } from '@server/net/incoming-packets/buy-item-packet';
import { widgetsClosedPacket } from '@server/net/incoming-packets/widgets-closed-packet';
import { pickupItemPacket } from '@server/net/incoming-packets/pickup-item-packet';

const ignore = [ 234, 160, 58 /* camera move */ ];

Expand All @@ -41,6 +39,7 @@ const packets: { [key: number]: incomingPacket } = {
102: itemEquipPacket,
38: itemOption1Packet,
29: dropItemPacket,
85: pickupItemPacket,

63: npcInteractionPacket,

Expand Down
27 changes: 27 additions & 0 deletions src/net/incoming-packets/pickup-item-packet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { incomingPacket } from '../incoming-packet';
import { Player } from '../../world/actor/player/player';
import { RsBuffer } from '@server/net/rs-buffer';
import { world } from '@server/game-server';
import { Position } from '@server/world/position';
import { worldItemAction } from '@server/world/actor/player/action/world-item-action';

export const pickupItemPacket: incomingPacket = (player: Player, packetId: number, packetSize: number, packet: RsBuffer): void => {
const y = packet.readNegativeOffsetShortBE();
const itemId = packet.readNegativeOffsetShortBE();
const x = packet.readUnsignedShortLE();

const level = player.position.level;
const worldItemPosition = new Position(x, y, level);
const chunk = world.chunkManager.getChunkForWorldPosition(worldItemPosition);
const worldItem = chunk.getWorldItem(itemId, worldItemPosition);

if(!worldItem || worldItem.removed) {
return;
}

if(worldItem.initiallyVisibleTo && !worldItem.initiallyVisibleTo.equals(player)) {
return;
}

worldItemAction(player, worldItem, 'pick-up');
};
54 changes: 54 additions & 0 deletions src/plugins/items/pickup-item-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ActionType, RunePlugin } from '@server/plugins/plugin';
import { worldItemAction } from '@server/world/actor/player/action/world-item-action';
import { world } from '../../game-server';
import { Item } from '../../world/items/item';
import { widgets } from '../../world/config/widget';
import { soundIds } from '@server/world/config/sound-ids';

export const action: worldItemAction = (details) => {
const { player, worldItem } = details;

const inventory = player.inventory;
let slot = -1;
const itemData = world.itemData.get(worldItem.itemId);
let amount = worldItem.amount;

if(itemData.stackable) {
const existingItemIndex = inventory.findIndex(worldItem.itemId);
if(existingItemIndex !== -1) {
const existingItem = inventory.items[existingItemIndex];
if(existingItem.amount + worldItem.amount < 2147483647) {
existingItem.amount += worldItem.amount;
amount += existingItem.amount;
slot = existingItemIndex;
}
}
}

if(slot === -1) {
slot = inventory.getFirstOpenSlot();
}

if(slot === -1) {
player.outgoingPackets.chatboxMessage(`You don't have enough free space to do that.`);
return;
}

world.chunkManager.removeWorldItem(worldItem);

const item: Item = {
itemId: worldItem.itemId,
amount
};

inventory.add(item);
player.outgoingPackets.sendUpdateSingleWidgetItem(widgets.inventory, slot, item);
player.outgoingPackets.playSound(soundIds.pickupItem, 3);
};

export default new RunePlugin({
type: ActionType.WORLD_ITEM_ACTION,
options: 'pick-up',
action,
walkTo: true
});
8 changes: 5 additions & 3 deletions src/plugins/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { ItemOnItemActionPlugin } from '@server/world/actor/player/action/item-o
import { CommandActionPlugin } from '@server/world/actor/player/action/input-command-action';
import { WidgetActionPlugin } from '@server/world/actor/player/action/widget-action';
import { ItemActionPlugin } from '@server/world/actor/player/action/item-action';
import { WorldItemActionPlugin } from '@server/world/actor/player/action/world-item-action';

export enum ActionType {
BUTTON = 'button',
WIDGET_ACTION = 'widget_action',
ITEM_ON_ITEM = 'item_on_item',
ITEM_ACTION = 'item_action',
WORLD_ITEM_ACTION = 'world_item_action',
NPC_ACTION = 'npc_action',
OBJECT_ACTION = 'object_action',
COMMAND = 'command'
Expand All @@ -23,12 +25,12 @@ export interface ActionPlugin {
export class RunePlugin {

public actions: (NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin |
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin)[];
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin)[];

public constructor(actions: NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin |
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin |
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin |
(NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin |
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin)[]) {
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin)[]) {
if(!Array.isArray(actions)) {
this.actions = [actions];
} else {
Expand Down
84 changes: 84 additions & 0 deletions src/world/actor/player/action/world-item-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Player } from '@server/world/actor/player/player';
import { walkToAction } from '@server/world/actor/player/action/action';
import { basicNumberFilter, basicStringFilter } from '@server/plugins/plugin-loader';
import { logger } from '@runejs/logger/dist/logger';
import { ActionPlugin } from '@server/plugins/plugin';
import { WorldItem } from '@server/world/items/world-item';

/**
* The definition for a world item action function.
*/
export type worldItemAction = (details: WorldItemActionDetails) => void;

/**
* Details about a world item being interacted with.
*/
export interface WorldItemActionDetails {
player: Player;
worldItem: WorldItem;
}

/**
* Defines an world item interaction plugin.
*/
export interface WorldItemActionPlugin extends ActionPlugin {
itemIds?: number | number[];
options: string | string[];
walkTo: boolean;
action: worldItemAction;
}

/**
* A directory of all world item interaction plugins.
*/
let worldItemInteractions: WorldItemActionPlugin[] = [
];

/**
* Sets the list of world item interaction plugins.
* @param plugins The plugin list.
*/
export const setWorldItemPlugins = (plugins: ActionPlugin[]): void => {
worldItemInteractions = plugins as WorldItemActionPlugin[];
};

// @TODO priority and cancelling other (lower priority) actions
export const worldItemAction = (player: Player, worldItem: WorldItem, option: string): void => {
// Find all world item action plugins that reference this world item
const interactionPlugins = worldItemInteractions.filter(plugin => {
if(plugin.itemIds !== undefined) {
if(!basicNumberFilter(plugin.itemIds, worldItem.itemId)) {
return false;
}
}

if(!basicStringFilter(plugin.options, option)) {
return false;
}

return true;
});

if(interactionPlugins.length === 0) {
player.outgoingPackets.chatboxMessage(`Unhandled world item interaction: ${option} ${worldItem.itemId}`);
return;
}

player.actionsCancelled.next();

// Separate out walk-to actions from immediate actions
const walkToPlugins = interactionPlugins.filter(plugin => plugin.walkTo);
const immediatePlugins = interactionPlugins.filter(plugin => !plugin.walkTo);

// Make sure we walk to the NPC before running any of the walk-to plugins
if(walkToPlugins.length !== 0) {
walkToAction(player, worldItem.position)
.then(() => walkToPlugins.forEach(plugin => plugin.action({ player, worldItem })))
.catch(() => logger.warn(`Unable to complete walk-to action.`));
}

// Immediately run any non-walk-to plugins
if(immediatePlugins.length !== 0) {
immediatePlugins.forEach(plugin => plugin.action({ player, worldItem }));
}
};
1 change: 1 addition & 0 deletions src/world/config/sound-ids.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const soundIds = {
dropItem: 2739,
pickupItem: 2582,
milkCow: 372,
lightingFire: 2599,
fireLit: 2594,
Expand Down
17 changes: 17 additions & 0 deletions src/world/map/chunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ export class Chunk {
this._worldItems = new Map<string, WorldItem[]>();
}

public getWorldItem(itemId: number, position: Position): WorldItem {
const key = position.key;

if(this._worldItems.has(key)) {
const list = this._worldItems.get(key);
const worldItem = list.find(item => item.itemId === itemId);

if(!worldItem) {
return null;
}

return worldItem;
}

return null;
}

public addWorldItem(worldItem: WorldItem): void {
const key = worldItem.position.key;

Expand Down

0 comments on commit 5adb476

Please sign in to comment.