source: trunk/game/scripts/dialoguevalidator.py @ 504

Revision 479, 8.2 KB checked in by maximinus_parpg, 10 years ago (diff)

More code cleanup.
Some preperation for sorting out the beer quest.

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 yaml
19import types
20import os
21
22class DialogueFormatException(Exception):
23    """ Exception thrown when the DialogueValidator has encountered an error"""
24    pass
25
26class DialogueValidator(object):
27    def validateDialogue(self, tree, topdir):
28        """Checks whether a given tree is containing a valid dialogue.
29           @type tree
30           @param tree
31           @type topdir: string
32           @param topdir: Top directory where to start searching for files.
33           @raises: dialogueFormatException on error
34           @return: true on success"""
35        self.tree = tree
36        self.topdir = topdir
37
38        # Test if the required top nodes are present
39        for node in ("NPC", "AVATAR", "START"):
40            if not node in self.tree or not isinstance(self.tree[node],\
41                                                       types.StringType):
42                raise DialogueFormatException("Node: " + node + \
43                                              " not found or invalid type")
44         
45        self.__validateAvatar()
46
47        # Test if the sections node is present
48        if not "SECTIONS" in self.tree or not isinstance(self.tree["SECTIONS"],\
49                                                         types.DictionaryType):
50            raise DialogueFormatException("Node: SECTIONS not found or invalid")
51
52        # Start node should be valid
53        if not self.tree["START"] in self.tree["SECTIONS"]:
54            raise DialogueFormatException("Main section " + self.tree["START"] \
55                                          + " could not be found")
56
57        # Validate all sections
58        for section in self.tree["SECTIONS"]:
59            self.__validateSection(self.tree["SECTIONS"][section], section)
60           
61        return True 
62   
63    def validateDialogueFromFile(self, file_name, topdir="."):
64        """Checks whether a yaml file is containing a valid dialogue
65           @type file_name: string
66           @param file_name: File name of the yaml file to validate
67           @type topdir: string
68           @param topdir: Top directory where to start searching for files.
69           @raises: DialogueFormatException on error
70           @return: True on success"""
71        tree = yaml.load(file(file_name))
72        return self.validateDialogue(tree, topdir)
73
74    def __validateAvatar(self):
75        """Check that the avatar is an existing file.
76           @raises: DialogueFormatException on error."""
77        fname = os.path.join(self.topdir,self.tree["AVATAR"])
78        if not os.path.isfile(fname):
79            raise DialogueFormatException("Avatar file could not " +\
80                                          "be found: " + fname)
81
82       
83    def __validateSection(self, section, section_name):
84        """Checks whether a section is a valid section.
85           @type section: dictionary
86           @type section_name: string
87           @param section: Section to validate
88           @param section_name: Name of the section to validate
89           @raises: DialogueFormatException on error """
90        for entry in section:
91            # TODO: This section is very repitive, could be coded better
92            for action in entry:
93                # Verify if the commands are known and if their parameters have
94                # the correct type.
95                if action == "say":
96                    if not isinstance(entry[action], types.StringType):
97                        raise DialogueFormatException(
98                            "Section: " + section_name + " has an invalid " +\
99                            action + " node")
100                elif action == "responses":
101                    if not isinstance(entry[action], types.ListType):
102                        raise DialogueFormatException(                       
103                            "Section: " + section_name + " has an invalid " +\
104                            action + " node")
105                    self.__validateResponses(entry["responses"], section_name)
106                elif action == "meet":
107                    if not isinstance(entry[action], types.StringType):
108                        raise DialogueFormatException(\
109                            "Section: " + section_name + " has an invalid " +\
110                            action + " node")
111                #TODO: verify person
112                elif action in ("complete_quest", "start_quest", \
113                                "delete_quest"):
114                    if not isinstance(entry[action], types.StringType):
115                        raise DialogueFormatException(\
116                            "Section: " + section_name + " has an invalid " +\
117                            action + " node")
118                #TODO: verify quest
119                elif action in ("get_stuff", "give_stuff"):
120                    if not isinstance(entry[action], types.StringType):
121                        raise DialogueFormatException(\
122                            "Section: " + section_name + " has an invalid " +\
123                            action + " node")
124                #TODO: verify object
125                elif action in ("increase_value", "decrease_value", \
126                                "set_value"):
127                    if not isinstance(entry[action], types.DictionaryType):
128                        raise DialogueFormatException(\
129                            "Section: " + section_name + " has an invalid " +\
130                            action + " node")                   
131                #TODO: verify value checks
132                elif action == "dialogue":
133                    if not isinstance(entry[action], types.StringType) \
134                       or not self.__isValidSectionName(entry[action]):
135                        raise DialogueFormatException(\
136                            "Section: " + section_name + " has an invalid " +\
137                            action + " node")
138                else:
139                    raise DialogueFormatException("Section: " + section_name + \
140                                                  " has an unknown action: " +\
141                                                  action)
142           
143
144    def __validateResponses(self, responses, section_name):
145        """Checks if the list of responses is a valid list.
146           @type responses: List
147           @param respones: A list of response
148           @type section_name: String
149           @param section_name: The section name these responses belong to
150           @raise DialogueFormatException on error
151           @return True When the lists is a valid list"""
152        for option in responses:
153            if not isinstance(option[0], types.StringType):
154                raise DialogueFormatException("Response should be a string")
155           
156            if not self.__isValidSectionName(option[1]):
157                raise DialogueFormatException("Section: " + section_name + \
158                                              " contains an invalid target: " +\
159                                              option[1] + " in response")
160            #TODO: option[2] might be a conditional (why?)
161       
162    def __isValidSectionName(self, name):
163        """Checks if a given name is valid section
164           @type name: string
165           @param name: Name of the section to check
166           @return True when name is a valid section name, False otherwise"""
167        if name=="back" or name=="end":
168            return True
169
170        if name in self.tree["SECTIONS"]:
171            return True
172
173        # Handle 'back name' used to go back 'n' in the stack.
174        if name.startswith("back "):
175            return self.__isValidSectionName(name.split(" ")[1])       
176        return False
Note: See TracBrowser for help on using the repository browser.