Entrar
PT
games/satisfying-bounce / src / Render / Fx.php · commit 8bdd6e4
<?phpnamespace App\Render;use JumPHP\Renderer\Renderer;use JumPHP\Shorts\Quality;use JumPHP\Shorts\Sprites;use JumPHP\Util\Color;/** Screen-space effects: sparks, shock waves (a ring that grows and fades) and soft glows. */final class Fx{    public const MAX = 360;    /** @var list<array{0: float, 1: float, 2: float, 3: float, 4: float, 5: float, 6: float, 7: array}> x y vx vy life max size rgb */    private array $sparks = [];    /** @var list<array{0: float, 1: float, 2: float, 3: float, 4: float, 5: array, 6: float}> x y radius maxRadius age rgb thickness */    private array $waves = [];    private float $seed = 1.0;    public function reset(int $seed): void    {        $this->sparks = $this->waves = [];        $this->seed = max(1.0, fmod(abs((float)$seed), 2147483647.0));    }    private function rand(): float    {        $this->seed = fmod($this->seed * 16807.0, 2147483647.0);       // Park-Miller on floats: the same on 32-bit PHP (the browser)        return ($this->seed - 1) / 2147483646.0;    }    public function burst(float $x, float $y, array $rgb, int $count, float $speed = 90.0, float $size = 2.2): void    {        $count = Quality::count($count);        for ($i = 0; $i < $count && count($this->sparks) < self::MAX; $i++) {            $a = $this->rand() * 2 * M_PI;            $v = $speed * (0.35 + 0.65 * $this->rand());            $life = 0.35 + 0.5 * $this->rand();            $this->sparks[] = [$x, $y, cos($a) * $v, sin($a) * $v, $life, $life, $size * (0.6 + 0.6 * $this->rand()), $rgb];        }    }    public function wave(float $x, float $y, array $rgb, float $maxRadius = 40.0, float $thickness = 1.5): void    {        if (count($this->waves) < (Quality::$live ? 10 : 24)) {            $this->waves[] = [$x, $y, 2.0, $maxRadius, 0.0, $rgb, $thickness];        }    }    public function update(float $dt): void    {        foreach ($this->sparks as $i => &$s) {            $s[0] += $s[2] * $dt;            $s[1] += $s[3] * $dt;            $drag = exp(-2.4 * $dt);            $s[2] *= $drag;            $s[3] = $s[3] * $drag + 90 * $dt;            $s[4] -= $dt;        }        unset($s);        $this->sparks = array_values(array_filter($this->sparks, fn (array $s) => $s[4] > 0));        foreach ($this->waves as &$w) {            $w[4] += $dt;            $w[2] = $w[3] * (1 - (1 - min(1.0, $w[4] / 0.55)) ** 2);        }        unset($w);        $this->waves = array_values(array_filter($this->waves, fn (array $w) => $w[4] < 0.55));    }    public function render(Renderer $r, array $bg = [10, 10, 26]): void    {        foreach ($this->waves as [$x, $y, $rad, , $age, $rgb, $th]) {            $k = 1 - $age / 0.55;            Gfx::arc($r, $x, $y, $rad, 0, 2 * M_PI, max(0.4, $th * $k), Gfx::mix($bg, $rgb, 0.25 + 0.75 * $k));        }        foreach ($this->sparks as [$x, $y, , , $life, $max, $size, $rgb]) {            $k = $life / $max;            if (Quality::$live) {                // four alpha levels: one sprite per colour and level                Sprites::disc($r, $x, $y, max(0.3, $size * (0.35 + 0.65 * $k)), max(0.3, $size * (0.35 + 0.65 * $k)), 0.0, $rgb, min(255, ((int)((60 + 195 * $k) / 64) + 1) * 64 - 1));                continue;            }            $r->fillCircle($x, $y, max(0.3, $size * (0.35 + 0.65 * $k)), new Color($rgb[0], $rgb[1], $rgb[2], (int)(60 + 195 * $k)));        }    }    public function count(): int    {        return count($this->sparks);    }}