docs: add nerve stat + gym training design spec

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Doar Mihai
2026-06-28 15:24:36 +03:00
co-authored by Claude Sonnet 4.6
parent ad91bf15e0
commit 97865f51a5
@@ -0,0 +1,194 @@
# Nerve Stat + Gym Training — Design Spec
**Date:** 2026-06-28
**Status:** Approved
---
## Overview
Add a **nerve** stat that acts as a daily crime-volume cap alongside the existing per-attempt energy cost. Add a **gym** where players spend energy to train the four combat stats (strength, defense, speed, dexterity), subject to a per-stat daily session cap.
---
## Data Model
### `characters` table — new columns
| Column | Type | Default | Notes |
|--------|------|---------|-------|
| `nerve` | int | 100 | Current nerve |
| `max_nerve` | int | 100 | Max nerve; grows +2 per level-up |
| `nerve_regen_at` | timestamp | null | Tracks next trickle tick |
### `crimes` table — new column
| Column | Type | Default | Notes |
|--------|------|---------|-------|
| `nerve_cost` | int | 1 | Nerve consumed per attempt |
### `crime_attempts` table — new column
| Column | Type | Default | Notes |
|--------|------|---------|-------|
| `nerve_cost` | int | 1 | Nerve actually spent (for logs) |
### `gym_sessions` table — new table
| Column | Type | Notes |
|--------|------|-------|
| `id` | bigint PK | |
| `character_id` | bigint FK | |
| `stat` | enum(strength, defense, speed, dexterity) | |
| `sessions_today` | int | Resets lazily when `reset_date` < today |
| `reset_date` | date | Date sessions_today was last reset |
| `created_at` | timestamp | |
| `updated_at` | timestamp | |
Unique constraint on `(character_id, stat)`. One row per character per stat, upserted on each training session.
---
## Nerve Mechanics
### Role
Nerve is a secondary resource gating **crime volume**. Each crime attempt costs nerve (default 1, configurable per-crime). Energy still gates individual attempts as today. A player with full energy but 0 nerve cannot commit crimes.
### Regen
- **Trickle:** +1 nerve every 5 minutes (same interval as energy). Handled by a new `ProcessNerveRegen` artisan command on the scheduler.
- **Midnight refill:** `nerve` set to `max_nerve` for all characters at `00:00` daily. Handled by a new `NerveMidnightRefill` artisan command.
### Max nerve scaling
Starts at 100. Grows by **+2 per level-up** (slower than max_energy's +5, since nerve caps daily crime volume rather than individual actions).
### Crime integration
`CrimeController@attempt` checks nerve after the existing energy check. If insufficient, returns an error. On success, deducts nerve and records `nerve_cost` on the `CrimeAttempt`.
---
## Gym Mechanics
### Route
`POST /game/gym/train` → `GymController@train`
`GET /game/gym` → `GymController@index` (the gym page)
### Training flow
1. Player selects a stat and submits the Train form.
2. Controller verifies: character is active (not in jail/hospital), has ≥ 20 energy, daily cap not reached for that stat.
3. `TrainingService::train($character, $stat)` executes:
- Deduct 20 energy from character.
- Increment the chosen stat by 1.
- Upsert `gym_sessions` row: if `reset_date` < today, reset `sessions_today` to 0 first; then increment.
4. Flash success/error message, redirect back to gym page.
### Daily cap
5 sessions per stat per day (configurable via `config/game.php` `gym.daily_cap`). Reset is **lazy** — no scheduled command; checked on each train attempt by comparing `reset_date` to today.
### Stat gain
Flat +1 per session. No diminishing returns in this iteration.
### Energy cost
20 energy per session. Uses existing `check.energy:gym` middleware (requires adding `gym` to `config/game.php` `energy.action_costs`).
---
## Config (`config/game.php`)
Add two new blocks:
```php
'nerve' => [
'start_amount' => 100,
'max' => 100,
'regen_rate' => 1, // nerve per tick
'regen_interval' => 300, // seconds (5 minutes)
'midnight_refill' => true,
],
'gym' => [
'energy_cost' => 20,
'daily_cap' => 5, // sessions per stat per day
'stat_gain' => 1, // stat points per session
],
```
Also add to existing `energy.action_costs`:
```php
'gym' => 20,
```
---
## Character Model Changes
Add to `$fillable`: `nerve`, `max_nerve`, `nerve_regen_at`
Add to `$casts`: `nerve_regen_at` → `datetime`
New methods (mirroring existing energy methods):
- `hasNerve(int $amount): bool`
- `consumeNerve(int $amount): bool`
- `addNerve(int $amount): void`
- `getNervePercentageAttribute(): float`
`levelUp()` updated to increment `max_nerve` by 2.
---
## New Files
| File | Purpose |
|------|---------|
| `app/Http/Controllers/Game/GymController.php` | `index()` and `train()` actions |
| `app/Services/Game/TrainingService.php` | Training logic, daily cap check, stat increment |
| `app/Models/GymSession.php` | Eloquent model for `gym_sessions` |
| `app/Console/Commands/Game/ProcessNerveRegen.php` | Trickle regen command |
| `app/Console/Commands/Game/NerveMidnightRefill.php` | Midnight full refill command |
| `resources/views/game/gym/index.blade.php` | Gym page (dark crime theme) |
| `database/migrations/..._add_nerve_to_characters_table.php` | nerve columns on characters |
| `database/migrations/..._add_nerve_cost_to_crimes_table.php` | nerve_cost on crimes |
| `database/migrations/..._add_nerve_cost_to_crime_attempts_table.php` | nerve_cost on crime_attempts |
| `database/migrations/..._create_gym_sessions_table.php` | gym_sessions table |
---
## Existing Files Modified
| File | Change |
|------|--------|
| `config/game.php` | Add nerve + gym blocks; add gym to energy.action_costs |
| `app/Models/Character.php` | Add nerve fields, methods, levelUp() update |
| `app/Http/Controllers/Game/CrimeController.php` | Add nerve check + deduction in `attempt()` |
| `app/Services/Game/CrimeService.php` | Record nerve_cost on CrimeAttempt |
| `routes/game.php` | Add gym routes |
| `bootstrap/app.php` (or Kernel) | Register two new scheduled commands |
| `resources/views/game/layouts/game.blade.php` (or nav partial) | Add Gym link to sidebar |
| `resources/views/game/partials/` | Add nerve bar partial (mirrors energy-bar) |
| `resources/views/game/dashboard.blade.php` | Show nerve bar alongside energy + health |
---
## UI — Gym Page
Dark crime theme, consistent with existing pages. Shows:
- Four stat cards (Strength, Defense, Speed, Dexterity)
- Each card: current stat value, sessions used today / daily cap, energy cost per session
- Train button: disabled (greyed) if capped or insufficient energy, active otherwise
- Flash message area for success/failure feedback
---
## Scheduler Registration
```php
Schedule::command('game:nerve-regen')->everyFiveMinutes();
Schedule::command('game:nerve-midnight-refill')->dailyAt('00:00');
```
---
## Out of Scope (this iteration)
- Diminishing returns on stat gains
- Money cost for training
- Nerve cost > 1 for specific crimes (column added, seeded at 1 for all)
- Nerve displayed on crime cards (can add later)