skara-brae-common/docs/game/combat-system-stat-scaling-design.md
Jan-Peter von Hunnius 7a9a5a3e7b docs(game): migrate game-design canon notes from Joplin into docs/game/
Eight canon notes (Game Concept Overview, Mechanics Source Mapping,
Combat System & Stat Scaling Design, Skill & Magic System, Medals &
Honours System, Lore & World Building, Idle Fantasy Inspirations,
Tech Stack & Architecture) as markdown, note title as H1, with source
line citing the Joplin note ID. Joplin keeps status notes.
2026-10-03 17:27:43 +02:00

5.8 KiB

Skara Brae — Combat System & Stat Scaling Design

Source: Joplin note 0ef630db4e3e4d6dab18ccaefccaaa5a, migrated 2026-10-03

The Core Tension

RoleMaster approach: Open-ended d100 roll + critical tables. A level 1 can theoretically kill a level 60 with a lucky crit (roll 01-00 on the crit table → "Instant Death"). Thrilling unpredictability, terrible long-term retention — months of work undone by RNG.

Torn City approach: Stats grow into hundreds of millions over years. Combat outcomes are overwhelmingly deterministic — a level 60 will always beat a level 1. Safe long-term progression, no surprise.

Decision: Hybrid "Scaled Brutality" — Torn-scale numbers with RoleMaster-inspired crits that are capped by level difference but never reach zero.


Exponential Stat Growth (Torn-Style)

  • Stats use BIGINT (up to 9.2 quintillion) — room for years of growth
  • Training, equipment, education, and crafting all feed into stat progression
  • Stats grow exponentially, not linearly — early levels feel meaningful, later levels require dedication
  • No rebirth/prestige system for now — stats only ever go up. Can revisit with player base later.

Stat Display

Raw Value Display
1,247 1.2K
12,432 12.4K
4,721,000 4.7M
1,234,567,890 1.2B
3,800,000,000,000 3.8T
  • Show effective power rating (composite of all stats) as a single number for quick comparison
  • Show stat growth rate (e.g., "+2.3K/day") so players feel momentum
  • Historical stat graphs (like Torn) for long-term engagement

RoleMaster-Inspired Critical System

Every attack rolls on a critical table (d100 open-ended: roll 96-100, roll again and add). But crit damage is capped by level difference.

Crit Damage Caps

Level Difference (target above attacker) Crit Damage Cap
Equal level Full crit range (01-100) — no cap
+10 levels 60% of target's max HP
+20 levels 40% of target's max HP
+50 levels 15% of target's max HP
+100 levels 5% of target's max HP

The cap never reaches zero — a level 1 can always chip away at a level 60.

Critical Effects

Crits don't just do damage. They apply status effects based on the crit roll and weapon type:

Effect Duration Description
Bleed 3-5 ticks Damage over time (scaling with weapon)
Stun 1 turn Target skips next action
Disarm 1 turn Target loses weapon bonuses
Cripple 3-5 turns Target agility/strength reduced
Blind 2-3 turns Target accuracy reduced

Key insight: Status effects work regardless of level difference. A level 1 landing a lucky crit can disrupt a level 60 (stun them for a turn) even if the damage is negligible. This creates the RoleMaster feel of "anyone can affect anyone" without the frustration of instant death.

Armor and Crit Mitigation

  • Heavy armor reduces crit roll by a flat amount (e.g., plate armor: -15 to crit roll)
  • Crit Shield enchantments: High-end enchantments absorb X crit damage per day
  • Constitution reduces bleed/cripple duration

Why This Works

  • Long-term players keep their billions-scale progression and feel powerful — they will win 99% of encounters
  • New players feel the thrill of landing a lucky crit (15% HP off a whale is still exciting)
  • Guild wars become strategic: A swarm of low-level players can chip away at a high-level defender through coordinated crit effects
  • Dueling has drama: Crits are always possible, but rarely fight-ending between equals
  • No instant death: No months of work lost to a single bad roll

Combat Formula (Draft)

base_damage = weapon_damage * (1 + strength / 100)
mitigation = armor_rating * (1 + constitution / 200)
net_damage = max(1, base_damage - mitigation)

crit_roll = d100_open_ended()  // 96-100: roll again and add
if crit_roll >= crit_threshold(attacker_agility, target_level_diff):
    crit_multiplier = crit_table(crit_roll, weapon_type)
    crit_damage = net_damage * crit_multiplier
    crit_damage = min(crit_damage, target_max_hp * crit_cap(target_level_diff))
    apply_critical_effect(crit_roll, target)
else:
    apply_damage(net_damage, target)

Open-Ended d100

fn d100_open_ended() -> u32:
    total = d100()
    while total >= 96:  // 96-100: roll again and add
        total += d100()
    return total

This means most rolls are 1-95, but occasionally you get 96+100+3 = 199, or even rarer chains. The open-ended mechanic creates the "anything can happen" feel without instant death.


Critical Tables (Structure)

Crit tables are weapon-type specific. Each entry maps a roll range to an effect:

# crit_tables/slash_unarmed.toml
[entries.01_20]
name = "Glancing blow"
damage_multiplier = 1.5
effect = "none"

[entries.21_50]
name = "Deep slash"
damage_multiplier = 2.0
effect = "bleed"
effect_duration = 3

[entries.51_75]
name = "Arterial strike"
damage_multiplier = 2.5
effect = "bleed"
effect_duration = 5

[entries.76_90]
name = "Debilitating wound"
damage_multiplier = 3.0
effect = "cripple"
effect_duration = 4

[entries.91_100]
name = "Devastating cleave"
damage_multiplier = 4.0
effect = "bleed"
effect_duration = 5
effect2 = "stun"
effect2_duration = 1

[entries.101_plus]
name = "Legendary strike"
damage_multiplier = 5.0
effect = "bleed"
effect_duration = 5
effect2 = "stun"
effect2_duration = 2

These tables live in skara-brae-common/crit-tables/ and are loaded by both server (for combat resolution) and client (for display).