Skara Brae — Combat System & Stat Scaling Design

# Combat System & Stat Scaling Design

## 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:

```toml
# 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).

---

## Related Notes

- [Game Concept Overview](joplin://fcd381c235694f29abf73665317a40f5)
- [Idle Fantasy Inspirations](joplin://) *(to be created)*
- [Tech Stack & Architecture](joplin://980c3eb587294e4383474b94988f2f88)
- [Project Plan](joplin://088737f31c514f1da21cc42c0ab6acc1)

id: 5ff1fed180fa4b39b4cdb925f34c1008
parent_id: d1892c7c531848f5a5a3ac5e1749f7cf
created_time: 2026-06-11T15:35:57.669Z
updated_time: 2026-06-11T15:35:57.669Z
is_conflict: 0
latitude: 0.00000000
longitude: 0.00000000
altitude: 0.0000
author: 
source_url: 
is_todo: 0
todo_due: 0
todo_completed: 0
source: joplin-desktop
source_application: net.cozic.joplin-desktop
application_data: 
order: 1781192157669
user_created_time: 2026-06-11T15:35:57.669Z
user_updated_time: 2026-06-11T15:35:57.669Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 0
type_: 1