feat(city): add airports, ATC agent, luminous signs, and AI model fallback chain

- Add two airports (north/south) with full geometry: runways, taxiways,
  aprons, terminals, hangars, stands, and helipads; walkable airport
  grounds extend beyond city bounds.
- Introduce ATC AI agent ("Torre de Controle City10") using AI_ATC_MODEL
  to generate takeoff clearances; includes dispatch endpoint with
  30-second cooldown and deterministic fallback on failure.
- Implement AI model fallback chain: preferred model → AI_FALLBACK_MODEL
  before failing, applied in ChatController and PersonTalkController.
- Add luminous sign labels (letreiros) for plazas, beach, food, hospital,
  school, city hall, and airports with day/night glow.
- Fix city presence cache: compute is_me per viewer instead of baking
  into shared per-second cache to prevent cross-user flag leakage.
- Add tests: AirportTest, CityPresenceTest, SignLabelsTest, and
  PersonTalkTest fallback model retry scenario.
This commit is contained in:
2026-10-31 22:06:18 -03:00
parent a12807492f
commit e7498e1566
26 changed files with 2226 additions and 108 deletions
+27
View File
@@ -2,6 +2,7 @@
namespace Database\Seeders;
use App\Models\AiAgent;
use App\Models\Building;
use App\Models\Character;
use App\Models\City;
@@ -9,6 +10,7 @@ use App\Models\Job;
use App\Models\Mission;
use App\Models\Store;
use App\Models\User;
use App\Support\Airports;
use Illuminate\Database\Seeder;
/**
@@ -40,6 +42,31 @@ class CityWorldSeeder extends Seeder
],
);
// --- Control tower agent (airport clearances) ---------------------
// Positioned at the north airport tower; model comes from
// AI_ATC_MODEL (poolside/laguna-xs-2.1:free by default) and is
// re-validated against the allow-list before use.
$tower = Airports::geometry($city)['sites'][0]['tower']['center'];
$atcModel = (string) config('ai.atc_model');
if (! in_array($atcModel, config('ai.allowed_models', []), true)) {
$atcModel = 'poolside/laguna-xs-2.1:free';
}
AiAgent::updateOrCreate(
['name' => 'Torre de Controle City10'],
[
'type' => 'atc',
'model' => $atcModel,
'system_prompt' => 'Você é a torre de controle aéreo do Aeroporto City10 Norte. '
.'Autorize decolagens e pousos com linguagem formal, curta e clássica de controle '
.'de tráfego aéreo, sempre em português, citando o voo, a companhia e a pista.',
'current_goal' => 'Gerenciar decolagens, pousos e o tráfego dos dois aeroportos com segurança',
'status' => 'active',
'pos_x' => (float) $tower[0],
'pos_y' => 0,
'pos_z' => (float) $tower[1],
],
);
$bakery = Store::where('type', 'bakery')->orderBy('id')->first();
$market = Store::where('type', 'market')->orderBy('id')->first();