engine, $params); $this->selected = 0; $this->time = 0; $this->message = (string)($params['message'] ?? ''); $this->backend = Rtc::prepare($this->webrtc); } /** @return list [text, on press, on left / right] */ private function items(): array { $items = [ [Lang::t('Two peers on this screen'), fn () => $this->scenes()->switchTo(LinkScene::class)], [Lang::t('Play online (2 players)'), fn () => $this->online()], [Lang::t('Practice against a bot'), fn () => $this->scenes()->switchTo(DuelScene::class, ['mode' => 'bot'])], ['< ' . Lang::t('Language: {name}', ['name' => Look::LANGUAGES[$this->engine->lang->getLang()] ?? 'English']) . ' >', fn () => Look::cycle($this->engine, 1), fn (int $dir) => Look::cycle($this->engine, $dir)], [Lang::t('Tutorial') . ' ยท ' . Lang::t('Documentation'), fn () => $this->open(self::DOCS . 'tutoriel')], ]; if (Platform::isDesktop()) { $items[] = [Lang::t('Quit'), fn () => $this->engine->quit()]; } return $items; } private function online(): void { if (!Rtc::real($this->webrtc)) { $this->message = 'Online play needs real WebRTC: a browser, or GStreamer on Linux.'; return; } $this->scenes()->switchTo(LobbyScene::class, ['next' => DuelScene::class, 'nextParams' => ['mode' => 'online'], 'back' => self::class, 'players' => 2]); } private function open(string $url): void { Platform::isWasm() ? $this->engine->bridge->send('open', ['url' => $url]) : Shell::openUrl($url); } public function update(float $dt): void { $this->time += $dt; $items = $this->items(); $n = count($items); $a = $this->actions(); if ($a->isPressed('up')) { $this->selected = ($this->selected + $n - 1) % $n; } if ($a->isPressed('down')) { $this->selected = ($this->selected + 1) % $n; } $x = ($this->width() - self::BW) / 2; foreach ($items as $i => $item) { $y = self::TOP + $i * self::GAP; if (Look::hover($this->engine, $x, $y, self::BW, self::BH) && $this->engine->input->getMouse()->isPressed(1)) { $this->selected = $i; $item[1](); return; } } $item = $items[$this->selected] ?? null; if ($item === null) { return; } if ($a->isPressed('confirm') || $a->isPressed('send')) { $item[1](); } elseif (isset($item[2]) && ($a->isPressed('left') || $a->isPressed('right'))) { $item[2]($a->isPressed('left') ? -1 : 1); } } public function render(Renderer $r): void { $r->clear(Look::BG); $w = $this->width(); // two peers and the packets between them for ($i = 0; $i < 4; $i++) { $t = fmod($this->time * 0.6 + $i / 4, 1.0); $r->fillCircle(40 + $t * ($w - 80), 18, 1.5, $i % 2 ? Look::OTHER : Look::ME); } $r->fillRect(28, 12, 12, 12, Look::ME); $r->fillRect($w - 40, 12, 12, 12, Look::OTHER); $font = $this->font(); $font->draw($r, 'WebRTC', $w / 2, 8, 2, [255, 255, 255], 'center'); $font->draw($r, Lang::t('Peer to peer from PHP, in your browser'), $w / 2, 32, 1, Look::MUTED, 'center'); $x = ($w - self::BW) / 2; foreach ($this->items() as $i => $item) { $y = self::TOP + $i * self::GAP; Look::button($r, $this->engine, $x, $y, self::BW, self::BH, $item[0], $i === $this->selected); } $bottom = $this->height() - 12; if ($this->message !== '') { $font->draw($r, Lang::t($this->message), $w / 2, $bottom - 11, 1, [255, 120, 120], 'center'); } $font->draw($r, Lang::t('WebRTC here: {what}', ['what' => Lang::t(Rtc::describe($this->backend))]), $w / 2, $bottom, 1, Look::MUTED, 'center'); } }