x y vx vy life max size rgb */ private array $sparks = []; /** @var list 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); } }