source: trunk/game/scripts/agents/agent.py @ 21

Revision 21, 5.4 KB checked in by icelus_parpg, 10 years ago (diff)

Apply ActionStack? patch from tie (ticket #1).

  • queue actions for hero to perform (e.g. move to target, act)
  • unit tests for ActionStack?
  • new run_tests script
  • kick added as an always present to allow queueing to be seen.
  • Property svn:eol-style set to native
Line 
1import fife
2from scripts.common.common import ProgrammingError
3
4class ActionStack (object):
5        """
6    Class, which implements queueing of various user actions.
7    Each entry in the ActoinStack is a 6-item tuple consisting of
8    (action_func, action_func_args, action_func_kw_args,
9    (success_func, success_func_args, success_func_kw_args).
10
11    Items are processed from index 0 upwards. New actions
12    are appended to the list. Successful actions are popped from the
13    bottom of the list. If the success function of the current
14    action returns True, the current action is popped out, and the
15    next action is processed.
16
17    IMPORTANT: To avoid the danger of endlessly pushing actions to the
18    stack, it is automatically cleared when an action is added while
19    the stack is in a running state.
20
21    Example:
22    To define something like "Kick him while he's down", you coud do:
23    .add_action (kick, (him,), isDown, (him,))
24
25    """
26        def __init__ (self):
27               
28                # This is the actual list that stores the entries
29                self.action_list = []
30                # A flag to track if the stack was ran after the last item was added
31                self.running = False
32
33        def add_kw_args_action (self, 
34                        action_func, action_args = (), action_kw_args = {},
35                        success_func = None, success_args = (),success_kw_args = {}):
36                """
37                Appends an action entry to the action_list. Supports both args and
38                kwargs. If a previous batch of actions was inserted, and then ran,
39                this will first empty the stack before adding the new entry.
40                """
41                # Basic checks to see if the action and success funcitons are callable
42                if not callable (action_func):
43                        raise ValueError ("%s is not callable!" % action_func)
44                if success_func and not callable(success_func):
45                        raise ValueError ("%s is not callable!" % success_func)
46                if self.running:
47                        # We have a running queue; clean it up before adding anything new
48                        self.clear()
49                        self.running = False
50
51                self.action_list.append ( (action_func, action_args, action_kw_args, 
52                                           success_func, success_args, success_kw_args))
53
54        def add_action (self, action_func, action_args = (),
55                        success_func = None, success_args = ()):
56                """
57                Appends an action entry to the action_list. Shortcut to
58                add_kw_args_action() without kwargs. Only supports positinal arguments
59                for the action_func/success_func
60                """
61                self.add_kw_args_action (action_func, action_args, {}, 
62                                        success_func, success_args , {})
63
64        def remove_current_action (self):
65                """Removes an action from the bottom of the list"""
66                if self.action_list:
67                        self.action_list.pop(0)
68
69        def clear (self):
70                """Removes all actions from the stack"""
71                self.action_list = []
72       
73        def perform_action (self, action_item):
74                """Runs the provided action_item. It is a tuple following the same
75                format as the items in self.action_list """
76                a_func, a_args, a_kw_args = action_item[:3]
77                a_func (*a_args, **a_kw_args)
78                return True
79
80        def run (self):
81                """
82                Runs an action from the queue.
83
84                If there is no success function, just executes the current action
85                and removes it from the queue. If the success function evaluates
86                to True, removes the current action and proceeds with the next one.
87                If the success function evaluates to False, only performs
88                the current action.
89               
90                Returns True if some action was executed, False otherwise.
91                """
92
93                if not self.action_list:
94                        return False
95                # We set the running flag
96                self.running = True
97               
98                # Loop that will go over the aciton_list and exit after performing an
99                # action. Note that the loop iterates over a copy of the action_list
100                for action in self.action_list[:]:
101                        # Load the current entry success function details
102                        s_func, s_args, s_kw_args = self.action_list[0][3:6]
103
104                        if (not s_func):
105                                # No success function defined - meaning that the
106                                # action will be popped form the queue and executed once.
107                                return self.perform_action(self.action_list.pop(0))
108                        elif not s_func (*s_args, **s_kw_args):
109                                # The current success conditions evaluated to False, keep
110                                # performing the current action
111                                return self.perform_action(self.action_list[0])
112                        else:
113                                # The success condition is true - meaning that the current action is
114                                # popped out, and we proceed to the next item in the list
115                                self.action_list.pop(0)
116               
117                # We went through the entire action_list but no action was performed
118                return False
119
120class Agent(fife.InstanceActionListener):
121        def __init__(self, model, agentName, layer, uniqInMap=True):
122                fife.InstanceActionListener.__init__(self)
123                self.model = model
124                self.agentName = agentName
125                self.layer = layer
126                self.action_stack = ActionStack()
127                if uniqInMap:
128                        self.agent = layer.getInstance(agentName)
129                        self.agent.addActionListener(self)
130
131        def onInstanceActionFinished(self, instance, action):
132                raise ProgrammingError('No OnActionFinished defined for Agent')
133
134        def start(self):
135                raise ProgrammingError('No start defined for Agent')
136       
137        def kickButtonHandler (self):
138                """Placeholder function to enable kick menu display for all children"""
139                pass
140        def talkButtonHandler (self):
141                """Placeholder function to enable talk menu display for all children"""
142                pass
143
144def create_anonymous_agents(model, objectName, layer, agentClass):
145        agents = []
146        instances = [a for a in layer.getInstances() if a.getObject().getId() == objectName]
147        i = 0
148        for a in instances:
149                agentName = '%s:i:%d' % (objectName, i)
150                i += 1
151                agent = agentClass(model, agentName, layer, False)
152                agent.agent = a
153                a.addActionListener(agent)
154                agents.append(agent)
155        return agents
Note: See TracBrowser for help on using the repository browser.