Changeset 679


Ignore:
Timestamp:
12/04/10 00:41:10 (9 years ago)
Author:
technomage
Message:

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).
Location:
trunk/game
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/game/dialogue_demo.py

    r674 r679  
    11#!/usr/bin/env python2 
    2  
    32#   This file is part of PARPG. 
    4  
     3# 
    54#   PARPG is free software: you can redistribute it and/or modify 
    65#   it under the terms of the GNU General Public License as published by 
    76#   the Free Software Foundation, either version 3 of the License, or 
    87#   (at your option) any later version. 
    9  
     8# 
    109#   PARPG is distributed in the hope that it will be useful, 
    1110#   but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1211#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1312#   GNU General Public License for more details. 
    14  
     13# 
    1514#   You should have received a copy of the GNU General Public License 
    1615#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>. 
    17 """A very simple demonstration of the dialogue engine in effect. It mocks up a 
    18    simple user object and allows the dialogue engine to interact with it via 
    19    callbacks we provide.""" 
     16""" 
     17A very simple demonstration of the dialogue engine used for testing dialogue 
     18files. 
     19""" 
    2020import logging 
     21import os 
    2122import sys 
    2223 
     24from scripts.common.optionparser import OptionParser, OptionError 
    2325from scripts.dialogueparsers import YamlDialogueParser 
    2426from scripts.dialogueengine import DialogueEngine 
    2527from scripts.quest_engine import QuestEngine 
    2628 
     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 
    2734def setup_logging(): 
     35    """Set various logging parameters for this module.""" 
    2836    logging.basicConfig(filename='dialogue_demo.log') 
    2937setup_logging() 
    3038 
    31 class Player(object): 
     39class MockPlayerCharacter(object): 
    3240    """ 
    33     Mock player object that always has complete quests 
     41    Mock object representing the player character. 
    3442    """ 
    3543    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        """ 
    3650        self.inventory = set(['beer']) 
    37         self.people_i_know = set() 
    38  
    39     def meet(self,npc): 
    40         if npc in self.people_i_know: 
    41             raise RuntimeError("I already know %s" % npc) 
    42         self.people_i_know.add(npc) 
    43  
    44     def met(self,npc): 
    45         return npc in self.people_i_know 
     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 
    4666 
    4767 
    48 class Beer(object): 
     68class MockBeer(object): 
     69    """Mock object representing a 'beer' item.""" 
    4970    quality = 3 
    5071 
    5172 
    52 class Box(object): 
     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(): 
    5393    """ 
    54     Mock box object than can be opened or closed 
    55     This would normally be a container 
     94    List all YAML dialogue files in the dialogue directory and prompt the user 
     95    to select one for testing. 
    5696    """ 
    57     # States for the mock Box object 
    58     OPENED, CLOSED = range(2) 
     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 
    59117     
    60     def __init__(self): 
    61         self.state = self.CLOSED 
    62  
    63     def isOpen(self): 
    64         return self.state == self.OPENED 
    65  
    66     def isClosed(self): 
    67         return self.state == self.CLOSED 
    68  
    69     def open(self): 
    70         self.state = self.OPENED 
    71         return self.state 
    72  
    73     def close(self): 
    74         self.state = self.CLOSED 
    75         return self.state 
     118    selected_file_path = os.path.join(DIALOGUE_DIR, selected_file_name) 
     119    return selected_file_path 
    76120 
    77121def 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    """ 
    78129    while (True): 
    79130        print('Available responses:') 
     
    83134            chosen_response_n = int(raw_input('Choose a response number> ')) 
    84135            chosen_response = dialogue_responses[chosen_response_n] 
    85         except ValueError, IndexError: 
    86             print('invalid response, please enter an integer between 0 and {0}' 
    87                   .format(len(dialogue_responses) - 1)) 
     136        except (ValueError, IndexError): 
     137            print(('\ninvalid response, please enter an integer between 0 ' 
     138                   'and {0}').format(len(dialogue_responses) - 1)) 
    88139        else: 
    89140            break 
     
    91142    return chosen_response 
    92143 
    93 def main(dialogue_filepath): 
    94     pc = Player() 
    95     box = Box() 
    96     quest = QuestEngine('quests') 
    97     beer = Beer() 
     144def processDialogue(dialogue, game_state): 
     145    """ 
     146    Process a L{Dialogue} until the user selects a response that ends it. 
    98147     
    99     parser = YamlDialogueParser() 
    100     with file(dialogue_filepath, 'r') as dialogue_file: 
    101         dialogue = parser.load(dialogue_file) 
    102      
    103     game_state = { 
    104         'quest': quest, 
    105         'pc': pc, 
    106         'box': box, 
    107         'beer': beer 
    108     } 
    109  
     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    """ 
    110154    DialogueEngine.initiateDialogue(dialogue, game_state) 
    111155    npc_name = dialogue.npc_name 
     
    114158        current_dialogue_section = DialogueEngine.getCurrentDialogueSection() 
    115159        dialogue_text = current_dialogue_section.text.replace('\n', '\n    ') 
    116         print('{0}: {1}'.format(npc_name, dialogue_text)) 
     160        print('\n{0}: {1}'.format(npc_name, dialogue_text)) 
    117161        chosen_reply = getReply(responses) 
    118162        DialogueEngine.reply(chosen_reply) 
    119163 
     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 
    120174if __name__ == "__main__": 
    121     print("1 - Dialogue sample\n2 - Quest Sample") 
    122     choice = input("> ") 
    123     if choice == 1: 
    124         dialogue_file = 'dialogue/drunkard.yaml' 
    125     elif choice == 2: 
    126         dialogue_file = 'dialogue/quest_sample.yaml' 
    127     if len(sys.argv) > 1: 
    128         dialogue_file = sys.argv[1] 
    129  
    130     main(dialogue_file) 
     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) 
  • trunk/game/scripts/common/optionparser.py

    r668 r679  
    145145            message = 'expected a {0} argument but got nothing' 
    146146            message = message.format(getattr(type_, '__name__', type_)) 
    147             self.error(message) 
     147            raise OptionError(message) 
    148148        try: 
    149149            result = type_(arg) 
Note: See TracChangeset for help on using the changeset viewer.