Files
City10/app/Support/Port.php
T
marcocosta10000 580c830ffb feat(port): add sea port with quay, terminal, berths, and AI port master
Adds a new port district on the north-east coast with walkable quay, container yard, terminal, gantry cranes, and berths. Includes AI-driven port master (Capitão do Porto) and dock workers for ship dispatch, plus full frontend rendering of ships, boats, and trucks.
2026-10-31 23:33:06 -03:00

99 lines
3.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Support;
use App\Models\City;
/**
* Sea port on the north-east coast (past the beach, on the same east
* shoreline).
*
* The port occupies only the FAR HALF of that side (z −430..−290): the
* near half (z −290..−150) is intentionally left empty so the city can
* keep growing north/east without ever colliding with the port lands.
*
* Geometry derives from the Beach waterline so the quay always meets
* the sea, and feeds the world payload: quay, container yard, terminal,
* gantry cranes, berths and the fleet descriptions. Ships' movements are
* authorised by the port AI agent (Capitão do Porto) through
* /city/port/dispatch — same pattern as the airports.
*/
class Port
{
/** West edge of the walkable port strip (east of the city bounds). */
public const VISIT_MIN_X = 150.0;
/** Visit Z range: from the port up to the start of the beach sand. */
public const VISIT_MIN_Z = -430.0;
public const VISIT_MAX_Z = -290.0;
/**
* Full port descriptor for the world payload.
*
* @return array<string, mixed>
*/
public static function geometry(City $city): array
{
$waterline = Beach::sandTo($city); // 264 for the shipped bounds
$gridEast = (float) (ceil(((float) $city->bounds_max_x) / 40) * 40 + 40); // 200
$quayX = $waterline + 2; // quay straddles the shore
$yardWest = $gridEast + 4;
$yardEast = $waterline - 6;
$yardCenterX = ($yardWest + $yardEast) / 2;
return [
'visit' => [
'min_x' => self::VISIT_MIN_X,
'max_x' => $quayX + 2, // walk to the quay edge (266)
'min_z' => self::VISIT_MIN_Z,
'max_z' => self::VISIT_MAX_Z,
],
'water' => [
// The east sea extends north to cover the port basin
// (initBeach widens its plane down to this Z).
'z_min' => self::VISIT_MIN_Z - 10,
],
// Far-half layout (zone z −430..−290): terminal at the city
// (south) gate, yard behind it, quay along the shore.
'quay' => ['center' => [$quayX, -360.0], 'size' => [8.0, 130.0]],
'yard' => ['center' => [$yardCenterX, -362.0], 'size' => [$yardEast - $yardWest, 116.0]],
'terminal' => ['center' => [$yardCenterX, -297.0], 'size' => [46.0, 14.0]],
'cranes' => [
['center' => [$quayX, -330.0]],
['center' => [$quayX, -395.0]],
],
// Berth centres spaced so full hulls never touch: 60-unit
// container ship + 88-unit liner need >=82 between centres.
'berths' => [
['id' => 1, 'center' => [$quayX + 16, -310.0]],
['id' => 2, 'center' => [$quayX + 20, -393.0]],
],
'ships' => [
['id' => 'c1', 'name' => 'CITY VOYAGER', 'company' => 'City10 Cargo', 'kind' => 'container', 'berth' => 1],
['id' => 'c2', 'name' => 'AZUL MAR', 'company' => 'AzulCéu Cargo', 'kind' => 'container', 'berth' => null],
['id' => 'l1', 'name' => 'ATLÂNTICO CELESTE', 'company' => 'NorteLine', 'kind' => 'liner', 'berth' => 2],
],
'boats' => [
['name' => 'Lancha V1'],
['name' => 'Lancha V2'],
['name' => 'Lancha V3'],
],
];
}
/**
* Whether a point is on the walkable port grounds/quay.
*/
public static function isOnPort(City $city, float $x, float $z): bool
{
$visit = self::geometry($city)['visit'];
return $x >= $visit['min_x']
&& $x <= $visit['max_x']
&& $z >= $visit['min_z']
&& $z <= $visit['max_z'];
}
}