<?phpnamespace App\Sim;/** Makes the World of a level definition (Levels). */final class LevelBuilder{ public static function build(array $def): World { $t = new Terrain((int)$def['width'], Levels::HEIGHT); foreach ($def['shapes'] as $s) { [$kind, $a, $b, $c, $d] = $s; switch ($kind) { case 'E': $t->fillRect($a, $b, $c, $d, Terrain::EARTH); break; case 'S': $t->fillRect($a, $b, $c, $d, Terrain::STEEL); break; case 'C': $t->fillRect($a, $b, $c, $d, Terrain::EMPTY); break; case 'O': self::ellipse($t, $a, $b, $c, $d, Terrain::EARTH); break; case 'H': self::ellipse($t, $a, $b, $c, $d, Terrain::EMPTY); break; } } self::roughen($t); $t->changes = []; $w = new World($t); $w->total = (int)$def['total']; $w->need = (int)$def['need']; $w->rate = $w->minRate = (int)$def['rate']; $w->ticksLeft = (int)$def['time'] * World::TICKS_PER_SECOND; foreach ($def['skills'] as $skill => $n) { $w->skills[$skill] = (int)$n; } $w->liquids = $def['liquids'] ?? []; $w->hatch = $def['hatch']; [$ex, $ey] = $def['exit']; $ey -= 6; while ($ey < $t->height - 1 && !$t->solid($ex, $ey)) { $ey++; } $w->exit = [$ex, $ey]; return $w; } private static function ellipse(Terrain $t, int $cx, int $cy, int $rx, int $ry, int $material): void { for ($dy = -$ry; $dy <= $ry; $dy++) { $dx = (int)floor($rx * sqrt(max(0, 1 - ($dy * $dy) / ($ry * $ry)))); $t->fillSpan($cy + $dy, $cx - $dx, $cx + $dx, $material); } } /** Natural ground: the top of the earth loses 0 to 2 pixels, in soft waves. */ private static function roughen(Terrain $t): void { for ($x = 0; $x < $t->width; $x++) { $n = (int)floor(1.25 + 1.2 * sin($x * 0.19) * sin($x * 0.053 + 1.3) + 0.5 * sin($x * 0.71)); if ($n <= 0) { continue; } for ($y = 1; $y < $t->height; $y++) { if ($t->mask[$y * $t->width + $x] === "\1" && $t->mask[($y - 1) * $t->width + $x] === "\0") { for ($k = 0; $k < min(2, $n); $k++) { if ($t->at($x, $y + $k) === Terrain::EARTH) { $t->fillSpan($y + $k, $x, $x, Terrain::EMPTY); } } $y += 3; } } } }}