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
@@ -9,6 +9,7 @@ use App\Models\City;
use App\Models\InventoryItem;
use App\Models\Job;
use App\Models\Transaction;
use App\Support\Airports;
use App\Support\Beach;
use App\Support\CityPlazas;
use App\Support\MissionTracker;
@@ -53,7 +54,15 @@ class CityActionController extends Controller
// but never beyond the shoreline.
$maxX = Beach::sandTo($city) + 2;
$x = max((float) $city->bounds_min_x, min($maxX, (float) $data['pos_x']));
$z = max((float) $city->bounds_min_z, min((float) $city->bounds_max_z, (float) $data['pos_z']));
// Airport grounds (north/south strips) are walkable beyond the
// city bounds inside their X band.
$minZ = (float) $city->bounds_min_z;
$maxZ = (float) $city->bounds_max_z;
if (abs($x) <= Airports::VISIT_HALF_WIDTH) {
$minZ = Airports::VISIT_MIN_Z;
$maxZ = Airports::VISIT_MAX_Z;
}
$z = max($minZ, min($maxZ, (float) $data['pos_z']));
$character->forceFill([
'pos_x' => $x,