2022-11-19 12:58:07 +00:00
const BaseCharacter = require ( "./BaseCharacter" ) ;
window . ATK _CHARACTER _STATE = {
Idle1 : [ 0 , "Idle1" ] ,
Walking : [ 1 , "Walking" ] ,
Atk1 : [ 2 , "Atk1" ] ,
2022-11-23 04:30:30 +00:00
Atked1 : [ 3 , "Atked1" ] ,
2022-12-10 05:46:24 +00:00
InAirIdle1 : [ 4 , "InAirIdle1" ] ,
2022-12-09 09:22:04 +00:00
InAirAtk1 : [ 5 , "Atk1" ] ,
InAirAtked1 : [ 6 , "Atked1" ] ,
2022-11-19 12:58:07 +00:00
} ;
2022-12-10 04:30:44 +00:00
window . toInAirConjugate = function ( foo ) {
switch ( foo ) {
case window . ATK _CHARACTER _STATE . Idle1 [ 0 ] :
case window . ATK _CHARACTER _STATE . Walking [ 0 ] :
return window . ATK _CHARACTER _STATE . InAirIdle1 [ 0 ] ;
case window . ATK _CHARACTER _STATE . Atk1 [ 0 ] :
return window . ATK _CHARACTER _STATE . InAirAtk1 [ 0 ] ;
case window . ATK _CHARACTER _STATE . Atked1 [ 0 ] :
return window . ATK _CHARACTER _STATE . InAirAtked1 [ 0 ] ;
case window . ATK _CHARACTER _STATE . InAirIdle1 [ 0 ] :
return window . ATK _CHARACTER _STATE . Idle1 [ 0 ] ;
case window . ATK _CHARACTER _STATE . InAirAtk1 [ 0 ] :
return window . ATK _CHARACTER _STATE . Atk1 [ 0 ] ;
case window . ATK _CHARACTER _STATE . InAirAtked1 [ 0 ] :
return window . ATK _CHARACTER _STATE . Atked1 [ 0 ] ;
default :
console . warn ( ` Invalid characterState ${ foo } received, no in air conjugate is available! ` ) ;
return null ;
}
}
2022-11-20 13:07:45 +00:00
window . ATK _CHARACTER _STATE _ARR = [ ] ;
for ( let k in window . ATK _CHARACTER _STATE ) {
window . ATK _CHARACTER _STATE _ARR . push ( window . ATK _CHARACTER _STATE [ k ] ) ;
}
2022-11-27 11:38:26 +00:00
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET = new Set ( ) ;
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . add ( window . ATK _CHARACTER _STATE . Idle1 [ 0 ] ) ;
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . add ( window . ATK _CHARACTER _STATE . Walking [ 0 ] ) ;
2022-12-09 09:22:04 +00:00
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . add ( window . ATK _CHARACTER _STATE . InAirIdle1 [ 0 ] ) ;
2022-11-27 11:38:26 +00:00
2022-12-09 16:07:03 +00:00
window . ATK _CHARACTER _STATE _IN _AIR _SET = new Set ( ) ;
window . ATK _CHARACTER _STATE _IN _AIR _SET . add ( window . ATK _CHARACTER _STATE . InAirIdle1 [ 0 ] ) ;
window . ATK _CHARACTER _STATE _IN _AIR _SET . add ( window . ATK _CHARACTER _STATE . InAirAtk1 [ 0 ] ) ;
window . ATK _CHARACTER _STATE _IN _AIR _SET . add ( window . ATK _CHARACTER _STATE . InAirAtked1 [ 0 ] ) ;
2022-11-26 16:00:39 +00:00
/ *
2022-11-27 11:38:26 +00:00
Kindly note that the use of dragonBones anim is an informed choice for the feasibility of "gotoAndPlayByFrame" , which is a required feature by "Map.rollbackAndChase" . You might find that "cc.Animation" -- the traditional frame anim -- can also suffice this requirement , yet if we want to develop 3 D frontend in the future , working with skeletal anim will make a smoother transition .
Moreover , frame anim doesn ' t support "compositie playing" and consumes more memory ( yet less CPU ) than a same skeletal anim , thus should only be used properly .
2022-11-26 16:00:39 +00:00
I 've also spent sometime in extending "ccc wrapped dragoneBones.ArmatureDisplay" for enabling "gotoAndPlayByFrame" in CACHE mode (in REALTIME mode it' s just the same as what ' s done here ) , but the debugging is an unexpected brainteaser -- not worth the time .
* /
2022-11-19 12:58:07 +00:00
cc . Class ( {
extends : BaseCharacter ,
properties : {
animNode : {
type : cc . Node ,
default : null
} ,
} ,
ctor ( ) {
2022-11-20 10:53:33 +00:00
this . speciesName = null ;
2022-11-22 09:12:51 +00:00
this . hp = 100 ;
this . maxHp = 100 ;
this . framesToRecover = 0 ;
2022-12-09 09:22:04 +00:00
this . inAir = true ;
2022-11-20 10:53:33 +00:00
} ,
setSpecies ( speciesName ) {
this . speciesName = speciesName ;
2022-11-19 12:58:07 +00:00
} ,
onLoad ( ) {
BaseCharacter . prototype . onLoad . call ( this ) ;
2022-11-27 11:38:26 +00:00
this . effAnimNode = this . animNode . getChildByName ( this . speciesName ) ;
this . animComp = this . effAnimNode . getComponent ( dragonBones . ArmatureDisplay ) ;
if ( ! this . animComp ) {
this . animComp = this . effAnimNode . getComponent ( cc . Animation ) ;
2022-12-11 04:20:42 +00:00
this . effAnimNode . anchorY = 0.0 ; // Otherwise the anim will show with an incorrect y-offset even if the collider boundaries are all correct!
}
2022-11-27 11:38:26 +00:00
this . effAnimNode . active = true ;
2022-11-19 12:58:07 +00:00
} ,
2022-11-25 03:20:05 +00:00
updateCharacterAnim ( rdfPlayer , prevRdfPlayer , forceAnimSwitch ) {
2022-11-27 13:33:34 +00:00
// As this function might be called after many frames of a rollback, it's possible that the playing animation was predicted, different from "prevRdfPlayer.characterState" but same as "newCharacterState". More granular checks are needed to determine whether we should interrupt the playing animation.
2022-11-19 12:58:07 +00:00
2022-11-27 13:33:34 +00:00
// Update directions
if ( this . animComp && this . animComp . node ) {
if ( 0 > rdfPlayer . dirX ) {
this . animComp . node . scaleX = ( - 1.0 ) ;
} else if ( 0 < rdfPlayer . dirX ) {
this . animComp . node . scaleX = ( 1.0 ) ;
2022-11-25 03:20:05 +00:00
}
2022-11-27 11:38:26 +00:00
}
2022-11-27 13:33:34 +00:00
let newCharacterState = rdfPlayer . characterState ;
const newAnimName = window . ATK _CHARACTER _STATE _ARR [ newCharacterState ] [ 1 ] ;
let playingAnimName = null ;
let underlyingAnimationCtrl = null ;
if ( this . animComp instanceof dragonBones . ArmatureDisplay ) {
underlyingAnimationCtrl = this . animComp . _armature . animation ; // ALWAYS use the dragonBones api instead of ccc's wrapper!
playingAnimName = underlyingAnimationCtrl . lastAnimationName ;
} else {
underlyingAnimationCtrl = this . animComp . currentClip ;
playingAnimName = ( ! underlyingAnimationCtrl ? null : underlyingAnimationCtrl . name ) ;
}
// It turns out that "prevRdfPlayer.characterState" is not useful in this function :)
if ( newAnimName == playingAnimName && window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . has ( newCharacterState ) ) {
// No need to interrupt
2022-11-29 04:49:49 +00:00
// console.warn(`JoinIndex=${rdfPlayer.joinIndex}, not interrupting ${newAnimName} while the playing anim is also ${playingAnimName}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, to: ${JSON.stringify(rdfPlayer)}`);
2022-11-27 11:38:26 +00:00
return ;
}
2022-11-27 13:33:34 +00:00
if ( this . animComp instanceof dragonBones . ArmatureDisplay ) {
2022-11-29 04:49:49 +00:00
this . _interruptPlayingAnimAndPlayNewAnimDragonBones ( rdfPlayer , prevRdfPlayer , newCharacterState , newAnimName , underlyingAnimationCtrl , playingAnimName ) ;
2022-11-27 13:33:34 +00:00
} else {
2022-11-29 04:49:49 +00:00
this . _interruptPlayingAnimAndPlayNewAnimFrameAnim ( rdfPlayer , prevRdfPlayer , newCharacterState , newAnimName , playingAnimName ) ;
2022-11-25 13:53:30 +00:00
}
} ,
2022-11-29 04:49:49 +00:00
_interruptPlayingAnimAndPlayNewAnimDragonBones ( rdfPlayer , prevRdfPlayer , newCharacterState , newAnimName , underlyingAnimationCtrl , playingAnimName ) {
if ( window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . has ( newCharacterState ) ) {
2022-11-25 13:53:30 +00:00
// No "framesToRecover"
2022-12-13 10:10:30 +00:00
// console.warn(`#DragonBones JoinIndex=${rdfPlayer.joinIndex}, ${playingAnimName} -> ${newAnimName}`);
2022-11-25 15:50:13 +00:00
underlyingAnimationCtrl . gotoAndPlayByFrame ( newAnimName , 0 , - 1 ) ;
2022-11-25 13:53:30 +00:00
} else {
2022-11-25 15:50:13 +00:00
const animationData = underlyingAnimationCtrl . _animations [ newAnimName ] ;
2022-11-25 13:53:30 +00:00
let fromAnimFrame = ( animationData . frameCount - rdfPlayer . framesToRecover ) ;
2022-11-27 13:33:34 +00:00
if ( fromAnimFrame < 0 ) {
2022-11-25 13:53:30 +00:00
// For Atk1 or Atk2, it's possible that the "meleeBullet.recoveryFrames" is configured to be slightly larger than corresponding animation duration frames
fromAnimFrame = 0 ;
}
2022-11-25 15:50:13 +00:00
underlyingAnimationCtrl . gotoAndPlayByFrame ( newAnimName , fromAnimFrame , 1 ) ;
2022-11-19 12:58:07 +00:00
}
} ,
2022-11-27 13:33:34 +00:00
2022-11-29 04:49:49 +00:00
_interruptPlayingAnimAndPlayNewAnimFrameAnim ( rdfPlayer , prevRdfPlayer , newCharacterState , newAnimName , playingAnimName ) {
2022-11-27 13:33:34 +00:00
if ( window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . has ( newCharacterState ) ) {
// No "framesToRecover"
2022-12-09 16:07:03 +00:00
//console.warn(`#FrameAnim JoinIndex=${rdfPlayer.joinIndex}, ${playingAnimName} -> ${newAnimName}`);
2022-11-27 13:33:34 +00:00
this . animComp . play ( newAnimName , 0 ) ;
return ;
}
// The "playTimes" counterpart is managed by each "cc.AnimationClip.wrapMode", already preset in the editor.
const targetClip = this . animComp . getClips ( ) [ newCharacterState ] ; // The clips follow the exact order in ATK_CHARACTER_STATE
let fromTime = ( targetClip . duration - rdfPlayer . framesToRecover / targetClip . sample ) ; // TODO: Anyway to avoid using division here?
if ( fromTime < 0 ) {
// For Atk1 or Atk2, it's possible that the "meleeBullet.recoveryFrames" is configured to be slightly larger than corresponding animation duration frames
fromTime = 0 ;
}
this . animComp . play ( newAnimName , fromTime ) ;
} ,
2022-11-19 12:58:07 +00:00
} ) ;