Skip to main content

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.
Skills are managed by SkillsSystem in packages/shared/src/systems/shared/character/SkillsSystem.ts.

Available Skills

Combat Skills

SkillDescriptionTraining Methods
AttackMelee accuracyCombat (Accurate style)
StrengthMelee max hitCombat (Aggressive style)
DefenseDamage reductionCombat (Defensive style)
ConstitutionMaximum HPAll combat (always granted)
RangedRanged combatBow/crossbow attacks
MagicSpellcastingCasting combat spells
PrayerCombat bonuses, protectionBurying bones

Gathering Skills

SkillDescriptionTraining Methods
WoodcuttingChop treesUsing hatchet on trees
MiningMine oreUsing pickaxe on rocks
FishingCatch fishUsing fishing rod at spots

Support Skills

SkillDescriptionTraining Methods
AgilityMovement and shortcutsAgility courses (future)

Artisan Skills

SkillDescriptionTraining Methods
FiremakingLight firesUsing tinderbox on logs
CookingCook foodUsing raw food on fires/ranges
SmithingSmelt bars and smith equipmentSmelting ores at furnaces, smithing bars at anvils
CraftingCreate leather armor, dragonhide, jewelry, cut gemsUsing needle/chisel/furnace on materials
FletchingCreate bows and arrowsUsing knife on logs, item-on-item for arrows/stringing
RunecraftingConvert essence into runesClicking altars with essence in inventory
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).
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.
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.
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.

Prayer Training

Prayer is trained by burying bones dropped from combat:
Bone TypeXPSource
Bones4.5Most monsters
Big Bones15Larger monsters
Dragon Bones72Dragons
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
See Prayer System for complete prayer mechanics, drain formulas, and altar usage.

XP Formula

The XP table uses the authentic RuneScape formula. Level 92 represents the halfway point to 99 in total XP.
// 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

LevelTotal XPXP to Next
1083
101,154229
204,470899
3013,3632,673
4037,2247,195
50101,33318,389
60273,74246,236
70737,627115,590
801,986,068287,883
905,346,332716,427
926,517,253~50% of 99 XP
9913,034,431MAX
XP Cap: Maximum XP per skill is 200,000,000 (200M). This matches OSRS.

Level Calculations

Get Level from XP

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

public getXPToNextLevel(skill: SkillData): number {
  if (skill.level >= 99) return 0;

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

Progress Percentage

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.
// 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));
}
Magic & Prayer Persistence: Magic and Prayer skills now persist to the database. Previously, these skills were not saved between sessions.

Combat Level Examples

StatsCombat Level
All 1s3
40 Atk/Str, 1 everything else32
60 Atk/Str/Def, 60 HP66
99 all combat126

Total Level

Total level is the sum of all skill levels.
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
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.

XP Sources

Combat XP

ActionXP 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):
TreeLevelXP
Normal125
Oak1537.5
Willow3067.5
Teak3585
Maple45100
Mahogany50125
Yew60175
Magic75250
Mining (gathering/mining.json):
OreLevelXPSuccess Rate (L1 → L99)
Copper/Tin117.539.5% → 100% (at L62)
Iron153552% (at L15) → 100% (at L63)
Coal305016.4% (at L30) → 39.5% (at L99)
Mithril558011.7% (at L55) → 19.9% (at L99)
Adamantite70957.4% (at L70) → 10.2% (at L99)
Runite851256.64% (at L85) → 7.42% (at L97+)
Mining rocks always deplete after yielding one ore (OSRS-accurate). Success rate depends only on Mining level, not pickaxe tier. Pickaxe tier affects roll frequency (speed), not success chance.

Pickaxe Speed Bonuses

Dragon and Crystal pickaxes have a chance for bonus speed:
PickaxeBase SpeedBonus ChanceBonus SpeedAverage Speed
Bronze-Rune3-8 ticks--3-8 ticks
Dragon3 ticks1/6 (16.7%)2 ticks2.83 ticks
Crystal3 ticks1/4 (25%)2 ticks2.75 ticks
The bonus speed roll is server-authoritative to prevent client/server desyncs.
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.

Model Scale Normalization

Mining rocks (and all resources) now have normalized model scales to prevent “squished” or “stretched” appearance:
// 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();
  }
});
This normalization runs once when a model is first loaded, before caching. All clones will have correct proportions.
Fishing (gathering/fishing.json):
FishLevelXP
Shrimp110
Sardine520
Herring1030
Trout2050
Pike2560
Salmon3070
Lobster4090
Swordfish50100
Shark76110

Artisan XP

Processing recipes are defined in manifests/recipes/ with OSRS-accurate XP values: Crafting (recipes/crafting.json):
ItemLevelMaterialsXP
Leather gloves11 leather + thread13.8
Leather boots71 leather + thread16.3
Leather body141 leather + thread25
Coif181 leather + thread37
Green d’hide vambraces571 green dragon leather + thread62
Green d’hide body633 green dragon leather + thread186
Gold ring51 gold bar + ring mould15
Sapphire ring201 gold bar + sapphire + ring mould40
Sapphire (cut)201 uncut sapphire + chisel50
Emerald (cut)271 uncut emerald + chisel67.5
Ruby (cut)341 uncut ruby + chisel85
Diamond (cut)431 uncut diamond + chisel107.5
Thread has 5 uses before being consumed. Tanning converts hides to leather for a coin fee (no XP).
Fletching (recipes/fletching.json):
ItemLevelMaterialsXPOutput Qty
Arrow shaft11 logs + knife515
Headless arrow115 arrow shafts + 15 feathers115
Bronze arrow115 headless arrows + 15 bronze arrowtips1.315
Iron arrow1515 headless arrows + 15 iron arrowtips2.515
Steel arrow3015 headless arrows + 15 steel arrowtips515
Shortbow (u)51 logs + knife51
Shortbow51 shortbow (u) + 1 bowstring51
Oak shortbow (u)201 oak logs + knife16.51
Willow shortbow (u)351 willow logs + knife33.31
Yew longbow (u)701 yew logs + knife751
Yew longbow701 yew longbow (u) + 1 bowstring751
Fletching produces multiple items per action for arrows/shafts (15 per action). Arrowtips are created via Smithing (15 per bar).
Runecrafting (recipes/runecrafting.json):
RuneLevelXP/EssenceMulti-Rune Levels
Air1511, 22, 33, 44, 55, 66, 77, 88, 99
Mind25.514, 28, 42, 56, 70, 84, 98
Water5619, 38, 57, 76, 95
Earth96.526, 52, 78
Fire14735, 70
Body207.546, 92
Cosmic27859
Chaos358.574
Nature449-
Law549.5-
Death6510-
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).
Smelting (recipes/smelting.json):
BarLevelOres RequiredXPSuccess Rate
Bronze11 Copper + 1 Tin6.2100%
Iron151 Iron12.550%
Steel301 Iron + 2 Coal17.5100%
Mithril501 Mithril + 4 Coal30100%
Adamant701 Adamantite + 6 Coal37.5100%
Rune851 Runite + 8 Coal50100%
Iron ore has a 50% success rate when smelting - failed attempts consume the ore but grant no bar or XP.
Smithing (recipes/smithing.json): Smithing requires a hammer in inventory and access to an anvil. Recipes are organized by category: Weapons:
ItemLevelBarsXP
Bronze dagger1112.5
Bronze sword4112.5
Bronze scimitar5225
Iron sword19125
Steel sword34137.5
Mithril sword54150
Adamant sword74162.5
Rune sword89175
Armor:
ItemLevelBarsXP
Bronze platebody18562.5
Iron platebody335125
Steel platebody485187.5
Mithril platebody685250
Tools:
ItemLevelBarsXP
Bronze hatchet1112.5
Bronze pickaxe1112.5
Iron hatchet16125
Steel hatchet31137.5
Smithing recipes support “Make X” functionality - players can smith multiple items in one session. The system remembers the last custom quantity entered.
Cooking (recipes/cooking.json):
FoodLevelXPHeals
Shrimp1303 HP
Sardine1403 HP
Herring5505 HP
Trout15707 HP
Pike20808 HP
Salmon25909 HP
Lobster4012012 HP
Swordfish4514014 HP
Shark8021020 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
Food is consumed even at full health (OSRS-accurate). The eat delay and attack delay still apply.
Firemaking (recipes/firemaking.json):
LogsLevelXP
Normal140
Oak1560
Willow3090
Teak35105
Maple45135
Mahogany50157.5
Yew60202.5
Magic75303.8

Firemaking Mechanics

OSRS-Accurate: Players must stand still while lighting fires. Movement during the lighting animation cancels the action.
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:
// 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:
// 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:
// 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):
TierLevel
Bronze/Iron1
Steel5
Black10
Mithril20
Adamant30
Rune40
Dragon60
Tools (Woodcutting/Mining):
TierAttackWoodcuttingMining
Bronze/Iron111
Steel566
Mithril202121
Adamant303131
Rune404141
Dragon606161

Gathering Resource Requirements

Defined in gathering/*.json: Woodcutting:
TreeLevel
Normal1
Oak15
Willow30
Teak35
Maple45
Mahogany50
Yew60
Magic75
Mining:
OreLevel
Copper/Tin1
Iron15
Coal30
Mithril55
Adamantite70
Runite85

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
OreSuccess at Min LevelSuccess at L99Notes
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
Rock Depletion: Mining rocks ALWAYS deplete after yielding one ore (100% chance). This matches OSRS behavior. The only exception would be Mining gloves (not currently implemented).

Pickaxe Speed Bonuses

Pickaxe tier determines roll frequency (ticks between attempts):
PickaxeRoll TicksSpeedSpecial Bonus
Bronze8SlowestNone
Iron7SlowNone
Steel6MediumNone
Mithril5FastNone
Adamant4FasterNone
Rune3Very fastNone
Dragon3Very fast1/6 chance for 2-tick roll (avg 2.83)
Crystal3Very fast1/4 chance for 2-tick roll (avg 2.75)
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.
Fishing:
FishLevel
Shrimp1
Sardine5
Herring10
Trout20
Pike25
Salmon30
Lobster40
Swordfish50
Shark76

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

ItemSmithing LevelBars Required
Bronze Bar11 copper ore + 1 tin ore
Iron Bar151 iron ore (50% success)
Steel Bar301 iron ore + 2 coal
Mithril Bar501 mithril ore + 4 coal
Adamant Bar701 adamantite ore + 6 coal
Rune Bar851 runite ore + 8 coal
Bronze Equipment11-5 bronze bars
Iron Equipment151-5 iron bars
Steel Equipment302-5 steel bars
Mithril Equipment502-5 mithril bars
Adamant Equipment703-5 adamant bars
Rune Equipment853-5 rune bars

Anti-Exploit Protections

Resource Spam Click Prevention

The gathering system prevents duplicate harvesting when players spam-click resources:
// 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
This fix prevents the spam-click exploit where players could reset gathering timers by rapidly clicking the same resource.

Noted Items Tool Validation

Noted items (bank notes) cannot be used as skilling tools:
// From ToolUtils.ts
export function itemMatchesToolCategory(
  itemId: string,
  category: string,
): boolean {
  // Noted items are bank notes - cannot be used as tools
  if (isNotedItemId(itemId)) {
    return false;
  }
  // ... rest of validation
}
Affected Tools:
  • Pickaxes (mining)
  • Hatchets/axes (woodcutting)
  • Fishing rods, nets, harpoons (fishing)
Noted items must be un-noted at a bank before they can be equipped or used for skilling.

Skill Events

The system emits events for UI updates and logging:
EventDataDescription
SKILLS_XP_GAINEDplayerId, skill, amountXP added to skill
SKILLS_LEVEL_UPentityId, skill, oldLevel, newLevelLevel increased
SKILLS_UPDATEDplayerId, skillsSkills state changed
SKILLS_MILESTONEentityId, skill, milestoneMilestone reached
COMBAT_LEVEL_CHANGEDentityId, oldLevel, newLevelCombat level changed
TOTAL_LEVEL_CHANGEDentityId, oldLevel, newLevelTotal level changed

Milestones

Special milestones trigger events and messages:
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

// 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

// Check if entity meets skill requirements
const canEquip = skillsSystem.meetsRequirements(entity, {
  attack: 40,
  defense: 40,
});

Set Level (Admin)

// Directly set skill level (for admin commands)
skillsSystem.setSkillLevel(entityId, "attack", 99);

Reset Skill

// 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
See manifests/skill-unlocks.json for the complete list of unlocks for all skills.

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.