Rival Raiders¶
Source: PROJECT_CONTEXT.md §8.5, §8.6, §9 (#3);
config.jsbot;game.jsmkBot,updateBot. Status: ✅ Implemented (simulated multiplayer — real PvP 🅿️ out of scope, §9 #3)
What it is¶
The map holds ~4 rival raider bots — AI opponents that loot, fight, and try to extract just like the player. They are the "other players" of the extraction loop: harder than mobs, matched to the player's Power, and carrying a haul you can steal by killing them. One is the Keybearer, who races to unlock the Vault.
How it works¶
- Population & matchmaking (
mkBot): count =map.bots(default 5). Each rival's Power is rolled around the player's:pwr = basePower × rand(0.62, 1.25)(Arc-style matching, so rivals stay a credible threat as the player gears up). HP, atk, and projDmg scale off that Power. - Per-rival loadout: each rolls one of the 4 ranged weapon archetypes (
assault/smg/shotgun/sniper) — some snipe from range, some rush with a shotgun. Personality rolls:aggression,reaction, a startinghaul, and anextractAftertimer. - AI (
updateBot) — loot / flee / extract / combat: - Goal arbiter: default
botState = "loot"; flips toextractoncehaul >= 3or past itsextractAftertime; flips tofleeat low HP or when a mob threatens it. - Combat — kiting + burst-fire: on seeing the player it computes a bravery score (
aggressionminus a Power-disadvantage penalty); brave →fight, timid →flee. It reactively dodge-rolls incoming shots (botMaybeDodge, human-paced), backs away when hurt (fightBackoffDist), and burst-fires its weapon's magazine then reloads. - Aim/reaction delay (
bot.aimTime): a rival tracks the player foraimTimebefore opening fire (not an instant snap-shot); aim bleeds off slowly out of sight, so a disengage-and-return re-pays the beat. - Fear of mobs: rivals treat nearby/alerted mobs as threats (
mobThreatNear/mobThreatAlerted) and flee them — a genuine 3-way dynamic. Rivals are also harder for creatures to spot (stealth.raiderVsMob). - Self-extract: rivals call/board portals on the same rules as the player (
extraction.boardBot=boardPassive= 9s). - Keybearer variant: rival index 0 gets
hasKey,dropsKey,botGoal = "gate", name "Keybearer". It races to the locked Vault gate and channels it open (map.gateTime); the player can deny the channel by pressuring it at close range. The key mission is a commitment — generic haul/time extract triggers are suspended untilbot.gateGiveUp(330s). When it cracks the gate, a banner broadcasts "ambush the looters!" (the concept §6 "let someone else unlock it, then steal" play). It won't race the gate belowbot.gateMinHp(permanent — bots don't regen). - Harder than mobs — 360° sight, can't be backstabbed:
mkBotgives rivalsvisA: Math.PI * 2(full-circle vision) andvisR = fog.radius, so they have no blind-spot cone to sneak up on. The first-raid intro coach explicitly frames rivals as "360° sight, no backstab" (§8.6) — they are a straight-up fight, unlike mobs. - Kill reward: a slain rival drops its haul on the ground to steal — hunting rivals for their loot is a core supported strategy (Pillar / §2).
Tunables¶
bot group (config.js):
| key | default | meaning |
|---|---|---|
mobThreatNear |
130 | mob within this px is always a threat |
mobThreatAlerted |
230 | alerted mob within this px is a threat |
fleeHpCrit |
0.3 | flee from any state below this HP fraction |
fleeHpExtract |
0.55 | abort extraction below this HP if timid |
fleeHpPost |
0.5 | after fleeing: extract if HP below this, else loot |
fleeHpBackoff |
0.6 | back away from player in-fight below this HP |
braveryFight |
0.1 | min bravery to stand and fight |
braveryTimid |
0.5 | below = timid (bails on extraction when hurt) |
fleeSprintMult |
1.5 | panic-sprint × (below player sprint) |
aimTime |
0.5 | reaction delay (s) before opening fire |
commitTime |
2.2 | fight commitment after engaging (s) |
alertDecay |
0.2 | alert decay /s when not seeing anyone |
fleeBreak |
4.5 | clear-contact (s) before flee reassesses |
fightBackoffDist |
240 | px to player that triggers backing-away when hurt |
gateMinHp |
0.35 | Keybearer won't race the gate below this HP |
gateGiveUp |
330 | (s) after which the Keybearer abandons the key mission |
Related: map.bots 5 (rival count), extraction.boardBot 9 (rival board time), enemyScale.enemySpeedMul 1.0 (rivals walk at player base pace) and enemyScale.atkCdMul 1.5 (applies to rival fire too).
Design intent¶
Rivals deliver the extraction-genre tension of "other players on the map" while staying single-player and shippable. They make every strategy in §2 live: avoid them, or hunt them for a fat haul. 360° sight / no-backstab makes them the genre's apex threat — the one enemy stealth can't trivialize — so a rival fight is a real Power test (Pillar 1's "head-on confrontation is always risky"). Matching their Power to the player keeps them dangerous through the gear curve. The Keybearer turns the Vault into an emergent flashpoint: race it yourself or let a rival open it and ambush the looters.
Open questions / deltas¶
- Simulated, not networked (§9 #3): "other players" are AI bots, not humans. Real PvP is out of scope for the prototype — the Loot Station exposure design (§8.3.1) is described as "the foundation for the planned multiplayer," so the bot AI is the stand-in until/if networking lands. Flag for the Unity port: decide whether bots remain or are replaced by real players.
- No-backstab is framing, not a hard mechanic block: rivals can't be sneaked up on (360°
visA, no blind cone), which is how "no backstab" is enforced. The crit-arc check indoMeleeis geometric, so confirm whether the Unity port wants an explicit hard rule vs. relying on full-circle sight.