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.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
<?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'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user