Skip to content

Commit

Permalink
allow plugins to listen on equipment changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Promises committed Oct 12, 2020
1 parent 7c1a42c commit 4f2e239
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 106 deletions.
2 changes: 2 additions & 0 deletions src/game-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { setQuestPlugins } from '@server/world/config/quests';
import { setPlayerPlugins } from '@server/world/actor/player/action/player-action';
import { loadPackets } from '@server/net/inbound-packets';
import { watchForChanges } from '@server/util/files';
import { setEquipPlugins } from '@server/world/actor/player/action/equip-action';


export let serverConfig: ServerConfig;
Expand Down Expand Up @@ -57,6 +58,7 @@ export async function injectPlugins(): Promise<void> {
setItemOnNpcPlugins(actionPluginMap[ActionType.ITEM_ON_NPC_ACTION]);
setItemOnItemPlugins(actionPluginMap[ActionType.ITEM_ON_ITEM_ACTION]);
setItemPlugins(actionPluginMap[ActionType.ITEM_ACTION]);
setEquipPlugins(actionPluginMap[ActionType.EQUIP_ACTION]);
setWorldItemPlugins(actionPluginMap[ActionType.WORLD_ITEM_ACTION]);
setCommandPlugins(actionPluginMap[ActionType.COMMAND]);
setWidgetPlugins(actionPluginMap[ActionType.WIDGET_ACTION]);
Expand Down
100 changes: 4 additions & 96 deletions src/plugins/equipment/equip-item-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,108 +1,16 @@
import { ActionType, RunePlugin } from '@server/plugins/plugin';
import { widgets } from '@server/world/config/widget';
import { getItemFromContainer, itemAction } from '@server/world/actor/player/action/item-action';
import { updateBonusStrings } from '@server/plugins/equipment/equipment-stats-plugin';
import { EquipmentSlot, equipmentSlotIndex, ItemDetails, WeaponType } from '@server/world/config/item-data';
import { Item } from '@server/world/items/item';
import { world } from '@server/game-server';
import { Player } from '@server/world/actor/player/player';
import { ItemContainer } from '@server/world/items/item-container';
import { itemAction } from '@server/world/actor/player/action/item-action';
import { equipmentSlotIndex } from '@server/world/config/item-data';

function unequipItem(player: Player, inventory: ItemContainer, equipment: ItemContainer, slot: EquipmentSlot): boolean {
const inventorySlot = inventory.getFirstOpenSlot();

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

const itemInSlot = equipment.items[slot];

if(!itemInSlot) {
return true;
}

equipment.remove(slot);
inventory.set(inventorySlot, itemInSlot);
return true;
}

export const action: itemAction = (details) => {
const { player, itemId, itemSlot, itemDetails, widgetId } = details;

const inventory = player.inventory;
const equipment = player.equipment;
const itemToEquip = getItemFromContainer(itemId, itemSlot, inventory);

if(!itemToEquip) {
// The specified item was not found in the specified slot.
return;
}

if(!itemDetails || !itemDetails.equipment || !itemDetails.equipment.slot) {
player.sendMessage(`Unable to equip item ${itemId}/${itemDetails.name}: Missing equipment data.`);
return;
}
const { player, itemId, itemSlot, itemDetails } = details;

const equipmentSlot = equipmentSlotIndex(itemDetails.equipment.slot);

const itemToUnequip: Item = equipment.items[equipmentSlot];
let shouldUnequipOffHand: boolean = false;
let shouldUnequipMainHand: boolean = false;

if(itemDetails && itemDetails.equipment) {
if(itemDetails.equipment.weaponType === WeaponType.TWO_HANDED) {
shouldUnequipOffHand = true;
}

if(equipmentSlot === EquipmentSlot.OFF_HAND && equipment.items[EquipmentSlot.MAIN_HAND]) {
const mainHandItemData: ItemDetails = world.itemData.get(equipment.items[EquipmentSlot.MAIN_HAND].itemId);

if(mainHandItemData && mainHandItemData.equipment && mainHandItemData.equipment.weaponType === WeaponType.TWO_HANDED) {
shouldUnequipMainHand = true;
}
}
}

if(itemToUnequip) {
if(shouldUnequipOffHand && !unequipItem(player, inventory, equipment, EquipmentSlot.OFF_HAND)) {
return;
}

if(shouldUnequipMainHand && !unequipItem(player, inventory, equipment, EquipmentSlot.MAIN_HAND)) {
return;
}

equipment.remove(equipmentSlot, false);
inventory.remove(itemSlot, false);

equipment.set(equipmentSlot, itemToEquip);
inventory.set(itemSlot, itemToUnequip);
} else {
equipment.set(equipmentSlot, itemToEquip);
inventory.remove(itemSlot);

if(shouldUnequipOffHand) {
unequipItem(player, inventory, equipment, EquipmentSlot.OFF_HAND);
}

if(shouldUnequipMainHand) {
unequipItem(player, inventory, equipment, EquipmentSlot.MAIN_HAND);
}
}

player.updateBonuses();

// @TODO change packets to only update modified container slots
player.outgoingPackets.sendUpdateAllWidgetItems(widgets.inventory, inventory);
player.outgoingPackets.sendUpdateAllWidgetItems(widgets.equipment, equipment);

if(player.hasWidgetOpen(widgets.equipmentStats.widgetId)) {
player.outgoingPackets.sendUpdateAllWidgetItems(widgets.equipmentStats, equipment);
updateBonusStrings(player);
}

player.updateFlags.appearanceUpdateRequired = true;
player.equipItem(itemId, itemSlot, equipmentSlot);
};

export default new RunePlugin({
Expand Down
32 changes: 24 additions & 8 deletions src/plugins/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import { WorldItemActionPlugin } from '@server/world/actor/player/action/world-i
import { ItemActionPlugin } from '@server/world/actor/player/action/item-action';
import { QuestPlugin } from '@server/world/config/quests';
import { PlayerActionPlugin } from '@server/world/actor/player/action/player-action';
import { EquipActionPlugin } from '@server/world/actor/player/action/equip-action';

export enum ActionType {
BUTTON = 'button',
WIDGET_ACTION = 'widget_action',
ITEM_ON_ITEM_ACTION = 'item_on_item_action',
ITEM_ACTION = 'item_action',
EQUIP_ACTION = 'equip_action',
WORLD_ITEM_ACTION = 'world_item_action',
NPC_ACTION = 'npc_action',
OBJECT_ACTION = 'object_action',
Expand Down Expand Up @@ -47,37 +49,51 @@ export function sort(plugins: ActionPlugin[]): ActionPlugin[] {
}

export function questFilter(player: Player, plugin: ActionPlugin): boolean {
if(!plugin.questAction) {
if (!plugin.questAction) {
return true;
}

const questId = plugin.questAction.questId;
const playerQuest = player.quests.find(quest => quest.questId === questId);
if(!playerQuest) {
if (!playerQuest) {
// @TODO quest requirements
return plugin.questAction.stage === 'NOT_STARTED';
}

return playerQuest.stage === plugin.questAction.stage;
}

export type RunePluginAction = NpcActionPlugin | ObjectActionPlugin | ButtonActionPlugin | ItemOnItemActionPlugin | ItemOnObjectActionPlugin | ItemOnNpcActionPlugin |
CommandActionPlugin | WidgetActionPlugin | ItemActionPlugin | WorldItemActionPlugin | PlayerInitPlugin | NpcInitPlugin | QuestPlugin | PlayerActionPlugin;
export type RunePluginAction =
NpcActionPlugin
| ObjectActionPlugin
| ButtonActionPlugin
| ItemOnItemActionPlugin
| ItemOnObjectActionPlugin
| ItemOnNpcActionPlugin
| CommandActionPlugin
| WidgetActionPlugin
| ItemActionPlugin
| WorldItemActionPlugin
| PlayerInitPlugin
| NpcInitPlugin
| QuestPlugin
| PlayerActionPlugin
| EquipActionPlugin;

export class RunePlugin {

public actions: RunePluginAction[];

public constructor(actions: RunePluginAction | RunePluginAction[], quest?: QuestAction) {
if(!Array.isArray(actions)) {
if(quest !== undefined && !actions.questAction) {
if (!Array.isArray(actions)) {
if (quest !== undefined && !actions.questAction) {
actions.questAction = quest;
}
this.actions = [actions];
} else {
if(quest !== undefined) {
if (quest !== undefined) {
actions.forEach(action => {
if(!action.questAction) {
if (!action.questAction) {
action.questAction = quest;
}
});
Expand Down
92 changes: 92 additions & 0 deletions src/world/actor/player/action/equip-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Player } from '@server/world/actor/player/player';
import { ActionPlugin, questFilter } from '@server/plugins/plugin';
import { basicNumberFilter, basicStringFilter } from '@server/plugins/plugin-loader';
import { world } from '@server/game-server';
import { ItemDetails } from '@server/world/config/item-data';

/**
* The definition for an equip action function.
*/
export type equipAction = (details: EquipDetails) => void;

export type EquipType = 'EQUIP' | 'UNEQUIP';

/**
* Details about an item being equipped/unequipped.
*/
export interface EquipDetails {
// The player performing the action.
player: Player;
// The ID of the item being interacted with.
itemId: number;
// Additional details about the item.
itemDetails: ItemDetails;
// The option that the player used (ie "equip" or "drop").
equipType: EquipType;
}

/**
* Defines an equipment change plugin.
*/
export interface EquipActionPlugin extends ActionPlugin {
// A single game item ID or a list of item IDs that this action applies to.
itemIds?: number | number[];
// A single option name or a list of option names that this action applies to.
equipType?: EquipType | EquipType[];
// The action function to be performed.
action: equipAction;
}

/**
* A directory of all object interaction plugins.
*/
let equipInteractions: EquipActionPlugin[] = [];

/**
* Sets the list of object interaction plugins.
* @param plugins The plugin list.
*/
export const setEquipPlugins = (plugins: ActionPlugin[]): void => {
equipInteractions = plugins as EquipActionPlugin[];
};

export const equipAction = (player: Player, itemId: number, equipType: EquipType): void => {

// Find all object action plugins that reference this location object
let equipActions = equipInteractions.filter(plugin => {
if(!questFilter(player, plugin)) {
return false;
}

if(plugin.itemIds !== undefined) {
if(!basicNumberFilter(plugin.itemIds, itemId)) {
return false;
}
}


if(plugin.equipType !== undefined) {
if(!basicStringFilter(plugin.equipType, equipType)) {
return false;
}
}
return true;
});

const questActions = equipActions.filter(plugin => plugin.questAction !== undefined);

if(questActions.length !== 0) {
equipActions = questActions;
}


for(const plugin of equipActions) {
plugin.action({
player,
itemId,
itemDetails: world.itemData.get(itemId),
equipType
});
}

};
Loading

0 comments on commit 4f2e239

Please sign in to comment.