"""State module: contains State class""" from direct.showbase.DirectObject import * import types # This gets set by a dconfig variable in ShowBase.py # We cannot put a dconfig in here because ClassicFSM is not # dependent on Panda FsmRedefine = 0 # Map function pointers back into states so the Finder # can find a function and swap a newly redefined function # back into the original state # The map is keyed off function pointers which map to # a list of states that have that function pointer defined # as their enter function (or exit function as the case may be) EnterFuncRedefineMap = {} ExitFuncRedefineMap = {} def redefineEnterFunc(oldMethod, newFunction): import new if not FsmRedefine: return for method in EnterFuncRedefineMap.keys(): if (type(method) == types.MethodType): function = method.im_func else: function = method #print ('function: ' + `function` + '\n' + # 'method: ' + `method` + '\n' + # 'oldMethod: ' + `oldMethod` + '\n' + # 'newFunction: ' + `newFunction` + '\n') if (function == oldMethod): newMethod = new.instancemethod(newFunction, method.im_self, method.im_class) stateList = EnterFuncRedefineMap[method] for state in stateList: state.setEnterFunc(newMethod) return 1 return 0 def redefineExitFunc(oldMethod, newFunction): import new if not FsmRedefine: return for method in ExitFuncRedefineMap.keys(): if (type(method) == types.MethodType): function = method.im_func else: function = method #print ('function: ' + `function` + '\n' + # 'method: ' + `method` + '\n' + # 'oldMethod: ' + `oldMethod` + '\n' + # 'newFunction: ' + `newFunction` + '\n') if (function == oldMethod): newMethod = new.instancemethod(newFunction, method.im_self, method.im_class) stateList = ExitFuncRedefineMap[method] for state in stateList: state.setExitFunc(newMethod) return 1 return 0 class State(DirectObject): notify = directNotify.newCategory("State") # this 'constant' can be used to specify that the state # can transition to any other state Any = 'ANY' def __init__(self, name, enterFunc=None, exitFunc=None, transitions=Any, inspectorPos = []): """__init__(self, string, func, func, string[], inspectorPos = []) State constructor: takes name, enter func, exit func, and a list of states it can transition to (or State.Any).""" self.__name = name self.__enterFunc = enterFunc self.__exitFunc = exitFunc self.__transitions = transitions self.__FSMList = [] if __debug__: self.setInspectorPos(inspectorPos) # setters and getters def getName(self): return(self.__name) def setName(self, stateName): self.__name = stateName def getEnterFunc(self): return(self.__enterFunc) if __debug__: def redefineFunc(self, oldMethod, newMethod, map): if not FsmRedefine: return # Methods are allowed to be None if oldMethod is None: return if map.has_key(oldMethod): # Get the list of states for the old function stateList = map[oldMethod] # Remove this state from that list of states stateList.remove(self) # If the stateList is now empty, remove this entry altogether if not stateList: del(map[oldMethod]) # Now add the new function, creating a starter state list # if there is not one already stateList = map.get(newMethod, []) stateList.append(self) map[newMethod] = stateList def setEnterFunc(self, stateEnterFunc): if __debug__: self.redefineFunc(self.__enterFunc, stateEnterFunc, EnterFuncRedefineMap) self.__enterFunc = stateEnterFunc def getExitFunc(self): return(self.__exitFunc) def setExitFunc(self, stateExitFunc): if __debug__: self.redefineFunc(self.__exitFunc, stateExitFunc, ExitFuncRedefineMap) self.__exitFunc = stateExitFunc def transitionsToAny(self): """ returns true if State defines transitions to any other state """ return self.__transitions is State.Any def getTransitions(self): """ warning -- if the state transitions to any other state, returns an empty list (falsely implying that the state has no transitions) see State.transitionsToAny() """ if self.transitionsToAny(): return [] return self.__transitions def isTransitionDefined(self, otherState): if self.transitionsToAny(): return 1 # if we're given a state object, get its name instead if type(otherState) != type(''): otherState = otherState.getName() return (otherState in self.__transitions) def setTransitions(self, stateTransitions): """setTransitions(self, string[])""" self.__transitions = stateTransitions def addTransition(self, transition): """addTransitions(self, string)""" if not self.transitionsToAny(): self.__transitions.append(transition) else: State.notify.warning( 'attempted to add transition %s to state that ' 'transitions to any state') if __debug__: def getInspectorPos(self): """getInspectorPos(self)""" return(self.__inspectorPos) def setInspectorPos(self, inspectorPos): """setInspectorPos(self, [x, y])""" self.__inspectorPos = inspectorPos # support for HFSMs def getChildren(self): """ Return the list of child FSMs """ return(self.__FSMList) def setChildren(self, FSMList): """setChildren(self, ClassicFSM[]) Set the children to given list of FSMs """ self.__FSMList = FSMList def addChild(self, ClassicFSM): """ Add the given ClassicFSM to list of child FSMs """ self.__FSMList.append(ClassicFSM) def removeChild(self, ClassicFSM): """ Remove the given ClassicFSM from list of child FSMs """ if ClassicFSM in self.__FSMList: self.__FSMList.remove(ClassicFSM) def hasChildren(self): """ Return true if state has child FSMs """ return len(self.__FSMList) > 0 def __enterChildren(self, argList): """ Enter all child FSMs """ for fsm in self.__FSMList: # Check to see if the child fsm is already in a state # if it is, politely request the initial state if fsm.getCurrentState(): # made this 'conditional_request()' instead of 'request()' to avoid warning when # loading minigames where rules->frameworkInit transition doesnt exist and you # dont want to add it since it results in hanging the game fsm.conditional_request((fsm.getInitialState()).getName()) # If it has no current state, I assume this means it # has never entered the initial state, so enter it # explicitly else: fsm.enterInitialState() def __exitChildren(self, argList): """ Exit all child FSMs """ for fsm in self.__FSMList: fsm.request((fsm.getFinalState()).getName()) # basic State functionality def enter(self, argList=[]): """ Call the enter function for this state """ # enter child FSMs first. It is assumed these have a start # state that is safe to enter self.__enterChildren(argList) if (self.__enterFunc != None): apply(self.__enterFunc, argList) def exit(self, argList=[]): """ Call the exit function for this state """ # first exit child FSMs self.__exitChildren(argList) # call exit function if it exists if (self.__exitFunc != None): apply(self.__exitFunc, argList) def __str__(self): return "State: name = %s, enter = %s, exit = %s, trans = %s, children = %s" %\ (self.__name, self.__enterFunc, self.__exitFunc, self.__transitions, self.__FSMList)