TypeScript 97.6%
SQL 1.4%
JavaScript 0.5%
1import { defineArcadeGame, type ArcadeGameDefinition } from "@spinza/game-core";2import calibration from "../calibration.json";34/**5 * Spinza Originals — Beyond Slots. Five interactive originals sharing the6 * arcade engine: two ladder games (analytic RTP) and three instant games7 * (payScale calibrated by simulation, see calibration.json).8 */910export const dropzone = defineArcadeGame({11 slug: "dropzone",12 name: "Dropzone",13 version: "1.0.0",14 tagline: "Release the capsule. Pray for the edge.",15 description:16 "Drop a capsule from the top of a vertical tower. It ricochets through pegs, energy gates and portals down to a row of buckets whose values depend on the risk profile you pick. Some descents trigger Deep Drop: the tower extends and the capsule keeps falling into a far riskier zone where the multipliers stack — or vanish.",17 theme: "arcade tower drop",18 mode: "instant",19 rtp: 0.96,20 maxMultiplier: 3000,21 volatility: "high",22 presentation: { scene: "dropzone", palette: { primary: "#22d3ee", secondary: "#f472b6", glow: "#67e8f9", bg: "#050a14", surface: "#0b1a2e" }, ambience: "arcade", verb: "DROP" },23 config: {24 rows: 12,25 lanes: 13,26 buckets: {27 low: [8, 3, 1.6, 1.2, 1, 0.7, 0.5, 0.7, 1, 1.2, 1.6, 3, 8],28 medium: [25, 6, 2.5, 1.4, 0.8, 0.4, 0.2, 0.4, 0.8, 1.4, 2.5, 6, 25],29 high: [120, 18, 4, 1.2, 0.4, 0.1, 0, 0.1, 0.4, 1.2, 4, 18, 120],30 },31 gateChancePerRow: 0.035,32 gateValues: [1.5, 2, 3],33 portalChancePerRow: 0.012,34 deepDropChance: 0.08,35 deepRows: 8,36 deepBuckets: [10, 4, 2, 1, 0, 1, 2, 4, 10],37 },38 featureNames: ["3 risk profiles", "Energy gates", "Portals", "Deep Drop"],39 rules: [40 "Choose a risk profile (Low / Medium / High) and a release lane, then press DROP.",41 "The capsule bounces left or right at every peg row (12 rows) and lands in one of 13 buckets.",42 "Energy gates multiply the result by ×1.5, ×2 or ×3; portals hop the capsule one to three lanes sideways.",43 "Bucket values are normalised per release lane, so every lane has the same expected value — edges are just wilder.",44 "Deep Drop (8% of drops): the tower extends by 8 rows and the capsule falls into a second bucket row that multiplies the result by 0× to 10×.",45 "Bucket values shown are already scaled to the game's certified RTP. Maximum win: 3,000× the bet.",46 ],47});4849export const gridBreak = defineArcadeGame({50 slug: "grid-break",51 name: "Grid//Break",52 version: "1.0.0",53 tagline: "One wave. Infinite chains.",54 description:55 "An 8×8 grid of energy blocks. Fire a wave down a column: every cluster it touches detonates, blocks fall, fresh ones drop in and new clusters detonate in a chain. Longer chains climb the multiplier ladder. Bombs clear 3×3, line blocks wipe a row, ×2 blocks double the step. It plays like a lightning-fast puzzle game.",56 theme: "futuristic puzzle grid",57 mode: "instant",58 rtp: 0.96,59 maxMultiplier: 2500,60 volatility: "medium",61 presentation: { scene: "gridbreak", palette: { primary: "#a3e635", secondary: "#38bdf8", glow: "#bef264", bg: "#05080c", surface: "#0e1620" }, ambience: "quantum", verb: "FIRE WAVE" },62 config: {63 size: 8,64 colors: 6,65 values: [1, 1.5, 2.5, 4, 7, 12],66 weights: [26, 22, 19, 15, 11, 7],67 specialChance: { bomb: 0.02, line: 0.012, x2: 0.02 },68 chainLadder: [1, 2, 4, 8, 16, 32, 64, 128],69 minCluster: 4,70 maxChains: 40,71 },72 featureNames: ["Chain reactions", "Bombs", "Line clears", "×2 blocks"],73 rules: [74 "Pick a column and fire the wave. Clusters of 4+ connected same-colour blocks touching that column detonate.",75 "Blocks fall, new blocks drop in; any new cluster of 4+ detonates automatically — a chain.",76 "Each chain step doubles the multiplier: ×1, ×2, ×4, ×8, ×16, ×32, ×64, ×128.",77 "Bomb blocks clear the 3×3 around them, line blocks clear their whole row, ×2 blocks double the step.",78 "Block values: the rarer the colour, the higher the value. Maximum win: 2,500× the bet.",79 ],80});8182export const theVault = defineArcadeGame({83 slug: "the-vault",84 name: "The Vault",85 version: "1.0.0",86 tagline: "Five layers. One decision at each.",87 description:88 "Face a colossal vault with five locked layers. Each layer reveals its digits one by one; when a layer opens, secure your multiplier or attempt the next lock. The deeper you go, the more spectacular the vault — and the higher the risk. Open all five in one run to trigger the fictional jackpot.",89 theme: "monumental vault",90 mode: "ladder",91 rtp: 0.96,92 maxMultiplier: 50,93 volatility: "high",94 presentation: { scene: "vault", palette: { primary: "#c9a961", secondary: "#38bdf8", glow: "#e8cf8f", bg: "#08070a", surface: "#16131a" }, ambience: "heist", verb: "OPEN NEXT LOCK", secondaryVerb: "SECURE" },95 config: {96 layers: [0.8, 0.7, 0.55, 0.4, 0.25],97 digitsPerLayer: 3,98 layerNames: ["Outer door", "Time lock", "Biometric ring", "Pressure chamber", "The core"],99 },100 featureNames: ["Secure or push", "5 layers", "Fictional jackpot"],101 rules: [102 "Place a bet. The first layer opens automatically — its 3 digits are revealed one by one.",103 "After each opened layer, SECURE your multiplier or OPEN the next lock.",104 "Chances the lock yields: 80% → 70% → 55% → 40% → 25%. Multipliers after each layer: 1.20× → 1.71× → 3.12× → 7.79× → 31.2×.",105 "A failed digit triggers the alarm: the run ends and the bet is lost.",106 "Opening all five layers in one run pays the fictional jackpot (31.2×). Expected return is 96% whatever your stopping point.",107 ],108});109110export const orbit = defineArcadeGame({111 slug: "orbit",112 name: "Orbit",113 version: "1.0.0",114 tagline: "Fire the impulse. Ride the chaos.",115 description:116 "Planets, satellites and debris circle a burning core. Aim an impulse through the orbits: every object it touches adds value, may deflect the impulse or spawn a brand-new orbit. Some rounds cascade into fifteen objects spinning at once. Supernova collapses everything and turns the survivors into multipliers.",117 theme: "orbital mechanics",118 mode: "instant",119 rtp: 0.96,120 maxMultiplier: 2000,121 volatility: "high",122 presentation: { scene: "orbit", palette: { primary: "#f0abfc", secondary: "#facc15", glow: "#f5d0fe", bg: "#07030f", surface: "#150a26" }, ambience: "space", verb: "FIRE IMPULSE" },123 config: {124 orbits: 3,125 objectsPerOrbit: [3, 6],126 beamHalfWidth: 14,127 objectTypes: [128 { id: "debris", label: "Debris", value: 0.3, weight: 34 },129 { id: "satellite", label: "Satellite", value: 0.8, weight: 26, deflect: 40 },130 { id: "planet", label: "Planet", value: 2, weight: 18, deflect: 20 },131 { id: "comet", label: "Comet", value: 4, weight: 10, spawn: true },132 { id: "gasgiant", label: "Gas giant", value: 8, weight: 5, deflect: 60 },133 { id: "quasar", label: "Quasar", value: 25, weight: 1, spawn: true },134 ],135 supernovaChance: 0.04,136 maxOrbits: 7,137 },138 featureNames: ["Aim the impulse", "Deflections", "New orbits", "Supernova"],139 rules: [140 "Choose an angle and fire. The impulse travels outward through every orbit.",141 "An object within the beam is collected: Debris 0.3×, Satellite 0.8×, Planet 2×, Comet 4×, Gas giant 8×, Quasar 25× (values scaled to the certified RTP).",142 "Satellites, planets and gas giants deflect the impulse; comets and quasars spawn a new orbit (up to 7).",143 "Supernova (4% of scoring rounds): every remaining object becomes +0.5× on the collected total.",144 "Maximum win: 2,000× the bet.",145 ],146});147148export const escape99 = defineArcadeGame({149 slug: "escape-99",150 name: "Escape 99",151 version: "1.0.0",152 tagline: "Ninety-nine floors. Cash out on any of them.",153 description:154 "A lightning-fast roguelike. Start on floor 1 of a 99-floor tower and climb room by room: chests, guardians, traps, runes, portals and forks between a safe corridor and a risky one. Cash out on any floor, or push for the summit — floor 99 triggers the final sequence.",155 theme: "roguelike tower",156 mode: "ladder",157 rtp: 0.96,158 maxMultiplier: 5000,159 volatility: "extreme",160 presentation: { scene: "escape", palette: { primary: "#34d399", secondary: "#f97316", glow: "#6ee7b7", bg: "#06090a", surface: "#0f1a16" }, ambience: "jungle", verb: "CLIMB", secondaryVerb: "CASH OUT" },161 config: {162 floors: 99,163 bands: [164 [10, 0.97],165 [25, 0.95],166 [50, 0.93],167 [75, 0.91],168 [99, 0.89],169 ],170 checkpoints: [10, 25, 50, 75, 99],171 rooms: [172 { kind: "chest", label: "Treasure room", weight: 26, detail: "a chest glints in the dark" },173 { kind: "enemy", label: "Guardian hall", weight: 22, detail: "something stirs" },174 { kind: "trap", label: "Trap corridor", weight: 18, detail: "pressure plates everywhere" },175 { kind: "multiplier", label: "Rune chamber", weight: 16, detail: "runes pulse with energy" },176 { kind: "room", label: "Quiet stairwell", weight: 18, detail: "nothing but echoes" },177 ],178 forkChance: 0.18,179 portalChance: 0.06,180 },181 featureNames: ["99 floors", "Forks", "Portals", "Checkpoints", "Final sequence"],182 rules: [183 "Place a bet: you enter floor 1 automatically. Each CLIMB advances one floor (three through a portal).",184 "Floor safety: 97% up to floor 10, 95% to 25, 93% to 50, 91% to 75, 89% beyond. Each survived floor multiplies by 1 ÷ safety.",185 "Forks offer a safe corridor (+6% safety, smaller jump) or a risky one (−18%, bigger jump) — equal expected value.",186 "CASH OUT at any floor after the first. Checkpoints (10, 25, 50, 75, 99) are marked on the tower.",187 "Floor 99 ends the run with the final sequence. Expected return is 96% for any stopping strategy; maximum win 5,000×.",188 ],189});190191type Calibration = Record<string, { payScale: number; version: string }>;192const withCalibration = (def: ArcadeGameDefinition): ArcadeGameDefinition => {193 const c = (calibration as Calibration)[def.slug];194 return c && c.version === def.version ? { ...def, payScale: c.payScale } : def;195};196197export const RAW_ARCADE_GAMES: ArcadeGameDefinition[] = [dropzone, gridBreak, theVault, orbit, escape99];198export const ARCADE_GAMES: ArcadeGameDefinition[] = RAW_ARCADE_GAMES.map(withCalibration);199export const ARCADE_BY_SLUG = new Map(ARCADE_GAMES.map((g) => [g.slug, g]));200