IT

Slave Rift

di tueb2024 · Sparatutto
5,0 (1)
▶ Gioca 💬 Opinioni Sorgenti 🕘 Versioni Modifica GitHub
games/slave-rift / src / Gfx / WeaponRenderer.php · commit 9ea6094
<?phpnamespace App\Gfx;use App\Game\Run;use JumPHP\Renderer\Renderer;/** * Resolution-aware first-person weapon renderer. * * Each primary weapon owns a 576x96 atlas made of four 144x96 FPS frames: * ready, cooldown/return, fire, recoil. The weapon is always rendered; firing * only changes the selected frame and applies a tiny recoil displacement. */final class WeaponRenderer{    public const READY = 'ready';    public const FIRE = 'fire';    public const RECOIL = 'recoil';    public const COOLDOWN = 'cooldown';    public const FRAME_W = 144;    public const FRAME_H = 96;    private string $state = self::READY;    private float $stateStarted = 0.0;    private float $lastMuzzle = 0.0;    public function reset(float $time = 0.0): void    {        $this->state = self::READY;        $this->stateStarted = $time;        $this->lastMuzzle = 0.0;    }    /** Four visible primary weapons over the seven simulation stages. */    public function textureKey(int $stage): string    {        return match (true) {            $stage <= 1 => 'weapon_colt',            $stage <= 3 => 'weapon_mp44',            $stage <= 4 => 'weapon_bar',            default => 'weapon_gatling',        };    }    public function update(float $time, float $muzzle): string    {        if ($muzzle > $this->lastMuzzle + 0.015) {            $this->state = self::FIRE;            $this->stateStarted = $time;        } else {            $age = max(0.0, $time - $this->stateStarted);            $this->state = match (true) {                $age < 0.034 => self::FIRE,                $age < 0.078 => self::RECOIL,                $age < 0.132 => self::COOLDOWN,                default => self::READY,            };        }        $this->lastMuzzle = $muzzle;        return $this->state;    }    public function frameForState(string $state): int    {        return match ($state) {            self::FIRE => 2,            self::RECOIL => 3,            self::COOLDOWN => 1,            default => 0,        };    }    /** Atlases are one row high; kept for compatibility with older callers/tests. */    public function rowForStage(int $stage): int    {        return 0;    }    /** @return array{drawW:int,drawH:int,x:int,y:int,copies:int,spread:int} */    public function layout(int $width, int $height, int $stage, float $time): array    {        $dual = in_array($stage, [1, 3, 6], true);        $ratio = match ($stage) {            1 => 0.46,            3 => 0.48,            6 => 0.52,            default => match (true) {                $stage <= 1 => 0.54,                $stage <= 3 => 0.58,                $stage <= 4 => 0.61,                default => 0.64,            },        };        $drawH = max(72, min((int)round($height * 0.70), (int)round($height * $ratio)));        $drawW = (int)round($drawH * self::FRAME_W / self::FRAME_H);        $bob = (int)round((sin($time * 6.0) + 1.0) * 0.8);        $spread = $dual ? max(24, (int)round($drawW * 0.82)) : 0;        $x = $dual            ? (int)round($width / 2 - $drawW / 2 - $spread / 2)            : (int)round($width / 2 - $drawW * 0.46);        $x = max(0, min($width - $drawW - $spread, $x));        $y = $height - $drawH - 2 - $bob;        return [            'drawW' => $drawW,            'drawH' => $drawH,            'x' => $x,            'y' => max(0, min($height - $drawH, $y)),            'copies' => $dual ? 2 : 1,            'spread' => $spread,        ];    }    /** @param array{texture:mixed} $texture */    public function render(Renderer $r, Run $run, array $texture): void    {        $state = $this->update($run->time, $run->muzzle);        $frame = $this->frameForState($state);        $layout = $this->layout($r->getWidth(), $r->getHeight(), $run->primaryStage, $run->time);        $recoilY = match ($state) {            self::FIRE => -3,            self::RECOIL => -6,            self::COOLDOWN => -2,            default => 0,        };        $y = max(0, min($r->getHeight() - $layout['drawH'], $layout['y'] + $recoilY));        $src = [$frame * self::FRAME_W, 0, self::FRAME_W, self::FRAME_H];        if ($layout['copies'] === 2) {            $r->copy(                $texture['texture'],                $src,                $layout['x'],                $y,                $layout['drawW'],                $layout['drawH'],                true,            );            $r->copy(                $texture['texture'],                $src,                $layout['x'] + $layout['spread'],                $y,                $layout['drawW'],                $layout['drawH'],            );            return;        }        $r->copy(            $texture['texture'],            $src,            $layout['x'],            $y,            $layout['drawW'],            $layout['drawH'],        );    }    public function state(): string    {        return $this->state;    }}