<?phpnamespace App\Scene;use App\Audio\Scale;use JumPHP\Physics2d\Ball;use JumPHP\Physics2d\Ring;use JumPHP\Renderer\Renderer;/** Closed circles inside each other, a ball in the middle: each circle breaks after a few hits, from the inside, with a higher note. */final class ConcentricScene extends VideoScene{ public const SECONDS = 24.0; public const RINGS = 7; public const HITS = 3; protected float $punch = 0.035; protected function melodyNotes(): string { // a pentatonic scale going up and down return 'C4 D4 E4 G4 A4 C5 D5 E5 G5 A5 G5 E5 D5 C5 A4 G4 E4 D4'; } protected function headingText(): string { return 'BREAK ALL THE RINGS'; } protected function hookText(): string { return 'SEVEN RINGS. ONE BALL.'; } protected function captionText(): string { return $this->t('RINGS LEFT {n}', ['n' => count(array_filter($this->world->rings(), fn (Ring $r) => $r->solid))]); } protected function build(): void { $this->world->reset($this->seed + $this->round * 7919); $this->world->gravity = 520; $rand = $this->world->random; for ($i = 0; $i < self::RINGS; $i++) { $ring = new Ring(self::CX, self::CY, 24 + $i * 15, angle: $rand->between(0, 2 * M_PI), spin: ($i % 2 ? -1 : 1) * (0.3 + 0.08 * $i), thickness: 3, color: self::hue($i / self::RINGS * 0.8 + 0.05)); $ring->data['hp'] = self::HITS; $this->world->addRing($ring); } $a = $rand->between(0, 2 * M_PI); $this->world->addBall(new Ball(self::CX, self::CY, cos($a) * 130, sin($a) * 130, 5, '#ffffff', 100)); } protected function onHit(array $hit): void { if ($hit['type'] !== 'ring') { return; } $this->bounces++; /** @var Ring $ring */ $ring = $hit['body']; $ring->data['flash'] = 1.0; $ring->data['hp'] = (int)$ring->data['hp'] - 1; $ring->thickness = 1.6 + (int)$ring->data['hp']; $this->squash($hit['ball'], (float)$hit['x'], (float)$hit['y']); $this->burst((float)$hit['x'], (float)$hit['y'], $ring->color, 7, 80); if ($ring->data['hp'] <= 0) { $ring->solid = false; $this->burstRing($ring->x, $ring->y, $ring->radius, \App\Render\Gfx::rgb($ring->color)); $this->play(Scale::freq(Scale::MAJOR_PENTA, 7 + $this->round % 3 + (int)round(($ring->radius - 24) / 15)), 'bell', 0.9, 0.3, 0.0); if (!array_filter($this->world->rings(), fn (Ring $r) => $r->solid)) { $this->banner = $this->t('ALL BROKEN!'); $this->roundEnd = $this->time + 2.2; } } else { $this->note(1.0, 0.35, 0.26, 0.06); } } protected function drawWorld(Renderer $r): void { foreach ($this->world->rings() as $ring) { if ($ring->solid) { $this->drawRing($r, $ring); } } foreach ($this->world->balls() as $ball) { $this->drawBall($r, $ball); } }}