<?phpnamespace App\Service;use JumPHP\Kernel\Engine;/** The game's pictures (assets/, drawn by tools/art.py), loaded once. */final class Art{ public const THEMES = ['forest', 'cave', 'forge', 'night', 'desert', 'ice', 'jungle', 'sky', 'autumn', 'reef', 'clockwork']; /** @var array<string, array<string, mixed>> */ public array $themes = []; /** @var array<string, int> icon name => index in sprites/icons*.png (12 px cells) */ public array $icons = []; private bool $loaded = false; public function __construct(public readonly Effects $effects) { } /** 1: retro pictures (assets/), 3: the HD ones (assets/hd/, 3 times finer, drawn at the same size) */ public int $scale = 1; private ?Engine $engine = null; /** @var array<string, int> pictures whose scale is not $scale */ private array $scales = []; public function hdOn(Engine $engine): bool { return (bool)$engine->options->get('hd_art', !\JumPHP\Kernel\Platform::isMobile()); } /** Switches between retro and HD pictures: everything is loaded again. */ public function toggleHd(Engine $engine): void { $engine->options->set('hd_art', !$this->hdOn($engine)); $this->loaded = false; $this->load($engine); } public function load(Engine $engine): void { $this->effects->apply($engine); if ($this->loaded) { return; } $this->loaded = true; $this->engine = $engine; $sdl = $engine->sdl; $hd = $this->hdOn($engine); $this->scale = $hd ? 3 : 1; $dir = $hd ? 'assets/hd' : 'assets'; // HD pictures are scaled smoothly (the window is rarely exactly 3 times the view); retro ones stay crisp \SDL_SetHint(\SDL_HINT_RENDER_SCALE_QUALITY, $hd ? '1' : '0'); $this->themes = (array)json_decode((string)file_get_contents($engine->config->path('assets/themes.json')), true); $this->icons = array_flip((array)json_decode((string)file_get_contents($engine->config->path('assets/icons.json')), true)); foreach (self::THEMES as $t) { $sdl->loadTexture("bg-$t-sky", "$dir/bg/$t-sky." . ($hd ? 'jpg' : 'png')); foreach (['far', 'near'] as $layer) { $sdl->loadTexture("bg-$t-$layer", "$dir/bg/$t-$layer.png"); } } $sprites = ['lumi', 'hatch', 'exit', 'icons', 'icons-dim', 'icons-gold', 'logo', 'hud', 'btn', 'btn-on', 'btn-off', 'btn-small', 'btn-small-on', 'btn-wide', 'btn-wide-on', 'vignette', 'cursor', 'cursor-on', 'flash', 'glow-amber', 'glow-cyan', 'glow-white', 'glow-red', 'glow-violet', 'glow-green']; // icons and the cursor stay pixel art in both sets: sharp at any size (never smoothed) $crisp = ['cursor', 'cursor-on']; $this->scales = []; foreach ($sprites as $name) { if (in_array($name, $crisp, true)) { continue; } $sdl->loadTexture($name, "$dir/sprites/$name.png"); } // the hint is read when a texture is made: back to crisp for everything made later (texts, icons) \SDL_SetHint(\SDL_HINT_RENDER_SCALE_QUALITY, '0'); foreach ($crisp as $name) { $sdl->loadTexture($name, "assets/sprites/$name.png"); $this->scales[$name] = 1; } $this->scales['vignette'] = $hd ? 2 : 1; if ($hd) { \SDL_SetHint(\SDL_HINT_RENDER_SCALE_QUALITY, '1'); $sdl->loadTexture('shadow', 'assets/hd/sprites/shadow.png'); \SDL_SetHint(\SDL_HINT_RENDER_SCALE_QUALITY, '0'); } } /** The folder of the tiles of the current set. */ public function tile(string $name): string { return ($this->scale > 1 ? 'assets/hd' : 'assets') . "/tiles/$name.png"; } /** * Draws a picture in retro pixels whatever the set: $src (in retro pixels) and the size are those of the retro * picture, the HD one is just finer. */ public function draw(\JumPHP\Renderer\Renderer $r, string $name, ?array $src, float $x, float $y, ?float $w = null, ?float $h = null, bool $flip = false): void { $t = $this->engine->sdl->getTexture($name); $k = $this->scales[$name] ?? $this->scale; if ($src === null) { $r->copy($t['texture'], null, $x, $y, $w ?? $t['width'] / $k, $h ?? $t['height'] / $k, $flip); return; } $r->copy($t['texture'], [$src[0] * $k, $src[1] * $k, $src[2] * $k, $src[3] * $k], $x, $y, $w ?? $src[2], $h ?? $src[3], $flip); } /** @return array{0: float, 1: float} the size of a picture in retro pixels */ public function size(string $name): array { $t = $this->engine->sdl->getTexture($name); $k = $this->scales[$name] ?? $this->scale; return [$t['width'] / $k, $t['height'] / $k]; } /** Draws an icon by name, 12 x $scale pixels wide (the sheets have 16 px cells). */ public function icon(\JumPHP\Renderer\Renderer $r, Engine $engine, string $name, float $x, float $y, string $variant = '', float $scale = 1): void { $i = $this->icons[$name] ?? 0; $this->draw($r, 'icons' . $variant, [$i * 16, 0, 16, 16], $x, $y, 12 * $scale, 12 * $scale); }}