From 97865f51a520669ac550cd92e151a67141f4230d Mon Sep 17 00:00:00 2001 From: Doar Mihai Date: Sun, 28 Jun 2026 15:24:36 +0300 Subject: [PATCH] docs: add nerve stat + gym training design spec Co-Authored-By: Claude Sonnet 4.6 --- .../specs/2026-06-28-nerve-gym-design.md | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-28-nerve-gym-design.md diff --git a/docs/superpowers/specs/2026-06-28-nerve-gym-design.md b/docs/superpowers/specs/2026-06-28-nerve-gym-design.md new file mode 100644 index 0000000..2b8d00a --- /dev/null +++ b/docs/superpowers/specs/2026-06-28-nerve-gym-design.md @@ -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) \ No newline at end of file