Skip to content

Commit

Permalink
Merge pull request runejs#361 from runejs/feature/item-groups
Browse files Browse the repository at this point in the history
Feature: Item grouping
  • Loading branch information
SchauweM authored Nov 25, 2022
2 parents ad8b48a + fde1a32 commit e1df341
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"defence": 30
}
}
}
},
"groups": ["adamant_metal", "equipment"]
}
},

Expand All @@ -31,7 +32,8 @@
"magic": -4,
"ranged": 31
}
}
},
"groups": ["legs"]
},

"rs:adamant_plateskirt": {
Expand All @@ -52,6 +54,7 @@
"magic": -4,
"ranged": 31
}
}
},
"groups": ["legs"]
}
}
9 changes: 6 additions & 3 deletions data/config/items/equipment/standard-metals/black-armour.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"defence": 10
}
}
}
},
"groups": ["black_metal", "equipment"]
}
},

Expand All @@ -31,7 +32,8 @@
"magic": -4,
"ranged": 20
}
}
},
"groups": ["legs"]
},

"rs:black_plateskirt": {
Expand All @@ -52,6 +54,7 @@
"magic": -4,
"ranged": 20
}
}
},
"groups": ["legs"]
}
}
22 changes: 20 additions & 2 deletions data/config/items/equipment/standard-metals/bronze-armour.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
{
"presets": {
"rs:bronze_armour_base": {
"tradable": true,
"equippable": true,
"equipment_data": {
"requirements": {
"skills": {
"defence": 1
}
}
},
"groups": ["bronze_metal", "equipment"]
}
},
"rs:bronze_platelegs": {
"extends": "rs:bronze_armour_base",
"game_id": 1075,
"examine": "These look pretty heavy.",
"weight": 9.071,
Expand All @@ -18,11 +33,13 @@
"magic": -4,
"ranged": 7
}
}
},
"groups": ["legs"]
},

"rs:bronze_plateskirt": {
"game_id": 1087,
"extends": "rs:bronze_armour_base",
"examine": "Designer leg protection.",
"weight": 8.164,
"tradable": true,
Expand All @@ -40,6 +57,7 @@
"magic": -4,
"ranged": 7
}
}
},
"groups": ["legs"]
}
}
18 changes: 17 additions & 1 deletion data/config/items/equipment/standard-metals/bronze-weapons.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
{
"presets": {
"rs:bronze_weapon_base": {
"tradable": true,
"equippable": true,
"equipment_data": {
"requirements": {
"skills": {
"defence": 1
}
}
},
"groups": ["bronze_metal", "equipment", "weapon"]
}
},
"rs:bronze_dagger": {
"game_id": 1205,
"extends": "rs:bronze_weapon_base",
"tradable": true,
"weight": 0.453,
"equippable": true,
Expand Down Expand Up @@ -28,6 +43,7 @@
"weapon_info": {
"style": "dagger"
}
}
},
"groups": ["main_hand", "dagger"]
}
}
60 changes: 44 additions & 16 deletions src/engine/config/config-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
ItemDetails,
ItemPresetConfiguration,
loadItemConfigurations,
translateItemConfig
} from '@engine/config/item-config';
import { filestore } from '@server/game/game-server';
import {
Expand All @@ -24,6 +23,7 @@ import { questMap } from '@engine/plugins';


export let itemMap: { [key: string]: ItemDetails };
export let itemGroupMap: Record<string, Record<string, boolean>>;
export let itemIdMap: { [key: number]: string };
export let objectMap: { [key: number]: ObjectConfig };
export let itemPresetMap: ItemPresetConfiguration;
Expand All @@ -46,8 +46,9 @@ export async function loadCoreConfigurations(): Promise<void> {
export async function loadGameConfigurations(): Promise<void> {
logger.info(`Loading server configurations...`);

const { items, itemIds, itemPresets } = await loadItemConfigurations('data/config/items/');
const { items, itemIds, itemPresets, itemGroups } = await loadItemConfigurations('data/config/items/');
itemMap = items;
itemGroupMap = itemGroups;
itemIdMap = itemIds;
itemPresetMap = itemPresets;

Expand All @@ -70,6 +71,47 @@ export async function loadGameConfigurations(): Promise<void> {
}


/**
* find all items in all select groups
* @param groupKeys array of string of which to find items connected with
* @return itemsKeys array of itemkeys in all select groups
*/
export const findItemTagsInGroups = (groupKeys: string[]): string[] => {
return Object.keys(groupKeys.reduce<Record<string, boolean>>((all, groupKey)=> {
const items = itemGroupMap[groupKey] || {};
return { ...all, ...items };
}, {}));
}


/**
* find all items which are shared by all the groups, and discard items not in all groups
* @param groupKeys groups keys which to find items shared by
* @return itemKeys of items shared by all groups
*/
export const findItemTagsInGroupFilter = (groupKeys: string[]): string[] => {
if(!groupKeys || groupKeys.length === 0) {
return [];
}
let collection: Record<string, boolean> | undefined = undefined;
groupKeys.forEach((groupKey) => {
if(!collection) {
collection = { ...(itemGroupMap[groupKey] || {}) };
return;
}
const current = itemGroupMap[groupKey] || {};

Object.keys(collection).forEach((existingItemKey) => {
if(!(existingItemKey in current)) {
delete collection[existingItemKey];
}
});
});

return Object.keys(collection);
}


export const findItem = (itemKey: number | string): ItemDetails | null => {
if(!itemKey) {
return null;
Expand All @@ -96,20 +138,6 @@ export const findItem = (itemKey: number | string): ItemDetails | null => {
if(item?.gameId) {
gameId = item.gameId;
}

if(item?.extends) {
let extensions = item.extends;
if(typeof extensions === 'string') {
extensions = [ extensions ];
}

extensions.forEach(extKey => {
const extensionItem = itemPresetMap[extKey];
if(extensionItem) {
item = _.merge(item, translateItemConfig(undefined, extensionItem));
}
});
}
}

if(gameId) {
Expand Down
88 changes: 68 additions & 20 deletions src/engine/config/item-config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { loadConfigurationFiles } from '@runejs/common/fs';
import { SkillName } from '@engine/world/actor/skills';
import _ from 'lodash';
import { logger } from '@runejs/common';
import { deepMerge } from '@engine/util/objects';


export type WeaponStyle = 'axe' | 'hammer' | 'bow' | 'claws' | 'crossbow' | 'longsword'
Expand Down Expand Up @@ -125,6 +125,7 @@ export interface ItemConfiguration {
equippable?: boolean;
consumable?: boolean;
destroy?: string | boolean;
groups?: string[];
equipment_data?: {
equipment_slot: EquipmentSlot;
equipment_type?: EquipmentType;
Expand Down Expand Up @@ -155,6 +156,7 @@ export class ItemDetails {
consumable?: boolean;
stackable: boolean = false;
value: number = 0;
groups: string[] = [];
members: boolean = false;
groundOptions: string[] = [];
inventoryOptions: string[] = [];
Expand Down Expand Up @@ -194,6 +196,7 @@ export function translateItemConfig(key: string, config: ItemConfiguration): any
equippable: config.equippable,
weight: config.weight,
destroy: config.destroy || undefined,
groups: config.groups || [],
consumable: config.consumable,
equipmentData: config.equipment_data ? {
equipmentType: config.equipment_data?.equipment_type || undefined,
Expand All @@ -209,43 +212,88 @@ export function translateItemConfig(key: string, config: ItemConfiguration): any
}

export async function loadItemConfigurations(path: string): Promise<{ items: { [key: string]: ItemDetails };
itemIds: { [key: number]: string }; itemPresets: ItemPresetConfiguration; }> {
itemIds: { [key: number]: string }; itemPresets: ItemPresetConfiguration; itemGroups: Record<string, Record<string, boolean>>; }> {
const itemIds: { [key: number]: string } = {};
const items: { [key: string]: ItemDetails } = {};
const itemGroups : Record<string, Record<string, boolean>> = {} // Record where key is group id, and value is an array of all itemstags in group
let itemPresets: ItemPresetConfiguration = {};

const files = await loadConfigurationFiles(path);
const itemConfigurations: Record<string, ItemConfiguration> = {};

files.forEach(itemConfigs => {
const itemKeys = Object.keys(itemConfigs);
itemKeys.forEach(key => {
if(key === 'presets') {
itemPresets = { ...itemPresets, ...itemConfigs[key] };
} else {
itemConfigurations[key] = itemConfigs[key] as ItemConfiguration;
}
});
});
Object.entries(itemConfigurations).forEach(([key, itemConfig]) => {
if(!isNaN(itemConfig.game_id)) {
itemIds[itemConfig.game_id] = key;
let item = { ...translateItemConfig(key, itemConfig) }
if(item?.extends) {
let extensions = item.extends;
if(typeof extensions === 'string') {
extensions = [ extensions ];
}

const itemConfig: ItemConfiguration = itemConfigs[key] as ItemConfiguration;
if(!isNaN(itemConfig.game_id)) {
itemIds[itemConfig.game_id] = key;
items[key] = { ...translateItemConfig(key, itemConfig) };
extensions.forEach(extKey => {
const extensionItem = itemPresets[extKey];
if(extensionItem) {
const preset = translateItemConfig(undefined, extensionItem);
item = deepMerge(item, preset);
}
});
}
items[key] = item;
item.groups.forEach((group) => {
if(!itemGroups[group]) {
itemGroups[group] = {};
}
itemGroups[group][key] = true;
})
}

if(itemConfig.variations) {
for(const subItem of itemConfig.variations) {
const subKey = subItem.suffix ? key + ':' + subItem.suffix : key;
const baseItem = JSON.parse(JSON.stringify({ ...translateItemConfig(key, itemConfig) }));
const subBaseItem = JSON.parse(JSON.stringify({ ...translateItemConfig(subKey, subItem) }));
itemIds[subItem.game_id] = subKey;

if(!items[subKey]) {
items[subKey] = _.merge(baseItem, subBaseItem);
} else {
logger.warn(`Duplicate item key ${subKey} found - the item was not loaded.`);
if(itemConfig.variations) {
for(const subItem of itemConfig.variations) {
const subKey = subItem.suffix ? key + ':' + subItem.suffix : key;
const baseItem = JSON.parse(JSON.stringify({ ...translateItemConfig(key, itemConfig) }));
const subBaseItem = JSON.parse(JSON.stringify({ ...translateItemConfig(subKey, subItem) }));
itemIds[subItem.game_id] = subKey;

if(!items[subKey]) {
let item = deepMerge(baseItem, subBaseItem);
if(item?.extends) {
let extensions = item.extends;
if(typeof extensions === 'string') {
extensions = [ extensions ];
}

extensions.forEach(extKey => {
const extensionItem = itemPresets[extKey];
if(extensionItem) {
const preset = translateItemConfig(undefined, extensionItem);
item = deepMerge(item, preset);
}
});
}
items[subKey] = item;
items[subKey].groups.forEach((group) => {
if(!itemGroups[group]) {
itemGroups[group] = {};
}
itemGroups[group][subKey] = true;
})
} else {
logger.warn(`Duplicate item key ${subKey} found - the item was not loaded.`);
}
}
});
});
}
})

return { items, itemIds, itemPresets };
return { items, itemIds, itemPresets, itemGroups };
}
Loading

0 comments on commit e1df341

Please sign in to comment.