reikhelm/combat/configs/default.json
Parley Hatch 57dba5e149 feat(combat): order-of-magnitude slowdown — fights now ~20-30s
Fights were ~3-5s; the user wanted 20-30s. Tuned the table (and added one
mechanic) so they land there, re-validated against the sim throughout.

- Engine: a `global_cooldown` (the genre swing timer, §10 "attack delay") — an
  actor can't start a new action until it elapses, so no button-masher can
  out-DPS the intended tempo. Config-driven (`global_cooldown` ticks; 0 = off).
  Actor gains `next_action_at`; engine gates decisions on it.
- Controller: ScriptedPlayer now pressures with its heavy proactively when
  healthy (not only on the exact overrun frame), so the sim measures what a real
  player experiences instead of an unrealistically patient bound.
- Config rebalance: damage cut + flattened, fatigue/regen/recovery co-tuned, and
  the kit made roughly **DPS-neutral** with auto-attack (each ability's cooldown
  matched to auto's damage-per-second). Under the global cooldown that's the key
  insight: abilities win through stagger leverage + timing, not raw throughput —
  otherwise strong abilities trivialize the pace and flat ones aren't worth their
  fill (the guardrail kept inverting until this landed). Auto-attacks (difficulty
  0) also no longer fizzle.
- Front-end: cache-bust the config fetch so edits are always picked up; widened
  the sim's archetype sweep to all six monsters.

Result (sim, skilled play ≈ real play): monster fights 16-38s (centered ~24s);
guardrail steep and green (even-con auto 0% → skill20 2% → skill50 12% →
skill80 44% → skill100 78%); all six monsters winnable. Verified live in-browser:
realistic play lands ~15-30s and a clean mid-fight reads perfectly on the dual bar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 13:34:10 -06:00

221 lines
6 KiB
JSON

{
"_comment": "The combat 'table' (spec §11). Magnitudes are displayed units; ratios/percentages are basis points (10000 = 1.0 = 100%); durations are ticks. Editing this file is the primary balancing act. tick_rate ticks = 1 second.",
"tick_rate": 10,
"global_cooldown": 15,
"damage_variance_bp": 1500,
"regen": {
"dazed_regen_bp": 5000,
"floor_per_sec": 1
},
"stagger": {
"daze_threshold_bp": 13000,
"unconscious_threshold_bp": 20000,
"daze_duration": 8,
"revive_daze": 12,
"fatigue_ratio_bp": 2300
},
"competency": {
"level_per_point_bp": 200
},
"fizzle": {
"base_bp": 200,
"per_gap_bp": 150,
"max_bp": 9000
},
"cost_model": {
"base_fill_self": 0,
"base_fill_touch": 1,
"base_fill_melee": 1,
"base_fill_projectile": 2,
"base_fill_beam": 3,
"base_fill_aoe": 4,
"fatigue_ratio_bp": 2300,
"difficulty_per_fill_bp": 10000
},
"con_curve": [
{ "delta": -8, "mult_bp": 4000 },
{ "delta": -5, "mult_bp": 6000 },
{ "delta": -2, "mult_bp": 8500 },
{ "delta": 0, "mult_bp": 10000 },
{ "delta": 2, "mult_bp": 11500 },
{ "delta": 5, "mult_bp": 15000 },
{ "delta": 8, "mult_bp": 19000 }
],
"abilities": [
{
"id": "auto_attack",
"name": "Auto-Attack",
"school": "martial",
"delivery": "melee",
"effects": [ { "kind": "fill_damage", "magnitude_bp": 560, "cost_fill": 0 } ],
"cooldown": 10,
"cost_override": { "fill": 0, "fatigue": 1 }
},
{
"id": "quick_jab",
"name": "Quick Jab",
"school": "martial",
"delivery": "melee",
"effects": [ { "kind": "fill_damage", "magnitude_bp": 1100, "cost_fill": 5 } ],
"cooldown": 20
},
{
"id": "heavy_strike",
"name": "Heavy Strike",
"school": "martial",
"delivery": "melee",
"effects": [ { "kind": "fill_damage", "magnitude_bp": 2800, "cost_fill": 14 } ],
"cooldown": 45
},
{
"id": "fireball",
"name": "Fireball",
"school": "invocation",
"delivery": "projectile",
"effects": [ { "kind": "fill_damage", "magnitude_bp": 2800, "cost_fill": 14 } ],
"potency_bp": 10000,
"cast_time": 12,
"cooldown": 40
},
{
"id": "curse_of_weariness",
"name": "Curse of Weariness",
"school": "affliction",
"delivery": "projectile",
"effects": [ { "kind": "ceiling_damage", "magnitude_bp": 1250, "cost_fill": 10 } ],
"cast_time": 8,
"cooldown": 40
},
{
"id": "venom",
"name": "Venom",
"school": "affliction",
"delivery": "touch",
"effects": [ { "kind": "dot", "per_tick_bp": 260, "duration": 20, "cost_fill": 10 } ],
"cooldown": 30
},
{
"id": "enervate",
"name": "Enervate",
"school": "affliction",
"delivery": "projectile",
"effects": [ { "kind": "regen_sabotage", "reduce_bp": 6000, "duration": 30, "cost_fill": 10 } ],
"cast_time": 6,
"cooldown": 35
},
{
"id": "second_wind",
"name": "Second Wind",
"school": "restoration",
"delivery": "self_cast",
"effects": [ { "kind": "recovery", "magnitude_bp": 700, "cost_fill": 3 } ],
"cooldown": 65
},
{
"id": "mend",
"name": "Mend",
"school": "restoration",
"delivery": "self_cast",
"effects": [ { "kind": "hot", "per_tick_bp": 70, "duration": 15, "cost_fill": 6 } ],
"cast_time": 10,
"cooldown": 45
},
{
"id": "guard",
"name": "Guard",
"school": "martial",
"delivery": "self_cast",
"effects": [ { "kind": "mitigation_buff", "stage": "active_defense", "amount_bp": 8000, "duration": 3, "cost_fill": 0 } ],
"cooldown": 15,
"cost_override": { "fill": 0, "fatigue": 2 }
}
],
"stat_blocks": {
"duelist": {
"name": "Duelist",
"team": 0,
"level": 10,
"max_vigor": 150,
"regen_idle_per_sec": 9,
"regen_combat_per_sec": 5,
"armor_bp": 2500,
"competency": { "martial": 50, "restoration": 30 },
"loadout": ["auto_attack", "quick_jab", "heavy_strike", "second_wind", "guard"]
},
"battlemage": {
"name": "Battlemage",
"team": 0,
"level": 10,
"max_vigor": 100,
"regen_idle_per_sec": 12,
"regen_combat_per_sec": 7,
"armor_bp": 500,
"competency": { "invocation": 50, "affliction": 40, "restoration": 25, "martial": 15 },
"loadout": ["auto_attack", "fireball", "curse_of_weariness", "second_wind", "guard"]
},
"golem": {
"name": "Golem",
"team": 1,
"level": 10,
"max_vigor": 260,
"regen_idle_per_sec": 6,
"regen_combat_per_sec": 3,
"armor_bp": 3500,
"competency": { "martial": 40 },
"loadout": ["auto_attack", "heavy_strike"]
},
"rat": {
"name": "Dire Rat",
"team": 1,
"level": 8,
"max_vigor": 45,
"regen_idle_per_sec": 16,
"regen_combat_per_sec": 9,
"armor_bp": 0,
"competency": { "martial": 20 },
"loadout": ["auto_attack", "quick_jab"]
},
"skeleton": {
"name": "Skeleton Knight",
"team": 1,
"level": 10,
"max_vigor": 90,
"regen_idle_per_sec": 8,
"regen_combat_per_sec": 4,
"armor_bp": 1000,
"competency": { "martial": 35 },
"loadout": ["auto_attack", "quick_jab", "heavy_strike"]
},
"troll": {
"name": "Cave Troll",
"team": 1,
"level": 10,
"max_vigor": 190,
"regen_idle_per_sec": 8,
"regen_combat_per_sec": 4,
"armor_bp": 2000,
"competency": { "martial": 30, "restoration": 20 },
"loadout": ["auto_attack", "heavy_strike", "second_wind"]
},
"wraith": {
"name": "Wraith",
"team": 1,
"level": 10,
"max_vigor": 110,
"regen_idle_per_sec": 12,
"regen_combat_per_sec": 7,
"armor_bp": 500,
"competency": { "affliction": 45, "martial": 15 },
"loadout": ["auto_attack", "curse_of_weariness", "enervate", "venom"]
}
}
}