NL

SONIC PHP

door davidnurdin · Platformers
5,0 (1)
▶ Spelen 💬 Reacties van spelers Broncode 🕘 Versies Bewerken GitHub
. / src / Entities / Objects / EndSign.php · commit a20d00e
<?phpnamespace App\Entities\Objects;use App\Entities\GameObject;use App\Level\Level;use App\Gfx\Gfx;class EndSign extends GameObject{    public const FACE_DOCTOR = [240, 104, 32, 40];    public const FACE_HERO = [280, 104, 32, 40];    public const FACE_QUESTION = [320, 104, 32, 40];    public const FACE_RING = [360, 104, 32, 40];    public const FACE_EXCLAMATION = [400, 104, 32, 40];    private const TURN_A = [444, 104, 24, 40];    private const EDGE = [485, 104, 6, 40];    private const TURN_B = [508, 104, 24, 40];    /** The spin sequence _6157: 6 entries per face (?, side, edge, side), 24 in all. */    private const SPIN = [self::FACE_QUESTION, self::TURN_A, self::EDGE, self::TURN_B];    private const FRAME = 1 / 60;    private string $signState = 'waiting';    private array $finalFace = self::FACE_DOCTOR;    private float $clock = 0;    // doObjectCode_boss_endSign, in ROM units (1/256 px per frame): Y speed, spin speed ($14), spin index ($12 + $16)    private int $signVy = 0;    private int $spinSpeed = 0;    private int $spinIndex = 0;    private bool $onFloor = true;    private ?float $floorY = null;    private bool $heroWasGrounded = true;    public function __construct(Gfx $sdl, int $tileX, int $tileY)    {        parent::__construct($sdl, 0x07, 'End sign', 0, 0, 32, 40);        $this->animations = ['done' => ['flags' => [], 'coords' => self::frames([self::FACE_HERO])]];        foreach (self::SPIN as $i => $face) {            $this->animations['spin' . $i] = ['flags' => [], 'coords' => self::frames([$face])];        }        $this->setAnimation('spin0');        $this->placeOnSpawn($tileX, $tileY, 48);    }    /** The original sign is a normal object with gravity: it rests on the floor below its spawn point. */    public function setLevel(Level $level): void    {        parent::setLevel($level);        $surface = $level->findSurfaceY((int)($this->x + $this->width / 2), (int)$this->y, 192);        if ($surface !== null) {            $this->y = $surface - $this->height;        }        $this->floorY = $this->y;    }    /**     * Returns true on the tick the sign is first passed. The ROM starts the spin at     * min(|Sonic X speed|, $300) * 2, and not while the sign is going up.     */    public function checkPassed(float $playerCenterX, float $playerVx): bool    {        if ($this->signState !== 'waiting' || $this->signVy < 0 || $playerCenterX < $this->x + $this->width / 2) {            return false;        }        $this->signState = 'spinning';        $this->spinSpeed = min((int)(abs($playerVx) / 60 * 256), 0x300) * 2;        $this->spinIndex = 0;        return true;    }    public function setFinalFace(array $face): void    {        $this->finalFace = $face;        $this->animations['done']['coords'] = self::frames([$face]);    }    public function isPassed(): bool    {        return $this->signState !== 'waiting';    }    public function isFinished(): bool    {        return $this->signState === 'done';    }    /** Sonic's state for the next frames: he lands within $64 px of the sign -> the sign hops ($D414 bit 7). */    public function watchHero(float $heroX, bool $grounded): void    {        if ($grounded && !$this->heroWasGrounded && $this->onFloor && abs($heroX - $this->x) < 0x64) {            $this->signVy = -0x200;        }        $this->heroWasGrounded = $grounded;    }    public function update(float $deltaTime): void    {        if (!$this->active) return;        $this->clock += $deltaTime;        while ($this->clock >= self::FRAME) {            $this->clock -= self::FRAME;            $this->tick();        }        $face = match ($this->signState) {            'waiting' => 0,            'done' => null,            default => intdiv($this->spinIndex >> 8, 6) % 4,        };        $this->setAnimation($face === null ? 'done' : 'spin' . $face);        $this->updateSprite($deltaTime);    }    private function tick(): void    {        $this->signVy += 0x1A;        if ($this->signState === 'spinning') {            // the spin goes on at its speed; a fast spin lifts the sign (Y speed - speed / 32, not beyond -4px)            $this->spinIndex = ($this->spinIndex + $this->spinSpeed) % (24 << 8);            if ($this->signVy > -0x400) {                $this->signVy -= $this->spinSpeed >> 5;                $this->spinSpeed -= 0x10;                if ($this->spinSpeed < 0 || $this->y < 8) {                    // stopped: still turning slowly (one entry a frame) until it is back on the floor                    $this->signVy = 0;                    $this->signState = 'falling';                    $this->spinIndex = 0;                }            }        } elseif ($this->signState === 'falling') {            $this->spinIndex = ($this->spinIndex + 0x100) % (24 << 8);        }        $this->y += $this->signVy / 256;        $this->onFloor = false;        if ($this->floorY !== null && $this->y >= $this->floorY && $this->signVy >= 0) {            $this->y = $this->floorY;            $this->signVy = 0;            $this->onFloor = true;        }        if ($this->signState === 'falling' && $this->onFloor) {            $this->signState = 'done';        }    }}