로그인
KO
games/open-lemmings / src / Sim / LevelBuilder.php · commit 04e2e2a
<?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;                }            }        }    }}