def __init__(self,FighterClassInstance,characterPath): FSM.__init__(self,"fighterFsm") path = characterPath self.cfgData = readCharacter(path) self.fighterinstance = FighterClassInstance actorPath = path+self.cfgData["actorFile"] self.fighter = Actor(actorPath, { 'idle' :actorPath+'-idle' , 'jump' :actorPath+'-jump', 'crouch' :actorPath+'-crouch', 'run-in' :actorPath+'-run-in' , 'run-out' :actorPath+'-run-out' , 'punch' :actorPath+'-punch', 'hit' :actorPath+'-hit' , 'defense' :actorPath+'-defense', 'kick' :actorPath+'-kick' , 'ko' :actorPath+'-ko' , 'crouch-punch':actorPath+'-crouch-punch', 'crouch-kick' :actorPath+'-crouch-kick', 'crouch-defense':actorPath+'-crouch-defense', 'crouch-hit' :actorPath+'-crouch-hit', 'jump-in' :actorPath+'-jump-in', 'jump-out' :actorPath+'-jump-out', #'evade-cw' :actorPath+'-evade-cw' 'evade-ccw' :actorPath+'-evade-ccw' }) #model was rotated the wrong way in blender.. darn fixing it self.fighter.setH(180) self.fighter.flattenMedium() self.fighter.reparentTo(render) self.fighter.setBlend(frameBlend=True) self.fighter.setPlayRate(4.0, 'step') self.activeTimer = None #we will store our active sequence,parallel or interval here, so we can easily clean it up self.transitionTimer = None #usually holds a sequence like sequence(Wait(time),self.request('nextstate')) #loading sounds... could go in an extra-file self.sounds = PlayerSoundFX() self.request("Idle")
class FighterFsm(FSM): #inherits from direct.fsm.FSM ##this class has to be written for each character in the game ####unfortunately that much coding per char is required until we can autogenerate based on artists input ## i am not sure where to put the fighter actor. logically it belongs to the fighter class, but the fsm does a lot more with it. ## guess it will end up in the fsm as this is the file created for each fighter individually. ## or if we should inherit Fighter from FSM and simply stuff everything in there wich would be bad cause we copy all shared code around #bitmasks are 0 for on the floor, 1 for legs, 2 for torso&head , #3 for vertical down (like hammer smackdown, bodyslam form the back of a horse, meteroids...) def __init__(self,FighterClassInstance,characterPath): FSM.__init__(self,"fighterFsm") path = characterPath self.cfgData = readCharacter(path) self.fighterinstance = FighterClassInstance actorPath = path+self.cfgData["actorFile"] self.fighter = Actor(actorPath, { 'idle' :actorPath+'-idle' , 'jump' :actorPath+'-jump', 'crouch' :actorPath+'-crouch', 'run-in' :actorPath+'-run-in' , 'run-out' :actorPath+'-run-out' , 'punch' :actorPath+'-punch', 'hit' :actorPath+'-hit' , 'defense' :actorPath+'-defense', 'kick' :actorPath+'-kick' , 'ko' :actorPath+'-ko' , 'crouch-punch':actorPath+'-crouch-punch', 'crouch-kick' :actorPath+'-crouch-kick', 'crouch-defense':actorPath+'-crouch-defense', 'crouch-hit' :actorPath+'-crouch-hit', 'jump-in' :actorPath+'-jump-in', 'jump-out' :actorPath+'-jump-out', #'evade-cw' :actorPath+'-evade-cw' 'evade-ccw' :actorPath+'-evade-ccw' }) #model was rotated the wrong way in blender.. darn fixing it self.fighter.setH(180) self.fighter.flattenMedium() self.fighter.reparentTo(render) self.fighter.setBlend(frameBlend=True) self.fighter.setPlayRate(4.0, 'step') self.activeTimer = None #we will store our active sequence,parallel or interval here, so we can easily clean it up self.transitionTimer = None #usually holds a sequence like sequence(Wait(time),self.request('nextstate')) #loading sounds... could go in an extra-file self.sounds = PlayerSoundFX() self.request("Idle") def getNP(self): return self.fighter def _attackSeq(self,data): attackMask = BitMask32() attackMask.setBit(data["attackbit"]) return Sequence( Wait(data["delay"]), Func(self._attack,attackMask,data["range"],data["damage"], data["blockeddamage"],data["angle"]) ) def _setSBM(self,bitmask): """ yet another convenience function, sets the status bit mask """ self.fighterinstance.setStatusBitMask(bitmask) def _setDBM(self,bitmask): """ aaand yet another convenience function, sets the defense bit mask """ self.fighterinstance.setDefenseBitMask(bitmask) def _attack(self,attackBitMask,attackrange,damageHit,damageDodge=0,angle=30): """ more convenience function, this one attacks the opponent """ hit = self.fighterinstance.attack(attackBitMask,attackrange,damageHit,damageDodge,angle) if hit == 0: self.sounds.playMiss() elif hit == 1: self.sounds.playBlock() elif hit == 2: self.sounds.playHit() elif hit == 3: #the other player went ko , go to win-state self.sounds.playHit() elif hit == 4: pass #the other player is ko already.. #we still miss the hit self.sounds.playMiss() #---------- def _stand(self): newBitMask = BitMask32() newBitMask.setRange(0,4) self._setSBM(newBitMask) #----------- def _crouch(self): newBitMask = BitMask32() newBitMask.setRange(0,2) newBitMask.setBit(3) self._setSBM(newBitMask) #----------- def _cancelTransition(self,task=0): if self.transitionTimer: self.transitionTimer.clearIntervals() #trying to fix a rarely occuring bug that triggers a state change during the cancelTransition call self.transitionTimer.pause() self.transitionTimer #----------- def _cancelActive(self,task=0): if self.activeTimer: self.activeTimer.clearIntervals() self.activeTimer.pause() self.activeTimer = None #---------- def enterKo(self): taskMgr.doMethodLater(0.2,self._cancelActive,"cancelActive") #timer to allow double-KO newBitMask = BitMask32() self._setSBM(newBitMask) self.fighter.play("ko") def filterKo(self,request,args): #this blocks the fsm. but will be forced to idle by the fighter class return def exitKo(self): pass #----------- def enterHit(self): self._stand() self.fighter.play("hit") self.fighterinstance.setSpeed(-1,0) self.transitionTimer= Sequence(Wait(self.fighter.getDuration()), Func(self.fighterinstance.updateState ) ) self.transitionTimer.start() def filterHit(self,request,options): if self.transitionTimer.getT() > self.transitionTimer.getDuration()-0.1 : #allow player to hit the next strike back return request def exitHit(self): self.fighterinstance.setSpeed(0,0) self._cancelTransition() self._cancelActive() #------------------------- def enterCrouchHit(self): self._crouch() self.fighter.play("crouch-hit") self.fighterinstance.setSpeed(-1,0) self.transitionTimer= Sequence(Wait(self.fighter.getDuration()), Func(self.fighterinstance.updateState ) ) self.transitionTimer.start() def filterCrouchHit(self,request,options): if self.transitionTimer.getT() > self.transitionTimer.getDuration()-0.1 : #allow player to hit the next strike back return request def exitCrouchHit(self): self.fighterinstance.setSpeed(0,0) self._cancelTransition() self._cancelActive() #--------- def enterDefense(self): self._stand() newBitMask = BitMask32() #newBitMask.setBit(1) newBitMask.setBit(2) self._setDBM(newBitMask) self.fighter.stop() self.fighter.loop('defense') def filterDefense(self,request,options): if request != "Defense": return request def exitDefense(self): newBitMask = BitMask32() self._setDBM(newBitMask) self._cancelTransition() self._cancelActive() #--------- def enterRunIn(self): self._stand() self.fighter.loop("run-in") self.fighterinstance.setSpeed(self.cfgData["run-in"]["speedx"],self.cfgData["run-in"]["speedy"]) def filterRunIn(self,request,options): if request != "RunIn": return request def exitRunIn(self): self.fighter.stop() self.fighterinstance.setSpeed(0 ,0) #--------------------- def enterRunOut(self): self._stand() self.fighter.loop("run-out") self.fighterinstance.setSpeed(self.cfgData["run-out"]["speedx"],self.cfgData["run-out"]["speedy"]) def filterRunOut(self,request,options): if request != "RunOut": return request def exitRunOut(self): self.fighter.stop() self.fighterinstance.setSpeed(0 ,0) #--------------------- def enterEvadeCCW(self): self._stand() self.fighterinstance.faceOpponent(False) self.fighter.stop() self.fighter.play('evade-ccw') self.fighterinstance.setSpeed(self.cfgData["evade-ccw"]["speedx"],self.cfgData["evade-ccw"]["speedy"]) self.transitionTimer= Sequence(Wait(self.fighter.getDuration()), Func(self.fighterinstance.updateState ) ) self.transitionTimer.start() def filterEvadeCCW(self,request,args): if self.transitionTimer.getT() > self.transitionTimer.getDuration()-0.1 : return request def exitEvadeCCW(self): self._stand() self.fighterinstance.setSpeed(0,0) self.fighterinstance.faceOpponent(True) self._cancelTransition() #--------------------- def enterJumpIn(self): self.fighterinstance.faceOpponent(False) self.fighter.stop() self.fighter.play('jump-in') self.fighterinstance.setSpeed(self.cfgData["jump-in"]["speedx"],self.cfgData["jump-in"]["speedy"]) #TODO:add a parallele here, modifying the bitmasks during jump #till then. jump all the time newBitMask = BitMask32() newBitMask.setRange(2,3) self._setSBM(newBitMask) self.transitionTimer= Sequence(Wait(self.fighter.getDuration()), Func(self.fighterinstance.updateState ) ) self.transitionTimer.start() def filterJumpIn(self,request,args): if self.transitionTimer.getT() > self.transitionTimer.getDuration()-0.1 : #allow player to hit the next strike 0.2 to 0 seconds befor the animation finished return request def exitJumpIn(self): self._stand() self.fighterinstance.setSpeed(0,0) self.fighterinstance.faceOpponent(True) self._cancelTransition() #--------------------- def enterJumpOut(self): self.fighterinstance.faceOpponent(False) self.fighter.stop() self.fighter.play('jump-out') self.fighterinstance.setSpeed(self.cfgData["jump-out"]["speedx"],self.cfgData["jump-out"]["speedy"]) #TODO:add a parallele here, modifying the bitmasks during jump #till then. jump all the time newBitMask = BitMask32() newBitMask.setRange(2,3) self._setSBM(newBitMask) self.transitionTimer= Sequence(Wait(self.fighter.getDuration()), Func(self.fighterinstance.updateState ) ) self.transitionTimer.start() def filterJumpOut(self,request,args): if self.transitionTimer.getT() > self.transitionTimer.getDuration()-0.1 : #allow player to hit the next strike 0.2 to 0 seconds befor the animation finished return request def exitJumpOut(self): self._stand() self.fighterinstance.setSpeed(0,0) self.fighterinstance.faceOpponent(True) self._cancelTransition() #--------------------- def enterJump(self): self.fighterinstance.faceOpponent(False) self.fighter.stop() self.fighter.play('jump') #TODO:add a parallele here, modifying the bitmasks during jump #till then. jump all the time newBitMask = BitMask32() newBitMask.setRange(2,3) #newBitMask.setBit(3) self._setSBM(newBitMask) self.transitionTimer= Sequence(Wait(self.fighter.getDuration()), Func(self.fighterinstance.updateState ) ) self.transitionTimer.start() def filterJump(self,request,args): if self.transitionTimer.getT() < 0.1 and "Evade" in request: return request if self.transitionTimer.getT() > self.transitionTimer.getDuration()-0.1 : #allow player to hit the next strike 0.2 to 0 seconds befor the animation finished return request def exitJump(self): self._stand() self.fighterinstance.faceOpponent(True) self._cancelTransition() #------------ def enterIdle(self): #self.fighterinstance.setSpeed(0,0) self._stand() self.fighter.loop("idle") Func(self.fighterinstance.updateState).start() #slightly hacky but we cant call that WITHIN the transition of entering idle. so it will be called next frame. #doesnt look logic but saves craploads of uncool code, trust me def filterIdle(self,request,options): if request != "Idle": return request def exitIdle(self): #self.fighterinstance.setSpeed(0,0) #cant hurt self._cancelTransition() self._cancelActive() #------------ def enterCrouch(self): self._crouch() #self.fighterinstance.setSpeed(0,0) self.fighter.loop("crouch") #Func(self.fighterinstance.updateState).start() #slightly hacky but we cant call that WITHIN the transition of entering idle. so it will be called next frame. #doesnt look logic but saves craploads of uncool code, trust me def filterCrouch(self,request,options): if request != "Crouch": return request def exitCrouch(self): #self.fighterinstance.setSpeed(0,0) #cant hurt pass #--------------- def enterCrouchPunch(self): self._crouch() self.fighterinstance.faceOpponent(False) self.fighter.stop() self.fighter.play('crouch-punch') self.transitionTimer= Sequence(Wait(self.fighter.getDuration()), Func(self.fighterinstance.updateState ) ) self.transitionTimer.start() data = self.cfgData["crouch-punch"] self.activeTimer = self._attackSeq(data) self.activeTimer.start() def filterCrouchPunch(self,request,args): if self.transitionTimer.getT() > self.transitionTimer.getDuration()-0.2 : #allow player to hit the next strike 0.2 to 0 seconds befor the animation finished return request def exitCrouchPunch(self): self.fighterinstance.faceOpponent(True) self._cancelTransition() self._cancelActive() #--------------- def enterCrouchKick(self): self._crouch() self.fighterinstance.faceOpponent(False) self.fighter.stop() self.fighter.play('crouch-kick') self.transitionTimer= Sequence(Wait(self.fighter.getDuration()), Func(self.fighterinstance.updateState ) ) self.transitionTimer.start() data = self.cfgData["crouch-kick"] self.activeTimer = self._attackSeq(data) self.activeTimer.start() def filterCrouchKick(self,request,args): if self.transitionTimer.getT() > self.transitionTimer.getDuration()-0.2 : #allow player to hit the next strike 0.2 to 0 seconds befor the animation finished return request def exitCrouchKick(self): self.fighterinstance.faceOpponent(True) self._cancelTransition() self._cancelActive() #------------------------- def enterCrouchDefense(self): self._crouch() newBitMask = BitMask32() newBitMask.setBit(1) #newBitMask.setBit(2) self._setDBM(newBitMask) self.fighter.stop() self.fighter.loop('crouch-defense') def filterCrouchDefense(self,request,options): if request != "CrouchDefense": return request def exitCrouchDefense(self): newBitMask = BitMask32() self._setDBM(newBitMask) self._cancelTransition() self._cancelActive() #--------------- def enterPunch(self): self._stand() self.fighterinstance.faceOpponent(False) self.fighter.stop() self.fighter.play('punch') self.transitionTimer= Sequence(Wait(self.fighter.getDuration()), Func(self.fighterinstance.updateState ) ) self.transitionTimer.start() data = self.cfgData["punch"] self.activeTimer = self._attackSeq(data) self.activeTimer.start() def filterPunch(self,request,args): if self.transitionTimer.getT() > self.transitionTimer.getDuration()-0.2 : #allow player to hit the next strike 0.2 to 0 seconds befor the animation finished return request def exitPunch(self): self.fighterinstance.faceOpponent(True) self._cancelTransition() self._cancelActive() #------------- def enterKick(self): self._stand() self.fighterinstance.faceOpponent(False) self.fighter.stop() self.fighter.play('kick') self.transitionTimer= Sequence(Wait(self.fighter.getDuration()), Func(self.request,"Idle" ) ) self.transitionTimer.start() data = self.cfgData["kick"] self.activeTimer = self._attackSeq(data) self.activeTimer.start() def filterKick(self,request,args): if self.transitionTimer.getT() > self.transitionTimer.getDuration()-0.2 : return request def exitKick(self): self.fighterinstance.faceOpponent(True) self._cancelTransition() self._cancelActive() #---------------- """