Skip to content

Commit 5ed4a98

Browse files
authored
Merge branch 'develop' into docker-instructions
2 parents e0fb969 + 301fa7e commit 5ed4a98

File tree

5 files changed

+229
-146
lines changed

5 files changed

+229
-146
lines changed

src/engine/world/skill-util/harvest-skill.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ import { findItem } from '@engine/config/config-handler';
1313
import { activeWorld } from '@engine/world';
1414
import { loopingEvent } from '@engine/plugins';
1515

16+
/**
17+
* Check if a player can harvest a given {@link IHarvestable}
18+
*
19+
* @returns a {@link HarvestTool} if the player can harvest the object, or undefined if they cannot.
20+
*/
1621
export function canInitiateHarvest(player: Player, target: IHarvestable, skill: Skill): undefined | HarvestTool {
1722
if (!target) {
1823
switch (skill) {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { randomBetween } from '@engine/util';
2+
import { IHarvestable } from '@engine/world/config';
3+
4+
/**
5+
* Roll a random number between 0 and 255 and compare it to the percent needed to cut the tree.
6+
*
7+
* @param tree The tree to cut
8+
* @param toolLevel The level of the axe being used
9+
* @param woodcuttingLevel The player's woodcutting level
10+
*
11+
* @returns True if the tree was successfully cut, false otherwise
12+
*/
13+
export const canCut = (
14+
tree: IHarvestable,
15+
toolLevel: number,
16+
woodcuttingLevel: number
17+
): boolean => {
18+
const successChance = randomBetween(0, 255);
19+
20+
const percentNeeded =
21+
tree.baseChance + toolLevel + woodcuttingLevel;
22+
return successChance <= percentNeeded;
23+
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {
2+
ObjectInteractionActionHook,
3+
} from '@engine/action';
4+
import { getTreeIds } from '@engine/world/config/harvestable-object';
5+
import { runWoodcuttingTask } from './woodcutting-task';
6+
7+
/**
8+
* Woodcutting plugin
9+
*
10+
* This uses the task system to schedule actions.
11+
*/
12+
export default {
13+
pluginId: 'rs:woodcutting',
14+
hooks: [
15+
/**
16+
* "Chop down" / "chop" object interaction hook.
17+
*/
18+
{
19+
type: 'object_interaction',
20+
options: [ 'chop down', 'chop' ],
21+
objectIds: getTreeIds(),
22+
handler: ({ player, object }) => {
23+
runWoodcuttingTask(player, object);
24+
}
25+
} as ObjectInteractionActionHook
26+
]
27+
};
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import { Skill } from '@engine/world/actor/skills';
2+
import { canInitiateHarvest } from '@engine/world/skill-util/harvest-skill';
3+
import { getTreeFromHealthy, IHarvestable } from '@engine/world/config/harvestable-object';
4+
import { randomBetween } from '@engine/util/num';
5+
import { colorText } from '@engine/util/strings';
6+
import { colors } from '@engine/util/colors';
7+
import { rollBirdsNestType } from '@engine/world/skill-util/harvest-roll';
8+
import { soundIds } from '@engine/world/config/sound-ids';
9+
import { findItem, findObject } from '@engine/config/config-handler';
10+
import { activeWorld } from '@engine/world';
11+
import { canCut } from './chance';
12+
import { ActorLandscapeObjectInteractionTask } from '@engine/task/impl';
13+
import { Player } from '@engine/world/actor';
14+
import { LandscapeObject } from '@runejs/filestore';
15+
import { logger } from '@runejs/common';
16+
17+
class WoodcuttingTask extends ActorLandscapeObjectInteractionTask<Player> {
18+
/**
19+
* The tree being cut down.
20+
*/
21+
private treeInfo: IHarvestable;
22+
23+
/**
24+
* The number of ticks that `execute` has been called inside this task.
25+
*/
26+
private elapsedTicks = 0;
27+
28+
/**
29+
* Create a new woodcutting task.
30+
*
31+
* @param player The player that is attempting to cut down the tree.
32+
* @param landscapeObject The object that represents the tree.
33+
* @param sizeX The size of the tree in x axis.
34+
* @param sizeY The size of the tree in y axis.
35+
*/
36+
constructor(
37+
player: Player,
38+
landscapeObject: LandscapeObject,
39+
sizeX: number,
40+
sizeY: number
41+
) {
42+
super(
43+
player,
44+
landscapeObject,
45+
sizeX,
46+
sizeY
47+
);
48+
49+
if (!landscapeObject) {
50+
this.stop();
51+
return;
52+
}
53+
54+
this.treeInfo = getTreeFromHealthy(landscapeObject.objectId);
55+
if (!this.treeInfo) {
56+
this.stop();
57+
return;
58+
}
59+
}
60+
61+
/**
62+
* Execute the main woodcutting task loop. This method is called every game tick until the task is completed.
63+
*
64+
* As this task extends {@link ActorLandscapeObjectInteractionTask}, it's important that the
65+
* `super.execute` method is called at the start of this method.
66+
*
67+
* The base `execute` performs a number of checks that allow this task to function healthily.
68+
*/
69+
public execute(): void {
70+
super.execute();
71+
72+
if (!this.isActive || !this.landscapeObject) {
73+
return;
74+
}
75+
76+
// store the tick count before incrementing so we don't need to keep track of it in all the separate branches
77+
const taskIteration = this.elapsedTicks++;
78+
79+
const tool = canInitiateHarvest(this.actor, this.treeInfo, Skill.WOODCUTTING);
80+
81+
if (!tool) {
82+
this.stop();
83+
return;
84+
}
85+
86+
if(taskIteration === 0) {
87+
this.actor.sendMessage('You swing your axe at the tree.');
88+
this.actor.face(this.landscapeObjectPosition);
89+
this.actor.playAnimation(tool.animation);
90+
return;
91+
}
92+
93+
// play a random axe sound at the correct time
94+
if(taskIteration % 3 !== 0) {
95+
if(taskIteration % 1 === 0) {
96+
const randomSoundIdx = Math.floor(Math.random() * soundIds.axeSwing.length);
97+
this.actor.playSound(soundIds.axeSwing[randomSoundIdx], 7, 0);
98+
}
99+
return;
100+
}
101+
102+
// Get tool level, and set it to 2 if the tool is an iron hatchet or iron pickaxe axe
103+
// TODO why is this set to 2? Was ported from the old code
104+
let toolLevel = tool.level - 1;
105+
if(tool.itemId === 1349 || tool.itemId === 1267) {
106+
toolLevel = 2;
107+
}
108+
109+
// roll for success
110+
const succeeds = canCut(this.treeInfo, toolLevel, this.actor.skills.woodcutting.level);
111+
if(!succeeds) {
112+
return;
113+
}
114+
115+
const targetName: string = findItem(this.treeInfo.itemId).name.toLowerCase();
116+
117+
// if player doesn't have space in inventory, stop the task
118+
if(!this.actor.inventory.hasSpace()) {
119+
this.actor.sendMessage(`Your inventory is too full to hold any more ${targetName}.`, true);
120+
this.actor.playSound(soundIds.inventoryFull);
121+
this.stop();
122+
return;
123+
}
124+
125+
const itemToAdd = this.treeInfo.itemId;
126+
const roll = randomBetween(1, 256);
127+
// roll for bird nest chance
128+
if(roll === 1) {
129+
this.actor.sendMessage(colorText(`A bird's nest falls out of the tree.`, colors.red));
130+
activeWorld.globalInstance.spawnWorldItem(rollBirdsNestType(), this.actor.position,
131+
{ owner: this.actor || null, expires: 300 });
132+
} else { // Standard log chopper
133+
this.actor.sendMessage(`You manage to chop some ${targetName}.`);
134+
this.actor.giveItem(itemToAdd);
135+
}
136+
137+
this.actor.skills.woodcutting.addExp(this.treeInfo.experience);
138+
139+
// check if the tree should be broken
140+
if(randomBetween(0, 100) <= this.treeInfo.break) {
141+
this.actor.playSound(soundIds.oreDepeleted);
142+
this.actor.instance.replaceGameObject(this.treeInfo.objects.get(this.landscapeObject.objectId),
143+
this.landscapeObject, randomBetween(this.treeInfo.respawnLow, this.treeInfo.respawnHigh));
144+
this.stop();
145+
return;
146+
}
147+
148+
this.actor.playAnimation(tool.animation);
149+
150+
}
151+
152+
/**
153+
* This method is called when the task stops.
154+
*/
155+
public onStop(): void {
156+
super.onStop();
157+
158+
this.actor.stopAnimation();
159+
}
160+
}
161+
162+
export function runWoodcuttingTask(player: Player, landscapeObject: LandscapeObject): void {
163+
const objectConfig = findObject(landscapeObject.objectId);
164+
165+
if (!objectConfig) {
166+
logger.warn(`Player ${player.username} attempted to run a woodcutting task on an invalid object (id: ${landscapeObject.objectId})`);
167+
return;
168+
}
169+
170+
const sizeX = objectConfig.rendering.sizeX;
171+
const sizeY = objectConfig.rendering.sizeY;
172+
173+
player.enqueueTask(WoodcuttingTask, [ landscapeObject, sizeX, sizeY ]);
174+
}

src/plugins/skills/woodcutting/woodcutting.plugin.ts

Lines changed: 0 additions & 146 deletions
This file was deleted.

0 commit comments

Comments
 (0)