source: trunk/game/scripts/dialoguegui.py @ 287

Revision 287, 5.7 KB checked in by orlandov, 10 years ago (diff)

Ticket #74: Patch by or1andov, Integrate dialogue GUI and engine into game.

It should now be possible to engage in a conversation with NPC characters by
selecting talk from their context menu. fixes[s:trac, t:74]

Known Issues:


  • the GUI is terrible. instead of a dialog window, we could use a better looking background image and fixed position widgets
  • requires PyYAML to be installed sudo apt-get install python-yaml easy_install PyYAML
  • we have to create a simple state model for the PC can remember tasks
  • need to make NPC's be able to remember things - need a model in NPCs for this
  • no way currently to give or take items from npc's; again, more model work. this could be done by adding another callback similar to start_quest. may be necessary for npc's to have an inventory for this
  • The dialogue engine is not hooked up to specific NPC's... it currently always loads dialogue/sample.yaml - need to have the dialogue looked up from some XML attribute
  • dialogue is not broken off if the PC moves away from the NPC
  • npc image is hardcoded, it should use the image specified in the yaml file
Line 
1#!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 pychan
19import fife
20import pychan.widgets as widgets
21from scripts.dialogue import DialogueEngine
22
23class Player(object):
24    """A mock player object until we have a proper Player/Quest model"""
25    def __init__(self):
26        self.current_quests = set()
27        self.finished_quests = set()
28
29    def canAcceptQuest(self, quest):
30        return     quest not in self.finished_quests \
31               and quest not in self.current_quests
32
33    def hasSatisfiedQuest(self, quest):
34        return quest in self.current_quests
35
36    def startQuest(self, quest):
37        if quest in self.current_quests:
38            raise RuntimeError("Already have quest, %s" % quest)
39        self.current_quests.add(quest)
40
41    def completeQuest(self, quest):
42        self.finished_quests.add(quest)
43        self.current_quests.remove(quest)
44
45class DialogueGUI(object):
46    def __init__(self, npc):
47        dialogue_callbacks = {
48            'say': self.handleSay,
49            'responses': self.handleResponses,
50            'start_quest': self.startQuest,
51            'complete_quest': self.completeQuest,
52            'end': self.handleEnd
53        }
54
55        # For testing purposes we're just using a a dummy player object  here
56        # until we decide on a player/quest model.
57        # TODO
58        # link this up to the actual PC and NPC instances, so that state can
59        # be persistent.
60        pc = Player()
61        self.npc = npc
62        state = {
63            'pc': pc
64        }
65        self.dialogue_engine = DialogueEngine('dialogue/sample.yaml',
66                                              dialogue_callbacks, state)
67        self.dialogue_gui = pychan.loadXML("gui/dialogue.xml")
68
69    def startQuest(self, state, quest):
70        """Callback for starting a quest"""
71        print "You've picked up the '%s' quest!" % quest,
72        state['pc'].startQuest(quest)
73
74    def completeQuest(self, state, quest):
75        """Callback for starting a quest"""
76        print "You've finished the '%s' quest" % quest
77        state['pc'].completeQuest(quest)
78
79    def initiateDialogue(self):
80        """Callback for starting a quest"""
81        stats_label = self.dialogue_gui.findChild(name='stats_label')
82        stats_label.text = u'Name: Ronwell\nA grizzled villager'
83
84        events = {
85            'end_button': self.handleEnd
86        }
87        self.dialogue_gui.mapEvents(events)
88        self.dialogue_gui.show()
89        responses_list = self.dialogue_gui.findChild(name='choices_list')
90        responses = self.dialogue_engine.run()
91        self.setResponses(responses)
92
93    def handleSay(self, state, say):
94        """Callback for NPC speech"""
95        speech = self.dialogue_gui.findChild(name='speech')
96        # to append text to npc speech box, uncomment the following line
97        #speech.text = speech.text + "\n-----\n" + unicode(say)
98        speech.text = unicode(say)
99
100    def handleEntered(self, *args):
101        """Callback for when user hovers over response label"""
102        pass
103
104    def handleExited(self, *args):
105        """Callback for when user hovers out of response label"""
106        pass
107
108    def handleClicked(self, *args):
109        """Handle a response being clicked"""
110        response = int(args[0].name.replace('response', ''))
111        if not self.dialogue_engine.reply(response):
112            self.handleEnd()
113
114    def handleEnd(self):
115        """Handle the end of the conversation being reached. Either from the
116           GUI or from within the conversation itself."""
117        self.dialogue_engine = None
118        self.dialogue_gui.hide()
119        self.npc.behaviour.state = 1
120        self.npc.behaviour.idle()
121
122    def handleResponses(self, *args):
123        """Callback to handle when the dialogue engine wants to display a new
124           list of options"""
125        self.setResponses(args[1])
126
127    def setResponses(self, responses):
128        """Creates the list of clickable response labels and sets their
129           respective on-click callbacks
130           @type responses: [ [ "response text", section, condition ], ...]
131           @param responses: the list of response objects from the dialogue
132                             engine"""
133        choices_list = self.dialogue_gui.findChild(name='choices_list')
134        choices_list.removeAllChildren()
135        for i,r in enumerate(responses):
136            button = widgets.Label(
137                name="response%s"%(i,),
138                text=unicode(r[0]),
139                hexpand="1",
140                min_size=(100,16),
141                max_size=(490,48),
142                position_technique='center:center'
143            )
144            button.margins=(5,5)
145            button.background_color=fife.Color(0,0,0)
146            button.color=fife.Color(0,255,0)
147            button.border_size = 0
148            button.wrap_text = 1
149            button.capture(lambda button=button: self.handleEntered(button), event_name='mouseEntered')
150            button.capture(lambda button=button: self.handleExited(button), event_name='mouseExited')
151            button.capture(lambda button=button: self.handleClicked(button), event_name='mouseClicked')
152            choices_list.addChild(button)
153            self.dialogue_gui.adaptLayout(True)
154
155
Note: See TracBrowser for help on using the repository browser.