Connexion
FR
games/doom-jumphp / src / Scene / TitleScene.php · commit 8e1cb9f
<?phpnamespace App\Scene;use App\Engine\Art;use App\Engine\Screen;use App\Engine\SoftRenderer;use App\Engine\Palette;use App\Game\Campaign;use App\Game\Graphics;use App\Game\HdView;use App\Game\SoundBank;use JumPHP\Kernel\Platform;use JumPHP\Renderer\Renderer;use JumPHP\Ui\MenuScene;/** * The title: the first level seen by a slowly turning camera (the real renderer), the logo, the main menu. * After a while without a key, the demo plays. */final class TitleScene extends MenuScene{    protected string $title = '';    protected float $menuTop = 0.55;    /** seconds without a key: the demo starts after a while (the camera has its own clock: the engine's) */    private float $idle = 0.0;    private ?SoftRenderer $view = null;    private ?Screen $screen = null;    private ?Art $art = null;    private ?\App\Engine\Map\Level $level = null;    /** @var mixed the logo's SDL texture (HD mode) */    private mixed $logo = null;    public function enter(array $params = []): void    {        if (isset($params['graphics'])) {            $this->engine->options->set('graphics', $params['graphics']);        }        parent::enter($params);        $this->art ??= Art::get($this->engine->config->path('assets/data/art.pak'));        $this->view ??= new SoftRenderer($this->art, 320, 200);        $this->screen ??= new Screen($this->engine);        $this->level ??= Campaign::level($this->engine->config->path('assets/maps/m1.json'), 'm1');        SoundBank::load($this->sound(), $this->engine->config);        $this->idle = 0.0;    }    protected function items(): array    {        $items = [            ['NEW GAME', null, fn () => $this->scenes()->push(SkillScene::class)],        ];        if ($this->engine->gl()->isAvailable()) {            $toggle = function (): void {                Graphics::toggle($this->engine);                $this->refresh();            };            $items[] = ['GRAPHICS', fn () => Graphics::mode($this->engine) === 'hd' ? 'HD' : 'RETRO', $toggle, fn (int $dir) => $toggle()];        }        $items = [            ...$items,            ['OPTIONS', null, fn () => $this->scenes()->push(OptionsScene::class)],            ['HOW TO PLAY', null, fn () => $this->scenes()->push(HelpScene::class)],        ];        if (Platform::isDesktop()) {            $items[] = ['QUIT', null, fn () => $this->engine->quit()];        }        return $items;    }    /** @return array<int, array> the rows of the main menu (tests) */    public function menuItems(): array    {        return $this->items();    }    public function update(float $dt): void    {        $this->idle += $dt;        $a = $this->actions();        if ($a->isPressed('up') || $a->isPressed('down') || $a->isPressed('left') || $a->isPressed('right') || $a->isPressed('confirm')) {            $this->idle = 0.0;        }        if ($this->idle > 30 && $this->scenes()->current() === $this) {            $this->scenes()->switchTo(PlayScene::class, ['demo' => true, 'level' => 'm1']);            return;        }        parent::update($dt);    }    public function exit(): void    {        Graphics::retro($this->engine);    }    protected function renderBackground(Renderer $r): void    {        $start = $this->level->things[0];        foreach ($this->level->things as $t) {            if ($t['type'] === 'player') {                $start = $t;            }        }        $sector = $this->level->sectorAt($start['x'], $start['y']);        // the engine's clock: the camera keeps turning while sub-menus come and go        $angle = $start['angle'] + sin($this->engine->time() * 0.25) * 1.2;        $z = $sector->floor + 48;        $logo = $this->art->hud['LOGO'];        $hd = Graphics::hd($this->engine);        if ($hd !== null) {            $r->clear([255, 0, 255]);            $hd->setTint([0, 0, 0, 0.35]);            $hd->render($this->level, $start['x'], $start['y'], $z, $angle, [], HdView::levelLights($this->level, $start['x'], $start['y']), null);            // the logo in 2D, over the 3D            $this->logo ??= $this->logoTexture($logo);            $r->copy($this->logo, null, (320 - $logo['w']) / 2, 24, $logo['w'], $logo['h']);        } else {            $this->view->extraLight = 0;            $this->view->render($this->level, $start['x'], $start['y'], $z, $angle);            $this->view->blit($logo, (int)((320 - $logo['w']) / 2), 24);            $this->screen->setTint([0, 0, 0, 0.45]);            $this->screen->present($r, $this->view->fb, 320, 200, '', 0);        }        $this->font()->draw($r, 'A RETRO FPS WRITTEN IN PHP - JUMPHP', $this->width() / 2, 66, 1, [255, 190, 120], 'center', [0, 0, 0]);        $this->font()->draw($r, 'ENGINE PORTED FROM THE 1993 GPL SOURCE', $this->width() / 2, 190, 1, [150, 150, 170], 'center', [0, 0, 0]);    }    /** The logo as an SDL texture (palette picture, transparent pixels kept). */    private function logoTexture(array $logo)    {        $img = imagecreatetruecolor($logo['w'], $logo['h']);        imagealphablending($img, false);        imagesavealpha($img, true);        $rgb = Palette::rgb();        $clear = imagecolorallocatealpha($img, 0, 0, 0, 127);        for ($y = 0; $y < $logo['h']; $y++) {            for ($x = 0; $x < $logo['w']; $x++) {                $i = ord($logo['px'][$y * $logo['w'] + $x]);                imagesetpixel($img, $x, $y, $i === Palette::TRANSPARENT ? $clear : imagecolorallocatealpha($img, ...[...$rgb[$i], 0]));            }        }        $this->sdl()->createTextureFromGd('iron-logo', $img);        return $this->sdl()->getTexture('iron-logo')['texture'];    }}