diff --git a/src/plugins/skills/crafting/spinning-wheel-plugin.ts b/src/plugins/skills/crafting/spinning-wheel-plugin.ts index 2c91394c1..7a1d35aa2 100644 --- a/src/plugins/skills/crafting/spinning-wheel-plugin.ts +++ b/src/plugins/skills/crafting/spinning-wheel-plugin.ts @@ -148,7 +148,7 @@ export const buttonClicked: buttonAction = (details) => { // Close the widget as it is no longer needed details.player.closeActiveWidgets(); - if (!details.player.skills.hasSkillLevel(Skill.CRAFTING, product.spinnable.requiredLevel)) { + if (!details.player.skills.hasLevel(Skill.CRAFTING, product.spinnable.requiredLevel)) { details.player.sendMessage(`You need a crafting level of ${product.spinnable.requiredLevel} to craft ${cache.itemDefinitions.get(product.spinnable.output).name.toLowerCase()}.`, true); return; } diff --git a/src/plugins/skills/firemaking-plugin.ts b/src/plugins/skills/firemaking-plugin.ts index 4670686a0..beccfc9cb 100644 --- a/src/plugins/skills/firemaking-plugin.ts +++ b/src/plugins/skills/firemaking-plugin.ts @@ -51,7 +51,7 @@ const lightFire = (player: Player, position: Position, worldItemLog: WorldItem, player.playAnimation(null); player.sendMessage(`The fire catches and the logs begin to burn.`); - player.skills.addExp('firemaking', burnExp); + player.skills.firemaking.addExp(burnExp); if(!player.walkingQueue.moveIfAble(-1, 0)) { if(!player.walkingQueue.moveIfAble(1, 0)) { @@ -93,7 +93,7 @@ const action: itemOnItemAction = (details) => { const worldItemLog = world.spawnWorldItem(log, player.position, player, 300); if(player.metadata['lastFire'] && Date.now() - player.metadata['lastFire'] < 1200 && - canChain(skillInfo.requiredLevel, player.skills.getSkillLevel('firemaking'))) { + canChain(skillInfo.requiredLevel, player.skills.firemaking.level)) { lightFire(player, position, worldItemLog, skillInfo.burnExp); } else { player.sendMessage(`You attempt to light the logs.`); @@ -121,7 +121,7 @@ const action: itemOnItemAction = (details) => { player.playAnimation(animationIds.lightingFire); } - canLightFire = elapsedTicks > 10 && canLight(skillInfo.requiredLevel, player.skills.getSkillLevel('firemaking')); + canLightFire = elapsedTicks > 10 && canLight(skillInfo.requiredLevel, player.skills.firemaking.level); if(!canLightFire && (elapsedTicks === 0 || elapsedTicks % 4 === 0)) { player.playSound(soundIds.lightingFire, 10, 0); diff --git a/src/plugins/skills/smithing/smelting-plugin.ts b/src/plugins/skills/smithing/smelting-plugin.ts index 58c6fce42..bb0368b95 100644 --- a/src/plugins/skills/smithing/smelting-plugin.ts +++ b/src/plugins/skills/smithing/smelting-plugin.ts @@ -180,7 +180,7 @@ const loadSmeltingInterface = (details: ObjectActionDetails) => { // Send the items to the widget. widgetItems.forEach((item) => { details.player.outgoingPackets.setItemOnWidget(widgets.furnace.widgetId, item.slot.modelId, item.bar.barId, 125); - if (!details.player.skills.hasSkillLevel(Skill.SMITHING, item.bar.requiredLevel)) { + if (!details.player.skills.hasLevel(Skill.SMITHING, item.bar.requiredLevel)) { details.player.modifyWidget(widgets.furnace.widgetId, { childId: item.slot.titleId, textColor: colors.red}); } else { details.player.modifyWidget(widgets.furnace.widgetId, { childId: item.slot.titleId, textColor: colors.black}); @@ -204,7 +204,7 @@ const hasIngredients = (details: ButtonActionDetails, ingredients: Item[], inven }; const canSmelt = (details: ButtonActionDetails, bar: Bar): boolean => { - return details.player.skills.hasSkillLevel(Skill.SMITHING, bar.requiredLevel); + return details.player.skills.hasLevel(Skill.SMITHING, bar.requiredLevel); }; const smeltProduct = (details: ButtonActionDetails, bar: Bar, count: number) => { @@ -303,4 +303,4 @@ export default new RunePlugin([ buttonIds: Array.from(widgetButtonIds.keys()), action: buttonClicked } -]); \ No newline at end of file +]); diff --git a/src/world/actor/actor.ts b/src/world/actor/actor.ts index 524917829..2c97a2654 100644 --- a/src/world/actor/actor.ts +++ b/src/world/actor/actor.ts @@ -1,6 +1,6 @@ import { WalkingQueue } from './walking-queue'; import { ItemContainer } from '../items/item-container'; -import { Animation, Graphic, UpdateFlags } from './update-flags'; +import { Animation, DamageType, Graphic, UpdateFlags } from './update-flags'; import { Npc } from './npc/npc'; import { Skills } from '@server/world/actor/skills'; import { Item } from '@server/world/items/item'; @@ -42,6 +42,10 @@ export abstract class Actor { this.pathfinding = new Pathfinding(this); } + public damage(amount: number, damageType: DamageType = DamageType.DAMAGE): void { + + } + public face(face: Position | Actor | null, clearWalkingQueue: boolean = true, autoClear: boolean = true, clearedByWalking: boolean = true): void { if(face === null) { this.clearFaceActor(); diff --git a/src/world/actor/skills.ts b/src/world/actor/skills.ts index 532d1c869..85f83b573 100644 --- a/src/world/actor/skills.ts +++ b/src/world/actor/skills.ts @@ -67,13 +67,74 @@ export const skillDetails: SkillDetail[] = [ export interface SkillValue { exp: number; level: number; + modifiedLevel?: number; } -export class Skills { +export class SkillShortcut { + + public constructor(private skills: Skills, private skillName: SkillName) { + } + + public addExp(exp: number): void { + this.skills.addExp(this.skillName, exp); + } + + public get level(): number { + return this.skills.getLevel(this.skillName); + } + + public get exp(): number { + return this.skills.get(this.skillName).exp; + } + + public get levelForExp(): number { + return this.skills.getLevelForExp(this.exp); + } + +} + +type SkillShortcutMap = { + [skillName in SkillName]: SkillShortcut; +}; + +class SkillShortcuts implements SkillShortcutMap { + agility: SkillShortcut; + attack: SkillShortcut; + construction: SkillShortcut; + cooking: SkillShortcut; + crafting: SkillShortcut; + defence: SkillShortcut; + farming: SkillShortcut; + firemaking: SkillShortcut; + fishing: SkillShortcut; + fletching: SkillShortcut; + herblore: SkillShortcut; + hitpoints: SkillShortcut; + magic: SkillShortcut; + mining: SkillShortcut; + prayer: SkillShortcut; + ranged: SkillShortcut; + runecrafting: SkillShortcut; + slayer: SkillShortcut; + smithing: SkillShortcut; + strength: SkillShortcut; + thieving: SkillShortcut; + woodcutting: SkillShortcut; +} + +export class Skills extends SkillShortcuts { private _values: SkillValue[]; public constructor(private actor: Actor, values?: SkillValue[]) { + super(); + + Object.keys(Skill) + .map(skillName => skillName.toLowerCase()) + .forEach(skillName => + this[skillName] = new SkillShortcut(this, skillName as SkillName) + ); + if(values) { this._values = values; } else { @@ -81,24 +142,14 @@ export class Skills { } } - private defaultValues(): SkillValue[] { - const values: SkillValue[] = []; - skillDetails.forEach(s => values.push({ exp: 0, level: 1 })); - values[Skill.HITPOINTS] = { exp: 1154, level: 10 }; - return values; - } - - /* - * @TODO make an additional field for boostedLevel that this reads from - * Also add a new method to get the unboostedLevel incase it's ever needed - * Then think about some way to reliably and easily fade those boosts out over time - */ - public getSkillLevel(skill: number | SkillName): number { - return this.get(skill).level; + public getLevel(skill: number | SkillName, ignoreLevelModifications: boolean = false): number { + const s = this.get(skill); + return (s.modifiedLevel !== undefined && !ignoreLevelModifications ? s.modifiedLevel : s.level); } - public hasSkillLevel(skill: number | SkillName, level: number): boolean { - return this.get(skill).level >= level; + public hasLevel(skill: number | SkillName, level: number, ignoreLevelModifications: boolean = false): boolean { + const s = this.get(skill); + return (ignoreLevelModifications ? s.level : s.modifiedLevel) >= level; } public getLevelForExp(exp: number): number { @@ -181,16 +232,6 @@ export class Skills { }); } - public setExp(skill: number | SkillName, exp: number): void { - const skillId = this.getSkillId(skill); - this._values[skillId].exp = exp; - } - - public setLevel(skill: number | SkillName, level: number): void { - const skillId = this.getSkillId(skill); - this._values[skillId].level = level; - } - public getSkillId(skill: number | SkillName) : number { if(typeof skill === 'number') { return skill; @@ -209,6 +250,23 @@ export class Skills { } } + private defaultValues(): SkillValue[] { + const values: SkillValue[] = []; + skillDetails.forEach(s => values.push({ exp: 0, level: 1 })); + values[Skill.HITPOINTS] = { exp: 1154, level: 10 }; + return values; + } + + private setExp(skill: number | SkillName, exp: number): void { + const skillId = this.getSkillId(skill); + this._values[skillId].exp = exp; + } + + private setLevel(skill: number | SkillName, level: number): void { + const skillId = this.getSkillId(skill); + this._values[skillId].level = level; + } + public get values(): SkillValue[] { return this._values; } diff --git a/src/world/config/harvest-tool.ts b/src/world/config/harvest-tool.ts index 2be2934a6..be7f95198 100644 --- a/src/world/config/harvest-tool.ts +++ b/src/world/config/harvest-tool.ts @@ -57,7 +57,7 @@ const Axes: HarvestTool[] = [ */ export function getBestPickaxe(player: Player): HarvestTool | null { for (let i = Pickaxes.length - 1; i >= 0; i--) { - if (player.skills.hasSkillLevel(Skill.MINING, Pickaxes[i].level)) { + if (player.skills.hasLevel(Skill.MINING, Pickaxes[i].level)) { if (player.hasItemOnPerson(Pickaxes[i].itemId)) { return Pickaxes[i]; } @@ -72,7 +72,7 @@ export function getBestPickaxe(player: Player): HarvestTool | null { */ export function getBestAxe(player: Player): HarvestTool | null { for (let i = Axes.length - 1; i >= 0; i--) { - if (player.skills.hasSkillLevel(Skill.WOODCUTTING, Axes[i].level)) { + if (player.skills.hasLevel(Skill.WOODCUTTING, Axes[i].level)) { if (player.hasItemOnPerson(Axes[i].itemId)) { return Axes[i]; } diff --git a/src/world/skill-util/harvest-skill.ts b/src/world/skill-util/harvest-skill.ts index 4ed83aa8b..240d49ebc 100644 --- a/src/world/skill-util/harvest-skill.ts +++ b/src/world/skill-util/harvest-skill.ts @@ -34,7 +34,7 @@ export function canInitiateHarvest(player: Player, target: IHarvestable, skill: // Check player level against the required level - if (!player.skills.hasSkillLevel(skill, target.level)) { + if (!player.skills.hasLevel(skill, target.level)) { switch (skill) { case Skill.MINING: player.sendMessage(`You need a Mining level of ${target.level} to mine this rock.`, true);