Files

138 lines
4.1 KiB
Markdown
Raw Permalink Normal View History

2026-06-28 18:12:12 +03:00
# 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:
```php
'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
```php
/**
* @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
```php
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)