Skip to content

Commit

Permalink
Merge pull request #394 from Jameskmonger/smelting-task
Browse files Browse the repository at this point in the history
refactor: ♻️  use new task system for smithing plugin
  • Loading branch information
Promises authored Jul 11, 2023
2 parents 9dc557d + 8ca145d commit 98e2205
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 201 deletions.
69 changes: 69 additions & 0 deletions src/plugins/skills/smithing/forging-task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { ActorTask } from '@engine/task/impl';
import { widgets } from '@engine/config/config-handler';
import { Player, Skill } from '@engine/world/actor';
import { Smithable } from './forging-types';

/**
* A task that handles the forging of an item.
*
* Operates repeatedly every 4 ticks, and stops when the player has forged the amount they wanted.
*
* @author jameskmonger
*/
export class ForgingTask extends ActorTask<Player> {
private elapsedTicks = 0;
private amountForged = 0;

constructor(player: Player, private readonly smithable: Smithable, private readonly amount: number) {
super(player);
}

public execute(): void {
const taskIteration = this.elapsedTicks++;

// completed the task if we've forged the amount we wanted
if (this.amountForged >= this.amount) {
this.stop();
return;
}

// TODO (Jameskmonger) remove magic number
this.actor.playAnimation(898);

// only do something every 4 ticks
if (taskIteration % 4 !== 0) {
return;
}

// can't continue
if (!this.hasMaterials()) {
this.stop();
// TODO (Jameskmonger) send message
return;
}

// Remove ingredients
for (let i = 0; i < this.smithable.ingredient.amount; i++) {
this.actor.inventory.removeFirst(this.smithable.ingredient.itemId);
}

// Add item to inventory
this.actor.inventory.add({
itemId: this.smithable.item.itemId,
amount: this.smithable.item.amount
});

this.actor.outgoingPackets.sendUpdateAllWidgetItems(widgets.inventory, this.actor.inventory);
this.actor.skills.addExp(Skill.SMITHING, this.smithable.experience);

this.amountForged++;
}

/**
* Whether the player has the required materials to forge the item.
* @returns {boolean} True if the player has the required materials, false otherwise.
*/
private hasMaterials() {
return this.smithable.ingredient.amount <= this.actor.inventory.findAll(this.smithable.ingredient.itemId).length
}
}
132 changes: 34 additions & 98 deletions src/plugins/skills/smithing/forging.plugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
itemOnObjectActionHandler, ItemOnObjectActionHook, ItemInteractionAction, ItemInteractionActionHook, TaskExecutor
itemOnObjectActionHandler, ItemOnObjectActionHook, ItemInteractionActionHook
} from '@engine/action';
import { widgets } from '@engine/config/config-handler';
import { Skill } from '@engine/world/actor/skills';
Expand All @@ -9,18 +9,9 @@ import { Smithable } from '@plugins/skills/smithing/forging-types';
import { Player } from '@engine/world/actor/player/player';
import { findItem } from '@engine/config/config-handler';
import { Position } from '@engine/world/position';
import { ForgingTask } from './forging-task';
import { logger } from '@runejs/common';

/**
* The amount of items the player wants to forge.
*/
let wantedAmount = 0;

/**
* The amount of items already forged.
*/
let forgedAmount = 0;

/**
* Get the item ids of all the smithable items, as a flat array.
*
Expand Down Expand Up @@ -71,20 +62,9 @@ const findSmithableByItemId = (itemId: number) : Smithable | null => {
};

/**
* Check if the player is able to perform the action.
* @param task
* Check if the player is able to forge an item.
*/
const canActivate = (task: TaskExecutor<ItemInteractionAction>): boolean => {
const { actor, player, actionData } = task.getDetails();

if (!player) {
logger.warn('Forging plugin task `canActivate` has no player.');
return false;
}

const itemId = actionData.itemId;
const smithable = findSmithableByItemId(itemId);

const canForge = (player: Player, smithable: Smithable): boolean => {
// In case the smithable doesn't exist.
if (!smithable) {
return false;
Expand All @@ -94,8 +74,8 @@ const canActivate = (task: TaskExecutor<ItemInteractionAction>): boolean => {
if (smithable.level > player.skills.getLevel(Skill.SMITHING)) {
const item = findItem(smithable.item.itemId);

if (item === null) {
logger.warn(`Could not find item for item id ${smithable.item.itemId}`);
if (!item) {
logger.error(`Could not find smithable item with id ${smithable.item.itemId}`);
return false;
}

Expand All @@ -105,14 +85,14 @@ const canActivate = (task: TaskExecutor<ItemInteractionAction>): boolean => {

// Check if the player has sufficient materials.
if (!hasMaterials(player, smithable)) {
const bar = findItem(smithable.ingredient.itemId);
const ingredient = findItem(smithable.ingredient.itemId);

if (bar === null) {
logger.warn(`Could not find ingredient for item id ${smithable.item.itemId}`);
if (!ingredient) {
logger.error(`Could not find smithable ingredient with id ${smithable.ingredient.itemId}`);
return false;
}

player.sendMessage(`You don't have enough ${bar.name}s.`, true);
player.sendMessage(`You don't have enough ${ingredient.name}s.`, true);
return false;
}

Expand All @@ -121,67 +101,6 @@ const canActivate = (task: TaskExecutor<ItemInteractionAction>): boolean => {
return true;
};

/**
* The actual forging loop.
* @param task
* @param taskIteration
*/
const activate = (task: TaskExecutor<ItemInteractionAction>, taskIteration: number): boolean | undefined => {
const { player, actionData } = task.getDetails();

if (!player) {
logger.warn('Forging plugin task `activate` has no player.');
return false;
}

const itemId = actionData.itemId;
const smithable = findSmithableByItemId(itemId);

if (!smithable) {
logger.warn(`Could not find smithable for item id ${itemId}`);
return false;
}

// How many? Quick and dirty.
switch (actionData.option) {
case 'make' : wantedAmount = 1; break;
case 'make-5' : wantedAmount = 5; break;
case 'make-10' : wantedAmount = 10; break;
}

for(let m=0; m<wantedAmount; m++) {
player.playAnimation(898);
if(taskIteration % 4 === 0) {
if (!hasMaterials(player, smithable) || wantedAmount === forgedAmount) {
return false;
}

// Remove ingredients
for (let i=0; i<smithable.ingredient.amount; i++) {
player.inventory.removeFirst(smithable.ingredient.itemId);
}

// Add item to inventory
player.inventory.add({
itemId: smithable.item.itemId, amount: smithable.item.amount
});

player.outgoingPackets.sendUpdateAllWidgetItems(widgets.inventory, player.inventory);
player.skills.addExp(Skill.SMITHING, smithable.experience);

forgedAmount++;
return true;
}
}

// Reset the properties, and strap in for the next batch.
if (forgedAmount === wantedAmount) {
forgedAmount = 0;
wantedAmount = 0;
return false;
}
};

/**
* Checks if the player has enough materials
* @param player
Expand Down Expand Up @@ -216,12 +135,11 @@ const openForgingInterface: itemOnObjectActionHandler = (details) => {

const bar = findItem(item.itemId);

if (bar === null) {
logger.warn(`Could not find bar for item id ${item.itemId}`);
if (barLevel === undefined || !bar) {
logger.error(`Could not find bar with id ${item.itemId}`);
return;
}


if (barLevel > player.skills.getLevel(Skill.SMITHING)) {
player.sendMessage(`You have to be at least level ${barLevel} to smith ${bar.name}s.`, true);
return;
Expand Down Expand Up @@ -267,10 +185,28 @@ export default {
itemIds: [...mapSmithableItemIdsToFlatArray(smithables)],
options: ['make', 'make-5', 'make-10'],
cancelOtherActions: true,
task: {
canActivate,
activate,
interval: 1
handler: ({ player, itemId, option }) => {
const smithable = findSmithableByItemId(itemId);

if (!smithable) {
logger.error(`Could not find smithable with item id ${itemId}`);
player.sendMessage('Could not find smithable, please tell a dev.');
return;
}

let wantedAmount = 0;

switch (option) {
case 'make': wantedAmount = 1; break;
case 'make-5': wantedAmount = 5; break;
case 'make-10': wantedAmount = 10; break;
}

if (!canForge(player, smithable)) {
return;
}

player.enqueueTask(ForgingTask, [smithable, wantedAmount]);
}
} as ItemInteractionActionHook
]
Expand Down
92 changes: 92 additions & 0 deletions src/plugins/skills/smithing/smelting-task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { ActorTask } from '@engine/task/impl';
import { findItem } from '@engine/config/config-handler';
import { Player, Skill } from '@engine/world/actor';
import { Smeltable } from './smelting-types';
import { animationIds, soundIds } from '@engine/world/config';

/**
* A task that handles the smelting of an item.
*
* Operates repeatedly every 3 ticks, and stops when the player has smelted the amount they wanted.
*
* @author jameskmonger
*/
export class SmeltingTask extends ActorTask<Player> {
private elapsedTicks = 0;
private amountSmelted = 0

constructor(player: Player, private readonly smeltable: Smeltable, private readonly amount: number) {
super(player);
}

public execute(): void {
const taskIteration = this.elapsedTicks++;

// completed the task if we've smelted the amount we wanted
if (this.amountSmelted >= this.amount) {
this.stop();
return;
}

const bar = this.smeltable.bar;
const barItem = findItem(bar.barId);

if (!barItem) {
this.actor.sendMessage(`Could not find item with id ${bar.barId}. Please tell a dev.`);
this.stop();
return;
}

if (!this.hasMaterials()) {
this.actor.sendMessage(`You don't have enough ${barItem.name.toLowerCase()}.`, true);
this.stop();
return;
}

if (!this.hasLevel()) {
this.actor.sendMessage(`You need a smithing level of ${bar.requiredLevel} to smelt ${barItem.name.toLowerCase()}s.`, true);
return;
}

// Smelting takes 3 ticks for each item
if (taskIteration % 3 !== 0) {
return;
}

bar.ingredients.forEach((item) => {
for (let i = 0; i < item.amount; i++) {
this.actor.removeFirstItem(item.itemId);
}
});

this.actor.giveItem(bar.barId);
this.actor.skills.addExp(Skill.SMITHING, bar.experience);
this.amountSmelted++;

this.actor.playAnimation(animationIds.smelting);
this.actor.outgoingPackets.playSound(soundIds.smelting, 5);
}

/**
* Whether the player has the required materials to smelt the item.
* @returns {boolean} True if the player has the required materials, false otherwise.
*/
private hasMaterials() {
return this.smeltable.bar.ingredients.every((item) => {
const itemIndex = this.actor.inventory.findIndex(item);
if (itemIndex === -1 || this.actor.inventory.amountInStack(itemIndex) < item.amount) {
return false;
}

return true;
});
}

/**
* Whether the player has the required level to smelt the item.
* @returns {boolean} True if the player has the required level, false otherwise.
*/
private hasLevel() {
return this.actor.skills.hasLevel(Skill.SMITHING, this.smeltable.bar.requiredLevel);
}
}
Loading

0 comments on commit 98e2205

Please sign in to comment.