# City10 — Cidadãos Persistentes, Economia Social e Expansão Territorial **Data:** 2026-10-18 **Status:** Aprovado pelo usuário (design + spec + implementação em um ciclo) ## 1. Objetivo Cada usuário registrado e **aprovado pelo Admin** ganha um **personagem (Character)** próprio na plataforma 3D, capaz de viver como um humano simulado: mover-se, construir e habitar, trabalhar, comer, estudar, comprar/vender, dormir/acordar, fazer amigos, formar família e ter filhos, conversar com outros personagens. O mundo **persiste em nuvem sem reiniciar** quando alguém entra ou sai, progride continuamente via ticker agendado, e suporta **expansão de território** e **criação de novas cidades**. ## 2. Aprovação de contas e personagem - `users.status`: `pending` | `active` | `banned` (campo novo; `is_active` permanece espelhando `status === active` para compatibilidade). - `POST /register` cria usuário como `pending` (sem login no mundo até aprovação; login pendente mostra tela "aguardando aprovação"). - Admin: rota `/admin/citizens` — listar, aprovar (`active`), banir (`banned`). - Ao transitar para `active`, cria-se `Character` automaticamente (factory: nome = first name do usuário, aparência aleatória, spawn no centro de `cities` ativa). - Usuários existentes com `is_active=true` migram para `status=active`; character é criado sob demanda no primeiro acesso se faltar (lazy-seed). ## 3. Modelo de dados ### cities `id, name, slug, origin_x/z (não usado — bounds), bounds_min_x, bounds_max_x, bounds_min_z, bounds_max_z, is_active, created_at, updated_at` - Seed: `City10` com bounds equivalentes ao grid atual (−80..80). ### characters `id, user_id (unique), city_id, name, skin, hair, shirt, pos_x, pos_y, pos_z, money (decimal), energy (0-100), hunger (0-100), thirst (0-100), state (awake|sleeping|working|studying|eating), job_id (nullable), house_id (nullable), education (0-100), is_online (boolean), last_tick_at, created_at, updated_at` ### buildings `id, city_id, owner_character_id, type (house|shop_extension|school|restaurant), name, pos_x, pos_z, width, depth, height, is_complete, build_progress (0-100), created_at, updated_at` ### relationships `id, character_a_id, character_b_id, type (friend|spouse|parent_child), status (pending|accepted), created_at` - `parent_child`: `character_a` = filho, `character_b` = pai/mãe (para filhos NPCs: child pode ter `user_id` null via character órfão — na prática filhos são `characters` sem `user_id`). ### inventory_items `id, character_id, item (food|water|materials|product), qty, created_at, updated_at` (unique character_id+item) ### transactions `id, from_character_id, to_character_id (nullable = loja/NPC), store_id (nullable), item, qty, amount, created_at` ### jobs `id, city_id, store_id (nullable), title, salary, requires_education (0-100), created_at, updated_at` - Seed: Padeiro (Padaria), Mercado, Prefeitura, Escola. ### children_npc Filhos nascem como `characters` com `user_id = NULL`, `state=awake`, idade simulada via `created_at` + `growth_days` opcional. Sem login. ## 4. Ticker de mundo (`city:tick`) Comando Artisan agendado a cada 1 minuto (`routes/console.php` → `Schedule::command('city:tick')->everyMinute()`): Para cada character com `state != 'dead'` (não existe morte): 1. **Necessidades:** `hunger -= 0.5`, `thirst -= 0.8`, `energy -= 0.4` por tick (proporção ao delta real de tempo desde `last_tick_at`, com cap). 2. **Dormindo** (`state=sleeping`): `energy += 15` até 100; se energy>=100 → `state=awake`. 3. **Trabalhando** (`state=working` com `job_id`): creditar `salary/30` (dia de trabalho ≈ 30 ticks) em `money`; consumir energy extra. 4. **Estudando:** `education += 0.5` até 100, consome energy. 5. **Fome/sede severas** (hunger<=0 ou thirst<=0): energy drena 2× e `state` vira `awake` (não morre). 6. **IA offline simples:** se `is_online=false` e energy<20 e house existe → dormir; se energy>80 e job existe e horário “dia” (tick % 1440 entre 480–1080) → trabalhar; senão awake. 7. **Filhos:** characters sem `user_id` com `education` crescendo automaticamente se `age_days < 18*365` (simplificado: `growth` +1/tick até cap de adulto). `city:tick` é idempotente por `last_tick_at` (skip se < 50s). ## 5. Gameplay (ações do jogador) Endereços web (sessão) + espelho API Sanctum opcional: | Ação | Rota | Efeito | |---|---|---| | Comer | `POST /city/actions/eat` | consome 1 food do inventário; hunger +40 | | Beber | `POST /city/actions/drink` | consome water; thirst +40 | | Dormir | `POST /city/actions/sleep` | state=sleeping (exige casa completa) | | Acordar | `POST /city/actions/wake` | state=awake | | Trabalhar | `POST /city/actions/work` | state=working (exige emprego) | | Parar trabalho | `POST /city/actions/stop-work` | state=awake | | Estudar | `POST /city/actions/study` | state=studying (exige school na cidade) | | Comprar | `POST /city/actions/buy` | store → inventário, money−preço | | Vender | `POST /city/actions/sell` | inventário → money | | Construir casa | `POST /city/actions/build` | valida terreno livre em quadra + `materials ≥ 50`; cria building `build_progress=0` e **de**bita os materials no ato; ticker avança `build_progress += 10` por tick até 100 → `is_complete=true` e vira `house_id` do dono se ainda não tiver casa | | Amizade | `POST /city/social/friend` + `accept` | relationship friend pending/accepted | | Casamento | `POST /city/social/marry` | ambos accepted friends → spouse (1 esposo) | | Filho | `POST /city/social/child` | exige casal spouse; cria character NPC criança | | Chat | `POST /city/chat/{character}` (estender fluxo existente) | mensagens persistidas em `character_messages` (nova tabela simples: id, from, to, body, created_at) | Validações em Form Requests; autorização via policy/ownership (só o próprio character). ## 6. Viewer 3D (evolução do existente) - Rota `GET /city` usa `?city=` (slug/id; default ativa). - `/city/data` retorna também `characters` (posição, estado, nome, money/needs do próprio), `buildings`, `jobs`, bounds da cidade. - Controla **character do usuário**: WASD grava posição localmente e **persiste** (throttled POST `/city/actions/position`, 1–2 s). - HUD: fome, sede, energia, dinheiro, estado. - Menu de ações (botões) chamando as rotas de ações; inventário simples. - Cidadãos (characters com user ou NPC filhos) renderizados como humanoides; `is_online` destaca. - Terrenos livres: quadras sem building de casa → painel “Construir aqui” quando próximo. - Limites da cidade aplicados (clipe nos bounds da cidade ativa). ## 7. Expansão e multi-cidade - Admin `POST /admin/cities` cria cidade nova (bounds default 160×160) + layout grid gerado no cliente a partir de bounds (estender `CityViewerController::cityLayout` para usar bounds da cidade, não só stores). - Admin `POST /admin/cities/{city}/expand` aumenta `bounds_max_*` em 40 (uma quadra) — limitado a 10 expansões/cidade (coluna `expansions`). - Viewer e ticker filtram por `city_id`. ## 8. Persistência em nuvem - Estado exclusivamente em MariaDB; `city:tick` via cron `* * * * * php artisan schedule:run` documentado no README de deploy. - Entrar/sair não reseta nada; múltiplas sessões simultâneas: posição mais recente vence (`updated_at`). - Fila opcional não é obrigatória neste ciclo. ## 9. LGPD e regras do repo - Sem dados pessoais em payloads de IA; chat entre characters é dado do usuário (consentimento LGPD já coberto pelos termos). - Nenhuma remoção de proteção LGPD; CAPTCHA mantido; CSRF; i18n completa (pt_BR/en/es) para TODAS as strings novas. - PHPDoc em inglês; Pint; testes PHPUnit nos fluxos; licenças registradas em RULES.md se houver dependência nova (não planejado). ## 10. Testes e critérios de aceite **PHPUnit:** - Registro cria `pending`; login pendente bloqueado com mensagem. - Admin aprova → `active` + character criado. - Tick degrada necessidades; dormir recupera energy; trabalhar paga salário; comer/beber funcionam com inventário. - Comprar/vender alteram money/inventário/transactions. - Construir valida terreno e materiais; progresso avança no tick. - Amizade → casamento → filho cria character NPC. - Expansão de bounds e criação de cidade pelo admin. **Playwright MCP:** - Fluxo completo: register → pending → login vê espera → admin aprova → usuário entra no city com character → HUD necessidades → comer/dormir/trabalhar via UI → construir casa → adicionar amigo (2º usuário) → chat → admin expande território → nova cidade visível. - Console sem erros; screenshots de evidência; corrigir bugs até limpo. ## 11. Fora de escopo Morte permanente, combate, Reverb realtime, OpenRouter obrigatório no tick, moeda fiat real, mobile nativo.