MOKTAR

par davidnurdin · Remakes
pas encore noté
▶ Jouer Sources Éditer GitHub
. / src / Scene / GameScene.php · commit 2fb9809
<?phpnamespace App\Scene;use App\Titus\Game;use App\Titus\Input;use JumPHP\Kernel\Platform;use JumPHP\Renderer\Renderer;use JumPHP\Scene\AbstractScene;/** * The whole game: runs the OpenTitus port (App\Titus\Game::main(), a generator) frame by frame and * draws its screen. Each step of the generator ends with a flip of the original screen; the value it * yields is the minimum duration of that frame in ms (29 ms = the 35 Hz of the original game loop). * * Params: level (0-15) starts that level directly; demo: true = a bot plays (smoke test). */final class GameScene extends AbstractScene{    /** JumPHP action => key name of the C code (App\Titus\Game::KEY_*) */    private const KEYS = [        'left' => Game::KEY_LEFT,        'right' => Game::KEY_RIGHT,        'up' => Game::KEY_UP,        'down' => Game::KEY_DOWN,        'fire' => Game::KEY_SPACE,        'start' => Game::KEY_RETURN,        'quit' => Game::KEY_ESC,        'pause' => Game::KEY_P,        'music' => Game::KEY_MUSIC,        'energy' => Game::KEY_E,        'status' => Game::KEY_F4,        'suicide' => Game::KEY_F1,        'cheat_god' => Game::KEY_GODMODE,        'cheat_noclip' => Game::KEY_NOCLIP,        'cheat_skip' => Game::KEY_SKIP,    ];    private ?Game $game = null;    private ?\Generator $main = null;    private Input $input;    private float $acc = 0.0;    private int $wait = 0;    private bool $demo = false;    private int $frame = 0;    private ?string $error = null;    public function enter(array $params = []): void    {        $this->input = new Input();        $this->error = null;        $this->acc = 0.0;        $this->wait = 0;        $this->frame = 0;        $this->demo = (bool)($params['demo'] ?? false);        $dataDir = $this->engine->config->path('assets/data');        if (!Game::hasData($dataDir)) {            $this->error = "THE ORIGINAL GAME FILES ARE MISSING.\n\nCOPY THE MOKTAR FILES (*.SQZ)\nINTO assets/data/";            $this->game = null;            $this->main = null;            return;        }        $this->game = new Game($this->sdl(), $this->input, $dataDir, $this->sound());        $this->game->devmode = (int)Platform::setting('MOKTAR_CHEATS', 0);        if (isset($params['level'])) {            $this->game->startLevel = (int)$params['level'];        }        $this->game->onQuit = function (): void {            if (Platform::isDesktop()) {                $this->engine->quit();            }        };        $this->main = $this->game->main();        $this->wait = (int)$this->main->current();    }    public function update(float $dt): void    {        if ($this->main === null || $this->game === null) {            return;        }        $this->frame++;        $this->readInput();        $ms = $dt * 1000;        $this->game->advanceTime($ms);        $this->acc += $ms;        if ($this->wait <= 0) {            $this->step();            $this->acc = 0.0;            return;        }        // the 35 Hz game loop: as many steps as the time allows (at most 3 per frame)        for ($n = 0; $n < 3 && $this->acc >= $this->wait; $n++) {            $this->acc -= $this->wait;            $this->step();            if ($this->wait <= 0) {                $this->acc = 0.0;                break;            }        }        if ($this->acc > 100) {            $this->acc = 0.0;        }    }    private function step(): void    {        if ($this->main === null || !$this->main->valid()) {            return;        }        $this->main->next();        $this->wait = (int)$this->main->current();    }    private function readInput(): void    {        if ($this->demo) {            $this->demoInput();            return;        }        $actions = $this->actions();        foreach (self::KEYS as $action => $key) {            $this->input->setHeld($key, $actions->isHeld($action) || $actions->isPressed($action));        }        // KEY_JUMP is the up key of the original game        $this->input->setHeld(Game::KEY_JUMP, $actions->isHeld('up'));        $typed = '';        foreach ($this->engine->input->getKeyboard()->getTyped() as $code) {            if (($code >= 48 && $code <= 57) || ($code >= 97 && $code <= 102)) {                $typed .= chr($code);            }        }        if ($typed !== '') {            $this->input->type($typed);        }    }    /** A bot: walks right, jumps from time to time, validates the screens. */    private function demoInput(): void    {        $f = $this->frame;        $this->input->setHeld(Game::KEY_RIGHT, $f % 300 < 240);        $this->input->setHeld(Game::KEY_LEFT, $f % 300 >= 250);        $jump = $f % 50 < 12;        $this->input->setHeld(Game::KEY_UP, $jump);        $this->input->setHeld(Game::KEY_JUMP, $jump);        $this->input->setHeld(Game::KEY_SPACE, $f % 90 > 80);        if ($f % 40 === 0) {            $this->input->press(Game::KEY_RETURN);        }    }    public function render(Renderer $r): void    {        if ($this->error !== null || $this->game === null) {            $r->clear([0, 0, 0]);            $this->font()->draw($r, (string)$this->error, $this->width() / 2, 70, 1, [255, 220, 120], 'center');            return;        }        $this->game->screen->render($r, $this->sdl());    }}