import { _decorator, Component, Node } from 'cc'; import { App } from '../app/App'; import { LobbyScreen } from './lobby/LobbyScreen'; import { GameScreen, MatchOutcome } from './game/GameScreen'; const { ccclass, property } = _decorator; /** * Owns the two screens the lobby scene contains and moves between them. * * ARCHITECTURE.md puts the main game UI in the lobby scene, so a match is a * change of visible screen rather than a scene load: no reload cost between * games, and no way for a half-finished match to leak into a new one as long * as `GameScreen.teardown` is honest about clearing up. */ @ccclass('MainFlow') export class MainFlow extends Component { @property(LobbyScreen) lobby: LobbyScreen | null = null; @property(GameScreen) game: GameScreen | null = null; @property(Node) lobbyRoot: Node | null = null; @property(Node) gameRoot: Node | null = null; start(): void { if (this.lobby) { this.lobby.onStartRequested = () => this.startMatch(); } if (this.game) { this.game.onFinished = (outcome) => this.finishMatch(outcome); this.game.onQuitRequested = () => this.abandonMatch(); } this.showLobby(); } private showLobby(): void { setActive(this.gameRoot, false); setActive(this.lobbyRoot, true); this.lobby?.refresh(); } private startMatch(): void { setActive(this.lobbyRoot, false); setActive(this.gameRoot, true); this.game?.startNewMatch(); } private finishMatch(outcome: MatchOutcome): void { if (App.exists) { App.instance.recordMatch(outcome); } this.game?.teardown(); this.showLobby(); } /** Leaving early still ends the match — and it counts as a loss. */ private abandonMatch(): void { this.finishMatch('loss'); } } function setActive(node: Node | null, active: boolean): void { if (node) { node.active = active; } }