import { Game } from "./Game" ; import { Scene , SceneConstructor } from "./Scene" ; export class Scenes < T extends Game > { public activeScene : Scene < T , unknown > | null = null ; private sceneCache = new WeakMap < SceneConstructor < T , unknown > , Scene < T , unknown > > ( ) ; private scenes : Scene < T , unknown > [ ] = [ ] ; private sortedScenes : Scene < T , unknown > [ ] = [ ] ; public constructor ( public readonly game ) { } private createScene < A > ( sceneClass ) { let scene = this . sceneCache . get ( sceneClass ) ; if ( scene == null ) { scene = new sceneClass ( this . game ) ; this . sceneCache . set ( sceneClass , scene ) ; } return scene ; } public async pushScene < A > ( sceneClass , args ) ; public async pushScene < A > ( sceneClass , args ) ; public async pushScene < A > ( sceneClass , args ) { if ( this . activeScene != null ) { await this . activeScene . deactivate ( ) ; } const scene = this . createScene ( sceneClass ) ; await scene . setup ( args ) ; this . scenes . push ( scene ) ; this . updateSortedScenes ( ) ; if ( scene . inTransition != null ) { scene . currentTransition = scene . inTransition ; await scene . currentTransition . start ( "in" ) ; scene . currentTransition = null ; } await scene . activate ( ) ; this . activeScene = scene ; } public getPreviousScene ( ) { return this . scenes [ this . scenes . length - 2 ] ?? null ; } public async popScene ( { noTransition = false } = { } ) { const activeScene = this . activeScene ; if ( activeScene != null ) { await activeScene . deactivate ( ) ; if ( ! noTransition && activeScene . outTransition != null ) { activeScene . currentTransition = activeScene . outTransition ; await activeScene . currentTransition . start ( "out" ) ; activeScene . currentTransition = null ; } this . scenes . pop ( ) ; this . updateSortedScenes ( ) ; await activeScene . cleanup ( ) ; this . activeScene = this . scenes [ this . scenes . length - 1 ] ?? null ; if ( this . activeScene != null ) { await this . activeScene . activate ( ) ; } } return activeScene ; } public async setScene < A > ( newSceneClass , args ) ; public async setScene < A > ( newSceneClass , args ) ; public async setScene < A > ( newSceneClass , args ) { const currentScene = this . activeScene ; if ( currentScene == null ) { return this . pushScene ( newSceneClass , args ) ; } await currentScene . deactivate ( ) ; let outTransitionPromise = null ; if ( currentScene . outTransition ) { currentScene . currentTransition = currentScene . outTransition ; outTransitionPromise = currentScene . currentTransition . start ( "out" ) ; } const currentSceneIndex = this . scenes . length - 1 ; const newScene = this . createScene ( newSceneClass ) ; await newScene . setup ( args ) ; this . scenes . push ( newScene ) ; this . updateSortedScenes ( ) ; if ( newScene . inTransition != null ) { newScene . currentTransition = newScene . inTransition ; await newScene . currentTransition . start ( "in" ) ; newScene . currentTransition = null ; } await newScene . activate ( ) ; this . activeScene = newScene ; if ( outTransitionPromise != null ) { await outTransitionPromise ; currentScene . currentTransition = null ; } this . scenes . splice ( currentSceneIndex , 1 ) ; this . updateSortedScenes ( ) ; currentScene . cleanup ( ) ; } private updateSortedScenes ( ) { this . sortedScenes = this . scenes . slice ( ) . sort ( ( a , b ) => { if ( a . zIndex === b . zIndex ) { return this . scenes . indexOf ( a ) - this . scenes . indexOf ( b ) ; } else { return a . zIndex - b . zIndex ; } } ) ; } public update ( dt ) { this . sortedScenes . forEach ( scene => { scene . currentTransition ?. update ( dt ) ; scene . update ( dt ) ; } ) ; } public draw ( ctx , width , height ) { this . sortedScenes . forEach ( scene => { ctx . save ( ) ; if ( scene . currentTransition != null ) { scene . currentTransition . draw ( ctx , ( ) => scene . draw ( ctx , width , height ) , width , height ) ; } else { scene . draw ( ctx , width , height ) ; } ctx . restore ( ) ; } ) ; } }