UK
games/satisfying-bounce / src / Scene / ConcentricScene.php · commit 8bdd6e4
<?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);        }    }}