import _ from "lodash" ; import type { FrameEntryType , FramesType , GameStartType } from "../types" ; import { Frames } from "../types" ; import type { PlayerIndexedType } from "./common" ; import { getSinglesPlayerPermutationsFromSettings } from "./common" ; import type { StatComputer } from "./stats" ; enum JoystickRegion { DZ = 0 , NE = 1 , SE = 2 , SW = 3 , NW = 4 , N = 5 , E = 6 , S = 7 , W = 8 } export interface PlayerInput { playerIndex : number ; opponentIndex : number ; inputCount : number ; joystickInputCount : number ; cstickInputCount : number ; buttonInputCount : number ; triggerInputCount : number ; } export class InputComputer implements StatComputer < PlayerInput [ ] > { private state = new Map < PlayerIndexedType , PlayerInput > ( ) ; private playerPermutations = new Array < PlayerIndexedType > ( ) ; public setup ( settings ) { this . state = new Map ( ) ; this . playerPermutations = getSinglesPlayerPermutationsFromSettings ( settings ) ; this . playerPermutations . forEach ( ( indices ) => { const playerState = { playerIndex : indices . playerIndex , opponentIndex : indices . opponentIndex , inputCount : 0 , joystickInputCount : 0 , cstickInputCount : 0 , buttonInputCount : 0 , triggerInputCount : 0 , } ; this . state . set ( indices , playerState ) ; } ) ; } public processFrame ( frame , allFrames ) { this . playerPermutations . forEach ( ( indices ) => { const state = this . state . get ( indices ) ; if ( state ) { handleInputCompute ( allFrames , state , indices , frame ) ; } } ) ; } public fetch ( ) { return Array . from ( this . state . values ( ) ) ; } } function handleInputCompute ( frames , state , indices , frame ) { const playerFrame = frame . players [ indices . playerIndex ] ! . pre ; const currentFrameNumber = playerFrame . frame ! ; const prevFrameNumber = currentFrameNumber - 1 ; const prevPlayerFrame = frames [ prevFrameNumber ] ? frames [ prevFrameNumber ] . players [ indices . playerIndex ] ! . pre : null ; if ( currentFrameNumber < Frames . FIRST_PLAYABLE || ! prevPlayerFrame ) { return ; } const invertedPreviousButtons = ~ prevPlayerFrame . physicalButtons ! ; const currentButtons = playerFrame . physicalButtons ! ; const buttonChanges = invertedPreviousButtons & currentButtons & 0xfff ; const newInputsPressed = countSetBits ( buttonChanges ) ; state . inputCount += newInputsPressed ; state . buttonInputCount += newInputsPressed ; const prevAnalogRegion = getJoystickRegion ( prevPlayerFrame . joystickX ! , prevPlayerFrame . joystickY ! ) ; const currentAnalogRegion = getJoystickRegion ( playerFrame . joystickX ! , playerFrame . joystickY ! ) ; if ( prevAnalogRegion !== currentAnalogRegion && currentAnalogRegion !== JoystickRegion . DZ ) { state . inputCount += 1 ; state . joystickInputCount += 1 ; } const prevCstickRegion = getJoystickRegion ( prevPlayerFrame . cStickX ! , prevPlayerFrame . cStickY ! ) ; const currentCstickRegion = getJoystickRegion ( playerFrame . cStickX ! , playerFrame . cStickY ! ) ; if ( prevCstickRegion !== currentCstickRegion && currentCstickRegion !== JoystickRegion . DZ ) { state . inputCount += 1 ; state . cstickInputCount += 1 ; } if ( prevPlayerFrame . physicalLTrigger ! < 0.3 && playerFrame . physicalLTrigger ! >= 0.3 ) { state . inputCount += 1 ; state . triggerInputCount += 1 ; } if ( prevPlayerFrame . physicalRTrigger ! < 0.3 && playerFrame . physicalRTrigger ! >= 0.3 ) { state . inputCount += 1 ; state . triggerInputCount += 1 ; } } function countSetBits ( x ) { let bits = x ; let count ; for ( count = 0 ; bits ; count += 1 ) { bits &= bits - 1 ; } return count ; } function getJoystickRegion ( x , y ) { let region = JoystickRegion . DZ ; if ( x >= 0.2875 && y >= 0.2875 ) { region = JoystickRegion . NE ; } else if ( x >= 0.2875 && y <= - 0.2875 ) { region = JoystickRegion . SE ; } else if ( x <= - 0.2875 && y <= - 0.2875 ) { region = JoystickRegion . SW ; } else if ( x <= - 0.2875 && y >= 0.2875 ) { region = JoystickRegion . NW ; } else if ( y >= 0.2875 ) { region = JoystickRegion . N ; } else if ( x >= 0.2875 ) { region = JoystickRegion . E ; } else if ( y <= - 0.2875 ) { region = JoystickRegion . S ; } else if ( x <= - 0.2875 ) { region = JoystickRegion . W ; } return region ; }