source: trunk/game/dialogue_demo.py @ 679

Revision 679, 6.7 KB checked in by technomage, 9 years ago (diff)

Patch by Technomage

  • Cleaned up and redesigned the dialogue_demo.py script as per aspidites' suggestions.
  • Updated all code documentation in the dialogue_demo.py script.
  • The dialogue_demo.py script how has a proper command-line interface via the optionparser.py module.
  • dialogue_demo.py now prompts the user to select a YAML dialogue file from the dialogue subdirectory if a dialogue file path is not passed to the script.
  • Updated the optionparser.py module to raise an OptionError? when get_next_prog_arg is called and no program argument was found (e.g. for optional program arguments).
  • Property svn:executable set to *
Line 
1#!/usr/bin/env python2
2#   This file is part of PARPG.
3#
4#   PARPG is free software: you can redistribute it and/or modify
5#   it under the terms of the GNU General Public License as published by
6#   the Free Software Foundation, either version 3 of the License, or
7#   (at your option) any later version.
8#
9#   PARPG is distributed in the hope that it will be useful,
10#   but WITHOUT ANY WARRANTY; without even the implied warranty of
11#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12#   GNU General Public License for more details.
13#
14#   You should have received a copy of the GNU General Public License
15#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>.
16"""
17A very simple demonstration of the dialogue engine used for testing dialogue
18files.
19"""
20import logging
21import os
22import sys
23
24from scripts.common.optionparser import OptionParser, OptionError
25from scripts.dialogueparsers import YamlDialogueParser
26from scripts.dialogueengine import DialogueEngine
27from scripts.quest_engine import QuestEngine
28
29PARPG_ROOT_DIR = os.path.split(sys.argv[0])[0]
30"""Absolute path to the root of the PARPG installation."""
31DIALOGUE_DIR = os.path.join(PARPG_ROOT_DIR, 'dialogue')
32"""Absolute path to the dialogue directory of the PARPG installation."""
33
34def setup_logging():
35    """Set various logging parameters for this module."""
36    logging.basicConfig(filename='dialogue_demo.log')
37setup_logging()
38
39class MockPlayerCharacter(object):
40    """
41    Mock object representing the player character.
42    """
43    def __init__(self):
44        """
45        Initialize a new L{MockPlayerCharacter} instance.
46       
47        @ivar inventory: set of items carried by the L{MockPlayerCharacter}.
48        @ivar known_npcs: set of IDs for the NPCs the player has met.
49        """
50        self.inventory = set(['beer'])
51        self.known_npcs = set()
52   
53    def meet(self, npc_id):
54        """
55        Add an NPC to the list of NPCs known by the player.
56       
57        @param npc: ID of the NPC to add.
58        @type npc: str
59        """
60        if npc_id in self.known_npcs:
61            raise RuntimeError("I already know {0}".format(npc_id))
62        self.known_npcs.add(npc_id)
63   
64    def met(self, npc):
65        return npc in self.known_npcs
66
67
68class MockBeer(object):
69    """Mock object representing a 'beer' item."""
70    quality = 3
71
72
73class MockBox(object):
74    """Mock box object than can be opened or closed."""
75    def __init__(self):
76        """
77        Initialize a new {MockBox} instance.
78       
79        @ivar opened: whether the L{MockBox} has been "opened".
80        """
81        self.opened = False
82   
83    def open(self):
84        """Set the opened state of the L{MockBox} to True."""
85        self.opened = True
86   
87    def close(self):
88        """Set the opened state of the L{MockBox} to False."""
89        self.opened = False
90
91
92def selectDialogueFile():
93    """
94    List all YAML dialogue files in the dialogue directory and prompt the user
95    to select one for testing.
96    """
97    dialogue_files = []
98    for file_name in os.listdir(DIALOGUE_DIR):
99        dummy, file_ext = os.path.splitext(file_name)
100        file_path = os.path.join(DIALOGUE_DIR, file_name)
101        if os.path.isfile(file_path) and file_ext == '.yaml':
102            dialogue_files.append(file_name)
103            index = len(dialogue_files) - 1
104            print('{0} - {1}'.format(index, file_name))
105    while True:
106        str_input = raw_input("> ")
107        try:
108            choice_n = int(str_input)
109            selected_file_name = dialogue_files[choice_n]
110        except (ValueError, IndexError):
111            print(('"{0}" is an invalid selection, please choose a number '
112                   'between 0 and {1}').format(str_input,
113                                               len(dialogue_files) - 1))
114            continue
115        else:
116            break
117   
118    selected_file_path = os.path.join(DIALOGUE_DIR, selected_file_name)
119    return selected_file_path
120
121def getReply(dialogue_responses):
122    """
123    Prompt the user to choose a L{DialogueResponse} from a list of valid
124    responses.
125   
126    @param dialogue_responses: valid responses to choose from
127    @type dialogue_responses: list of L{DialogueResponses}
128    """
129    while (True):
130        print('Available responses:')
131        for index, response in enumerate(dialogue_responses):
132            print('{0} - {1}'.format(index, response.text))
133        try:
134            chosen_response_n = int(raw_input('Choose a response number> '))
135            chosen_response = dialogue_responses[chosen_response_n]
136        except (ValueError, IndexError):
137            print(('\ninvalid response, please enter an integer between 0 '
138                   'and {0}').format(len(dialogue_responses) - 1))
139        else:
140            break
141   
142    return chosen_response
143
144def processDialogue(dialogue, game_state):
145    """
146    Process a L{Dialogue} until the user selects a response that ends it.
147   
148    @param dialogue: dialogue data to process.
149    @type dialogue: L{Dialogue}
150    @param game_state: objects that should be made available for response
151        conditional testing.
152    @type game_state: dict of objects
153    """
154    DialogueEngine.initiateDialogue(dialogue, game_state)
155    npc_name = dialogue.npc_name
156    while DialogueEngine.in_dialogue:
157        responses = DialogueEngine.continueDialogue()
158        current_dialogue_section = DialogueEngine.getCurrentDialogueSection()
159        dialogue_text = current_dialogue_section.text.replace('\n', '\n    ')
160        print('\n{0}: {1}'.format(npc_name, dialogue_text))
161        chosen_reply = getReply(responses)
162        DialogueEngine.reply(chosen_reply)
163
164usage_message='''\
165usage: dialogue_demo.py [-h] [dialogue_file]
166Script for testing dialogue files.
167
168-h, --help                  Show this help message.
169dialogue_file               YAML file containing a dialogue; if not specified,
170                                the user will be prompted to choose a dialogue
171                                file from the dialogue directory.
172'''
173
174if __name__ == "__main__":
175    option_parser = OptionParser(usage=usage_message)
176    for option in option_parser:
177        if (option in ['-h', '--help']):
178            print(option_parser.usage)
179            sys.exit(0)
180        else:
181            option_parser.error('unrecognized option "{0}"'.format(option))
182    try:
183        dialogue_file_path = option_parser.get_next_prog_arg()
184    except OptionError:
185        dialogue_file_path = selectDialogueFile()
186    game_state = {
187        'quest': QuestEngine('quests'),
188        'pc': MockPlayerCharacter,
189        'box': MockBox(),
190        'beer': MockBeer()
191    }
192    dialogue_parser = YamlDialogueParser()
193    with file(dialogue_file_path, 'r') as dialogue_file:
194        dialogue = dialogue_parser.load(dialogue_file)
195    processDialogue(dialogue, game_state)
Note: See TracBrowser for help on using the repository browser.