RAINBOW ISLANDS

par davidnurdin · Remakes
pas encore noté
▶ Jouer Sources Éditer GitHub
games/rainbow-island / src / Arcade / ArcadeFont.php · commit d529485
<?phpnamespace App\Arcade;use JumPHP\Renderer\BitmapFont;use JumPHP\Renderer\Renderer;use JumPHP\Renderer\Sdl;use JumPHP\Renderer\TextDrawer;use JumPHP\Util\Color;/** * The font of the original game (assets/fonts/font.png, 8x8 glyphs extracted from the disc). * * The CPC version prints its texts in five colour schemes (each glyph has 2-3 shades); the sheet has * one row per scheme, and draw() picks the scheme closest to the colour asked for. Characters the * original font does not have (lower case is upper-cased, '<' '>' of the menus...) fall back to the * engine's built-in font. */final class ArcadeFont implements TextDrawer{    public const RED = [255, 0, 0];    public const GREEN = [0, 255, 0];    public const CYAN = [0, 255, 255];    public const YELLOW = [255, 255, 128];    public const WHITE = [255, 255, 255];    /** Glyph order of the sheet; '@' is the copyright sign. */    private const CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ !\"'+,-.?@()";    private const SCHEMES = [self::RED, self::GREEN, self::CYAN, self::YELLOW, self::WHITE];    private const ACCENTS = ['À' => 'A', 'Â' => 'A', 'Ä' => 'A', 'Ç' => 'C', 'É' => 'E', 'È' => 'E', 'Ê' => 'E',        'Ë' => 'E', 'Î' => 'I', 'Ï' => 'I', 'Ô' => 'O', 'Ö' => 'O', 'Ù' => 'U', 'Û' => 'U', 'Ü' => 'U', '©' => '@'];    private const SIZE = 8;    private BitmapFont $fallback;    public function __construct(private Sdl $sdl)    {        if (!$sdl->hasTexture('font')) {            $sdl->loadTexture('font', 'assets/fonts/font.png');        }        $this->fallback = new BitmapFont($sdl);    }    public function draw(Renderer $r, string $text, float $x, float $y, float $scale = 1, Color|array|string $color = self::WHITE,                         string $align = 'left', Color|array|string|null $shadow = null): void    {        $text = $this->normalize($text);        $texture = $this->sdl->getTexture('font')['texture'];        $row = $this->scheme(Color::of($color));        $size = self::SIZE * $scale;        foreach (explode("\n", $text) as $n => $line) {            $left = $x;            if ($align !== 'left') {                $left -= $align === 'center' ? $this->width($line, $scale) / 2 : $this->width($line, $scale);            }            $top = $y + $n * $this->lineHeight($scale);            for ($i = 0, $len = strlen($line); $i < $len; $i++) {                $char = $line[$i];                if ($char === ' ') {                    continue;                }                $glyph = strpos(self::CHARS, $char);                if ($glyph === false) {                    $this->fallback->draw($r, $char, $left + $i * $size + $scale, $top + $scale, $scale, $color);                    continue;                }                $r->copy($texture, [$glyph * self::SIZE, $row * self::SIZE, self::SIZE, self::SIZE], $left + $i * $size, $top, $size, $size);            }        }    }    /** The big titles of the original: glyphs stretched to twice their height, one space between letters. */    public function drawTall(Renderer $r, string $text, float $x, float $y, Color|array|string $color, string $align = 'center'): void    {        $text = implode(' ', str_split($this->normalize($text)));        $texture = $this->sdl->getTexture('font')['texture'];        $row = $this->scheme(Color::of($color));        $left = $align === 'center' ? $x - $this->width($text) / 2 : $x;        for ($i = 0, $len = strlen($text); $i < $len; $i++) {            $glyph = strpos(self::CHARS, $text[$i]);            if ($text[$i] !== ' ' && $glyph !== false) {                $r->copy($texture, [$glyph * self::SIZE, $row * self::SIZE, self::SIZE, self::SIZE], $left + $i * self::SIZE, $y, self::SIZE, self::SIZE * 2);            }        }    }    public function width(string $text, float $scale = 1): int    {        $lines = explode("\n", $this->normalize($text));        return (int)round(max(array_map('strlen', $lines)) * self::SIZE * $scale);    }    public function height(float $scale = 1): int    {        return (int)round(self::SIZE * $scale);    }    public function lineHeight(float $scale = 1): int    {        return (int)round(self::SIZE * $scale);    }    private function normalize(string $text): string    {        return strtoupper(strtr(mb_strtoupper($text, 'UTF-8'), self::ACCENTS));    }    /** The row of the sheet whose colour is the nearest to $color. */    private function scheme(Color $color): int    {        $best = 0;        $bestDistance = PHP_INT_MAX;        foreach (self::SCHEMES as $row => [$red, $green, $blue]) {            $distance = ($color->r - $red) ** 2 + ($color->g - $green) ** 2 + ($color->b - $blue) ** 2;            if ($distance < $bestDistance) {                $best = $row;                $bestDistance = $distance;            }        }        return $best;    }}