import _ from "lodash" ; import type { StatOptions , StatsType } from "./stats" ; import { ActionsComputer , ComboComputer , ConversionComputer , generateOverallStats , InputComputer , Stats , StockComputer , } from "./stats" ; import type { FrameEntryType , FramesType , GameEndType , GameStartType , MetadataType , RollbackFrames } from "./types" ; import { SlpParser , SlpParserEvent } from "./utils/slpParser" ; import type { SlpReadInput } from "./utils/slpReader" ; import { closeSlpFile , getMetadata , iterateEvents , openSlpFile , SlpInputSource } from "./utils/slpReader" ; /**
 * Slippi Game class that wraps a file
 */ export class SlippiGame { private input : SlpReadInput ; private metadata : MetadataType | null = null ; private finalStats : StatsType | null = null ; private parser : SlpParser ; private readPosition : number | null = null ; private actionsComputer : ActionsComputer = new ActionsComputer ( ) ; private conversionComputer : ConversionComputer = new ConversionComputer ( ) ; private comboComputer : ComboComputer = new ComboComputer ( ) ; private stockComputer : StockComputer = new StockComputer ( ) ; private inputComputer : InputComputer = new InputComputer ( ) ; protected statsComputer : Stats ; public constructor ( input , opts ? ) { if ( _ . isString ( input ) ) { this . input = { source : SlpInputSource . FILE , filePath : input as string , } ; } else if ( input instanceof Buffer ) { this . input = { source : SlpInputSource . BUFFER , buffer : input , } ; } else if ( input instanceof ArrayBuffer ) { this . input = { source : SlpInputSource . BUFFER , buffer : Buffer . from ( input ) , } ; } else { throw new Error ( "Cannot create SlippiGame with input of that type" ) ; } this . statsComputer = new Stats ( opts ) ; this . statsComputer . register ( this . actionsComputer , this . comboComputer , this . conversionComputer , this . inputComputer , this . stockComputer ) ; this . parser = new SlpParser ( ) ; this . parser . on ( SlpParserEvent . SETTINGS , ( settings ) => { this . statsComputer . setup ( settings ) ; } ) ; this . parser . on ( SlpParserEvent . FINALIZED_FRAME , ( frame ) => { this . statsComputer . addFrame ( frame ) ; } ) ; } private _process ( settingsOnly = false ) { if ( this . parser . getGameEnd ( ) !== null ) { return ; } const slpfile = openSlpFile ( this . input ) ; this . readPosition = iterateEvents ( slpfile , ( command , payload ) => { if ( ! payload ) { return false ; } this . parser . handleCommand ( command , payload ) ; return settingsOnly && this . parser . getSettings ( ) !== null ; } , this . readPosition ) ; closeSlpFile ( slpfile ) ; } /**
     * Gets the game settings, these are the settings that describe the starting state of
     * the game such as characters, stage, etc.
     */ public getSettings ( ) { this . _process ( true ) ; return this . parser . getSettings ( ) ; } public getLatestFrame ( ) { this . _process ( ) ; return this . parser . getLatestFrame ( ) ; } public getGameEnd ( ) { this . _process ( ) ; return this . parser . getGameEnd ( ) ; } public getFrames ( ) { this . _process ( ) ; return this . parser . getFrames ( ) ; } public getRollbackFrames ( ) { this . _process ( ) ; return this . parser . getRollbackFrames ( ) ; } public getStats ( ) { if ( this . finalStats ) { return this . finalStats ; } this . _process ( ) ; const settings = this . parser . getSettings ( ) ; if ( settings === null ) { return null ; } this . statsComputer . process ( ) ; const inputs = this . inputComputer . fetch ( ) ; const stocks = this . stockComputer . fetch ( ) ; const conversions = this . conversionComputer . fetch ( ) ; const playableFrames = this . parser . getPlayableFrameCount ( ) ; const overall = generateOverallStats ( settings , inputs , stocks , conversions , playableFrames ) ; const stats = { lastFrame : this . parser . getLatestFrameNumber ( ) , playableFrameCount : playableFrames , stocks : stocks , conversions : conversions , combos : this . comboComputer . fetch ( ) , actionCounts : this . actionsComputer . fetch ( ) , overall : overall , gameComplete : this . parser . getGameEnd ( ) !== null , } ; if ( this . parser . getGameEnd ( ) !== null ) { this . finalStats = stats ; } return stats ; } public getMetadata ( ) { if ( this . metadata ) { return this . metadata ; } const slpfile = openSlpFile ( this . input ) ; this . metadata = getMetadata ( slpfile ) ; closeSlpFile ( slpfile ) ; return this . metadata ; } public getFilePath ( ) { if ( this . input . source !== SlpInputSource . FILE ) { return null ; } return this . input . filePath ?? null ; } }