4.1 KiB
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_costcash - Set
status → active - Set
in_jail_until → null - Redirect to
game.dashboardwith success flash
On Failure
- Deduct
breakout_costcash - 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.jailwith 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)