A real-time consumer of combat-core (the front-end the spec designs for, §14): pick a foe, manage your single Vigor pool, try not to spend your survival. - combat-core: a HumanController (input-driven, spec §5) behind a shared intent cell, plus Encounter::tick_once / drain_events so a wall-clock front-end can drive the engine a tick at a time (§10). Trivial abilities (difficulty 0, e.g. auto-attack) no longer fizzle. Engine unchanged otherwise; 48 tests still green. - combat-wasm: a wasm-bindgen Game bridge — tick-stepping, JSON world state, the player's ability list, and a flavorful MonsterAI that uses each monster's signature kit (the wraith curses your ceiling) on a coin-flip so big hits space into a readable duel. Own workspace member, kept out of the default host build. - combat-web: an atmospheric battler. The signature dual Vigor bar shows fill, the regen-headroom ceiling, the ceiling marker, and the dark cracked fatigue zone in one bar — the squeeze made legible. Floating damage, cast telegraphs, combat log, result overlay. Verified end-to-end in a real browser. - tools/portraits.py: stdlib text-to-image client reusing the proven LAN Z-Image stack (minus the depth ControlNet) to render the bestiary. 7 portraits committed. - Added skeleton / troll / wraith monster stat blocks; bumped the duelist's durability so fights are readable (even-con ~30s) and active play beats every monster while passive auto-attacking loses the hard ones. Re-validated the sim: the skill gradient and anti-turtle guardrail still hold (auto ~1% → skill100 34% at even con). README findings updated to match. Verified live: roster screen, win/lose, the dual bars, telegraphed casts, and a 3s active-play victory over the Skeleton Knight. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
220 lines
5.9 KiB
JSON
220 lines
5.9 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,
|
|
"damage_variance_bp": 1500,
|
|
|
|
"regen": {
|
|
"dazed_regen_bp": 5000,
|
|
"floor_per_sec": 1
|
|
},
|
|
|
|
"stagger": {
|
|
"daze_threshold_bp": 10000,
|
|
"unconscious_threshold_bp": 15000,
|
|
"daze_duration": 8,
|
|
"revive_daze": 12,
|
|
"fatigue_ratio_bp": 2000
|
|
},
|
|
|
|
"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": 2000,
|
|
"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": 800, "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": 1700, "cost_fill": 6 } ],
|
|
"cooldown": 6
|
|
},
|
|
{
|
|
"id": "heavy_strike",
|
|
"name": "Heavy Strike",
|
|
"school": "martial",
|
|
"delivery": "melee",
|
|
"effects": [ { "kind": "fill_damage", "magnitude_bp": 4500, "cost_fill": 26 } ],
|
|
"cooldown": 22
|
|
},
|
|
{
|
|
"id": "fireball",
|
|
"name": "Fireball",
|
|
"school": "invocation",
|
|
"delivery": "projectile",
|
|
"effects": [ { "kind": "fill_damage", "magnitude_bp": 3500, "cost_fill": 24 } ],
|
|
"potency_bp": 10000,
|
|
"cast_time": 12,
|
|
"cooldown": 20
|
|
},
|
|
{
|
|
"id": "curse_of_weariness",
|
|
"name": "Curse of Weariness",
|
|
"school": "affliction",
|
|
"delivery": "projectile",
|
|
"effects": [ { "kind": "ceiling_damage", "magnitude_bp": 1400, "cost_fill": 16 } ],
|
|
"cast_time": 8,
|
|
"cooldown": 40
|
|
},
|
|
{
|
|
"id": "venom",
|
|
"name": "Venom",
|
|
"school": "affliction",
|
|
"delivery": "touch",
|
|
"effects": [ { "kind": "dot", "per_tick_bp": 250, "duration": 20, "cost_fill": 12 } ],
|
|
"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": 3000, "cost_fill": 3 } ],
|
|
"cooldown": 45
|
|
},
|
|
{
|
|
"id": "mend",
|
|
"name": "Mend",
|
|
"school": "restoration",
|
|
"delivery": "self_cast",
|
|
"effects": [ { "kind": "hot", "per_tick_bp": 200, "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": 12,
|
|
"regen_combat_per_sec": 7,
|
|
"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": 11,
|
|
"max_vigor": 220,
|
|
"regen_idle_per_sec": 20,
|
|
"regen_combat_per_sec": 12,
|
|
"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"]
|
|
}
|
|
}
|
|
}
|