ExVector: Engineering a Metroidvania Extraction Shooter (the Xtractvania)
Metroidvanias and multiplayer PvPvE extraction shooters want opposite things from a map. A classic 2D Metroidvania relies on solo progression: rooms are gated behind permanent upgrades, and backtracking through cleared corridors is safe. An extraction shooter drops rival players into the same shared space with permanent loot stakes, where a single locked choke point can trap or soft-lock someone who spawned in with baseline gear.
With our new game, ExVector (XV), we set out to combine both loops. We coined the term Xtractvania for this hybrid: a Metroidvania extraction shooter, where non-linear, upgrade-gated exploration meets the fog-of-war tension and permanent loot stakes of a PvPvE extraction game. In ExVector, up to 16 operatives and autonomous security drones drop into a shared, procedurally generated orbital facility, breach bulkhead hatches, harvest physical BioCore canisters, and either parley or fight for an extraction silo before a 30-minute Sector Purge wipes the station.
Making this work competitively in the browser at a deterministic 60Hz tick rate meant rethinking how doors, procedural layouts, server timers, and audio fog-of-war interact.
Utility Gating Without Soft-Locking Players
In a shared 16-player labyrinth, gating the main path behind optional loadouts or consumable pickups is a recipe for soft-locks. A player stripped of gear could easily end up trapped inside a contested room with no way out. On the other hand, if every doorway opened to basic gunfire, the facility would flatten into a standard arena deathmatch and lose its Metroidvania structure altogether.
So, in ExVector, we split traversal and breaching into two distinct tiers:
- Critical-Path Baseline: Every operative spawns with baseline movement (walk, jump, and air dash) alongside a blaster capable of both rapid fire and a 0.8-second charge shot. Standard cyan bulkheads unseal to normal gunfire, guaranteeing that anyone can traverse from any entry airlock to at least one active extraction silo using only their starting kit.
- Vaults and Shortcuts: High-yield biolab vaults and vertical shaft shortcuts sit behind heavy orange blast doors and high-ceiling grapple spans. Breaching an orange hatch forces you to commit to that audible 0.8-second charge wind-up: you unlock denser BioCore yields and flanking routes, at the cost of broadcasting your intent to neighboring rooms.
Seeded Labyrinths and Graph Reachability
Static maps get memorized within days, which kills the exploration tension that makes both genres work. Instead of hand-authoring fixed layouts, the ExVector server synthesizes a deterministic 12-to-16 sector labyrinth from a 32-bit seed and a declarative generation spec:
export const DEFAULT_MAP_GENERATION_SPEC: Omit<MapGenerationSpec, 'seed'> = {
minSectors: 12,
maxSectors: 16,
airlockCount: 4,
extractionSiloCount: 3,
biolabVaultCount: 3,
minLoops: 3,
minSpawnToSiloHops: 3,
siloCooldownMs: 120_000,
enemyRespawnMs: 60_000,
maxPlayers: 16,
};
Tree-like dungeon graphs are fatal in an extraction game: if one corridor bridges an entire quadrant, a single defender can camp it indefinitely. During layout synthesis, our generator enforces a minimum cyclomatic complexity (E - V + 1 >= 3), guaranteeing at least three independent loops across the station so cornered players always have an alternate breach or grapple route around an ambush.
Before a room accepts player traffic, every generated seed has to pass two automated checks:
- Kinematic Platform Reachability: Using exact jump apex and air-dash bounds from our shared physics step, the server builds a directed platform graph inside each sector and verifies that no platform island creates an inescapable pit.
- Anti-Camp Airlock Selection: When a new player joins mid-raid, the spawner runs a breadth-first search across the sector graph from every living operative to all peripheral entry airlocks. It picks the airlock that maximizes the minimum hop distance to the nearest rival (breaking ties by world distance and nearby drone activity), preventing spawn-camping even with a full lobby in motion.
Solving the Raid Timer Dilemma: Overlapping 30-Minute Epochs
Persistent extraction servers run into a classic lifecycle problem. If a room stays open forever, loot and enemy states drift stale. If the server hard-resets on a fixed 15-minute clock, someone who queues in at 14:30 gets killed by the map timer 30 seconds later.
To avoid both failure modes, the ExVector room manager runs a two-stage, overlapping 30-minute state machine:
sequenceDiagram
autonumber
participant Queue as Matchmaking Queue
participant Manager as Room Manager
participant RoomA as Raid Epoch A
participant RoomB as Raid Epoch B
Note over RoomA: Stage 1: OPEN (0:00 - 15:00)
Queue->>Manager: New Operative Joins
Manager->>RoomA: Admit via Anti-Camp BFS Airlock
Note over Manager, RoomB: Minute 15:00 Rotation
Manager->>RoomA: Transition to DRAINING_PURGE
Manager->>RoomB: Allocate Fresh Seed (OPEN)
par Draining Raid (15:00 - 30:00)
Note over RoomA: No new joins. 15m Sector Purge countdown
RoomA-->>RoomA: Remaining players extract or perish
and Active Arrivals (0:00 - 15:00)
Queue->>Manager: Subsequent Operatives Join
Manager->>RoomB: Route to Fresh Epoch B
end
During the first 15 minutes (OPEN), the room accepts incoming players up to the cap without recycling dead or extracted slots into an already-looted raid, while holding a 60-second grace window for brief disconnects. At the 15-minute mark, the room transitions into DRAINING_PURGE and the manager immediately spins up a fresh OPEN room on a new seed for incoming queue traffic. Inside the draining station, entries lock and remaining operatives get a full 15-minute Metroid-style Sector Purge countdown to secure final BioCores and fight for the remaining extraction silos.
This gives us two hard guarantees at once: every newly admitted player gets at least 15 minutes of raid time, and memory per shard stays strictly bounded to at most two concurrent rooms.
Honest Hitboxes and Procedural Bulkhead Audio
Competitive 2D combat lives or dies on hitbox honesty and readable audio cues.
On the client, operatives are rendered with @crenet/orthorig, our 2.5D skeletal sprite-stacking and inverse-kinematics runtime. Players can mix helmets, dorsal fins, antennas, and armor colors, with server-enforced luminance bounds so nobody can equip pure black camouflage to hide in dark corridors. On the server, physics ignores cosmetic silhouettes completely: every player is evaluated against an identical 28x64 capsule collider with zero steady-state replication overhead.
Firefights revolve around a deterministic Rock-Paper-Scissors triangle:
- Fast Fire (850 px/s, 34 damage) suppresses reckless dashes during recovery frames.
- Charge Shots (0.8s wind-up, 500 px/s, 75 damage) punch through rapid-fire trades and breach heavy orange doors.
- 180ms Phase Dash Parry: Initiating a dash within 180ms of an incoming charge bolt, from a rival or a sentry drone, triggers a 60ms hit-stop, resets your dash cooldown, and reflects the shot along your 360-degree aim vector at
1.5xspeed (750 px/s).
To keep the browser client under 150 KB gzipped and free of repetitive sample loops, our sound engine synthesizes every weapon shot, grapple tension harmonic, and ambient hum in real time using native WebAudio DSP nodes. Because the server replicates entities and sound events across the player’s current sector plus one hop into adjacent rooms, the client checks the live state of every shared bulkhead door before routing audio:
graph LR
Event([Sound in Adjacent Sector]) --> Check{"Connecting Hatch Open?"}
Check -- "Yes (At least one open)" --> Direct[Direct Bus: Full-Spectrum Output]
Check -- "No (All closed or locked)" --> Occluded["Occluded Bus: 420Hz Low-Pass + 28ms Hull Delay"]
Direct --> Out([Stereo Panner Output])
Occluded --> Out
style Occluded fill:#ff5500,stroke:#cc4400,stroke-width:2px,color:#fff
style Direct fill:#0088aa,stroke:#00e5ff,stroke-width:2px,color:#fff
When a rival operative fights a sentry drone or knocks on the opposite side of a sealed blast door, you hear a muffled sub-420Hz thump resonating through the steel hull. That acoustic telemetry gives both sides real options before anyone breaches: pre-charge a shot, toss a BioCore canister as a bribe, or project an overhead truce beacon to negotiate safe passage.
FAQ
What is an Xtractvania?
An Xtractvania is a 2D tactical subgenre coined by Crenet Games with ExVector (XV) that fuses non-linear Metroidvania sector exploration (shoot-to-open bulkheads, utility-gated vaults, and analog IonTether grapple traversal) with the permanent loot risk and extraction stakes of a multiplayer PvPvE extraction shooter.
How long is an ExVector raid, and can I join one mid-match?
Each raid runs on a 30-minute cycle: 15 minutes open to new players, then a 15-minute Sector Purge where no one new can enter. Anyone who joins is guaranteed at least 15 minutes to loot and extract.
Are ExVector maps procedurally generated?
Yes. Every raid generates a fresh 12-to-16 sector station from a seed. Each layout is checked to have at least three independent loops and no inescapable platforming pits before players are allowed in.
How does ExVector prevent spawn camping?
When you join, the server picks the entry airlock that is the most room-hops away from the nearest rival operative.
Can I play ExVector in the browser?
Yes. The pre-alpha sandbox runs directly in the browser at xv.crenet.games, with no download required.
For questions on combat, truce beacons and BioCore bribes, controller support, and cosmetics, see the full ExVector FAQ.
ExVector is in active pre-alpha development by Crenet Games. You can try the sandbox and explore the interactive tactical dossier right now in your browser at xv.crenet.games, join our Discord for coordinated playtests, or grab captures and logos from the ExVector press kit.