Files
thecrimesynticate/docs/superpowers/specs/2026-06-28-escape-from-jail-design.md
T

4.1 KiB
Raw Blame History

Escape from Jail — Design Spec

Date: 2026-06-28
Status: Approved


Overview

Add a Bust Out mechanic to the jail page. A jailed character can spend cash to attempt an escape. Success rate is based on dexterity + speed stats. Success releases immediately; failure multiplies remaining sentence by 1.5×. Cash is always deducted regardless of outcome.


Data Model

No new tables or columns required. Uses existing characters.status and characters.in_jail_until.


Bust-Out Mechanics

Cost

1000 cash (from config('game.jail.breakout_cost')). Deducted whether the attempt succeeds or fails.

Success Rate Formula

success_rate = min(
    config('game.jail.bust_out_max_chance'),
    config('game.jail.breakout_chance') + (dexterity + speed) * config('game.jail.bust_out_stat_multiplier')
)

With defaults:

  • breakout_chance = 0.05 (existing)
  • bust_out_stat_multiplier = 0.003 (new)
  • bust_out_max_chance = 0.80 (new)

A fresh character (dex=5, speed=5) starts at 8%. A well-trained character (dex=50, speed=50) reaches 35%. Max possible is 80%.

On Success

  • Deduct breakout_cost cash
  • Set status → active
  • Set in_jail_until → null
  • Redirect to game.dashboard with success flash

On Failure

  • Deduct breakout_cost cash
  • Multiply remaining jail time by sentence_multiplier (1.5×): in_jail_until = now()->addSeconds(ceil($character->in_jail_until->diffInSeconds(now()) * 1.5))
  • Redirect back to game.jail with failure flash showing new release time

Config Changes (config/game.php)

Add two keys to the existing jail block:

'jail' => [
    'sentence_length'        => 3600,
    'breakout_chance'        => 0.05,
    'breakout_cost'          => 1000,
    'sentence_multiplier'    => 1.5,
    'bust_out_stat_multiplier' => 0.003,  // per combined dex+speed point
    'bust_out_max_chance'    => 0.80,     // cap at 80%
],

New Files

File Purpose
app/Services/Game/JailService.php bustOut(Character): array, sendToJail(Character, int): void, releaseFromJail(Character): void
app/Http/Controllers/Game/JailController.php bustOut(Request): RedirectResponse

JailService interface

/**
 * @return array{success: bool, message: string}
 */
public function bustOut(Character $character): array

public function sendToJail(Character $character, int $seconds): void

public function releaseFromJail(Character $character): void

bustOut() returns ['success' => true/false, 'message' => string]. The message is a human-readable outcome description suitable for a flash notice.


Modified Files

File Change
config/game.php Add bust_out_stat_multiplier and bust_out_max_chance to jail block
resources/views/game/jail.blade.php Add Bust Out card below status card
routes/game.php Add POST /game/jail/bust-out → JailController@bustOut

Routes

Route::middleware(['auth'])->group(function () {
    Route::post('/game/jail/bust-out', [JailController::class, 'bustOut'])->name('game.jail.bust-out');
});

The bust-out route lives in the same auth-only group as the existing jail view route (no verify.character needed — jailed characters can't have characters, but do need auth).


UI — Jail Page

Dark crime theme, consistent with existing pages.

Bust Out card sits below the existing status card. Contains:

  • Success percentage (calculated from character stats, shown as e.g. "12% chance")
  • Cash cost ("Costs $1,000")
  • "Bust Out" button — disabled with tooltip if character has < 1000 cash
  • CSRF-protected POST form

Flash messages:

  • Success: "You escaped! You're free." → redirects to dashboard
  • Failure: "Bust failed — your sentence was extended to [new release time]." → stays on jail page

Out of Scope (this iteration)

  • Busting out another player (requires PvP)
  • Nerve/energy cost for bust attempts
  • Per-attempt cooldown (failure penalty is time extension only)
  • Bust attempt history / activity log entry (can add later)