Files
City10/app/Support/Beach.php
T
marcocosta10000 b8c31e7be4 feat(city): add east shore beach leisure activity
Characters can now relax on the beach sand to recover energy and
health. The server clamps position to the shoreline, exposes beach
geometry in the city payload, and the client renders animated waves,
sand, stalls, chairs, and ambient NPCs.

Includes Beach support class, new beach action endpoint, localization
strings, and feature tests.
2026-10-31 20:35:22 -03:00

88 lines
2.3 KiB
PHP

<?php
namespace App\Support;
use App\Models\City;
/**
* East-shore beach geometry shared by the world payload, the leisure
* action validation and the position clamp.
*
* Every value derives from the city bounds so the sand always starts just
* past the eastern road grid, whatever the city size is.
*/
class Beach
{
/** Gap between the last grid road and the sand. */
public const APPROACH = 4.0;
/** Sand depth from the approach line to the shoreline. */
public const SAND_DEPTH = 60.0;
/** Sea depth beyond the shoreline. */
public const SEA_DEPTH = 90.0;
/** Half of the beach span along Z (covers the whole layout). */
public const Z_HALF = 160.0;
/**
* Eastern edge of the road grid (same derivation the layout uses).
*/
public static function layoutMaxX(City $city): float
{
return (float) (ceil(((float) $city->bounds_max_x) / 40) * 40 + 40);
}
/**
* Where the sand begins (east of the grid).
*/
public static function sandFrom(City $city): float
{
return self::layoutMaxX($city) + self::APPROACH;
}
/**
* Shoreline (sand meets sea).
*/
public static function sandTo(City $city): float
{
return self::sandFrom($city) + self::SAND_DEPTH;
}
/**
* Far edge of the sea.
*/
public static function seaTo(City $city): float
{
return self::sandTo($city) + self::SEA_DEPTH;
}
/**
* Geometry descriptor sent to the world payload (single source of
* truth for the client-side beach build).
*
* @return array{sand_from: float, sand_to: float, sea_to: float, z_min: float, z_max: float}
*/
public static function geometry(City $city): array
{
return [
'sand_from' => self::sandFrom($city),
'sand_to' => self::sandTo($city),
'sea_to' => self::seaTo($city),
'z_min' => -self::Z_HALF,
'z_max' => self::Z_HALF,
];
}
/**
* Whether a point lies on the leisure beach (sand up to the shoreline).
*/
public static function isOnBeach(City $city, float $x, float $z): bool
{
return $x >= self::sandFrom($city) - 1
&& $x <= self::sandTo($city) + 2
&& $z >= -self::Z_HALF
&& $z <= self::Z_HALF;
}
}