import { Slot } from "./slot" ; import { Thing } from "./thing" ; import { rotEquals , SEAT_ROTATIONS } from "./utils" ; type SlotOp = ( slot : Slot ) => Slot | null ; export class Movement { private thingMap : Map < Thing , Slot > = new Map ( ) ; private reverseMap : Map < Slot , Thing > = new Map ( ) ; private shiftMap : Map < Thing , Slot > = new Map ( ) ; move ( thing , slot ) { if ( this . reverseMap . has ( slot ) ) { throw `move(): conflict` ; } const oldSlot = this . thingMap . get ( thing ) ; if ( oldSlot !== undefined ) { this . reverseMap . delete ( oldSlot ) ; } this . thingMap . set ( thing , slot ) ; this . reverseMap . set ( slot , thing ) ; } private shift ( thing , slot ) { this . shiftMap . set ( thing , slot ) ; this . reverseMap . set ( slot , thing ) ; } has ( thing ) { return this . thingMap . has ( thing ) ; } get ( thing ) { return this . thingMap . get ( thing ) ?? null ; } rotationIndex ( thing ) { const slot = this . thingMap . get ( thing ) ; if ( slot === undefined ) { return 0 ; } return thing . slot . group === slot . group ? thing . rotationIndex : 0 ; } slots ( ) { return this . thingMap . values ( ) ; } things ( ) { return this . thingMap . keys ( ) ; } hasSlot ( slot ) { return this . reverseMap . has ( slot ) ; } valid ( ) { for ( const slot of this . reverseMap . keys ( ) ) { if ( slot . thing !== null && ! this . thingMap . has ( slot . thing ) && ! this . shiftMap . has ( slot . thing ) ) { return false ; } } return true ; } apply ( ) { for ( const thing of this . thingMap . keys ( ) ) { thing . prepareMove ( ) ; } for ( const thing of this . shiftMap . keys ( ) ) { thing . prepareMove ( ) ; } for ( const [ thing , slot ] of this . thingMap . entries ( ) ) { let rotationIndex = 0 ; if ( thing . slot . group === slot . group ) { rotationIndex = thing . rotationIndex ; } else if ( ! slot . rotateHeld ) { const allHeldRotations = [ thing . heldRotation , thing . heldRotation . clone ( ) . premultiply ( SEAT_ROTATIONS [ 1 ] ) , thing . heldRotation . clone ( ) . premultiply ( SEAT_ROTATIONS [ 2 ] ) , thing . heldRotation . clone ( ) . premultiply ( SEAT_ROTATIONS [ 3 ] ) , ] ; find : for ( let i = 0 ; i < slot . rotations . length ; i ++ ) { for ( const held of allHeldRotations ) { if ( rotEquals ( slot . rotations [ i ] , held ) ) { rotationIndex = i ; break find ; } } } } thing . moveTo ( slot , rotationIndex ) ; thing . release ( ) ; } for ( const [ thing , slot ] of this . shiftMap . entries ( ) ) { const rotationIndex = thing . rotationIndex ; thing . moveTo ( slot , rotationIndex ) ; thing . release ( ) ; } } rotateHeld ( ) { if ( this . thingMap . size > 1 ) { return ; } for ( const [ thing , slot ] of this . thingMap . entries ( ) ) { if ( slot . rotateHeld ) { const rotationIndex = thing . slot . group === slot . group ? thing . rotationIndex : 0 ; const rotation = slot . rotations [ rotationIndex ] ; if ( ! thing . heldRotation . equals ( rotation ) ) { thing . heldRotation . copy ( rotation ) ; thing . sent = false ; } } } } findShift ( allThings , ops ) { let shift = new Map ( ) ; for ( const thing of allThings ) { if ( ! this . thingMap . has ( thing ) ) { shift . set ( thing . slot , thing ) ; } } for ( const slot of this . thingMap . values ( ) ) { if ( shift . has ( slot ) ) { shift = this . findShiftFor ( slot , ops , shift ) ; if ( shift === null ) { return false ; } } } for ( const [ slot , thing ] of shift . entries ( ) ) { if ( slot . thing !== thing ) { this . shift ( thing , slot ) ; } } return true ; } applyShift ( seat ) { for ( const [ thing , slot ] of this . shiftMap . entries ( ) ) { thing . shiftTo ( seat , slot ) ; } } private findShiftFor ( slot , ops , shift ) { if ( ! shift . has ( slot ) ) { return null ; } for ( const op of ops ) { const cloned = new Map ( shift . entries ( ) ) ; if ( this . tryShift ( slot , op , cloned ) ) { return cloned ; } } return null ; } private tryShift ( initialSlot , op , shift ) { let slot = initialSlot ; const thing = shift . get ( initialSlot ) ! ; if ( thing . claimedBy !== null ) { return false ; } while ( slot === initialSlot || this . reverseMap . has ( slot ) ) { const nextSlot = op ( slot ) ; if ( nextSlot === null ) { return false ; } if ( shift . has ( nextSlot ) ) { if ( ! this . tryShift ( nextSlot , op , shift ) ) { return false ; } } shift . delete ( slot ) ; shift . set ( nextSlot , thing ) ; slot = nextSlot ; } return true ; } }