1949 lines
71 KiB
JSON
1949 lines
71 KiB
JSON
{
|
||
"_comment": "COMBAT — all combat tuning, one file. Two contexts share the numbers: the standalone range (combat.html / the dev server's /combat — the WASD arena, js/combat/CombatScene.js) and the MAIN GAME (index.html?combat — js/combat/CombatSystem.js drops the pack into the normal game: click-to-fly, the deck swaps for the combat bar, the ship auto-fires at the nearest hostile). Pure rules (armor damage, orbit steering) are js/combat/Combat.js (tested: dev/combat.test.mjs).",
|
||
"enabled": true,
|
||
"game": {
|
||
"_comment": "COMBAT IN THE MAIN GAME (the ?combat boot). spawnCount = hostiles that appear around the ship when the player starts. SPAWNING: hostiles materialize OFF-SCREEN — the furthest of the (spawnRadius band, the camera's visible edge + spawnLead) — and FLY IN on their own chase AI (standoff ring, then lances): nothing pops into view mid-fight. spawnRadius px = the minimum materialization band around the ship; spawnLead px = how far PAST the camera's visible edge they materialize (the fly-in distance — bigger = a longer stream across the screen). autoFire = the ship's guns — while a hostile is within range the ship fires at the NEAREST one (the mouse steers, the cannon tracks; the cannon's own numbers are player.weapon below). ON-SCREEN RULE (js/combat/CombatSystem.js): a hostile must be VISUALLY ON SCREEN (at least partly in the camera's view) to fire or be fired upon — off-screen craft hold their lances, the cannon won't engage them, and their tracers pass straight through; they join the fight the moment they fly into view.",
|
||
"spawnCount": 3,
|
||
"spawnRadius": 340,
|
||
"spawnLead": 140,
|
||
"autoFire": {
|
||
"enabled": true,
|
||
"range": 800
|
||
}
|
||
},
|
||
"energy": {
|
||
"_comment": "The player's ENERGY — the skill resource (the combat bar's right ORB — the main game's and the range's). The eight seats (keys 1-8) spend it: a cast takes its cost off the orb and that skill's own recharge runs from there. regen = energy/s recovered while the ship is up — 6/s buys a 60-energy Ramming Charge back in 10s, so a seat is a TEMPO tool rather than one shot per fight; 0 puts the orb back on static.",
|
||
"max": 100,
|
||
"regen": 6
|
||
},
|
||
"player": {
|
||
"_comment": "The combat feel of YOUR ship in the range (the art, hull stats, and ship name still come from data/ship.json). WASD/arrows fly, SPACE/J fires (hold = auto-fire). The flight feel (js/combat/Combat.js → handleFeel, tested): thrust px/s^2 at full throttle, maxSpeed px/s cap, drag 1/s (exponential coast decay — the range OVERRIDES ship.drag with this, so data/combat.json is the one tuning surface), brake = the S/Down reverse-throttle as a fraction of FULL thrust (instant — a responsive stop). rotSpeed rad/s = the SUSTAINED keyboard turn rate; rotApproach 1/s = how fast the turn rate builds (a tap is a nudge, a hold is a full spin); thrustRamp 1/s = how fast the throttle reaches full on W; thrustDecay 1/s = how fast the throttle cuts when W lets go (no lingering kick). weapon.* = the player's cannon: cooldown s between shots, bulletSpeed px/s, damage per hit, spread rad of aim jitter, bulletLife s before a shot fizzles, color/size the tracer's glow; boltLength/boltWidth px + muzzle px are the laser's VISUALS (the bolt's length × thickness and the muzzle flash's size — js/combat/Bullet.js + Fx.muzzle).",
|
||
"thrust": 2000,
|
||
"maxSpeed": 560,
|
||
"drag": 1.8,
|
||
"rotSpeed": 6.4,
|
||
"rotApproach": 20,
|
||
"thrustRamp": 18,
|
||
"thrustDecay": 30,
|
||
"brake": 0.9,
|
||
"weapon": {
|
||
"key": "space",
|
||
"cooldown": 0.16,
|
||
"bulletSpeed": 680,
|
||
"damage": 10,
|
||
"spread": 0.03,
|
||
"bulletLife": 1.15,
|
||
"color": "#00e5ff",
|
||
"size": 16,
|
||
"boltLength": 46,
|
||
"boltWidth": 10,
|
||
"muzzle": 46
|
||
}
|
||
},
|
||
"enemy": {
|
||
"_comment": "The hostile craft. texture = spritesheet of frameWidth×frameHeight frames (frame picks which one — frame 0 = the red triple-boost fighter, art facing north, filling its frame; the sheet is 10×10 of 128px frames). size = hull length in world px (render = size × the art's footprint), hitRadius = the collision circle in world px (bullets + rams). count = hostiles in the arena at once. speed px/s + turn (1/s velocity-approach rate) = the chase feel. orbitRadius px = the standoff ring they keep around the player; orbitSpeed rad/s = how fast they circle it (direction alternates per craft). hull/shields = their armor (shields absorb first — js/combat/Combat.js). xp = the EXP a kill of one is worth (the XP bank's currency — js/combat/XpManager.js; the curve is data/progression.json, and the base value is 10, so five kills of the base craft = level 1). weapon.* = their lance: range px, cooldown s, bulletSpeed px/s, damage, spread rad, bulletLife s; boltLength/boltWidth px + muzzle px are the lance's VISUALS (the bolt's length × thickness and the muzzle flash's size — js/combat/Bullet.js + Fx.muzzle). ramDamage = hull damage a COLLISION deals you; ramDamageSelf = what the collision deals THEM (per hit, ramGrace s apart). respawn = the pack reforms (respawnDelay s) after a kill, so the range stays stocked while you tune.",
|
||
"texture": "assets/images/ships-enemy.png",
|
||
"frameWidth": 128,
|
||
"frameHeight": 128,
|
||
"frame": 0,
|
||
"artFacing": "north",
|
||
"size": 54,
|
||
"hitRadius": 20,
|
||
"count": 3,
|
||
"speed": 165,
|
||
"turn": 3.2,
|
||
"orbitRadius": 300,
|
||
"orbitSpeed": 0.6,
|
||
"hull": 60,
|
||
"shields": 0,
|
||
"xp": 10,
|
||
"weapon": {
|
||
"range": 560,
|
||
"cooldown": 1.4,
|
||
"bulletSpeed": 330,
|
||
"damage": 8,
|
||
"spread": 0.08,
|
||
"bulletLife": 2.4,
|
||
"color": "#ff2d6f",
|
||
"size": 18,
|
||
"boltLength": 54,
|
||
"boltWidth": 12,
|
||
"muzzle": 50
|
||
},
|
||
"ramDamage": 10,
|
||
"ramDamageSelf": 22,
|
||
"ramGrace": 0.8,
|
||
"respawn": true,
|
||
"respawnDelay": 2.5
|
||
},
|
||
"enemyTypes": {
|
||
"_comment": "The hostile roster a WAVE (combat.waves.scripts) can mix — js/combat/EnemyTypes.js deep-merges a type's overrides onto the base `enemy` above, so a type declares only what differs. `raider` is the base craft under a named id (frame 0). Frames 1–7 are the SEVEN new hostiles, each a distinct craft with a distinct WEAPON PATTERN (weapon.pattern) and a MODERATE difficulty step-up: hull/shields (armor to soak) climb monotonically bomber → warden, so each is a little harder to defeat than the last. PATTERNS (js/combat/Weapons.js): 'lance' (the default — one aimed bolt, the base behavior), 'spray' (a FAN of `bolts` spread over `fan` rad), 'burst' (a TIGHT `bolts`-shot stinger — staggered speeds so it fans over distance), 'rapid' (single bolts on a short cooldown — a machine gun), 'ring' (`bolts` fired RADIAL around the craft — break the orbit), 'missile' (`missiles` HEAT-SEEKING shots that home with a CAPPED turn-rate (missileTurn rad/s) so the player can out-turn them), 'combo' (a heavy lance + a missile, the elite). Add a craft here (a new `frame` or its own `texture` + stat overrides) and name it in a wave's roster; EnemyTypes.enemySheetList() is the preload seam for its textures. Each type's `xp` = what a kill of it is worth (the rule of thumb is (hull+shields)/5, rounded, so a tougher craft is worth more of the XP bank).",
|
||
"raider": {
|
||
"$comment": "The PIRATE (data/quests.json → pirate_problems — the ambush wave combat.waves.scripts.pirate_ambush is all raiders). comms = the pirate's combat VOICE (the rolls live in js/combat/CombatSystem.js; the comms themselves are data/comms.json's pirate-attack / pirate-defeat — the box's clip + one random speech clip + the message). attack.onScreenChance = the chance a pirate shows an ATTACK comm the first time it flies ON SCREEN (0.5 = 50%); attack.spawnChance = the chance a NEW pirate sent into a rolling wave (the opening burst, each kill's replacement) ATTACK-comms on arrival (0.2 = 20%) — a hit marks the ship, so its later on-screen roll doesn't stack (a pirate never double-comms); defeat.chance = the chance a downed pirate shows a DEFEAT comm (0.2 = 20%).",
|
||
"frame": 0,
|
||
"comms": {
|
||
"attack": {
|
||
"onScreenChance": 0.5,
|
||
"spawnChance": 0.2,
|
||
"trigger": "pirateAttack"
|
||
},
|
||
"defeat": {
|
||
"chance": 0.2,
|
||
"trigger": "pirateDefeat"
|
||
}
|
||
}
|
||
},
|
||
"bomber": {
|
||
"_comment": "frame 1 — the LONG LANCE: slow, sits back, one heavy bolt on a long fuse. The easy end of the new ramp.",
|
||
"frame": 1,
|
||
"speed": 105,
|
||
"turn": 2.2,
|
||
"hull": 80,
|
||
"shields": 10,
|
||
"xp": 18,
|
||
"weapon": {
|
||
"pattern": "lance",
|
||
"range": 760,
|
||
"cooldown": 2.0,
|
||
"bulletSpeed": 250,
|
||
"damage": 10,
|
||
"spread": 0.04,
|
||
"bulletLife": 3.0
|
||
}
|
||
},
|
||
"spitter": {
|
||
"_comment": "frame 2 — the SPRAY: a fan of cheap bolts covering an arc; low damage per bolt, wide cone.",
|
||
"frame": 2,
|
||
"speed": 130,
|
||
"turn": 3.0,
|
||
"hull": 90,
|
||
"shields": 20,
|
||
"xp": 22,
|
||
"weapon": {
|
||
"pattern": "spray",
|
||
"range": 480,
|
||
"cooldown": 1.9,
|
||
"bulletSpeed": 300,
|
||
"damage": 11,
|
||
"spread": 0.03,
|
||
"bulletLife": 2.2,
|
||
"bolts": 5,
|
||
"fan": 1.1,
|
||
"color": "#ff7a3d"
|
||
}
|
||
},
|
||
"stinger": {
|
||
"_comment": "frame 3 — the BURST: a fast, tight 3-shot stinger at close range (staggered speeds fan it out over distance).",
|
||
"frame": 3,
|
||
"speed": 185,
|
||
"turn": 4.0,
|
||
"hull": 100,
|
||
"shields": 25,
|
||
"xp": 25,
|
||
"weapon": {
|
||
"pattern": "burst",
|
||
"range": 400,
|
||
"cooldown": 1.5,
|
||
"bulletSpeed": 420,
|
||
"damage": 12,
|
||
"spread": 0.02,
|
||
"bulletLife": 1.8,
|
||
"bolts": 3,
|
||
"burstFan": 0.1,
|
||
"color": "#ffe14d"
|
||
}
|
||
},
|
||
"hunter": {
|
||
"_comment": "frame 4 — the HUNTER: launches a HEAT-SEEKING missile that homes with a capped turn-rate (missileTurn) — dodgeable by out-turning it.",
|
||
"frame": 4,
|
||
"speed": 150,
|
||
"turn": 3.2,
|
||
"hull": 110,
|
||
"shields": 30,
|
||
"xp": 28,
|
||
"weapon": {
|
||
"pattern": "missile",
|
||
"range": 620,
|
||
"cooldown": 2.4,
|
||
"missiles": 1,
|
||
"missileSpeed": 190,
|
||
"missileTurn": 2.2,
|
||
"missileDamage": 13,
|
||
"missileLife": 4.0,
|
||
"missileColor": "#ff5d2d",
|
||
"missileSize": 18,
|
||
"color": "#ff5d2d"
|
||
}
|
||
},
|
||
"gunner": {
|
||
"_comment": "frame 5 — the GUNNER: rapid single bolts on a short cooldown (a machine gun); solid per-bolt punch at a high rate.",
|
||
"frame": 5,
|
||
"speed": 190,
|
||
"turn": 4.2,
|
||
"hull": 120,
|
||
"shields": 40,
|
||
"xp": 32,
|
||
"weapon": {
|
||
"pattern": "rapid",
|
||
"range": 440,
|
||
"cooldown": 0.5,
|
||
"bulletSpeed": 380,
|
||
"damage": 14,
|
||
"spread": 0.06,
|
||
"bulletLife": 1.6,
|
||
"color": "#c8ff3d"
|
||
}
|
||
},
|
||
"barrage": {
|
||
"_comment": "frame 6 — the BARRAGE: a RADIAL ring of bolts (break the orbit); slow and armored, pokes a hole in the ring.",
|
||
"frame": 6,
|
||
"speed": 100,
|
||
"turn": 2.0,
|
||
"hull": 135,
|
||
"shields": 50,
|
||
"xp": 37,
|
||
"weapon": {
|
||
"pattern": "ring",
|
||
"range": 360,
|
||
"cooldown": 2.2,
|
||
"bulletSpeed": 260,
|
||
"damage": 15,
|
||
"spread": 0.0,
|
||
"bulletLife": 2.6,
|
||
"bolts": 8,
|
||
"color": "#b44dff"
|
||
}
|
||
},
|
||
"warden": {
|
||
"_comment": "frame 7 — the WARDEN (elite): a heavy lance PLUS a homing missile on every volley; the hardest of the seven.",
|
||
"frame": 7,
|
||
"speed": 90,
|
||
"turn": 1.8,
|
||
"hull": 150,
|
||
"shields": 60,
|
||
"xp": 42,
|
||
"weapon": {
|
||
"pattern": "combo",
|
||
"range": 700,
|
||
"cooldown": 2.6,
|
||
"bulletSpeed": 240,
|
||
"damage": 18,
|
||
"spread": 0.02,
|
||
"bulletLife": 3.2,
|
||
"size": 20,
|
||
"boltLength": 64,
|
||
"boltWidth": 14,
|
||
"missiles": 1,
|
||
"missileSpeed": 170,
|
||
"missileTurn": 2.4,
|
||
"missileDamage": 20,
|
||
"missileLife": 4.2,
|
||
"missileColor": "#ff2d6f",
|
||
"missileSize": 20,
|
||
"color": "#ff2d6f"
|
||
}
|
||
},
|
||
"$factions_note": "FRAMES 30–37 = THE APEX, frames 40–47 = THE MINDWEAVE — the two FACTION rosters (the ?combat=apex / ?combat=mindweave duel arena — combat.factions below names them). BALANCE RULE (the user's numbers, vs the PIRATE raider — 60 armor, 8 bolt damage): BOTH FACTIONS = 2× DAMAGE (16 = 8 × 2, every bolt/seeker/shell) and 3.5× ARMOR (175 hull + 35 shields = 210 = 60 × 3.5). Cooldowns 0.6–3.0 s (pattern character, not power), ramDamage 13 (10 × 1.3). xp = (hull+shields)/5 = 42 (the roster's rule of thumb). DIFFERENTIATION comes from the WEAPON PATTERN (weapon.pattern — the shared dispatch, js/combat/Weapons.js) + the ABILITY LAYER (abilities[].id — js/combat/EnemyAbilities.js: cloaked_ambush / flank_strafe for the Apex's predatory kit, blink_reposition / illusion_decoy / telekinetic_wave for the Mindweave's psychic kit). Each fighter is its own craft; the faction's SHARED skills are the kit's common entries (the Apex all carry the cloak and most the flank; the Mindweave all carry the blink or the phantoms and several the tides) at per-ship numbers. LEVEL RAMP: the 2×/3.5× above are the LEVEL-35 numbers — combat.factionScaling scales the damage (1.2× at level 10) and armor (2.0× at level 10) DOWN with the player's level below 35 (ramDamage and xp stay flat).",
|
||
"apex_raptor": {
|
||
"$comment": "APEX 1 (frame 30) — RAPTOR, the forward gun: a 5-bolt SWEEP out the nose (heavy forward firepower) on a medium beat; the flank's cut gets it behind the player every 10 s or so so the sweep lands on the back.",
|
||
"name": "Raptor",
|
||
"frame": 30,
|
||
"speed": 175,
|
||
"turn": 3.6,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "sweep",
|
||
"range": 520,
|
||
"cooldown": 1.2,
|
||
"bulletSpeed": 360,
|
||
"damage": 16,
|
||
"spread": 0.03,
|
||
"bulletLife": 2.0,
|
||
"bolts": 5,
|
||
"fan": 1.0,
|
||
"color": "#ff4d2d"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "flank_strafe",
|
||
"period": 10,
|
||
"radius": 210,
|
||
"color": "#ff4d2d"
|
||
}
|
||
]
|
||
},
|
||
"apex_mauler": {
|
||
"$comment": "APEX 2 (frame 31) — MAULER, the heavy hitter: a BIG slow 3-bolt BURST (16 damage each, wide visual) — slow, armored, deliberate; comes out of CLOAK to swing it (the shared ambush).",
|
||
"name": "Mauler",
|
||
"frame": 31,
|
||
"speed": 120,
|
||
"turn": 2.4,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "burst",
|
||
"range": 560,
|
||
"cooldown": 1.6,
|
||
"bulletSpeed": 240,
|
||
"damage": 16,
|
||
"spread": 0.02,
|
||
"bulletLife": 2.6,
|
||
"size": 22,
|
||
"boltLength": 70,
|
||
"boltWidth": 16,
|
||
"bolts": 3,
|
||
"burstFan": 0.12,
|
||
"color": "#ff2d4d"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "cloaked_ambush",
|
||
"cloakMs": 3200,
|
||
"color": "#ff2d4d"
|
||
}
|
||
]
|
||
},
|
||
"apex_stalker": {
|
||
"$comment": "APEX 3 (frame 32) — STALKER, the double-cloak (the signature): cloaks on contact, strikes, then DROPS BACK INTO CLOAK on its own clock (recloakCooldown/recloakMs) — strike, vanish, strike again. Fast, clean lance.",
|
||
"name": "Stalker",
|
||
"frame": 32,
|
||
"speed": 185,
|
||
"turn": 4.0,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "lance",
|
||
"range": 520,
|
||
"cooldown": 1.1,
|
||
"bulletSpeed": 380,
|
||
"damage": 16,
|
||
"spread": 0.04,
|
||
"bulletLife": 2.0,
|
||
"color": "#ff8a2d"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "cloaked_ambush",
|
||
"cloakMs": 3600,
|
||
"recloakCooldown": 16,
|
||
"recloakMs": 2800,
|
||
"color": "#ff8a2d"
|
||
}
|
||
]
|
||
},
|
||
"apex_screamer": {
|
||
"$comment": "APEX 4 (frame 33) — SCREAMER, the wide fan: a 9-bolt SPRAY across 1.6 rad (dodge the core, not the wall); the flank's cut re-positions it before each scream.",
|
||
"name": "Screamer",
|
||
"frame": 33,
|
||
"speed": 150,
|
||
"turn": 3.0,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "spray",
|
||
"range": 480,
|
||
"cooldown": 1.4,
|
||
"bulletSpeed": 340,
|
||
"damage": 16,
|
||
"spread": 0.02,
|
||
"bulletLife": 1.8,
|
||
"bolts": 9,
|
||
"fan": 1.6,
|
||
"color": "#ffd84d"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "flank_strafe",
|
||
"period": 9,
|
||
"radius": 200,
|
||
"color": "#ffd84d"
|
||
}
|
||
]
|
||
},
|
||
"apex_reaver": {
|
||
"$comment": "APEX 5 (frame 34) — REAVER, the gun: RAPID bolts at 0.6 s (the pack's highest DPS — out-turn it) with the double-cloak signature (a gun you can't see).",
|
||
"name": "Reaver",
|
||
"frame": 34,
|
||
"speed": 190,
|
||
"turn": 4.4,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "rapid",
|
||
"range": 460,
|
||
"cooldown": 0.6,
|
||
"bulletSpeed": 400,
|
||
"damage": 16,
|
||
"spread": 0.06,
|
||
"bulletLife": 1.5,
|
||
"color": "#ff6a2d"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "cloaked_ambush",
|
||
"cloakMs": 2800,
|
||
"recloakCooldown": 18,
|
||
"recloakMs": 2200,
|
||
"color": "#ff6a2d"
|
||
}
|
||
]
|
||
},
|
||
"apex_fang": {
|
||
"$comment": "APEX 6 (frame 35) — FANG, the stinger: a TIGHT 4-shot BURST (staggered speeds open it over distance — the close-range snap) at 16 damage each; cuts behind before it snaps.",
|
||
"name": "Fang",
|
||
"frame": 35,
|
||
"speed": 180,
|
||
"turn": 4.2,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "burst",
|
||
"range": 420,
|
||
"cooldown": 1.5,
|
||
"bulletSpeed": 420,
|
||
"damage": 16,
|
||
"spread": 0.02,
|
||
"bulletLife": 1.6,
|
||
"bolts": 4,
|
||
"burstFan": 0.14,
|
||
"color": "#ff3d2d"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "flank_strafe",
|
||
"period": 12,
|
||
"radius": 230,
|
||
"color": "#ff3d2d"
|
||
}
|
||
]
|
||
},
|
||
"apex_tyrant": {
|
||
"$comment": "APEX 7 (frame 36) — TYRANT, the swarm: TWO heat-seeking missiles per volley (out-turn both) on a long beat; cloaks in, then the seekers come — the flank's cut keeps it off your nose.",
|
||
"name": "Tyrant",
|
||
"frame": 36,
|
||
"speed": 150,
|
||
"turn": 3.2,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "missile",
|
||
"range": 620,
|
||
"cooldown": 2.2,
|
||
"missiles": 2,
|
||
"missileSpeed": 190,
|
||
"missileTurn": 2.2,
|
||
"missileDamage": 16,
|
||
"missileLife": 4.0,
|
||
"missileColor": "#ff5d2d",
|
||
"missileSize": 18,
|
||
"color": "#ff5d2d"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "cloaked_ambush",
|
||
"cloakMs": 3000,
|
||
"color": "#ff5d2d"
|
||
},
|
||
{
|
||
"id": "flank_strafe",
|
||
"period": 13,
|
||
"radius": 220,
|
||
"color": "#ff5d2d"
|
||
}
|
||
]
|
||
},
|
||
"apex_alpha": {
|
||
"$comment": "APEX 8 (frame 37) — ALPHA, the pack's top predator (the elite): a COMBO of heavy lance (16) + three seekers every 2.4 s, the FULL kit — the longest cloak, the double-cloak's nap, and the flank's cut. 2× the pirate's damage, 3.5× its armor — the whole predatory doctrine in everything else.",
|
||
"name": "Alpha",
|
||
"frame": 37,
|
||
"speed": 130,
|
||
"turn": 2.8,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "combo",
|
||
"range": 640,
|
||
"cooldown": 2.4,
|
||
"bulletSpeed": 260,
|
||
"damage": 16,
|
||
"spread": 0.02,
|
||
"bulletLife": 2.8,
|
||
"size": 20,
|
||
"boltLength": 64,
|
||
"boltWidth": 14,
|
||
"missiles": 2,
|
||
"missileSpeed": 180,
|
||
"missileTurn": 2.3,
|
||
"missileDamage": 16,
|
||
"missileLife": 4.2,
|
||
"missileColor": "#ffb02d",
|
||
"missileSize": 20,
|
||
"color": "#ffb02d"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "cloaked_ambush",
|
||
"cloakMs": 3800,
|
||
"recloakCooldown": 20,
|
||
"recloakMs": 3000,
|
||
"color": "#ffb02d"
|
||
},
|
||
{
|
||
"id": "flank_strafe",
|
||
"period": 10,
|
||
"radius": 210,
|
||
"color": "#ffb02d"
|
||
}
|
||
]
|
||
},
|
||
"weave_mindblade": {
|
||
"$comment": "MINDWEAVE 1 (frame 40) — MINDBLADE, the spiral: a 10-bolt SPIRAL (each bolt a step further around, staggered speeds — the 1942-boss bloom) at 16 damage; BLINKS to a fresh stand (biasing behind) and leaves a phantom at the old one.",
|
||
"name": "Mindblade",
|
||
"frame": 40,
|
||
"speed": 140,
|
||
"turn": 3.0,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "spiral",
|
||
"range": 560,
|
||
"cooldown": 2.2,
|
||
"bulletSpeed": 210,
|
||
"damage": 16,
|
||
"spread": 0.01,
|
||
"bulletLife": 3.0,
|
||
"bolts": 10,
|
||
"spiralStep": 0.55,
|
||
"stagger": 0.12,
|
||
"color": "#b44dff"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "blink_reposition",
|
||
"cooldown": 12,
|
||
"color": "#b44dff"
|
||
}
|
||
]
|
||
},
|
||
"weave_telekine": {
|
||
"$comment": "MINDWEAVE 2 (frame 41) — TELEKINE, the tide: a rapid mind-cannon (16 per 0.6 s) backed by a PSYCHIC TIDE — every 13 s a wave blooms and SHOVES the player out of its line (the dodge window: be far or be moved).",
|
||
"name": "Telekine",
|
||
"frame": 41,
|
||
"speed": 175,
|
||
"turn": 3.8,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "rapid",
|
||
"range": 500,
|
||
"cooldown": 0.6,
|
||
"bulletSpeed": 400,
|
||
"damage": 16,
|
||
"spread": 0.05,
|
||
"bulletLife": 1.6,
|
||
"color": "#4dd7ff"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "telekinetic_wave",
|
||
"cooldown": 13,
|
||
"dir": "push",
|
||
"radius": 290,
|
||
"power": 280,
|
||
"color": "#4dd7ff"
|
||
},
|
||
{
|
||
"id": "blink_reposition",
|
||
"cooldown": 17,
|
||
"color": "#4dd7ff"
|
||
}
|
||
]
|
||
},
|
||
"weave_phantasm": {
|
||
"$comment": "MINDWEAVE 3 (frame 42) — PHANTASM, the court: a blossoming VOLLEY (8 staggered bolts — the wall) with the FULL illusion kit — projects a COURT of three phantoms (the auto-fire's pool chases them) and blinks among them.",
|
||
"name": "Phantasm",
|
||
"frame": 42,
|
||
"speed": 150,
|
||
"turn": 3.2,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "volley",
|
||
"range": 540,
|
||
"cooldown": 1.6,
|
||
"bulletSpeed": 260,
|
||
"damage": 16,
|
||
"spread": 0.01,
|
||
"bulletLife": 2.6,
|
||
"bolts": 8,
|
||
"fan": 1.5,
|
||
"stagger": 0.4,
|
||
"color": "#ff4dc8"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "illusion_decoy",
|
||
"cooldown": 15,
|
||
"count": 3,
|
||
"radius": 180,
|
||
"lifeMs": 2800,
|
||
"color": "#ff4dc8"
|
||
},
|
||
{
|
||
"id": "blink_reposition",
|
||
"cooldown": 16,
|
||
"decoyMs": 2000,
|
||
"color": "#ff4dc8"
|
||
}
|
||
]
|
||
},
|
||
"weave_weaver": {
|
||
"$comment": "MINDWEAVE 4 (frame 43) — WEAVER, the threads: a 9-bolt SWEEP out its own heading (its threads fly where it FACES — aim your counter off its nose) with the most frequent BLINK of the faction (11 s — it is rarely where you last saw it).",
|
||
"name": "Weaver",
|
||
"frame": 43,
|
||
"speed": 160,
|
||
"turn": 3.6,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "sweep",
|
||
"range": 520,
|
||
"cooldown": 1.3,
|
||
"bulletSpeed": 300,
|
||
"damage": 16,
|
||
"spread": 0.02,
|
||
"bulletLife": 2.2,
|
||
"bolts": 9,
|
||
"fan": 1.4,
|
||
"color": "#4dffc8"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "blink_reposition",
|
||
"cooldown": 11,
|
||
"decoyMs": 2200,
|
||
"color": "#4dffc8"
|
||
}
|
||
]
|
||
},
|
||
"weave_lure": {
|
||
"$comment": "MINDWEAVE 5 (frame 44) — LURE, the net: a RADIAL ring of ten (break the orbit) with the phantoms and a PULL tide — it drags the player IN toward the ring (the push tide's inverse: its net closes on you).",
|
||
"name": "Lure",
|
||
"frame": 44,
|
||
"speed": 130,
|
||
"turn": 2.6,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "ring",
|
||
"range": 460,
|
||
"cooldown": 2.3,
|
||
"bulletSpeed": 240,
|
||
"damage": 16,
|
||
"spread": 0.0,
|
||
"bulletLife": 2.6,
|
||
"bolts": 10,
|
||
"color": "#4da6ff"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "illusion_decoy",
|
||
"cooldown": 14,
|
||
"count": 2,
|
||
"radius": 200,
|
||
"lifeMs": 3000,
|
||
"color": "#4da6ff"
|
||
},
|
||
{
|
||
"id": "telekinetic_wave",
|
||
"cooldown": 16,
|
||
"dir": "pull",
|
||
"radius": 300,
|
||
"power": 240,
|
||
"color": "#4da6ff"
|
||
}
|
||
]
|
||
},
|
||
"weave_dreamer": {
|
||
"$comment": "MINDWEAVE 6 (frame 45) — DREAMSHELL, the shells: one LARGE-RADIUS slow shell (44 px glow, 16 damage, a short fuse — the big slow rock) per volley, backed by a hard PUSH tide (300 px/s — the strongest shove of the faction).",
|
||
"name": "Dreamshell",
|
||
"frame": 45,
|
||
"speed": 110,
|
||
"turn": 2.2,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "shell",
|
||
"range": 600,
|
||
"cooldown": 2.6,
|
||
"damage": 16,
|
||
"shells": 1,
|
||
"shellDamage": 16,
|
||
"shellSize": 44,
|
||
"shellSpeed": 140,
|
||
"shellLife": 2.4,
|
||
"color": "#8a4dff"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "telekinetic_wave",
|
||
"cooldown": 14,
|
||
"dir": "push",
|
||
"radius": 300,
|
||
"power": 300,
|
||
"color": "#8a4dff"
|
||
},
|
||
{
|
||
"id": "illusion_decoy",
|
||
"cooldown": 17,
|
||
"count": 2,
|
||
"radius": 200,
|
||
"lifeMs": 2600,
|
||
"color": "#8a4dff"
|
||
}
|
||
]
|
||
},
|
||
"weave_serpent": {
|
||
"$comment": "MINDWEAVE 7 (frame 46) — SERPENTMIND, the coil: a LONG 14-bolt spiral (a tighter step, slower bolts — a COIL that hangs around longer than Mindblade's) with the blink and a PULL tide (it repositions, then reeled you in).",
|
||
"name": "Serpentmind",
|
||
"frame": 46,
|
||
"speed": 145,
|
||
"turn": 3.0,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "spiral",
|
||
"range": 580,
|
||
"cooldown": 2.4,
|
||
"bulletSpeed": 190,
|
||
"damage": 16,
|
||
"spread": 0.01,
|
||
"bulletLife": 3.2,
|
||
"bolts": 14,
|
||
"spiralStep": 0.42,
|
||
"stagger": 0.1,
|
||
"color": "#c84dff"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "blink_reposition",
|
||
"cooldown": 13,
|
||
"color": "#c84dff"
|
||
},
|
||
{
|
||
"id": "telekinetic_wave",
|
||
"cooldown": 18,
|
||
"dir": "pull",
|
||
"radius": 310,
|
||
"power": 260,
|
||
"color": "#c84dff"
|
||
}
|
||
]
|
||
},
|
||
"weave_overmind": {
|
||
"$comment": "MINDWEAVE 8 (frame 47) — OVERMIND, the faction's apex (the elite): a TRIPLE SHELL volley (three large orbs per turn — the slowest, heaviest barrage of the roster) with the FULL psychic kit — the most frequent blink, a standing phantom court, and the strongest PULL tide (300). Control every axis of the fight at 2× the pirate's offense and 3.5× its armor.",
|
||
"name": "Overmind",
|
||
"frame": 47,
|
||
"speed": 100,
|
||
"turn": 2.0,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "shell",
|
||
"range": 620,
|
||
"cooldown": 3.0,
|
||
"damage": 16,
|
||
"shells": 3,
|
||
"shellDamage": 16,
|
||
"shellSize": 44,
|
||
"shellSpeed": 150,
|
||
"shellLife": 2.6,
|
||
"color": "#e04dff"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "blink_reposition",
|
||
"cooldown": 10,
|
||
"decoyMs": 2200,
|
||
"color": "#e04dff"
|
||
},
|
||
{
|
||
"id": "illusion_decoy",
|
||
"cooldown": 13,
|
||
"count": 2,
|
||
"radius": 190,
|
||
"lifeMs": 2800,
|
||
"color": "#e04dff"
|
||
},
|
||
{
|
||
"id": "telekinetic_wave",
|
||
"cooldown": 15,
|
||
"dir": "pull",
|
||
"radius": 320,
|
||
"power": 300,
|
||
"color": "#e04dff"
|
||
}
|
||
]
|
||
},
|
||
"_zenith_note": "FRAMES 50–57 = THE ZENITH — the third FACTION roster (the ?combat=zenith duel arena). TWO ARCHETYPES, split 4/4: FRAMES 50–53 = THE COMBO SEEKERS — a fast low-damage WEAPON (the gun) paired with the flank_barrage ABILITY (js/combat/EnemyAbilities.js): a salvo of HEAT-SEEKERS out of BOTH WINGS at 90° off the heading, on a SLOW capped turn-rate (missileTurn 0.7–1.3, far below the nose seekers' 2.2) so they curve wide and a committed turn beats them. FRAMES 54–57 = THE RANGED ELEMENTALS — the shock_dart ABILITY: fast darts that detonate near the target and bloom an ELECTRIFIED ZONE (startRadius→maxRadius over growMs, finite lifeMs, fadeMs), ticking damage (tickDamage/tickMs) on anything inside — a growing, dodgeable field, not a one-shot. BALANCE (the same rule as the $factions_note above, vs the PIRATE raider — 60 armor, 8 bolt damage): 2× DAMAGE (16 every bolt/seeker/shell; the shock zones tick 3–5 per beat — a sustained, avoidable pressure, never a spike) and 3.5× ARMOR (175 hull + 35 shields = 210). xp = (hull+shields)/5 = 42. Differentiation comes from PACE AND SHAPE: the combo ships vary the gun (rapid / burst / spray / sweep) and the volley's size and seek-rate; the elemental ships vary the dart count, the zone's size, its bloom time and its tick rate. LEVEL RAMP: same as the Apex/Mindweave note — combat.factionScaling scales the damage (1.2× at level 10) and armor (2.0× at level 10) with the player's level up to these level-35 numbers; ramDamage and xp stay flat.",
|
||
"zenith_wingblade": {
|
||
"$comment": "ZENITH 1 (frame 50) — WINGBLADE, the gun-wing: a RAPID gun (0.55 s) for constant low-damage pressure, and on its own clock a SIX-SEEKER wing volley (three out of each wing at 90°, a slight fan) that curves wide toward the player — the archetype's base kit at balanced tempo.",
|
||
"name": "Wingblade",
|
||
"frame": 50,
|
||
"speed": 165,
|
||
"turn": 3.4,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "rapid",
|
||
"range": 500,
|
||
"cooldown": 0.55,
|
||
"bulletSpeed": 400,
|
||
"damage": 16,
|
||
"spread": 0.05,
|
||
"bulletLife": 1.6,
|
||
"color": "#ffd24d"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "flank_barrage",
|
||
"cooldown": 13,
|
||
"count": 6,
|
||
"missileSpeed": 230,
|
||
"missileTurn": 0.9,
|
||
"missileDamage": 16,
|
||
"missileLife": 5.5,
|
||
"missileSpread": 0.28,
|
||
"color": "#ffd24d"
|
||
}
|
||
]
|
||
},
|
||
"zenith_divefang": {
|
||
"$comment": "ZENITH 2 (frame 51) — DIVEFANG, the slow arc: a TIGHT burst stinger (three staggered bolts) for the snap at point-blank, and the roster's WIDEST seek — eight seekers on the slowest turn-rate (0.7) that peel around the arena's edge, giving the longest wind-up and the hardest to shed late.",
|
||
"name": "Divefang",
|
||
"frame": 51,
|
||
"speed": 150,
|
||
"turn": 3.0,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "burst",
|
||
"range": 480,
|
||
"cooldown": 1.15,
|
||
"bulletSpeed": 380,
|
||
"damage": 16,
|
||
"spread": 0.01,
|
||
"bulletLife": 1.8,
|
||
"bolts": 3,
|
||
"burstFan": 0.1,
|
||
"color": "#ffb84d"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "flank_barrage",
|
||
"cooldown": 15,
|
||
"count": 8,
|
||
"missileSpeed": 200,
|
||
"missileTurn": 0.7,
|
||
"missileDamage": 16,
|
||
"missileLife": 6.5,
|
||
"missileSpread": 0.3,
|
||
"color": "#ffb84d"
|
||
}
|
||
]
|
||
},
|
||
"zenith_kestrel": {
|
||
"$comment": "ZENITH 3 (frame 52) — KESTREL, the heavy wing: a SPRAY fan of five across the aim, and FOUR heavy seekers — the fewest but the biggest (26 px, fastest at 260 px/s) on a quicker seek (1.1): fewer, meaner, tighter.",
|
||
"name": "Kestrel",
|
||
"frame": 52,
|
||
"speed": 175,
|
||
"turn": 3.6,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "spray",
|
||
"range": 520,
|
||
"cooldown": 1.3,
|
||
"bulletSpeed": 340,
|
||
"damage": 16,
|
||
"spread": 0.02,
|
||
"bulletLife": 2.0,
|
||
"bolts": 5,
|
||
"fan": 0.7,
|
||
"color": "#ffe08a"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "flank_barrage",
|
||
"cooldown": 12,
|
||
"count": 4,
|
||
"missileSpeed": 260,
|
||
"missileTurn": 1.1,
|
||
"missileDamage": 16,
|
||
"missileLife": 4.5,
|
||
"missileSize": 26,
|
||
"missileSpread": 0.22,
|
||
"color": "#ffe08a"
|
||
}
|
||
]
|
||
},
|
||
"zenith_reefrunner": {
|
||
"$comment": "ZENITH 4 (frame 53) — REEFRUNNER, the pack's speed: a FORWARD sweep (seven-bolt cone out the nose — the craft commits to its heading while it fires), and a DOUBLE WING VOLLEY (ten seekers, quick seek at 1.3) on the shortest volley clock of the combo wing: the most shots in the air at once.",
|
||
"name": "Reefrunner",
|
||
"frame": 53,
|
||
"speed": 185,
|
||
"turn": 3.8,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "sweep",
|
||
"range": 500,
|
||
"cooldown": 1.2,
|
||
"bulletSpeed": 360,
|
||
"damage": 16,
|
||
"spread": 0.02,
|
||
"bulletLife": 1.9,
|
||
"bolts": 7,
|
||
"fan": 1.0,
|
||
"color": "#ffd84d"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "flank_barrage",
|
||
"cooldown": 11,
|
||
"count": 10,
|
||
"missileSpeed": 260,
|
||
"missileTurn": 1.3,
|
||
"missileDamage": 16,
|
||
"missileLife": 4.5,
|
||
"missileSpread": 0.34,
|
||
"color": "#ffd84d"
|
||
}
|
||
]
|
||
},
|
||
"zenith_voltspire": {
|
||
"$comment": "ZENITH 5 (frame 54) — VOLTSPIRE, the elemental base kit: a clean LANCE between volleys, and one VOLT DART per cycle that detonates near the player and blooms a field (40→150 px over 1.8 s, live 3.2 s) ticking 4 per 0.4 s inside — the archetype's signature at balanced tempo.",
|
||
"name": "Voltspire",
|
||
"frame": 54,
|
||
"speed": 150,
|
||
"turn": 3.0,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "lance",
|
||
"range": 560,
|
||
"cooldown": 1.8,
|
||
"bulletSpeed": 380,
|
||
"damage": 16,
|
||
"spread": 0.03,
|
||
"bulletLife": 1.9,
|
||
"color": "#7dfcff"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "shock_dart",
|
||
"cooldown": 11,
|
||
"count": 1,
|
||
"dartSpeed": 520,
|
||
"proximity": 90,
|
||
"dartLifeMs": 1400,
|
||
"startRadius": 40,
|
||
"maxRadius": 150,
|
||
"growMs": 1800,
|
||
"zoneLifeMs": 3200,
|
||
"zoneFadeMs": 700,
|
||
"tickMs": 400,
|
||
"tickDamage": 4,
|
||
"color": "#7dfcff"
|
||
}
|
||
]
|
||
},
|
||
"zenith_stormweft": {
|
||
"$comment": "ZENITH 6 (frame 55) — STORMWEFT, the double line: RAPID gun cover, and TWO darts in a loose fan — the roster's bigger, slower-blooming field (45→180 px over 2 s) on a longer cycle: two overlapping discs that fence the player's escape.",
|
||
"name": "Stormweft",
|
||
"frame": 55,
|
||
"speed": 140,
|
||
"turn": 2.8,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "rapid",
|
||
"range": 500,
|
||
"cooldown": 0.7,
|
||
"bulletSpeed": 380,
|
||
"damage": 16,
|
||
"spread": 0.05,
|
||
"bulletLife": 1.6,
|
||
"color": "#4dd7ff"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "shock_dart",
|
||
"cooldown": 13,
|
||
"count": 2,
|
||
"dartSpread": 0.5,
|
||
"dartSpeed": 500,
|
||
"proximity": 95,
|
||
"dartLifeMs": 1500,
|
||
"startRadius": 45,
|
||
"maxRadius": 180,
|
||
"growMs": 2000,
|
||
"zoneLifeMs": 3600,
|
||
"zoneFadeMs": 800,
|
||
"tickMs": 400,
|
||
"tickDamage": 4,
|
||
"color": "#4dd7ff"
|
||
}
|
||
]
|
||
},
|
||
"zenith_arcpulse": {
|
||
"$comment": "ZENITH 7 (frame 56) — ARCPULSE, the strobe: a RADIAL eight-bolt net, and the fastest elemental cycle (9 s) with THREE quick small darts — little fields (30→110 px, blooming in 1.4 s) but the most detonations in the air, a flickering minefield at point-blank range.",
|
||
"name": "Arcpulse",
|
||
"frame": 56,
|
||
"speed": 165,
|
||
"turn": 3.4,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "ring",
|
||
"range": 460,
|
||
"cooldown": 2.2,
|
||
"bulletSpeed": 260,
|
||
"damage": 16,
|
||
"spread": 0.0,
|
||
"bulletLife": 2.2,
|
||
"bolts": 8,
|
||
"color": "#5ee6ff"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "shock_dart",
|
||
"cooldown": 9,
|
||
"count": 3,
|
||
"dartSpread": 0.8,
|
||
"dartSpeed": 560,
|
||
"proximity": 80,
|
||
"dartLifeMs": 1200,
|
||
"startRadius": 30,
|
||
"maxRadius": 110,
|
||
"growMs": 1400,
|
||
"zoneLifeMs": 2600,
|
||
"zoneFadeMs": 600,
|
||
"tickMs": 350,
|
||
"tickDamage": 3,
|
||
"color": "#5ee6ff"
|
||
}
|
||
]
|
||
},
|
||
"zenith_tempest": {
|
||
"$comment": "ZENITH 8 (frame 57) — TEMPEST, the faction's apex (the elite): a heavy SHELL between the slowest cycles, and the roster's biggest field — one giant dart (50→230 px over 2.4 s, live 4.2 s, the hardest tick at 5/beat): a slow, enormous electrified storm that outlasts every escape and punishes every hold.",
|
||
"name": "Tempest",
|
||
"frame": 57,
|
||
"speed": 110,
|
||
"turn": 2.2,
|
||
"hull": 175,
|
||
"shields": 35,
|
||
"xp": 42,
|
||
"ramDamage": 13,
|
||
"ramDamageSelf": 29,
|
||
"weapon": {
|
||
"pattern": "shell",
|
||
"range": 600,
|
||
"cooldown": 2.8,
|
||
"damage": 16,
|
||
"shells": 1,
|
||
"shellDamage": 16,
|
||
"shellSize": 44,
|
||
"shellSpeed": 150,
|
||
"shellLife": 2.5,
|
||
"color": "#35c8ff"
|
||
},
|
||
"abilities": [
|
||
{
|
||
"id": "shock_dart",
|
||
"cooldown": 15,
|
||
"count": 1,
|
||
"dartSpeed": 480,
|
||
"proximity": 110,
|
||
"dartLifeMs": 1700,
|
||
"startRadius": 50,
|
||
"maxRadius": 230,
|
||
"growMs": 2400,
|
||
"zoneLifeMs": 4200,
|
||
"zoneFadeMs": 900,
|
||
"tickMs": 450,
|
||
"tickDamage": 5,
|
||
"color": "#35c8ff"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"bulletHoming": {
|
||
"_comment": "THE MINDWEAVE'S WILL (js/combat/CombatSystem.js → stepHomingBullets): bolts and shells fired by ships of these factions bend toward the NEAREST LIVE HOSTILE within range for the first durationMs of flight — the weaver's threads find their mark without negating a committed dodge (a capped turn-rate: subtle, yet effective). Hostiles = the rival faction's wing (never the shooter's own) and, for bolts hostile to the player, the player itself. War-zone factions stay neutral-to-the-player, so in an observed duel the will only curves fire at the rival wing.",
|
||
"factions": ["mindweave"],
|
||
"turnRate": 0.3,
|
||
"durationMs": 1000,
|
||
"range": 720
|
||
},
|
||
"factionScaling": {
|
||
"_comment": "THE FACTION RAMP (js/combat/EnemyTypes.js → factionScaleForLevel / enemyTypeConfig(typeId, level), applied by CombatSystem.spawnEnemy at the player's combat level): the FACTION rosters' (combat.factions — Apex / Mindweave / Zenith, 24 fighters) per-shot damage and armor scale with the player's level between levelStart and levelEnd (clamped at both ends), measured against the PIRATE raider baseline (the classic 2× DAMAGE / 3.5× ARMOR balance rule — 60 armor, 8 bolt damage). The data/*.json static values ARE the levelEnd numbers (16 damage, 175+35 armor); at lower levels they scale DOWN proportionally (× damageMult(level)/damageMultEnd, × armorMult(level)/armorMultEnd, rounded). Level 10: damage ×0.6 (16→10), armor ×0.5714 (210→120). Level 35 and above: the shipped 2×/3.5×. EVERYTHING ELSE is untouched — cooldowns, the weapon pattern, the ability kit, ramDamage (13 = 10×1.3) and xp (42) stay flat across the whole ramp. Non-faction types (the pirate classics, the neutrals, the transport) never scale.",
|
||
"levelStart": 10,
|
||
"levelEnd": 35,
|
||
"damageMultStart": 1.2,
|
||
"damageMultEnd": 2.0,
|
||
"armorMultStart": 2.0,
|
||
"armorMultEnd": 3.5
|
||
},
|
||
"factions": {
|
||
"_comment": "The FACTION DUEL ARENA (the ?combat=apex / ?combat=mindweave boots — js/scenes/GameScene.js → _combatBootMode, js/combat/DuelPicker.js the in-game picker, CombatSystem.startDuel/spawnDuel/spawnDuelPack the fight's seam). Each faction names its fighters (enemyTypes ids, display order = the picker's rows 1–8); 'style' is the picker's one-line read. Key '0' (or the row) sends the WHOLE pack. These rosters are NOT wired into any quest/galaxy content — the arena is their only home for now.",
|
||
"apex": {
|
||
"name": "Apex",
|
||
"tagline": "apex predators — cloaked ambushes, flanking strikes, devastating forward firepower",
|
||
"types": [
|
||
{
|
||
"id": "apex_raptor",
|
||
"name": "Raptor",
|
||
"style": "forward fan sweep + the flank's cut"
|
||
},
|
||
{
|
||
"id": "apex_mauler",
|
||
"name": "Mauler",
|
||
"style": "one heavy bolt out of cloak"
|
||
},
|
||
{
|
||
"id": "apex_stalker",
|
||
"name": "Stalker",
|
||
"style": "strike, vanish, strike again (double cloak)"
|
||
},
|
||
{
|
||
"id": "apex_screamer",
|
||
"name": "Screamer",
|
||
"style": "a nine-bolt wall across your nose"
|
||
},
|
||
{
|
||
"id": "apex_reaver",
|
||
"name": "Reaver",
|
||
"style": "the unseen gun — rapid + double cloak"
|
||
},
|
||
{
|
||
"id": "apex_fang",
|
||
"name": "Fang",
|
||
"style": "a tight stinger from behind"
|
||
},
|
||
{
|
||
"id": "apex_tyrant",
|
||
"name": "Tyrant",
|
||
"style": "twin seekers out of the dark"
|
||
},
|
||
{
|
||
"id": "apex_alpha",
|
||
"name": "Alpha",
|
||
"style": "the pack's apex — the full predatory kit"
|
||
}
|
||
]
|
||
},
|
||
"mindweave": {
|
||
"name": "Mindweave",
|
||
"tagline": "psychic weavers — blinks, phantoms, telekinetic tides, spiralling barrages",
|
||
"types": [
|
||
{
|
||
"id": "weave_mindblade",
|
||
"name": "Mindblade",
|
||
"style": "a ten-bolt spiral that blooms in flight"
|
||
},
|
||
{
|
||
"id": "weave_telekine",
|
||
"name": "Telekine",
|
||
"style": "rapid fire + a tide that shoves you"
|
||
},
|
||
{
|
||
"id": "weave_phantasm",
|
||
"name": "Phantasm",
|
||
"style": "a court of phantoms + the blink"
|
||
},
|
||
{
|
||
"id": "weave_weaver",
|
||
"name": "Weaver",
|
||
"style": "threads out its heading, blinks fastest"
|
||
},
|
||
{
|
||
"id": "weave_lure",
|
||
"name": "Lure",
|
||
"style": "a radial net + a tide that drags you in"
|
||
},
|
||
{
|
||
"id": "weave_dreamer",
|
||
"name": "Dreamshell",
|
||
"style": "large slow shells + the hardest shove"
|
||
},
|
||
{
|
||
"id": "weave_serpent",
|
||
"name": "Serpentmind",
|
||
"style": "a long hanging coil + the reeling pull"
|
||
},
|
||
{
|
||
"id": "weave_overmind",
|
||
"name": "Overmind",
|
||
"style": "triple shells + the full psychic kit"
|
||
}
|
||
]
|
||
},
|
||
"zenith": {
|
||
"name": "Zenith",
|
||
"tagline": "summit strike craft — wing volleys of slow seekers, arcs that bloom into electrified fields",
|
||
"types": [
|
||
{
|
||
"id": "zenith_wingblade",
|
||
"name": "Wingblade",
|
||
"style": "rapid gun + a six-seeker wing volley"
|
||
},
|
||
{
|
||
"id": "zenith_divefang",
|
||
"name": "Divefang",
|
||
"style": "a tight stinger + the slowest, widest seek"
|
||
},
|
||
{
|
||
"id": "zenith_kestrel",
|
||
"name": "Kestrel",
|
||
"style": "a five-bolt spray + four heavy seekers"
|
||
},
|
||
{
|
||
"id": "zenith_reefrunner",
|
||
"name": "Reefrunner",
|
||
"style": "a nose sweep + a double wing volley"
|
||
},
|
||
{
|
||
"id": "zenith_voltspire",
|
||
"name": "Voltspire",
|
||
"style": "a lance + one blooming electrified field"
|
||
},
|
||
{
|
||
"id": "zenith_stormweft",
|
||
"name": "Stormweft",
|
||
"style": "rapid fire + two overlapping fields"
|
||
},
|
||
{
|
||
"id": "zenith_arcpulse",
|
||
"name": "Arcpulse",
|
||
"style": "a radial net + a flickering minefield"
|
||
},
|
||
{
|
||
"id": "zenith_tempest",
|
||
"name": "Tempest",
|
||
"style": "a heavy shell + the roster's giant storm"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"neutral": {
|
||
"_comment": "The base NEUTRAL CRAFT — the friendly player-side ships (data/combat.json → neutralTypes deep-merges its overrides onto this base, exactly like enemyTypes onto enemy). They ride the SAME enemy spritesheet (assets/images/ships-enemy.png) on frames 20–27, one distinct craft per frame. side 'player' is the seam: they NEVER target or damage the player, the player's auto-fire never targets them, and CombatSystem flies them in its OWN allies roster (not enemies — js/combat/CombatSystem.js). Their AI (js/combat/NeutralShip.js): escort the player for a per-ship window (escortMin–escortMax seconds — the spawner rolls one per ship), then break off and fly off-screen; while any pirate is up they instead fight the NEAREST one with their own weapon pattern (the same Weapons.js dispatch the hostiles run). The stats ramp: hull/shields climb monotonically frame 20 → 27 (xp = (hull+shields)/5 — the same rule of thumb as the pirate roster). respawn false: encounter ships are spent, never reformed; ramDamage 0: a friendly ship's ram can't hurt the player.",
|
||
"texture": "assets/images/ships-enemy.png",
|
||
"frameWidth": 128,
|
||
"frameHeight": 128,
|
||
"frame": 20,
|
||
"artFacing": "north",
|
||
"side": "player",
|
||
"size": 54,
|
||
"hitRadius": 20,
|
||
"speed": 175,
|
||
"turn": 3.2,
|
||
"orbitRadius": 260,
|
||
"orbitSpeed": 0.6,
|
||
"escortRadius": 110,
|
||
"escortMin": 10,
|
||
"escortMax": 15,
|
||
"hull": 60,
|
||
"shields": 0,
|
||
"xp": 12,
|
||
"weapon": {
|
||
"pattern": "lance",
|
||
"range": 500,
|
||
"cooldown": 1.6,
|
||
"bulletSpeed": 340,
|
||
"damage": 9,
|
||
"spread": 0.05,
|
||
"bulletLife": 2.2,
|
||
"color": "#37e0a1",
|
||
"size": 16,
|
||
"boltLength": 48,
|
||
"boltWidth": 10,
|
||
"muzzle": 44
|
||
},
|
||
"ramDamage": 0,
|
||
"ramDamageSelf": 0,
|
||
"ramGrace": 0.8,
|
||
"respawn": false
|
||
},
|
||
"neutralTypes": {
|
||
"_comment": "The NEUTRAL roster (frames 20–27 of assets/images/ships-enemy.png) — the random-encounter director (GameScene → CombatSystem.spawnAlly) deploys them, js/combat/NeutralShip.js flies them. Each entry declares ONLY what differs from the neutral base above (the merge is EnemyTypes.neutralTypeConfig — the same deep-merge contract as the enemyTypes): a DISTINCT weapon pattern (the Weapons.js set — lance/spray/burst/rapid/ring/missile/combo — plus 'volley', the eighth pattern) and the STATS RAMP (hull/shields climb monotonically frame 20 → 27; xp = (hull+shields)/5). side stays 'player' (inherited — never hostile).",
|
||
"courier": {
|
||
"$comment": "frame 20 — the LANCE: the scout. Fast and light, one aimed bolt; the easy end of the ramp.",
|
||
"frame": 20,
|
||
"speed": 195,
|
||
"turn": 3.4,
|
||
"hull": 60,
|
||
"shields": 0,
|
||
"xp": 12,
|
||
"weapon": {
|
||
"pattern": "lance",
|
||
"range": 500,
|
||
"cooldown": 1.6,
|
||
"bulletSpeed": 340,
|
||
"damage": 9,
|
||
"spread": 0.05,
|
||
"bulletLife": 2.2,
|
||
"color": "#37e0a1"
|
||
}
|
||
},
|
||
"runner": {
|
||
"$comment": "frame 21 — the RAPID: the fast gun. Short cooldown, honest per-bolt punch.",
|
||
"frame": 21,
|
||
"speed": 200,
|
||
"turn": 4.0,
|
||
"hull": 70,
|
||
"shields": 10,
|
||
"xp": 16,
|
||
"weapon": {
|
||
"pattern": "rapid",
|
||
"range": 440,
|
||
"cooldown": 0.5,
|
||
"bulletSpeed": 380,
|
||
"damage": 8,
|
||
"spread": 0.06,
|
||
"bulletLife": 1.6,
|
||
"color": "#7df3c1"
|
||
}
|
||
},
|
||
"sloop": {
|
||
"$comment": "frame 22 — the SPRAY: a fan of cheap bolts covering an arc.",
|
||
"frame": 22,
|
||
"speed": 150,
|
||
"turn": 2.6,
|
||
"hull": 80,
|
||
"shields": 15,
|
||
"xp": 19,
|
||
"weapon": {
|
||
"pattern": "spray",
|
||
"range": 460,
|
||
"cooldown": 2.0,
|
||
"bulletSpeed": 300,
|
||
"damage": 8,
|
||
"spread": 0.03,
|
||
"bulletLife": 2.2,
|
||
"bolts": 5,
|
||
"fan": 1.1,
|
||
"color": "#37e0a1"
|
||
}
|
||
},
|
||
"sally": {
|
||
"$comment": "frame 23 — the BURST: a fast, tight 3-shot stinger at close range.",
|
||
"frame": 23,
|
||
"speed": 180,
|
||
"turn": 3.6,
|
||
"hull": 90,
|
||
"shields": 20,
|
||
"xp": 22,
|
||
"weapon": {
|
||
"pattern": "burst",
|
||
"range": 380,
|
||
"cooldown": 1.5,
|
||
"bulletSpeed": 420,
|
||
"damage": 9,
|
||
"spread": 0.02,
|
||
"bulletLife": 1.8,
|
||
"bolts": 3,
|
||
"burstFan": 0.1,
|
||
"color": "#aef27d"
|
||
}
|
||
},
|
||
"sentinel": {
|
||
"$comment": "frame 24 — the RING: a radial volley; slow, armored, pokes a hole in the line.",
|
||
"frame": 24,
|
||
"speed": 130,
|
||
"turn": 2.4,
|
||
"hull": 100,
|
||
"shields": 30,
|
||
"xp": 26,
|
||
"weapon": {
|
||
"pattern": "ring",
|
||
"range": 360,
|
||
"cooldown": 2.2,
|
||
"bulletSpeed": 260,
|
||
"damage": 10,
|
||
"spread": 0.0,
|
||
"bulletLife": 2.6,
|
||
"bolts": 8,
|
||
"color": "#4dd7ff"
|
||
}
|
||
},
|
||
"herald": {
|
||
"$comment": "frame 25 — the MISSILE: one heat-seeking shot per volley (dodgeable at the capped turn).",
|
||
"frame": 25,
|
||
"speed": 160,
|
||
"turn": 3.0,
|
||
"hull": 110,
|
||
"shields": 35,
|
||
"xp": 29,
|
||
"weapon": {
|
||
"pattern": "missile",
|
||
"range": 620,
|
||
"cooldown": 2.4,
|
||
"missiles": 1,
|
||
"missileSpeed": 190,
|
||
"missileTurn": 2.2,
|
||
"missileDamage": 13,
|
||
"missileLife": 4.0,
|
||
"missileColor": "#37e0a1",
|
||
"missileSize": 16,
|
||
"color": "#37e0a1"
|
||
}
|
||
},
|
||
"vanguard": {
|
||
"$comment": "frame 26 — the COMBO: a heavy bolt PLUS a heat-seeker on every volley.",
|
||
"frame": 26,
|
||
"speed": 110,
|
||
"turn": 2.0,
|
||
"hull": 130,
|
||
"shields": 45,
|
||
"xp": 35,
|
||
"weapon": {
|
||
"pattern": "combo",
|
||
"range": 700,
|
||
"cooldown": 2.6,
|
||
"bulletSpeed": 240,
|
||
"damage": 16,
|
||
"spread": 0.02,
|
||
"bulletLife": 3.2,
|
||
"size": 18,
|
||
"boltLength": 56,
|
||
"boltWidth": 12,
|
||
"missiles": 1,
|
||
"missileSpeed": 170,
|
||
"missileTurn": 2.4,
|
||
"missileDamage": 18,
|
||
"missileLife": 4.2,
|
||
"missileColor": "#7df3c1",
|
||
"missileSize": 18,
|
||
"color": "#7df3c1"
|
||
}
|
||
},
|
||
"bulwark": {
|
||
"$comment": "frame 27 — the VOLLEY: the ramp's top — a WIDE fan whose speeds are staggered, so the arc blossoms into a wall over distance (Weapons.js 'volley': the burst's stagger across the spray's fan).",
|
||
"frame": 27,
|
||
"speed": 95,
|
||
"turn": 1.8,
|
||
"hull": 150,
|
||
"shields": 60,
|
||
"xp": 42,
|
||
"weapon": {
|
||
"pattern": "volley",
|
||
"range": 520,
|
||
"cooldown": 2.4,
|
||
"bulletSpeed": 280,
|
||
"damage": 11,
|
||
"spread": 0.02,
|
||
"bulletLife": 2.8,
|
||
"bolts": 7,
|
||
"fan": 1.5,
|
||
"stagger": 0.35,
|
||
"color": "#c8f54d"
|
||
}
|
||
}
|
||
},
|
||
"transport": {
|
||
"_comment": "The ESCORT CONTRACT's transport (sidequests.escort — data/sidequests.json): the neutral FREIGHTER the player rendevouses with at a local waypoint and leads through the jump gates to a planet / space station 1–2 hops away. A PERSISTENT player-side craft (side 'player' — it never targets or damages the player; CombatSystem's active/neutralOnly checks skip it via its `persistent` flag, so a standing convoy never holds the jump gate / the landing door). js/combat/TransportShip.js flies it (the NeutralShip escort/engage AI with the break-off window lapsed — it stays for the life of the contract). ART: assets/images/ships-transport.png, frames 0–3 at 180×320 (the art faces north, filling its frame — the long side is the ship's length). SIZE 135 reads as the hull's world LENGTH: ~2.5× a fighter's 54 (TransportShip scales by the sheet's long side, not the frameWidth the square enemy sheet assumes). ARMOR far above the escort roster (hull 1500 / shields 650 vs the roster's 60–150/0–60) — it soaks fire off the player; WEAPON a wide 'volley' fan (the roster's top pattern) whose per-bolt punch is honest (the transport is a bodyguard, not a gunboat). respawns: false — the convoy is spent when it's lost (the contract's reset, not a re-form).",
|
||
"texture": "assets/images/ships-transport.png",
|
||
"frameWidth": 180,
|
||
"frameHeight": 320,
|
||
"frame": 0,
|
||
"artFacing": "north",
|
||
"side": "player",
|
||
"size": 135,
|
||
"hitRadius": 46,
|
||
"speed": 245,
|
||
"turn": 2.6,
|
||
"orbitRadius": 320,
|
||
"orbitSpeed": 0.45,
|
||
"escortRadius": 175,
|
||
"hull": 1500,
|
||
"shields": 650,
|
||
"xp": 0,
|
||
"weapon": {
|
||
"pattern": "volley",
|
||
"range": 660,
|
||
"cooldown": 2.3,
|
||
"bulletSpeed": 300,
|
||
"damage": 14,
|
||
"spread": 0.02,
|
||
"bulletLife": 3.0,
|
||
"bolts": 7,
|
||
"fan": 1.5,
|
||
"stagger": 0.3,
|
||
"size": 22,
|
||
"boltLength": 64,
|
||
"boltWidth": 14,
|
||
"color": "#7ce8a4"
|
||
},
|
||
"ramDamage": 0,
|
||
"ramDamageSelf": 0,
|
||
"ramGrace": 0.8,
|
||
"respawn": false
|
||
},
|
||
"encounters": {
|
||
"_comment": "PERIODIC RANDOM ENCOUNTERS (js/combat/Encounters.js → EncounterDirector; GameScene spawns on a hit) — NEUTRAL non-home space: pirates / neutrals / mixed; FACTION space (the war-zones — `encounters.faction` below): the local faction and one opposing faction, disposition off the player's standing with each (see that section's comment). The director rolls on the interval schedule (rollIntervalMin–rollIntervalMax s — `faction.rollIntervalMin..Max` in faction space; each attempt fires at chanceMin–chanceMax probability), and after an encounter ENDS it stays quiet for cooldownMin–cooldownMax s (or `faction.cooldownMin..Max` in faction space) before the next roll. KIND rolls at `weights` (shipped even — ~1/3 each; bypassed in faction space). SIZE: the player's combat level (js/combat/XpManager — a fresh run starts at 0) picks a band; mixed keeps both kinds present. Encounter hostiles are one-shots (no respawn); escorts carry the neutral.escortMin/escortMax window and break off on their own (js/combat/NeutralShip.js). Seconds + 0–1 probabilities — no code constants.",
|
||
"enabled": true,
|
||
"rollIntervalMin": 45,
|
||
"rollIntervalMax": 90,
|
||
"chanceMin": 0.5,
|
||
"chanceMax": 0.6,
|
||
"cooldownMin": 30,
|
||
"cooldownMax": 60,
|
||
"weights": {
|
||
"pirates": 1,
|
||
"neutrals": 1,
|
||
"mixed": 1
|
||
},
|
||
"bands": [
|
||
{
|
||
"minLevel": 0,
|
||
"maxLevel": 2,
|
||
"pirates": [
|
||
1,
|
||
2
|
||
],
|
||
"neutrals": [
|
||
1,
|
||
2
|
||
],
|
||
"mixedPirates": [
|
||
1,
|
||
1
|
||
],
|
||
"mixedNeutrals": [
|
||
1,
|
||
1
|
||
]
|
||
},
|
||
{
|
||
"minLevel": 3,
|
||
"maxLevel": 5,
|
||
"pirates": [
|
||
2,
|
||
3
|
||
],
|
||
"neutrals": [
|
||
2,
|
||
3
|
||
],
|
||
"mixedPirates": [
|
||
1,
|
||
2
|
||
],
|
||
"mixedNeutrals": [
|
||
1,
|
||
2
|
||
]
|
||
},
|
||
{
|
||
"minLevel": 6,
|
||
"maxLevel": 8,
|
||
"pirates": [
|
||
5,
|
||
7
|
||
],
|
||
"neutrals": [
|
||
5,
|
||
7
|
||
],
|
||
"mixedPirates": [
|
||
3,
|
||
4
|
||
],
|
||
"mixedNeutrals": [
|
||
2,
|
||
3
|
||
],
|
||
"roster": {
|
||
"raider": 5,
|
||
"spitter": 3,
|
||
"bomber": 2
|
||
}
|
||
},
|
||
{
|
||
"minLevel": 9,
|
||
"maxLevel": 12,
|
||
"pirates": [
|
||
7,
|
||
9
|
||
],
|
||
"neutrals": [
|
||
7,
|
||
9
|
||
],
|
||
"mixedPirates": [
|
||
4,
|
||
5
|
||
],
|
||
"mixedNeutrals": [
|
||
3,
|
||
4
|
||
],
|
||
"roster": {
|
||
"raider": 4,
|
||
"spitter": 2,
|
||
"stinger": 2,
|
||
"gunner": 2,
|
||
"bomber": 1
|
||
}
|
||
},
|
||
{
|
||
"minLevel": 13,
|
||
"maxLevel": 99,
|
||
"pirates": [
|
||
9,
|
||
12
|
||
],
|
||
"neutrals": [
|
||
9,
|
||
12
|
||
],
|
||
"mixedPirates": [
|
||
4,
|
||
5
|
||
],
|
||
"mixedNeutrals": [
|
||
4,
|
||
5
|
||
],
|
||
"roster": {
|
||
"raider": 3,
|
||
"spitter": 2,
|
||
"stinger": 2,
|
||
"gunner": 2,
|
||
"hunter": 2,
|
||
"barrage": 2
|
||
}
|
||
}
|
||
],
|
||
"_tiers": "THE LEVEL-6 JUMP (design): 1–2 → 2–3 → 5–7 (a ~2× step in the pack) → 7–9 → 9–12 (the cap — past level 13 the count holds and the roster's heavier types are the pressure). roster = the band's weighted pirate MIX (js/combat/Encounters.js → compose draws it into `pirateTypes`; a band without one leaves the scene's random pick standing — tiers 0–5 predate the rosters): raiders stay the anchor (the filler), spitter/bomber land at the jump, stinger/gunner at 9+, hunter (missiles) and barrage (rings) at 13+ — the heavy hitters, never the early pack.",
|
||
"faction": {
|
||
"_comment": "FACTION SPACE (the war-zones, js/combat/Encounters.js's compose context + GameScene._spawnFactionEncounter): in a faction-controlled system the ambient encounter is the LOCAL faction and one OPPOSING faction (js/combat/Encounters.js picks one of the other roster factions seeded; BOTH groups are sized off the `bands`' pirate counts, each drawn from its OWN combat.factions roster — no pirates/neutrals). DISPOSITION is per-faction, off the player's standing WITH each (js/reputation/Reputation.js → factionStanding, data/reputation.json): standing below `hostileBelow` → that faction targets the player and the player may return fire (the guns/skills' pools include it); at/above `hostileBelow` → it fights only the other faction, never the player (its bolts/seekers/rams can't touch the ship), and once its rival is spent it peels off (CombatSystem._peelOff) so the encounter ends on its own — the player observes. Factions ALWAYS fight each other while they're both up (CombatSystem.pickEnemyTarget). TIMERS: faction space rolls more often than neutral wild space (rollIntervalMin..Max, the director's roll schedule) with a longer quiet after each encounter (cooldownMin..Max, 40–60 s). The ships are the factions' OWN roster crafts (no new art).",
|
||
"rollIntervalMin": 20,
|
||
"rollIntervalMax": 40,
|
||
"cooldownMin": 40,
|
||
"cooldownMax": 60,
|
||
"hostileBelow": -5
|
||
}
|
||
},
|
||
"waves": {
|
||
"_comment": "WAVE SCRIPTS — the quest-step combat (js/combat/Waves.js + CombatSystem.startWaves; the quest hook that FIRES one lands later, random encounters too — for now the console drives it: window.__ORBIT_COMBAT__.waves). A script is an ordered list of waves. KINDS: 'rolling' = maxConcurrent on screen at a time, each kill sending in replacePerKill more until the force is spent; 'fixed' = the whole force at once, no replacements until exhausted. COMPOSITION: `roster` = enemyTypes id → weight (the mix); `total` = the units deployed (default = the weight sum); `type` + `count` is the single-type shorthand. lead = s before wave 1; gap = s between waves (per-wave `gap` s overrides). The quests reference scripts BY NAME (e.g. {\"combat\": \"raid\"}) when the hook lands.",
|
||
"lead": 1.5,
|
||
"gap": 2.0,
|
||
"defaults": {
|
||
"maxConcurrent": 3,
|
||
"replacePerKill": 1
|
||
},
|
||
"scripts": {
|
||
"probe": [
|
||
{
|
||
"kind": "fixed",
|
||
"type": "raider",
|
||
"count": 3
|
||
}
|
||
],
|
||
"skirmish": [
|
||
{
|
||
"kind": "rolling",
|
||
"roster": {
|
||
"raider": 2
|
||
},
|
||
"total": 8,
|
||
"maxConcurrent": 3,
|
||
"replacePerKill": 1
|
||
},
|
||
{
|
||
"kind": "fixed",
|
||
"roster": {
|
||
"raider": 3,
|
||
"bomber": 1
|
||
},
|
||
"total": 4
|
||
}
|
||
],
|
||
"raid": [
|
||
{
|
||
"kind": "fixed",
|
||
"roster": {
|
||
"raider": 4,
|
||
"bomber": 2
|
||
},
|
||
"total": 6
|
||
},
|
||
{
|
||
"kind": "rolling",
|
||
"roster": {
|
||
"raider": 3,
|
||
"bomber": 1
|
||
},
|
||
"total": 12,
|
||
"maxConcurrent": 4,
|
||
"replacePerKill": 1
|
||
}
|
||
],
|
||
"pirate_ambush": [
|
||
{
|
||
"$comment": "'Pirate Problems' (data/quests.json → pirate_problems) — the quest's combat step ('Defend your ship from pirates'): ONE rolling wave of SIX frame-0 raiders (EnemyTypes: raider = the sheet's frame 0). Fired when the player ENTERS the Neutral Capital's system with the quest live (GameScene._maybeStartPirateAmbush — create() and the grant's start beats); the wave ships materialize OFF-SCREEN around the player, each from a random direction (CombatSystem._spawnWaveEnemy — the same off-screen materialization as the ?combat pack), streaming in on their own chase AI; three at a time (maxConcurrent), each kill sending in a replacement (replacePerKill) until all six are down; the completion marks the 'combatDone' ledger (GameScene._recordCombatDone — the step's monotonic witness, SaveData-captured).",
|
||
"kind": "rolling",
|
||
"type": "raider",
|
||
"count": 6,
|
||
"maxConcurrent": 3,
|
||
"replacePerKill": 1
|
||
}
|
||
],
|
||
"combat_demo": [
|
||
{
|
||
"$comment": "?COMBAT DEMO (the tuning sandbox's boot — GameScene._wantsCombatDemo): a SEQUENCE of rolling waves, ONE per new hostile type in DIFFICULTY ORDER (bomber → warden, frames 1–7). Each wave is SIX of that single type, THREE on screen at a time (maxConcurrent), each kill sending in a replacement (replacePerKill) until the six are down — then the NEXT type's wave. This is the showcase for the seven new weapons (js/combat/Weapons.js).",
|
||
"kind": "rolling",
|
||
"type": "bomber",
|
||
"count": 6,
|
||
"maxConcurrent": 3,
|
||
"replacePerKill": 1
|
||
},
|
||
{
|
||
"kind": "rolling",
|
||
"type": "spitter",
|
||
"count": 6,
|
||
"maxConcurrent": 3,
|
||
"replacePerKill": 1
|
||
},
|
||
{
|
||
"kind": "rolling",
|
||
"type": "stinger",
|
||
"count": 6,
|
||
"maxConcurrent": 3,
|
||
"replacePerKill": 1
|
||
},
|
||
{
|
||
"kind": "rolling",
|
||
"type": "hunter",
|
||
"count": 6,
|
||
"maxConcurrent": 3,
|
||
"replacePerKill": 1
|
||
},
|
||
{
|
||
"kind": "rolling",
|
||
"type": "gunner",
|
||
"count": 6,
|
||
"maxConcurrent": 3,
|
||
"replacePerKill": 1
|
||
},
|
||
{
|
||
"kind": "rolling",
|
||
"type": "barrage",
|
||
"count": 6,
|
||
"maxConcurrent": 3,
|
||
"replacePerKill": 1
|
||
},
|
||
{
|
||
"kind": "rolling",
|
||
"type": "warden",
|
||
"count": 6,
|
||
"maxConcurrent": 3,
|
||
"replacePerKill": 1
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"playerShip": {
|
||
"_comment": "Your armor (both contexts — the real ship's stats.hullIntegrity/shields replace this when combat ships for real). shields absorb before the hull; after shieldRegenDelay s without a hit, shields regrow at shieldRegenRate/s up to their max. respawn = when your hull breaks you come back (respawnDelay s) instead of the run ending. The MAIN GAME now runs the real death flow (js/scenes/GameScene.js → _playerWentDown: the Game Over beat ends the run — it forces playerRespawn off, overridable only by a skill), so here it only keeps the ?combat range's sandbox convenience.",
|
||
"hull": 100,
|
||
"shields": 40,
|
||
"shieldRegenDelay": 4,
|
||
"shieldRegenRate": 14,
|
||
"respawn": true,
|
||
"respawnDelay": 2.0
|
||
},
|
||
"fx": {
|
||
"_comment": "The LOOK of combat (both contexts — js/combat/Fx.js + js/combat/CamShake.js). shake = the camera kick on the WORLD pass (the separate UI pass keeps the bar + feed still): each value is Phaser's shake intensity (a FRACTION of the viewport in px) and ms = the duration. hit = you take a hit, kill = an enemy goes down, break = the shield-collapse beat, ram = the Ramming Charge's impact (the heaviest beat). shield = the shield-sphere impact read: radius px = the sphere's world radius around the ship (the impact point sits this far off the ship), arcSpan rad = the bright arc's angular width (the highlighted hit region), ripple px = the shockwave that grows from the impact point, decayMs = how long the sphere flash lingers.",
|
||
"shake": {
|
||
"hit": 0.0045,
|
||
"kill": 0.007,
|
||
"break": 0.011,
|
||
"ram": 0.014,
|
||
"ms": 230
|
||
},
|
||
"shield": {
|
||
"radius": 40,
|
||
"arcSpan": 1.9,
|
||
"ripple": 90,
|
||
"decayMs": 520
|
||
}
|
||
},
|
||
"arena": {
|
||
"_comment": "The range walls. inset = px between the screen edge and the bounce wall; bounce = the fraction of speed a ship keeps off a wall (1 = full mirror bounce). wallColor = the rim line, wallGlow = its neon edge.",
|
||
"inset": 26,
|
||
"bounce": 0.9,
|
||
"wallColor": "#1b2c4a",
|
||
"wallGlow": "#00e5ff"
|
||
},
|
||
"sandbox": {
|
||
"_comment": "Sandbox conveniences — none of this ships with the real game. resetKey rebuilds the whole range (a fresh scene); pauseKey freezes the sim (handy for eyeballing positions via window.__COMBAT__ in the console); respawn = the player comes back (respawnDelay s) instead of dying permanently.",
|
||
"resetKey": "R",
|
||
"pauseKey": "P",
|
||
"respawn": true,
|
||
"respawnDelay": 1.6
|
||
},
|
||
"readability": {
|
||
"_comment": "The READABILITY LAYER (js/combat/CombatHud.js) — the 'what do I need to see' side of combat, shared by both contexts. reticle = the target/LOCK indicator: rotating corner brackets on the current target (the one your guns are on — the range's nearest hostile, the main game's auto-fire target; the shared rule is Combat.js → pickTarget). margin = px outside the target's hitRadius, spin = rad/s bracket rotation, color = the lock's amber. threat = the hostile cue: a small pulsing marker above EVERY enemy (size px, pulse 1/s, above = px over the ship's center, color = hot red). offscreen = the edge ARROWS for hostiles outside the camera's view (main game — the range's arena is one screen, so they rarely fire there): pad = px inside the screen edge, size = arrow px, max = the most arrows drawn (nearest hostiles win), color. feed = the KILL FEED, top-right (entries = max lines, life ms per entry, gap px between lines, size px font, xInset = px in from the screen's RIGHT edge, y0 = px DOWN from the top (the first line), ink + hit colors). barFlash = the combat bar's GAUGE FLASH when YOU take a hit: life ms, alpha = the white overlay's start (the shield gauge flashes on shield hits, the hull gauge on hull hits — both when the shields pop through).",
|
||
"reticle": {
|
||
"enabled": true,
|
||
"margin": 16,
|
||
"spin": 1.6,
|
||
"color": "#ffd84d"
|
||
},
|
||
"threat": {
|
||
"enabled": false,
|
||
"size": 8,
|
||
"pulse": 2.2,
|
||
"above": 36,
|
||
"color": "#ff2d6f"
|
||
},
|
||
"offscreen": {
|
||
"enabled": true,
|
||
"pad": 28,
|
||
"size": 13,
|
||
"max": 4,
|
||
"color": "#ff2d6f"
|
||
},
|
||
"feed": {
|
||
"enabled": true,
|
||
"entries": 5,
|
||
"life": 3000,
|
||
"gap": 19,
|
||
"size": 13,
|
||
"xInset": 18,
|
||
"y0": 64,
|
||
"ink": "#cfe9ff",
|
||
"kill": "#ffd84d"
|
||
},
|
||
"barFlash": {
|
||
"life": 340,
|
||
"alpha": 0.85
|
||
}
|
||
},
|
||
"hud": {
|
||
"_comment": "The range console chrome (colors fall back to data/theme.json). title = the corner label; hints = the control line (top-left — the bottom-center is the combat bar's now).",
|
||
"title": "COMBAT // SANDBOX",
|
||
"hints": "WASD / arrows — fly · space — fire · R — reset range · P — pause"
|
||
},
|
||
"bar": {
|
||
"_comment": "The COMBAT BAR (js/ui/CombatBar.js) — shown in place of the action deck while combat is live (the deck hides, the bar takes its place; when the pack is wiped it's the other way around). geometry = a BOTTOM-ALIGNED, CENTERED strip sized to its contents (assets/bar.png is the storyboard — not a color ref). Left end = the hull ORB (red) with the SHIELD orb (cyan, semi-transparent) overlapping it — shields in front of hull; middle = slot.count skill seats wearing keys 1-8 (the skills land later — for now they are empty seats); right end = the ENERGY orb (yellow, the skills' resource). The orbs fill like a Diablo orb: the liquid drains as the value drops. circle.radius = the hull/energy orb radius; circle.shield.scale = the shield orb's radius as a fraction of the hull radius — the shield orb shares the hull's CENTER (offsetX/offsetY default 0) and its liquid is semi-transparent over the hull's; orbGap = orb-to-slot spacing; margin.edge = the strip's overhang past the orbs. Colors fall back to data/theme.json where sensible.",
|
||
"strip": {
|
||
"height": 64
|
||
},
|
||
"margin": {
|
||
"bottom": 26,
|
||
"edge": 10
|
||
},
|
||
"orbGap": 24,
|
||
"circle": {
|
||
"radius": 46,
|
||
"hull": {
|
||
"fillAlpha": 0.95
|
||
},
|
||
"shield": {
|
||
"scale": 1.12,
|
||
"offsetX": 0,
|
||
"offsetY": 0,
|
||
"fillAlpha": 0.55
|
||
}
|
||
},
|
||
"colors": {
|
||
"bg": "#070d18",
|
||
"bgAlpha": 0.92,
|
||
"edge": "#22405f",
|
||
"shield": "#00e5ff",
|
||
"hull": "#ff2d6f",
|
||
"energy": "#ffd84d",
|
||
"slotBg": "#0b1322",
|
||
"slotBorder": "#22405f",
|
||
"key": "#5c7196"
|
||
},
|
||
"slot": {
|
||
"count": 8,
|
||
"size": 44,
|
||
"gap": 10,
|
||
"keySize": 12
|
||
}
|
||
}
|
||
} |