KO

Slave Rift

tueb2024 제작 · 슈팅
5.0 (1)
▶ 플레이 💬 후기 소스 🕘 버전 편집 GitHub
games/slave-rift / src / Gfx / PosterFactory.php · commit 9ea6094
<?phpnamespace App\Gfx;/** Build-time-free procedural wall posters, shared by native OpenGL and WASM/WebGL. */final class PosterFactory{    public const ATLAS_COLS = 4;    public const ATLAS_ROWS = 4;    public const TILE_W = 200;    public const TILE_H = 290;    /**     * 4x4 atlas. Four cells keep the banal "VOTRE PUB ICI" gag; the other     * twelve are generated by the DeepSeek MinitelRosePoster supplied for the game.     */    public static function advert(): \GdImage    {        $atlas = imagecreatetruecolor(self::ATLAS_COLS * self::TILE_W, self::ATLAS_ROWS * self::TILE_H);        imagealphablending($atlas, true);        imagesavealpha($atlas, true);        imagefill($atlas, 0, 0, imagecolorallocate($atlas, 16, 8, 24));        $rose = new MinitelRosePoster();        for ($i = 0; $i < self::ATLAS_COLS * self::ATLAS_ROWS; $i++) {            if ($i % 4 === 0) {                $tile = self::advertTile($i);            } else {                $tile = $rose->generate(361500 + $i * 97);                self::zoomPixelatePortrait($tile);            }            $x = ($i % self::ATLAS_COLS) * self::TILE_W;            $y = intdiv($i, self::ATLAS_COLS) * self::TILE_H;            imagecopyresized(                $atlas,                $tile,                $x,                $y,                0,                0,                self::TILE_W,                self::TILE_H,                imagesx($tile),                imagesy($tile),            );            imagedestroy($tile);        }        return $atlas;    }    /**     * Keep DeepSeek's generated portrait, but make it deliberately anonymous:     * crop tightly into the face, collapse it to a 10x10 source and blow it back     * up with nearest-neighbour blocks. At 320x180 it reads as "Minitel rose",     * not as a recognisable person.     */    private static function zoomPixelatePortrait(\GdImage $im): void    {        $tiny = imagecreatetruecolor(10, 10);        imagecopyresized($tiny, $im, 0, 0, 100, 90, 10, 10, 200, 200);        imagecopyresized($im, $tiny, 50, 45, 0, 0, 300, 300, 10, 10);        imagedestroy($tiny);        $frame = imagecolorallocate($im, 255, 96, 160);        imagerectangle($im, 48, 43, 351, 346, $frame);    }    private static function advertTile(int $variant): \GdImage    {        $im = imagecreatetruecolor(MinitelRosePoster::WIDTH, MinitelRosePoster::HEIGHT);        imagealphablending($im, true);        imagesavealpha($im, true);        $ink = imagecolorallocate($im, 18, 10, 28);        $paper = imagecolorallocate($im, 245, 214 - ($variant % 3) * 12, 70 + ($variant % 2) * 18);        $pink = imagecolorallocate($im, 242, 52, 151);        $cyan = imagecolorallocate($im, 55, 226, 226);        $white = imagecolorallocate($im, 250, 245, 224);        imagefilledrectangle($im, 0, 0, 399, 579, $paper);        imagefilledrectangle($im, 14, 14, 385, 565, $ink);        imagefilledrectangle($im, 26, 30, 373, 178, $pink);        imagefilledrectangle($im, 26, 194, 373, 486, $white);        imagefilledrectangle($im, 26, 508, 373, 536, $cyan);        // The wall poster is viewed from several metres away in a 320x180 game.        // Use deliberately huge bitmap typography so the gag reads immediately.        self::drawTextScaled($im, 'VOTRE', 200, 62, $white, 5);        self::drawTextScaled($im, 'PUB ICI', 200, 248, $ink, 5);        self::drawTextScaled($im, 'EMPLACEMENT', 200, 382, $pink, 2);        self::drawTextScaled($im, 'DISPONIBLE', 200, 420, $pink, 2);        self::drawTextScaled($im, '3615 / RESERVE', 200, 520, $ink, 1);        imagefilledrectangle($im, 38, 210, 72, 244, $cyan);        imagefilledrectangle($im, 328, 438, 362, 472, $cyan);        imagerectangle($im, 5, 5, 394, 574, $pink);        return $im;    }    private static function drawTextScaled(\GdImage $im, string $text, int $cx, int $y, int $color, int $scale): void    {        $font = 5;        $w = imagefontwidth($font) * strlen($text);        $h = imagefontheight($font);        $tmp = imagecreatetruecolor(max(1, $w), $h);        $white = imagecolorallocate($tmp, 255, 255, 255);        $black = imagecolorallocate($tmp, 0, 0, 0);        imagefill($tmp, 0, 0, $white);        imagestring($tmp, $font, 0, 0, $text, $black);        $drawW = $w * $scale;        $x0 = $cx - intdiv($drawW, 2);        for ($py = 0; $py < $h; $py++) {            for ($px = 0; $px < $w; $px++) {                if ((imagecolorat($tmp, $px, $py) & 0xFF) < 128) {                    imagefilledrectangle(                        $im,                        $x0 + $px * $scale,                        $y + $py * $scale,                        $x0 + ($px + 1) * $scale - 1,                        $y + ($py + 1) * $scale - 1,                        $color,                    );                }            }        }        imagedestroy($tmp);    }}