> ## Documentation Index
> Fetch the complete documentation index at: https://hyperscape-ai-mintlify-docs-update.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Skills & Progression

> RuneScape-style skill system with XP curves and leveling

# Skills & Progression

Hyperscape features a **skill-based progression system** where skills level independently through use. The XP formula and combat level calculation are authentic recreations of Old School RuneScape mechanics.

<Info>
  Skills are managed by `SkillsSystem` in `packages/shared/src/systems/shared/character/SkillsSystem.ts`.
</Info>

## Available Skills

### Combat Skills

| Skill            | Description                | Training Methods            |
| ---------------- | -------------------------- | --------------------------- |
| **Attack**       | Melee accuracy             | Combat (Accurate style)     |
| **Strength**     | Melee max hit              | Combat (Aggressive style)   |
| **Defense**      | Damage reduction           | Combat (Defensive style)    |
| **Constitution** | Maximum HP                 | All combat (always granted) |
| **Ranged**       | Ranged combat              | Bow/crossbow attacks        |
| **Magic**        | Spellcasting               | Casting combat spells       |
| **Prayer**       | Combat bonuses, protection | Burying bones               |

### Gathering Skills

| Skill           | Description | Training Methods           | Tool Validation                                  |
| --------------- | ----------- | -------------------------- | ------------------------------------------------ |
| **Woodcutting** | Chop trees  | Using hatchet on trees     | Manifest-based (prevents pickaxe usage)          |
| **Mining**      | Mine ore    | Using pickaxe on rocks     | Manifest-based (prevents hatchet usage)          |
| **Fishing**     | Catch fish  | Using fishing rod at spots | Exact match required (tools not interchangeable) |

<Info>
  **Tool Validation** (Updated March 2026): The gathering system now uses manifest-based tool validation to prevent cross-skill usage. Each tool in `tools.json` declares its skill explicitly ("woodcutting", "mining", "fishing"). This prevents exploits like using a pickaxe to cut trees.
</Info>

### Support Skills

| Skill       | Description            | Training Methods         |
| ----------- | ---------------------- | ------------------------ |
| **Agility** | Movement and shortcuts | Agility courses (future) |

### Artisan Skills

| Skill            | Description                                         | Training Methods                                       |
| ---------------- | --------------------------------------------------- | ------------------------------------------------------ |
| **Firemaking**   | Light fires                                         | Using tinderbox on logs                                |
| **Cooking**      | Cook food                                           | Using raw food on fires/ranges                         |
| **Smithing**     | Smelt bars and smith equipment                      | Smelting ores at furnaces, smithing bars at anvils     |
| **Crafting**     | Create leather armor, dragonhide, jewelry, cut gems | Using needle/chisel/furnace on materials               |
| **Fletching**    | Create bows and arrows                              | Using knife on logs, item-on-item for arrows/stringing |
| **Runecrafting** | Convert essence into runes                          | Clicking altars with essence in inventory              |

<Info>
  Smithing is a two-step process: first smelt ores into bars at a furnace, then smith bars into equipment at an anvil. Smithing now supports outputQuantity for multi-output recipes (e.g., 15 arrowtips per bar).
</Info>

<Info>
  Crafting uses thread (5 uses per item) for leather/dragonhide armor. Tanning converts hides to leather at tanner NPCs for a coin fee. Crafting features auto-select when only one recipe matches the input item.
</Info>

<Info>
  Fletching produces multiple items per action (e.g., 15 arrow shafts per log). Quantity selection refers to actions, not output items. Fletching UI groups recipes by category for easier navigation.
</Info>

<Info>
  Runecrafting is instant (no tick delay). Multi-rune multipliers grant additional runes per essence at higher levels (e.g., 2x air runes at level 11). Each altar is tied to a specific rune type and features mystical particle effects.
</Info>

***

## Prayer Training

Prayer is trained by burying bones dropped from combat:

| Bone Type        | XP  | Source          |
| ---------------- | --- | --------------- |
| **Bones**        | 4.5 | Most monsters   |
| **Big Bones**    | 15  | Larger monsters |
| **Dragon Bones** | 72  | Dragons         |

**Bury Mechanics:**

* Right-click bones in inventory → "Bury"
* 2-tick (1.2 second) delay between burials
* XP granted immediately on successful bury
* Bones consumed after burying

<Info>
  See [Prayer System](/wiki/game-systems/prayer) for complete prayer mechanics, drain formulas, and altar usage.
</Info>

***

## XP Formula

The XP table uses the authentic RuneScape formula. Level 92 represents the halfway point to 99 in total XP.

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// From SkillsSystem.ts
private generateXPTable(): void {
  this.xpTable = [0, 0]; // Levels 0 and 1

  for (let level = 2; level <= 99; level++) {
    const xp = Math.floor(level - 1 + 300 * Math.pow(2, (level - 1) / 7)) / 4;
    this.xpTable.push(Math.floor(this.xpTable[level - 1] + xp));
  }
}
```

### XP Requirements by Level

| Level | Total XP   | XP to Next     |
| ----- | ---------- | -------------- |
| 1     | 0          | 83             |
| 10    | 1,154      | 229            |
| 20    | 4,470      | 899            |
| 30    | 13,363     | 2,673          |
| 40    | 37,224     | 7,195          |
| 50    | 101,333    | 18,389         |
| 60    | 273,742    | 46,236         |
| 70    | 737,627    | 115,590        |
| 80    | 1,986,068  | 287,883        |
| 90    | 5,346,332  | 716,427        |
| 92    | 6,517,253  | \~50% of 99 XP |
| 99    | 13,034,431 | MAX            |

<Warning>
  **XP Cap**: Maximum XP per skill is 200,000,000 (200M). This matches OSRS.
</Warning>

***

## Level Calculations

### Get Level from XP

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
public getLevelForXP(xp: number): number {
  for (let level = 99; level >= 1; level--) {
    if (xp >= this.xpTable[level]) {
      return level;
    }
  }
  return 1;
}
```

### XP to Next Level

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
public getXPToNextLevel(skill: SkillData): number {
  if (skill.level >= 99) return 0;

  const nextLevelXP = this.getXPForLevel(skill.level + 1);
  return nextLevelXP - skill.xp;
}
```

### Progress Percentage

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
public getXPProgress(skill: SkillData): number {
  if (skill.level >= 99) return 100;

  const currentLevelXP = this.getXPForLevel(skill.level);
  const nextLevelXP = this.getXPForLevel(skill.level + 1);
  const progressXP = skill.xp - currentLevelXP;
  const requiredXP = nextLevelXP - currentLevelXP;

  return (progressXP / requiredXP) * 100;
}
```

***

## Combat Level

Combat level is calculated from combat skills using the OSRS formula. Magic and Prayer are now included in the calculation.

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// From SkillsSystem.ts
public getCombatLevel(stats: StatsComponent): number {
  const defenseLevel = stats.defense?.level ?? 1;
  const hitpointsLevel = stats.constitution?.level ?? 10;
  const prayerLevel = stats.prayer?.level ?? 1;
  const attackLevel = stats.attack?.level ?? 1;
  const strengthLevel = stats.strength?.level ?? 1;
  const rangedLevel = stats.ranged?.level ?? 1;
  const magicLevel = stats.magic?.level ?? 1;

  // Base = 0.25 × (Defense + Hitpoints + floor(Prayer / 2))
  const base = 0.25 * (defenseLevel + hitpointsLevel + Math.floor(prayerLevel / 2));

  // Melee = 0.325 × (Attack + Strength)
  const melee = 0.325 * (attackLevel + strengthLevel);

  // Ranged = 0.325 × floor(Ranged × 1.5)
  const rangedCalc = 0.325 * Math.floor(rangedLevel * 1.5);

  // Magic = 0.325 × floor(Magic × 1.5)
  const magicCalc = 0.325 * Math.floor(magicLevel * 1.5);

  // Combat Level = floor(Base + max(Melee, Ranged, Magic))
  return Math.floor(base + Math.max(melee, rangedCalc, magicCalc));
}
```

<Info>
  **Magic & Prayer Persistence**: Magic and Prayer skills now persist to the database. Previously, these skills were not saved between sessions.
</Info>

### Combat Level Examples

| Stats                         | Combat Level |
| ----------------------------- | ------------ |
| All 1s                        | 3            |
| 40 Atk/Str, 1 everything else | 32           |
| 60 Atk/Str/Def, 60 HP         | 66           |
| 99 all combat                 | 126          |

***

## Total Level

Total level is the sum of all skill levels.

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
public getTotalLevel(stats: StatsComponent): number {
  let total = 0;

  const skills = [
    "attack", "strength", "defense", "constitution", "ranged", "magic", "prayer",
    "woodcutting", "mining", "fishing", "firemaking", "cooking", "smithing",
    "agility", "crafting", "fletching", "runecrafting"
  ];

  for (const skill of skills) {
    const skillData = stats[skill];
    total += skillData?.level ?? 1;
  }

  return total;
}
```

### Maximum Total Level

With 17 skills at level 99: **1,683 total level**

<Info>
  **New Skills Added**: Crafting, Fletching, and Runecrafting are now fully implemented with OSRS-accurate mechanics, recipes, and XP formulas. All three skills persist to the database and integrate with the skill guide panel.
</Info>

***

## XP Sources

### Combat XP

| Action                       | XP Per Damage |
| ---------------------------- | ------------- |
| Main skill (style-dependent) | 4.0           |
| Constitution (always)        | 1.33          |
| Controlled (per skill)       | 1.33          |

### Gathering XP

Gathering resources is now defined in `manifests/gathering/` with OSRS-accurate XP values:

**Woodcutting** (`gathering/woodcutting.json`):

| Tree     | Level | XP   |
| -------- | ----- | ---- |
| Normal   | 1     | 25   |
| Oak      | 15    | 37.5 |
| Willow   | 30    | 67.5 |
| Teak     | 35    | 85   |
| Maple    | 45    | 100  |
| Mahogany | 50    | 125  |
| Yew      | 60    | 175  |
| Magic    | 75    | 250  |

**Mining** (`gathering/mining.json`):

| Ore        | Level | XP   | Success Rate (L1 → L99)          |
| ---------- | ----- | ---- | -------------------------------- |
| Copper/Tin | 1     | 17.5 | 39.5% → 100% (at L62)            |
| Iron       | 15    | 35   | 52% (at L15) → 100% (at L63)     |
| Coal       | 30    | 50   | 16.4% (at L30) → 39.5% (at L99)  |
| Mithril    | 55    | 80   | 11.7% (at L55) → 19.9% (at L99)  |
| Adamantite | 70    | 95   | 7.4% (at L70) → 10.2% (at L99)   |
| Runite     | 85    | 125  | 6.64% (at L85) → 7.42% (at L97+) |

<Info>
  **Mining Depletion** (Updated March 2026): Mining rocks use manifest-based `depleteChance` values. Regular ore rocks have `depleteChance: 1.0` (always deplete after yielding ore). Rune essence rocks have `depleteChance: 0` (never deplete, OSRS-accurate). Success rate depends only on Mining level, not pickaxe tier. Pickaxe tier affects roll frequency (speed), not success chance.
</Info>

<Info>
  **Resource Respawn** (Updated March 2026): All resource respawns are now purely tick-based and deterministic (OSRS-accurate). The `ResourceSystem.processRespawns()` method handles all respawn timing via tick counting. Legacy `setTimeout`-based respawn has been removed for reliability.
</Info>

### Pickaxe Speed Bonuses

Dragon and Crystal pickaxes have a chance for bonus speed:

| Pickaxe     | Base Speed | Bonus Chance | Bonus Speed | Average Speed |
| ----------- | ---------- | ------------ | ----------- | ------------- |
| Bronze-Rune | 3-8 ticks  | -            | -           | 3-8 ticks     |
| Dragon      | 3 ticks    | 1/6 (16.7%)  | 2 ticks     | 2.83 ticks    |
| Crystal     | 3 ticks    | 1/4 (25%)    | 2 ticks     | 2.75 ticks    |

<Info>
  The bonus speed roll is server-authoritative to prevent client/server desyncs.
</Info>

<Info>
  **Mining Mechanics**: Rocks always deplete after yielding one ore (100% depletion chance). Success rates are OSRS-accurate and depend only on Mining level, not pickaxe tier. Pickaxe tier affects roll frequency (speed) only. The bonus speed roll for Dragon/Crystal pickaxes is server-authoritative to prevent client/server desyncs.
</Info>

### Model Scale Normalization

Mining rocks (and all resources) now have normalized model scales to prevent "squished" or "stretched" appearance:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// From ModelCache.ts - normalizeScales()
// Detects and resets non-uniform scales in GLTF models
// Some 3D modeling tools export models with non-uniform scales on internal nodes
// (e.g., scale (2, 0.5, 1)) which causes distortion
scene.traverse((node) => {
  const s = node.scale;
  const isNonUniform = 
    Math.abs(s.x - s.y) > 0.001 ||
    Math.abs(s.y - s.z) > 0.001 ||
    Math.abs(s.x - s.z) > 0.001;

  if (isNonUniform) {
    node.scale.set(1, 1, 1);  // Reset to identity
    node.updateMatrix();
  }
});
```

<Info>
  This normalization runs once when a model is first loaded, before caching. All clones will have correct proportions.
</Info>

**Fishing** (`gathering/fishing.json`):

| Fish      | Level | XP  |
| --------- | ----- | --- |
| Shrimp    | 1     | 10  |
| Sardine   | 5     | 20  |
| Herring   | 10    | 30  |
| Trout     | 20    | 50  |
| Pike      | 25    | 60  |
| Salmon    | 30    | 70  |
| Lobster   | 40    | 90  |
| Swordfish | 50    | 100 |
| Shark     | 76    | 110 |

### Artisan XP

Processing recipes are defined in `manifests/recipes/` with OSRS-accurate XP values:

**Crafting** (`recipes/crafting.json`):

| Item                   | Level | Materials                          | XP    |
| ---------------------- | ----- | ---------------------------------- | ----- |
| Leather gloves         | 1     | 1 leather + thread                 | 13.8  |
| Leather boots          | 7     | 1 leather + thread                 | 16.3  |
| Leather body           | 14    | 1 leather + thread                 | 25    |
| Coif                   | 18    | 1 leather + thread                 | 37    |
| Green d'hide vambraces | 57    | 1 green dragon leather + thread    | 62    |
| Green d'hide body      | 63    | 3 green dragon leather + thread    | 186   |
| Gold ring              | 5     | 1 gold bar + ring mould            | 15    |
| Sapphire ring          | 20    | 1 gold bar + sapphire + ring mould | 40    |
| Sapphire (cut)         | 20    | 1 uncut sapphire + chisel          | 50    |
| Emerald (cut)          | 27    | 1 uncut emerald + chisel           | 67.5  |
| Ruby (cut)             | 34    | 1 uncut ruby + chisel              | 85    |
| Diamond (cut)          | 43    | 1 uncut diamond + chisel           | 107.5 |

<Info>
  Thread has 5 uses before being consumed. Tanning converts hides to leather for a coin fee (no XP).
</Info>

**Fletching** (`recipes/fletching.json`):

| Item                | Level | Materials                                | XP   | Output Qty |
| ------------------- | ----- | ---------------------------------------- | ---- | ---------- |
| Arrow shaft         | 1     | 1 logs + knife                           | 5    | 15         |
| Headless arrow      | 1     | 15 arrow shafts + 15 feathers            | 1    | 15         |
| Bronze arrow        | 1     | 15 headless arrows + 15 bronze arrowtips | 1.3  | 15         |
| Iron arrow          | 15    | 15 headless arrows + 15 iron arrowtips   | 2.5  | 15         |
| Steel arrow         | 30    | 15 headless arrows + 15 steel arrowtips  | 5    | 15         |
| Shortbow (u)        | 5     | 1 logs + knife                           | 5    | 1          |
| Shortbow            | 5     | 1 shortbow (u) + 1 bowstring             | 5    | 1          |
| Oak shortbow (u)    | 20    | 1 oak logs + knife                       | 16.5 | 1          |
| Willow shortbow (u) | 35    | 1 willow logs + knife                    | 33.3 | 1          |
| Yew longbow (u)     | 70    | 1 yew logs + knife                       | 75   | 1          |
| Yew longbow         | 70    | 1 yew longbow (u) + 1 bowstring          | 75   | 1          |

<Info>
  Fletching produces multiple items per action for arrows/shafts (15 per action). Arrowtips are created via Smithing (15 per bar).
</Info>

**Runecrafting** (`recipes/runecrafting.json`):

| Rune   | Level | XP/Essence | Multi-Rune Levels                  |
| ------ | ----- | ---------- | ---------------------------------- |
| Air    | 1     | 5          | 11, 22, 33, 44, 55, 66, 77, 88, 99 |
| Mind   | 2     | 5.5        | 14, 28, 42, 56, 70, 84, 98         |
| Water  | 5     | 6          | 19, 38, 57, 76, 95                 |
| Earth  | 9     | 6.5        | 26, 52, 78                         |
| Fire   | 14    | 7          | 35, 70                             |
| Body   | 20    | 7.5        | 46, 92                             |
| Cosmic | 27    | 8          | 59                                 |
| Chaos  | 35    | 8.5        | 74                                 |
| Nature | 44    | 9          | -                                  |
| Law    | 54    | 9.5        | -                                  |
| Death  | 65    | 10         | -                                  |

<Info>
  Runecrafting is instant (no tick delay). Multi-rune levels grant +1 rune per essence. Example: At level 22, you get 3 air runes per essence (base 1 + level 11 threshold + level 22 threshold).
</Info>

**Smelting** (`recipes/smelting.json`):

| Bar     | Level | Ores Required         | XP   | Success Rate |
| ------- | ----- | --------------------- | ---- | ------------ |
| Bronze  | 1     | 1 Copper + 1 Tin      | 6.2  | 100%         |
| Iron    | 15    | 1 Iron                | 12.5 | 50%          |
| Steel   | 30    | 1 Iron + 2 Coal       | 17.5 | 100%         |
| Mithril | 50    | 1 Mithril + 4 Coal    | 30   | 100%         |
| Adamant | 70    | 1 Adamantite + 6 Coal | 37.5 | 100%         |
| Rune    | 85    | 1 Runite + 8 Coal     | 50   | 100%         |

<Info>
  Iron ore has a 50% success rate when smelting - failed attempts consume the ore but grant no bar or XP.
</Info>

**Smithing** (`recipes/smithing.json`):

Smithing requires a hammer in inventory and access to an anvil. Recipes are organized by category:

*Weapons*:

| Item            | Level | Bars | XP   |
| --------------- | ----- | ---- | ---- |
| Bronze dagger   | 1     | 1    | 12.5 |
| Bronze sword    | 4     | 1    | 12.5 |
| Bronze scimitar | 5     | 2    | 25   |
| Iron sword      | 19    | 1    | 25   |
| Steel sword     | 34    | 1    | 37.5 |
| Mithril sword   | 54    | 1    | 50   |
| Adamant sword   | 74    | 1    | 62.5 |
| Rune sword      | 89    | 1    | 75   |

*Armor*:

| Item              | Level | Bars | XP    |
| ----------------- | ----- | ---- | ----- |
| Bronze platebody  | 18    | 5    | 62.5  |
| Iron platebody    | 33    | 5    | 125   |
| Steel platebody   | 48    | 5    | 187.5 |
| Mithril platebody | 68    | 5    | 250   |

*Tools*:

| Item           | Level | Bars | XP   |
| -------------- | ----- | ---- | ---- |
| Bronze hatchet | 1     | 1    | 12.5 |
| Bronze pickaxe | 1     | 1    | 12.5 |
| Iron hatchet   | 16    | 1    | 25   |
| Steel hatchet  | 31    | 1    | 37.5 |

<Info>
  Smithing recipes support "Make X" functionality - players can smith multiple items in one session. The system remembers the last custom quantity entered.
</Info>

**Cooking** (`recipes/cooking.json`):

| Food      | Level | XP  | Heals |
| --------- | ----- | --- | ----- |
| Shrimp    | 1     | 30  | 3 HP  |
| Sardine   | 1     | 40  | 3 HP  |
| Herring   | 5     | 50  | 5 HP  |
| Trout     | 15    | 70  | 7 HP  |
| Pike      | 20    | 80  | 8 HP  |
| Salmon    | 25    | 90  | 9 HP  |
| Lobster   | 40    | 120 | 12 HP |
| Swordfish | 45    | 140 | 14 HP |
| Shark     | 80    | 210 | 20 HP |

### Food Consumption Mechanics

Food consumption follows OSRS-accurate timing and combat integration:

**Eating Delay**:

* 3-tick (1.8s) cooldown between eating foods
* Managed by `EatDelayManager` (per-player tracking)
* Food consumed only after validation passes
* Message shown even at full health: "You eat the shrimp."

**Combat Integration**:

* Eating during combat adds 3-tick delay to attack cooldown
* OSRS rule: Only adds delay if weapon is **already on cooldown**
* If weapon is ready to attack, eating does NOT add delay
* Prevents eating from being used to delay attacks strategically

**Server-Authoritative Flow**:

```
Client → useItem packet → Server validates → INVENTORY_USE event →
InventorySystem validates item exists → ITEM_USED → PlayerSystem validates eat delay →
Consume item + heal + attack delay
```

**Security Features**:

* Rate limiting (3 requests/sec)
* Input validation (slot bounds, item ID mismatch detection)
* Heal amount capped at 99 HP (prevents manifest exploits)
* Server-side eat delay enforcement

<Info>
  Food is consumed even at full health (OSRS-accurate). The eat delay and attack delay still apply.
</Info>

**Firemaking** (`recipes/firemaking.json`):

| Logs     | Level | XP    |
| -------- | ----- | ----- |
| Normal   | 1     | 40    |
| Oak      | 15    | 60    |
| Willow   | 30    | 90    |
| Teak     | 35    | 105   |
| Maple    | 45    | 135   |
| Mahogany | 50    | 157.5 |
| Yew      | 60    | 202.5 |
| Magic    | 75    | 303.8 |

### Firemaking Mechanics

<Info>
  **OSRS-Accurate**: Players must stand still while lighting fires. Movement during the lighting animation cancels the action.
</Info>

**Lighting Process:**

1. Player clicks logs with tinderbox (or tinderbox on logs)
2. Player movement is stopped automatically
3. Lighting animation plays (3 seconds)
4. Fire spawns at the position where lighting began
5. Logs are consumed, XP is granted

**Movement Cancellation:**

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// From ProcessingSystem.ts
// Stop player movement before lighting fire (OSRS: player stands still to light)
this.tileMovementManager.stopPlayer(player.id);

// Cancel firemaking when the player moves during the lighting animation
private cancelFiremaking(playerId: string, action: ProcessingAction): void {
  this.activeProcessing.delete(playerId);
  this.releaseAction(action);
  this.resetPlayerEmote(playerId);
  
  this.emitTypedEvent(EventType.FIRE_LIGHTING_CANCELLED, { playerId });
  
  this.emitTypedEvent(EventType.UI_MESSAGE, {
    playerId,
    message: "You move and stop trying to light the fire.",
    type: "info",
  });
}
```

**Movement Detection:**

The system monitors player position during the 3-second lighting animation:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// Movement threshold: 0.5 world units (~half a tile)
private static readonly FIREMAKING_MOVE_THRESHOLD_SQ = 0.25;

// Check for movement in update loop
const dx = player.node.position.x - action.startPosition.x;
const dz = player.node.position.z - action.startPosition.z;
const distSq = dx * dx + dz * dz;

if (distSq > ProcessingSystem.FIREMAKING_MOVE_THRESHOLD_SQ) {
  this.cancelFiremaking(playerId, action);
}
```

**Fire Placement:**

Fires spawn at the cached start position, not the player's current position:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// Cache player start position for movement detection and fire placement
const startPosition = {
  x: player.node.position.x,
  y: player.node.position.y,
  z: player.node.position.z,
};
action.startPosition = startPosition;

// Fire spawns where lighting began, not where player ended up
this.completeFiremaking(playerId, processingAction, startPosition);
```

**Fire Lifecycle:**

* Fires burn for a limited time (configurable per log type)
* When fires extinguish, they spawn ashes as a ground item
* Ashes despawn after 2 minutes
* Fire models use bounding box snapping to sit flush on terrain

***

## Level Requirements

Skills gate access to equipment and activities. Requirements are now centralized in `tier-requirements.json` and `skill-unlocks.json`.

### Tier-Based Equipment Requirements

Equipment now uses a tier system that automatically looks up requirements:

**Melee Weapons & Armor** (Attack/Defence):

| Tier        | Level |
| ----------- | ----- |
| Bronze/Iron | 1     |
| Steel       | 5     |
| Black       | 10    |
| Mithril     | 20    |
| Adamant     | 30    |
| Rune        | 40    |
| Dragon      | 60    |

**Tools** (Woodcutting/Mining):

| Tier        | Attack | Woodcutting | Mining |
| ----------- | ------ | ----------- | ------ |
| Bronze/Iron | 1      | 1           | 1      |
| Steel       | 5      | 6           | 6      |
| Mithril     | 20     | 21          | 21     |
| Adamant     | 30     | 31          | 31     |
| Rune        | 40     | 41          | 41     |
| Dragon      | 60     | 61          | 61     |

### Gathering Resource Requirements

Defined in `gathering/*.json`:

**Woodcutting**:

| Tree     | Level |
| -------- | ----- |
| Normal   | 1     |
| Oak      | 15    |
| Willow   | 30    |
| Teak     | 35    |
| Maple    | 45    |
| Mahogany | 50    |
| Yew      | 60    |
| Magic    | 75    |

**Mining**:

| Ore        | Level |
| ---------- | ----- |
| Copper/Tin | 1     |
| Iron       | 15    |
| Coal       | 30    |
| Mithril    | 55    |
| Adamantite | 70    |
| Runite     | 85    |

### Mining Success Rates (OSRS-Accurate)

Mining success rates depend **only on Mining level**, not pickaxe tier. Pickaxe tier affects roll frequency (speed) only.

The formula uses linear interpolation between low and high values:

```
P(Level) = (1 + floor(low × (99 - L) / 98 + high × (L - 1) / 98 + 0.5)) / 256
```

| Ore        | Success at Min Level | Success at L99 | Notes             |
| ---------- | -------------------- | -------------- | ----------------- |
| Copper/Tin | \~39.5% (L1)         | 100% (L62+)    | Beginner-friendly |
| Iron       | \~52% (L15)          | 100% (L63+)    | Fast training     |
| Coal       | \~16.4% (L30)        | \~39.5% (L99)  | Slow but valuable |
| Mithril    | \~11.7% (L55)        | \~19.9% (L99)  | Very slow         |
| Adamantite | \~7.4% (L70)         | \~10.2% (L99)  | Rare              |
| Runite     | \~6.64% (L85)        | \~7.42% (L97+) | Extremely rare    |

<Warning>
  **Rock Depletion** (Updated March 2026): Regular mining rocks deplete after yielding one ore (`depleteChance: 1.0`). Rune essence rocks never deplete (`depleteChance: 0`, OSRS-accurate). Depletion chance is now read from the manifest instead of hardcoded constants. The only exception would be Mining gloves (not currently implemented).
</Warning>

### Pickaxe Speed Bonuses

Pickaxe tier determines roll frequency (ticks between attempts):

| Pickaxe | Roll Ticks | Speed     | Special Bonus                         |
| ------- | ---------- | --------- | ------------------------------------- |
| Bronze  | 8          | Slowest   | None                                  |
| Iron    | 7          | Slow      | None                                  |
| Steel   | 6          | Medium    | None                                  |
| Mithril | 5          | Fast      | None                                  |
| Adamant | 4          | Faster    | None                                  |
| Rune    | 3          | Very fast | None                                  |
| Dragon  | 3          | Very fast | 1/6 chance for 2-tick roll (avg 2.83) |
| Crystal | 3          | Very fast | 1/4 chance for 2-tick roll (avg 2.75) |

<Info>
  **Dragon/Crystal Pickaxe Bonus**: These pickaxes have a chance to trigger a faster roll. The server rolls this randomly and applies it deterministically to prevent client/server desyncs.
</Info>

**Fishing**:

| Fish      | Level |
| --------- | ----- |
| Shrimp    | 1     |
| Sardine   | 5     |
| Herring   | 10    |
| Trout     | 20    |
| Pike      | 25    |
| Salmon    | 30    |
| Lobster   | 40    |
| Swordfish | 50    |
| Shark     | 76    |

### Processing Recipe Requirements

Defined in `recipes/*.json`:

**Smelting**: Levels 1-85 (Bronze to Rune bars)
**Smithing**: Levels 1-91 (Bronze to Rune equipment)
**Cooking**: Levels 1-80 (Shrimp to Shark)
**Firemaking**: Levels 1-75 (Normal to Magic logs)

### Smithing Requirements

| Item              | Smithing Level | Bars Required             |
| ----------------- | -------------- | ------------------------- |
| Bronze Bar        | 1              | 1 copper ore + 1 tin ore  |
| Iron Bar          | 15             | 1 iron ore (50% success)  |
| Steel Bar         | 30             | 1 iron ore + 2 coal       |
| Mithril Bar       | 50             | 1 mithril ore + 4 coal    |
| Adamant Bar       | 70             | 1 adamantite ore + 6 coal |
| Rune Bar          | 85             | 1 runite ore + 8 coal     |
| Bronze Equipment  | 1              | 1-5 bronze bars           |
| Iron Equipment    | 15             | 1-5 iron bars             |
| Steel Equipment   | 30             | 2-5 steel bars            |
| Mithril Equipment | 50             | 2-5 mithril bars          |
| Adamant Equipment | 70             | 3-5 adamant bars          |
| Rune Equipment    | 85             | 3-5 rune bars             |

***

## Resource Respawn System (Updated March 2026)

**Tick-Based Respawn**: All resource respawns are now handled by `ResourceSystem.processRespawns()` via deterministic tick counting (OSRS-accurate). Legacy `setTimeout`-based respawn has been removed.

**How It Works**:

1. When a resource depletes, `depletedAtTick` is set to current tick
2. `ResourceSystem.processRespawns()` runs every tick
3. Checks if `currentTick - depletedAtTick >= respawnTicks`
4. If true, resource respawns deterministically

**Benefits**:

* **Deterministic**: Same respawn timing every time (no setTimeout variance)
* **OSRS-Accurate**: Matches Old School RuneScape tick-based mechanics
* **Reliable**: No race conditions or missed respawns
* **Testable**: Predictable behavior for integration tests

**Manifest-Based Depletion**:

* Mining now reads `depleteChance` from manifest instead of hardcoded constants
* Regular ore rocks: `depleteChance: 1.0` (always deplete)
* Rune essence rocks: `depleteChance: 0` (never deplete, OSRS-accurate)
* Allows per-resource depletion behavior without code changes

**Implementation** (`packages/shared/src/systems/shared/entities/gathering/ResourceSystem.ts`):

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
public processRespawns(): void {
  const currentTick = this.world.currentTick;
  
  for (const resource of this.world.entities.values()) {
    if (!(resource instanceof ResourceEntity)) continue;
    if (!resource.isDepleted) continue;
    
    const ticksSinceDepleted = currentTick - resource.depletedAtTick;
    if (ticksSinceDepleted >= resource.respawnTicks) {
      resource.respawn();
    }
  }
}
```

***

## Anti-Exploit Protections

### Resource Spam Click Prevention

The gathering system prevents duplicate harvesting when players spam-click resources:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// From ResourceSystem.ts
const existingSession = this.activeGathering.get(playerId);
if (existingSession) {
  // If already gathering this EXACT resource, silently ignore (prevents duplicate rewards)
  if (existingSession.resourceId === resource.id) {
    return;
  }
  // If switching to a DIFFERENT resource, cancel the old session first
  this.cancelGatheringForPlayer(playerId, "switch_resource");
}
```

**Protection Features**:

* Duplicate gather requests for the same resource are silently ignored
* Prevents timer resets and duplicate drops
* Allows switching to different resources (cancels old session first)
* Guard clause executes after resource resolution for accurate comparison

<Info>
  This fix prevents the spam-click exploit where players could reset gathering timers by rapidly clicking the same resource.
</Info>

### Tool Validation System (Updated March 2026)

**Manifest-Based Validation** (PR #1098): The gathering system now uses `tools.json` manifest as the single source of truth for tool-to-skill mappings.

**Problem Solved**: Substring matching allowed pickaxes to cut trees and hatchets to mine rocks because "pickaxe" contains "axe".

**Solution**: Each tool declares its skill explicitly in the manifest:

```json theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// tools.json
{
  "id": "bronze_hatchet",
  "skill": "woodcutting",
  "levelRequired": 1,
  "successRateLow": 0.25,
  "successRateHigh": 0.75
}
```

**Validation Logic**:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// From ToolUtils.ts
export function itemMatchesToolCategory(
  itemId: string,
  category: string,
): boolean {
  // 1. Reject noted items (bank notes cannot be used as tools)
  if (isNotedItemId(itemId)) {
    return false;
  }

  // 2. Fishing tools require exact match (not interchangeable)
  if (isExactMatchFishingTool(category)) {
    return lowerItemId === category;
  }

  // 3. Manifest lookup (primary path)
  const toolData = getExternalTool(lowerItemId);
  if (toolData) {
    const expectedSkill = CATEGORY_TO_SKILL[category] ?? category;
    return toolData.skill === expectedSkill;
  }

  // 4. Fallback with cross-skill guards
  if (category === "hatchet") {
    if (lowerItemId.includes("pickaxe") || lowerItemId.includes("pick")) {
      return false; // Reject pickaxes for woodcutting
    }
    return lowerItemId.includes("hatchet");
  }
  if (category === "pickaxe") {
    if (lowerItemId.includes("hatchet")) {
      return false; // Reject hatchets for mining
    }
    return lowerItemId.includes("pickaxe") || lowerItemId.includes("pick");
  }

  // 5. Unknown category: reject (forces manifest completeness)
  return false;
}
```

**Impact**:

* Prevents cross-skill tool usage (pickaxe for woodcutting, hatchet for mining)
* Eliminates false positives from combat weapons (battleaxe, greataxe)
* Maintains OSRS-accurate fishing tool behavior (exact match required)
* Forces all gathering tools to be in manifest for proper validation

**Fishing Tool Exact Match**:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// Fishing tools are NOT interchangeable (OSRS-accurate)
itemMatchesToolCategory("fishing_rod", "fishing_rod");        // true
itemMatchesToolCategory("fly_fishing_rod", "fishing_rod");    // false
itemMatchesToolCategory("small_fishing_net", "fishing_rod");  // false
```

**Cross-Skill Prevention**:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// Pickaxe cannot be used for woodcutting
itemMatchesToolCategory("bronze_pickaxe", "hatchet");  // false

// Hatchet cannot be used for mining
itemMatchesToolCategory("bronze_hatchet", "pickaxe");  // false

// Combat weapons rejected
itemMatchesToolCategory("rune_battleaxe", "hatchet");  // false
```

<Warning>
  **Noted Items**: Bank notes cannot be used as tools. Un-note at a bank before using for skilling.
</Warning>

<Info>
  **Fallback Warnings**: If a tool is not in the manifest, the system logs a warning and uses fallback substring matching. Add all tools to `tools.json` to eliminate warnings and ensure proper validation.
</Info>

***

## Skill Events

The system emits events for UI updates and logging:

| Event                  | Data                                        | Description          |
| ---------------------- | ------------------------------------------- | -------------------- |
| `SKILLS_XP_GAINED`     | `playerId`, `skill`, `amount`               | XP added to skill    |
| `SKILLS_LEVEL_UP`      | `entityId`, `skill`, `oldLevel`, `newLevel` | Level increased      |
| `SKILLS_UPDATED`       | `playerId`, `skills`                        | Skills state changed |
| `SKILLS_MILESTONE`     | `entityId`, `skill`, `milestone`            | Milestone reached    |
| `COMBAT_LEVEL_CHANGED` | `entityId`, `oldLevel`, `newLevel`          | Combat level changed |
| `TOTAL_LEVEL_CHANGED`  | `entityId`, `oldLevel`, `newLevel`          | Total level changed  |

***

## Milestones

Special milestones trigger events and messages:

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
const commonMilestones = [
  { level: 50, name: "Halfway", message: "Halfway to mastery!" },
  { level: 92, name: "Half XP", message: "Halfway to 99 in XP!" },
  { level: 99, name: "Mastery", message: "Skill mastered!" },
];

// Attack-specific milestones
const attackMilestones = [
  { level: 40, name: "Rune Weapons", message: "You can now wield rune weapons!" },
  { level: 60, name: "Dragon Weapons", message: "You can now wield dragon weapons!" },
];
```

***

## API Reference

### Grant XP

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// Grant XP via event system (recommended)
skillsSystem.grantXP(entityId, "woodcutting", 25);

// Direct internal grant (used by external handlers)
skillsSystem.addXPInternal(entityId, skill, amount);
```

### Check Requirements

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// Check if entity meets skill requirements
const canEquip = skillsSystem.meetsRequirements(entity, {
  attack: 40,
  defense: 40,
});
```

### Set Level (Admin)

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// Directly set skill level (for admin commands)
skillsSystem.setSkillLevel(entityId, "attack", 99);
```

### Reset Skill

```typescript theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
// Reset skill to level 1
skillsSystem.resetSkill(entityId, "attack");
```

***

## Skill Unlocks

The `skill-unlocks.json` manifest documents what players unlock at each level. This is used for UI display and progression tracking.

Example unlocks:

* **Attack 40**: Rune weapons
* **Attack 60**: Dragon weapons
* **Woodcutting 15**: Oak trees
* **Mining 30**: Coal
* **Cooking 40**: Lobster
* **Smithing 50**: Mithril bar

<Info>
  See `manifests/skill-unlocks.json` for the complete list of unlocks for all skills.
</Info>

## Manifest-Driven Design

All skill-related data is now defined in JSON manifests:

* **`tier-requirements.json`** - Equipment level requirements by tier
* **`skill-unlocks.json`** - What unlocks at each level
* **`gathering/*.json`** - Resource nodes, yields, and XP
* **`recipes/*.json`** - Processing recipes and XP

This allows easy content updates without code changes and enables community modding.

## Related Documentation

* [Prayer System](/wiki/game-systems/prayer) (Prayer mechanics, drain, altars, bone burying)
* [Combat System](/wiki/game-systems/combat) (Prayer bonuses in combat)
* [Data Manifests Overview](/wiki/data/overview)
* [Item Data Structure](/wiki/data/items)
* [NPC Data Structure](/wiki/data/npcs)
