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.
99 lines
3.8 KiB
PHP
99 lines
3.8 KiB
PHP
<?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'];
|
||
}
|
||
}
|