|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from .events import Events |
|
|
|
|
|
from .nodes import Node |
|
|
|
|
|
from .time import Time |
|
|
|
|
|
from .display import Display |
|
|
|
|
|
|
|
|
|
|
|
class StateMachineError(Exception): |
|
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# This is the main, active state (Node). |
|
|
|
|
|
_ACTIVE_STATE = None |
|
|
|
|
|
|
|
|
|
|
|
# In some cases, the previous state isn't totally shut down, but temporarily put on hold. |
|
|
|
|
|
# Only the _render() call will be made on hold states. |
|
|
|
|
|
_HOLD_STATE = None |
|
|
|
|
|
|
|
|
|
|
|
# List of all states registered into the State Manager. |
|
|
|
|
|
_STATE_LIST = [] |
|
|
|
|
|
|
|
|
|
|
|
_TIME = Time() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class StateMachine: |
|
|
|
|
|
def __init__(self): |
|
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
def has_node(self, name): |
|
|
|
|
|
""" |
|
|
|
|
|
Returns true if a Node with the given name has been registered in the State List. |
|
|
|
|
|
NOTE: This is only going to check the root Node's name, not any other node in the node tree. |
|
|
|
|
|
""" |
|
|
|
|
|
global _STATE_LIST |
|
|
|
|
|
for n in _STATE_LIST: |
|
|
|
|
|
if n.name == name: |
|
|
|
|
|
return True |
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
def register_node(self, node): |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
""" |
|
|
|
|
|
if not isinstance(node, Node): |
|
|
|
|
|
raise TypeError("Expected a Node instance.") |
|
|
|
|
|
# Check if given the root Node. If not, get the root Node. |
|
|
|
|
|
if node.parent != None: |
|
|
|
|
|
node = node.root |
|
|
|
|
|
if self.has_node(node.name): |
|
|
|
|
|
raise StateMachineError("State machine already registered node named '{}'. Names must be unique.".format(node.name)) |
|
|
|
|
|
global _STATE_LIST |
|
|
|
|
|
_STATE_LIST.append(node) |
|
|
|
|
|
|
|
|
|
|
|
def activate_node(self, name, hold_previous=False): |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
""" |
|
|
|
|
|
global _ACTIVE_STATE, _TIME |
|
|
|
|
|
if self.has_node(name): |
|
|
|
|
|
if _ACTIVE_STATE is not None: |
|
|
|
|
|
if _ACTIVE_STATE.name == name: |
|
|
|
|
|
return |
|
|
|
|
|
self._releaseActive(hold_previous) |
|
|
|
|
|
self._setActive(name) |
|
|
|
|
|
if _ACTIVE_STATE is not None: |
|
|
|
|
|
_TIME.reset() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _relaseActive(self, hold_previous): |
|
|
|
|
|
global _ACTIVE_STATE, _HOLD_STATE |
|
|
|
|
|
a = _ACTIVE_STATE |
|
|
|
|
|
a._pause() |
|
|
|
|
|
if _HOLD_STATE is not None: |
|
|
|
|
|
_ACTIVE_STATE = _HOLD_STATE |
|
|
|
|
|
_HOLD_STATE = None |
|
|
|
|
|
_ACTIVE_STATE._start() |
|
|
|
|
|
else: |
|
|
|
|
|
_ACTIVE_STATE = None |
|
|
|
|
|
|
|
|
|
|
|
if hold_previous == True: |
|
|
|
|
|
_HOLD_STATE = a |
|
|
|
|
|
else: |
|
|
|
|
|
a._close() |
|
|
|
|
|
|
|
|
|
|
|
def _setActive(self, name): |
|
|
|
|
|
global _ACTIVE_STATE, _STATE_LIST |
|
|
|
|
|
for n in _STATE_LIST: |
|
|
|
|
|
if n.name == name: |
|
|
|
|
|
_ACTIVE_STATE = n |
|
|
|
|
|
break |
|
|
|
|
|
if _ACTIVE_STATE is not None: |
|
|
|
|
|
_ACTIVE_STATE._init() |
|
|
|
|
|
_ACTIVE_STATE._start() |
|
|
|
|
|
elif _HOLD_STATE is not None: |
|
|
|
|
|
# If we failed to find an Active state, then we need to drop any hold state as well... |
|
|
|
|
|
# Keep things as clean as possible |
|
|
|
|
|
_HOLD_STATE._close() |
|
|
|
|
|
_HOLD_STATE = None |
|
|
|
|
|
|
|
|
|
|
|
def close(self): |
|
|
|
|
|
global _ACTIVE_STATE, _HOLD_STATE, _STATE_LIST |
|
|
|
|
|
if _HOLD_STATE is not None: |
|
|
|
|
|
_HOLD_STATE._close() |
|
|
|
|
|
_HOLD_STATE = None |
|
|
|
|
|
if _ACTIVE_STATE is not None: |
|
|
|
|
|
_ACTIVE_STATE._pause() |
|
|
|
|
|
_ACTIVE_STATE._close() |
|
|
|
|
|
_ACTIVE_STATE = None |
|
|
|
|
|
_STATE_LIST = [] |
|
|
|
|
|
|
|
|
|
|
|
def update(self): |
|
|
|
|
|
global _ACTIVE_STATE, _TIME |
|
|
|
|
|
if _ACTIVE_STATE is not None: |
|
|
|
|
|
_ACTIVE_STATE._update(_TIME.delta) |
|
|
|
|
|
|
|
|
|
|
|
def render(self): |
|
|
|
|
|
global _ACTIVE_STATE, _HOLD_STATE |
|
|
|
|
|
dsurf = Display.surface |
|
|
|
|
|
if dsurf is not None: |
|
|
|
|
|
Display.clear() |
|
|
|
|
|
|
|
|
|
|
|
# If there's a hold state, render that first. |
|
|
|
|
|
if _HOLD_STATE is not None: |
|
|
|
|
|
_HOLD_STATE._render(dsurf) |
|
|
|
|
|
# Render the active state next so it overdraws the hold state. |
|
|
|
|
|
if _ACTIVE_STATE is not None: |
|
|
|
|
|
_ACTIVE_STATE._render(dsurf) |
|
|
|
|
|
|
|
|
|
|
|
Display.flip() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|