import { Clock } from "three" ; import { PlayerObject } from "./model.js" ; export interface IAnimation { play ( player , time ) : void ; } export type AnimationFn = ( player : PlayerObject , time : number ) => void ; export type Animation = AnimationFn | IAnimation ; export function invokeAnimation ( animation , player , time ) { if ( animation instanceof Function ) { animation ( player , time ) ; } else { animation . play ( player , time ) ; } } export interface AnimationHandle { speed : number ; paused : boolean ; progress : number ; readonly animation : Animation ; reset ( ) : void ; } export interface SubAnimationHandle extends AnimationHandle { remove ( ) : void ; resetAndRemove ( ) : void ; } class AnimationWrapper implements SubAnimationHandle , IAnimation { speed : number = 1.0 ; paused : boolean = false ; progress : number = 0 ; readonly animation : Animation ; private lastTime : number = 0 ; private started : boolean = false ; private toResetAndRemove : boolean = false ; constructor ( animation ) { this . animation = animation ; } play ( player , time ) { if ( this . toResetAndRemove ) { invokeAnimation ( this . animation , player , 0 ) ; this . remove ( ) ; return ; } let delta ; if ( this . started ) { delta = time - this . lastTime ; } else { delta = 0 ; this . started = true ; } this . lastTime = time ; if ( ! this . paused ) { this . progress += delta * this . speed ; } invokeAnimation ( this . animation , player , this . progress ) ; } reset ( ) { this . progress = 0 ; } remove ( ) { } resetAndRemove ( ) { this . toResetAndRemove = true ; } } export class CompositeAnimation implements IAnimation { readonly handles : Set < AnimationHandle & IAnimation > = new Set ( ) ; add ( animation ) { const handle = new AnimationWrapper ( animation ) ; handle . remove = ( ) : void => { this . handles . delete ( handle ) ; } ; this . handles . add ( handle ) ; return handle ; } play ( player , time ) { this . handles . forEach ( handle => handle . play ( player , time ) ) ; } } export class RootAnimation extends CompositeAnimation implements AnimationHandle { speed : number = 1.0 ; progress : number = 0.0 ; private readonly clock : Clock = new Clock ( true ) ; get animation ( ) : RootAnimation { return this ; } get paused ( ) : boolean { return ! this . clock . running ; } set paused ( value ) { if ( value ) { this . clock . stop ( ) ; } else { this . clock . start ( ) ; } } runAnimationLoop ( player ) { if ( this . handles . size === 0 ) { return ; } this . progress += this . clock . getDelta ( ) * this . speed ; this . play ( player , this . progress ) ; } reset ( ) { this . progress = 0 ; } } export const IdleAnimation = ( player , time ) => { const skin = player . skin ; time *= 2 ; const basicArmRotationZ = Math . PI * 0.02 ; skin . leftArm . rotation . z = Math . cos ( time ) * 0.03 + basicArmRotationZ ; skin . rightArm . rotation . z = Math . cos ( time + Math . PI ) * 0.03 - basicArmRotationZ ; const basicCapeRotationX = Math . PI * 0.06 ; player . cape . rotation . x = Math . sin ( time ) * 0.01 + basicCapeRotationX ; } ; export const WalkingAnimation = ( player , time ) => { const skin = player . skin ; time *= 8 ; skin . leftLeg . rotation . x = Math . sin ( time ) * 0.5 ; skin . rightLeg . rotation . x = Math . sin ( time + Math . PI ) * 0.5 ; skin . leftArm . rotation . x = Math . sin ( time + Math . PI ) * 0.5 ; skin . rightArm . rotation . x = Math . sin ( time ) * 0.5 ; const basicArmRotationZ = Math . PI * 0.02 ; skin . leftArm . rotation . z = Math . cos ( time ) * 0.03 + basicArmRotationZ ; skin . rightArm . rotation . z = Math . cos ( time + Math . PI ) * 0.03 - basicArmRotationZ ; skin . head . rotation . y = Math . sin ( time / 4 ) * 0.2 ; skin . head . rotation . x = Math . sin ( time / 5 ) * 0.1 ; const basicCapeRotationX = Math . PI * 0.06 ; player . cape . rotation . x = Math . sin ( time / 1.5 ) * 0.06 + basicCapeRotationX ; } ; export const RunningAnimation = ( player , time ) => { const skin = player . skin ; time = time * 15 + Math . PI * 0.5 ; skin . leftLeg . rotation . x = Math . cos ( time + Math . PI ) * 1.3 ; skin . rightLeg . rotation . x = Math . cos ( time ) * 1.3 ; skin . leftArm . rotation . x = Math . cos ( time ) * 1.5 ; skin . rightArm . rotation . x = Math . cos ( time + Math . PI ) * 1.5 ; const basicArmRotationZ = Math . PI * 0.1 ; skin . leftArm . rotation . z = Math . cos ( time ) * 0.1 + basicArmRotationZ ; skin . rightArm . rotation . z = Math . cos ( time + Math . PI ) * 0.1 - basicArmRotationZ ; player . position . y = Math . cos ( time * 2 ) ; player . position . x = Math . cos ( time ) * 0.15 ; player . rotation . z = Math . cos ( time + Math . PI ) * 0.01 ; const basicCapeRotationX = Math . PI * 0.3 ; player . cape . rotation . x = Math . sin ( time * 2 ) * 0.1 + basicCapeRotationX ; } ; export const RotatingAnimation = ( player , time ) => { player . rotation . y = time ; } ; function clamp ( num , min , max ) { return num <= min ? min : num >= max ? max : num ; } export const FlyingAnimation = ( player , time ) => { if ( time < 0 ) time = 0 ; time *= 20 ; const startProgress = clamp ( ( time * time ) / 100 , 0 , 1 ) ; player . rotation . x = startProgress * Math . PI / 2 ; player . skin . head . rotation . x = startProgress > .5 ? Math . PI / 4 - player . rotation . x : 0 ; const basicArmRotationZ = Math . PI * .25 * startProgress ; player . skin . leftArm . rotation . z = basicArmRotationZ ; player . skin . rightArm . rotation . z = - basicArmRotationZ ; const elytraRotationX = .34906584 ; const elytraRotationZ = Math . PI / 2 ; const interpolation = Math . pow ( .9 , time ) ; player . elytra . leftWing . rotation . x = elytraRotationX + interpolation * ( .2617994 - elytraRotationX ) ; player . elytra . leftWing . rotation . z = elytraRotationZ + interpolation * ( .2617994 - elytraRotationZ ) ; player . elytra . updateRightWing ( ) ; } ;