source: trunk/game/scripts/dialogue.py @ 415

Revision 415, 9.8 KB checked in by eliedebrauwer, 10 years ago (diff)

Ticket #198: Patch by eliedebrauwer. Now reads the NPC name and uses this in the dialogue window as name. And left under the portrait. At this point a newline gets added after the first word, so the name/description shouldn't contains pages of text. fixes[s:trac, t:198]

  • Property svn:eol-style set to native
Line 
1#!/usr/bin/python
2
3#   This file is part of PARPG.
4
5#   PARPG is free software: you can redistribute it and/or modify
6#   it under the terms of the GNU General Public License as published by
7#   the Free Software Foundation, either version 3 of the License, or
8#   (at your option) any later version.
9
10#   PARPG is distributed in the hope that it will be useful,
11#   but WITHOUT ANY WARRANTY; without even the implied warranty of
12#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13#   GNU General Public License for more details.
14
15#   You should have received a copy of the GNU General Public License
16#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>.
17
18import logging
19import itertools
20
21class EndException(Exception):
22    """EndException is used to bail out from a deeply nested
23       runSection/continueWithResponse call stack and end the
24       conversation"""
25    pass
26
27class ResponseException(Exception):
28    """ResponseException is used to bail out from a deeply nested
29       runSection/continueWithResponse call stack and allow the user to
30       specify a response"""
31    pass
32
33class BackException(Exception):
34    """BackException is used to bail out from a deeply nested
35       runSection/continueWithResponse call stack and rewind the section
36       stack"""
37    pass
38
39class DialogueEngine(object):
40    def __init__(self, obj, callbacks={}, state={}):
41        """A very simple dialogue engine for a game.
42           d = DialogueEngine(tree, callbacks)
43           d = DialogueEngine('screenplay.yaml', callbacks)"""
44
45        if isinstance(obj, dict):
46            self.tree = obj
47        elif isinstance(obj, str):
48            import yaml
49            self.tree = yaml.load(file(obj))
50
51        logging.basicConfig(level=logging.INFO)
52
53        self.callbacks = callbacks
54        self.state = state
55        self.section_stack = []
56
57    def run(self):
58        """Start running the dialogue engine.
59        @returns: list of lists (if requesting a response)
60        @returns: None (if at the end of the script)"""
61        start_section = self.tree['START']
62
63        npc_name_cb = self.callbacks.get('npc_name')
64        if npc_name_cb:
65            npc_name_cb(self.tree['NPC'])
66           
67        npc_avatar_cb = self.callbacks.get('npc_avatar')
68        if npc_avatar_cb:
69            npc_avatar_cb(self.tree['AVATAR'])
70
71        try:
72            self.runSection(start_section)
73        except EndException:
74            # we stopped talking to the NPC
75            logging.debug("Reached the end")
76            end_cb = self.callbacks.get('end')
77            if end_cb:
78                end_cb()
79            return
80        except ResponseException, e:
81            return e.args[0]
82        except BackException, e:
83            self.section_stack.pop(-1)
84            try:
85                self.runSection(self.section_stack[-1])
86                return e
87            except ResponseException, e:
88                return e.args[0]
89
90    def getSection(self, section_name):
91        """Return a section object.
92        @type section_name: string
93        @param section_name: The section to get
94        @return: dict"""
95        return self.tree['SECTIONS'][section_name]
96
97    def reply(self, response):
98        """After being prompted to provide a response, reply is called to
99           submit a response.
100           @type choice: int
101           @param choice: the index of the response to submit
102           @return: list of lists (if requesting a response)
103           @return: None (if at the end of the script)"""
104        while True:
105            try:
106                if response is not None:
107                    self.continueWithResponse(self.section_stack[-1], \
108                                                response)
109                else:
110                    self.runSection(self.section_stack[-1])
111            except ResponseException, e:
112                logging.debug("Got response exception %s" % (e.args, ))
113                return e.args[0]
114            except BackException, e:
115                # e.args contains the section to jump back to
116                if e.args:
117                    stack = self.section_stack[:]
118                    stack.reverse()
119                    for i, s in enumerate(stack):
120                        if s == e.args[0]:
121                            # remove the end of the section stack up to desired
122                            # section
123                            del self.section_stack[-i:]
124                            break
125                else:
126                    self.section_stack.pop(-1)
127                response = None
128                continue
129            except EndException:
130                end_cb = self.callbacks.get('end')
131                if end_cb:
132                    end_cb()
133                logging.debug("Reached the end")
134                return
135
136    def continueWithResponse(self, section_name, response):
137        """Reply to a response in a section and continue executing dialogue
138           script
139           @type section_name: str
140           @param section_name: the section to continue
141           @type response: int
142           @param response: the index [0,n-1] of the desired response
143           @raises: EndException on end of script
144           @raises: BackException on "back" reply
145           @return: None"""
146        state = self.state
147        if len(self.section_stack) > 1:
148            if self.section_stack[-1] == self.section_stack[-2]:
149                self.section_stack.pop(-1)
150
151        for command in itertools.cycle(self.getSection(section_name)):
152            if not command.get('responses'):
153                continue
154
155            responses = []
156            for r in command.get('responses'):
157                cond = r[2:]
158                try:
159                    if not cond or eval(cond[0], state, {}):
160                        responses.append(r)
161                except Exception, e:
162                    print "Error in response conditional: %s" % (cond[0],)
163                    #raise e
164
165            section = responses[response][1]
166            logging.debug("User chose %s" % (section,))
167
168            if section == "back":
169                raise BackException()
170            elif section.startswith("back "):
171                raise BackException(section[5:])
172            elif section == "end":
173                raise EndException()
174
175            self.runSection(section)
176
177    def runSection(self, section_name):
178        """Run a section
179           @type section_name: string
180           @param section_name: The section to run
181           @return: None
182           @raises: EndException on end of script
183           @raises: BackException on "back" reply"""
184
185        state = self.state
186        self.section_stack.append(section_name)
187
188        if len(self.section_stack) > 1:
189            if self.section_stack[-1] == self.section_stack[-2]:
190                self.section_stack.pop(-1)
191
192        logging.debug("In runSection %s %s" % (section_name, \
193                                               self.section_stack,))
194        for command in itertools.cycle(self.getSection(section_name)):
195            logging.debug("command was %s" % (command,))
196            if command.get("say"):
197                if self.callbacks.get('say'):
198                    self.callbacks["say"](state, command["say"])
199
200            elif command.get("responses"):
201                responses = []
202                for response in command.get('responses'):
203                    cond = response[2:]
204                    if not cond or eval(cond[0], state, {}):
205                        responses.append(response)
206                if self.callbacks.get("responses"):
207                    self.callbacks["responses"](state, responses)
208
209                raise ResponseException(responses)
210
211            elif command.get("start_quest"):
212                self.callbacks["start_quest"](state,
213                        command.get("start_quest"))
214
215            elif command.get("complete_quest"):
216                self.callbacks["complete_quest"](state,
217                        command.get("complete_quest"))
218
219            elif command.get("delete_quest"):
220                self.callbacks["delete_quest"](state,
221                        command.get("delete_quest"))
222
223            elif command.get("increase_value"):
224                self.callbacks["increase_value"](state,
225                        command.get("increase_value")["quest"],
226                        command.get("increase_value")["variable"],
227                        command.get("increase_value")["value"])
228
229            elif command.get("decrease_value"):
230                self.callbacks["decrease_value"](state,
231                        command.get("decrease_value")["quest"],
232                        command.get("decrease_value")["variable"],
233                        command.get("decrease_value")["value"])
234
235            elif command.get("set_value"):
236                self.callbacks["set_value"](state, 
237                        command.get("set_value")["quest"],
238                        command.get("set_value")["variable"],
239                        command.get("set_value")["value"])
240
241            elif command.get("meet"):
242                self.callbacks["meet"](state, command.get("meet"))
243
244            elif command.get("get_stuff"):
245                self.callbacks["get_stuff"](state, command.get("get_stuff"))
246
247            elif command.get("take_stuff"):
248                self.callbacks["take_stuff"](state, command.get("take_stuff"))
249
250            elif command.get("dialogue"):
251                command = command.get("dialogue")
252                if command == "end":
253                    # indicate we"d like to stop talking
254                    raise EndException
255                elif command == "back":
256                    raise BackException()
257                elif command.startswith("back "):
258                    raise BackException(command[5:])
259                else:
260                    raise Exception("Unknown command %s" % (command,))
261
262            else:
263                raise Exception("Unknown command %s %s" % (command,))
Note: See TracBrowser for help on using the repository browser.