class DialogueGUI(object): def __init__(self, npc, quest_engine, pc): self.pc = pc # define dialogue engine callbacks def start_quest(state, quest): print "You've picked up the '%s' quest!" % quest state['quest'].addQuest(quest) def complete_quest(state, quest_id): print "You've finished the quest %s" % quest_id state['quest'].finishQuest(quest_id) def delete_quest(state, quest_id): print "You've deleted quest %s" % quest_id state['quest'].deleteQuest(quest_id) def increase_value(state, quest_id, variable, value): print "Increased %s by %i"%(variable,value) state['quest'][quest_id].increaseValue(variable,value) def decrease_value(state, quest_id, variable, value): print "Decreased %s by %i"%(variable,value) state['quest'][quest_id].decreaseValue(variable,value) def set_value(state,quest_id, variable, value): print "Set %s to %s"%(variable,value) state['quest'][quest_id].setValue(variable,value) def meet(state, npc): print "You've met %s!" % npc state['pc'].meet(npc) def get_stuff(state, thing): if not state['pc'].inventory.has(thing): state['pc'].inventory.placeItem(thing) print "You've now have the %s" % thing def take_stuff(state,thing): if state['pc'].inventory.has(thing): state['pc'].inventory.takeItem(thing) print "You no longer have the %s" % thing dialogue_callbacks = { 'complete_quest': complete_quest, 'decrease_value': decrease_value, 'delete_quest' : delete_quest, 'end' : self.handleEnd, 'get_stuff' : get_stuff, 'increase_value': increase_value, 'meet' : meet, 'npc_name' : self.handleNpcName, 'npc_avatar' : self.handleAvatarImage, 'responses' : self.handleResponses, 'say' : self.handleSay, 'set_value' : set_value, 'start_quest' : start_quest, 'take_stuff' : take_stuff } self.npc = npc state = { 'pc': self.pc, 'quest': quest_engine } self.dialogue_engine = DialogueEngine(npc.dialogue, dialogue_callbacks, state) self.dialogue_gui = pychan.loadXML("gui/dialogue.xml") def initiateDialogue(self): """Callback for starting a quest""" stats_label = self.dialogue_gui.findChild(name='stats_label') stats_label.text = u'Name: John Doe\nAn unnamed one' events = { 'end_button': self.handleEnd } self.dialogue_gui.mapEvents(events) self.dialogue_gui.show() responses_list = self.dialogue_gui.findChild(name='choices_list') responses = self.dialogue_engine.run() self.setResponses(responses) def handleSay(self, state, say): """Callback for NPC speech""" speech = self.dialogue_gui.findChild(name='speech') # to append text to npc speech box, uncomment the following line #speech.text = speech.text + "\n-----\n" + unicode(say) speech.text = unicode(say) def handleEntered(self, *args): """Callback for when user hovers over response label""" pass def handleExited(self, *args): """Callback for when user hovers out of response label""" pass def handleClicked(self, *args): """Handle a response being clicked""" response = int(args[0].name.replace('response', '')) if not self.dialogue_engine.reply(response): self.handleEnd() def handleEnd(self): """Handle the end of the conversation being reached. Either from the GUI or from within the conversation itself.""" self.dialogue_engine = None self.dialogue_gui.hide() self.npc.behaviour.state = 1 self.npc.behaviour.idle() def handleNpcName(self, name): """Callback to handle setting the NPC name. @type name: str @param name: The name of the NPC to set. """ stats_label = self.dialogue_gui.findChild(name='stats_label') try: (first_name, desc) = name.split(" ",1) stats_label.text = u'Name: ' + first_name + "\n" + desc except: stats_label.text = u'Name: ' + name self.dialogue_gui.title = name def handleAvatarImage(self, image): """Callback to handle when the dialogue engine wants to set the NPC image @type image: str @param image: filename of avatar image""" avatar_image = self.dialogue_gui.findChild(name='npc_avatar') avatar_image.image = image def handleResponses(self, *args): """Callback to handle when the dialogue engine wants to display a new list of options""" self.setResponses(args[1]) def setResponses(self, responses): """Creates the list of clickable response labels and sets their respective on-click callbacks @type responses: [ [ "response text", section, condition ], ...] @param responses: the list of response objects from the dialogue engine""" choices_list = self.dialogue_gui.findChild(name='choices_list') choices_list.removeAllChildren() for i,r in enumerate(responses): button = widgets.Label( name="response%s"%(i,), text=unicode(r[0]), hexpand="1", min_size=(100,16), max_size=(490,48), position_technique='center:center' ) button.margins = (5,5) button.background_color = fife.Color(0,0,0) button.color = fife.Color(0,255,0) button.border_size = 0 button.wrap_text = 1 button.capture(lambda button=button: self.handleEntered(button), \ event_name='mouseEntered') button.capture(lambda button=button: self.handleExited(button), \ event_name='mouseExited') button.capture(lambda button=button: self.handleClicked(button), \ event_name='mouseClicked') choices_list.addChild(button) self.dialogue_gui.adaptLayout(True)
class TestDialogue(unittest.TestCase): def setUp(self): self.tree = { 'NPC': 'Mr. Npc', 'START': 'main', 'AVATAR': 'gui/icons/npc.png', 'SECTIONS': { 'main': [ { "say": "Greetings stranger" }, { "responses": [ ["Hi, can you tell me where I am?", "friendly"], ["Watch your words", "aggro"], ["This one toggles", "toggles", "show == True"], ["Always display this one", "display", "True and True"], ["response3", "end"], ] } ], 'friendly': [ { "say": "You sure are lost" }, { "responses": [ ["Thanks, I know", "thanks"], ["Wait what did you say before?", "back"], ] } ], 'aggro': [ { "say": "Die Pig! PAAAAR!!!!" }, { "responses": [ ["ruh-ro raggy!", "toggles"], ["Uh, just kidding??", "back"], ] } ], 'toggles': [ { "say": "you turn me on!" }, { "responses": [ ["you turn me off", "back"], ] } ], 'display': [ { "say": "Forever Young!" }, { "responses": [ ["Alphaville sucks!", "back"], ] } ], 'thanks': [ { "say": "We haven't seen one of your kind in ages" }, { "responses": [ ["Blah blah blah", "display"], ["Say the other thing again", "back"], ] } ], } } # record actions in test_vars test_vars = { "say": None, "responses": [] } def sayCb(state, text): state["say"] = text self.replies = ["resp1", "back", "end"] def npcAvatarCb(state, image): state['npc_avatar'] = image def responsesCb(state, responses): state['responses'] = responses callbacks = { "say": sayCb, "responses": responsesCb, "npc_avatar": npcAvatarCb } self.dialogue = DialogueEngine(self.tree, callbacks, test_vars) def assertSay(self, text): self.assertEqual(text, self.dialogue.state['say']) def assertResponses(self, responses): self.assertEqual(responses, self.dialogue.state['responses']) def assertNpcImage(self, image): self.assertEqual(image, self.dialogue.state['npc_avatar']) def testSimple(self): """Test basic dialogue interaction""" self.dialogue.state['show'] = False self.dialogue.run() self.assertNpcImage('gui/icons/npc.png') self.assertSay('Greetings stranger') self.assertResponses([ ["Hi, can you tell me where I am?", "friendly"], ["Watch your words", "aggro"], ["Always display this one", "display", "True and True"], ["response3", "end"], ]) self.dialogue.reply(0) self.assertSay('You sure are lost') self.assertResponses([ ["Thanks, I know", "thanks"], ["Wait what did you say before?", "back"] ]) self.dialogue.state['show'] = True self.dialogue.reply(1) self.assertSay('Greetings stranger') self.assertResponses([ ["Hi, can you tell me where I am?", "friendly"], ["Watch your words", "aggro"], ["This one toggles", "toggles", "show == True"], ["Always display this one", "display", "True and True"], ["response3", "end"], ]) self.dialogue.reply(0) self.assertSay('You sure are lost') self.dialogue.reply(1) self.assertSay('Greetings stranger') self.dialogue.reply(0) self.assertSay('You sure are lost') self.dialogue.reply(0) self.assertSay("We haven't seen one of your kind in ages") self.dialogue.reply(1) self.assertSay('You sure are lost') self.dialogue.reply(1) self.assertSay('Greetings stranger') def testAllDialogueFiles(self): """ Runs the validator on all dialogue files available. """ val = DialogueValidator() diag_dir = os.path.join(os.path.curdir, "dialogue"); num_faulty_files = 0 # Test the dialogue files for dialogue in os.listdir(diag_dir): fname = os.path.join(diag_dir, dialogue) if os.path.isfile(fname) : try: assert(val.validateDialogueFromFile(fname,".")) except DialogueFormatException as dfe: print "\nError found in file: ", fname print "Error was: %s" % (dfe) num_faulty_files += 1 # Test the internal tree try: assert(val.validateDialogue(self.tree,".")) except DialogueFormatException, dfe: print "\nError found in internal tree: ", fname print "Error was: %s" % (dfe) num_faulty_files += 1 assert(num_faulty_files == 0)
class TestDialogue(unittest.TestCase): def setUp(self): self.tree = { 'START': 'main', 'AVATAR': 'npc.png', 'SECTIONS': { 'main': [ { "say": "Greetings stranger" }, { "responses": [ ["Hi, can you tell me where I am?", "friendly"], ["Watch your words", "aggro"], ["This one toggles", "toggles", "show == True"], ["Always display this one", "display", "True and True"], ["response3", "stop"], ] } ], 'friendly': [ { "say": "You sure are lost" }, { "responses": [ ["Thanks, I know", "thanks"], ["Wait what did you say before?", "back"], ] } ], 'thanks': [ { "say": "We haven't seen one of your kind in ages" }, { "responses": [ ["Blah blah blah", "foo"], ["Say the other thing again", "back"], ] } ], } } # record actions in test_vars test_vars = { "say": None, "responses": [] } def say_cb(state, text): state["say"] = text self.replies = ["resp1", "back", "stop"] def npc_avatar_cb(state, image): state['npc_avatar'] = image def responses_cb(state, responses): state['responses'] = responses callbacks = { "say": say_cb, "responses": responses_cb, "npc_avatar": npc_avatar_cb } self.dialogue = DialogueEngine(self.tree, callbacks, test_vars) def assert_say(self, text): self.assertEqual(text, self.dialogue.state['say']) def assert_responses(self, responses): self.assertEqual(responses, self.dialogue.state['responses']) def assert_npc_image(self, image): self.assertEqual(image, self.dialogue.state['npc_avatar']) def test_simple(self): """Test basic dialogue interaction""" self.dialogue.state['show'] = False self.dialogue.run() self.assert_npc_image('npc.png') self.assert_say('Greetings stranger') self.assert_responses([ ["Hi, can you tell me where I am?", "friendly"], ["Watch your words", "aggro"], ["Always display this one", "display", "True and True"], ["response3", "stop"], ]) self.dialogue.reply(0) self.assert_say('You sure are lost') self.assert_responses([ ["Thanks, I know", "thanks"], ["Wait what did you say before?", "back"] ]) self.dialogue.state['show'] = True self.dialogue.reply(1) self.assert_say('Greetings stranger') self.assert_responses([ ["Hi, can you tell me where I am?", "friendly"], ["Watch your words", "aggro"], ["This one toggles", "toggles", "show == True"], ["Always display this one", "display", "True and True"], ["response3", "stop"], ]) self.dialogue.reply(0) self.assert_say('You sure are lost') self.dialogue.reply(1) self.assert_say('Greetings stranger') self.dialogue.reply(0) self.assert_say('You sure are lost') self.dialogue.reply(0) self.assert_say("We haven't seen one of your kind in ages") self.dialogue.reply(1) self.assert_say('You sure are lost') self.dialogue.reply(1) self.assert_say('Greetings stranger')
class DialogueGUI(object): def __init__(self, npc): dialogue_callbacks = { 'say': self.handleSay, 'responses': self.handleResponses, 'start_quest': self.startQuest, 'complete_quest': self.completeQuest, 'npc_avatar': self.handleAvatarImage, 'end': self.handleEnd } # For testing purposes we're just using a a dummy player object here # until we decide on a player/quest model. # TODO # link this up to the actual PC and NPC instances, so that state can # be persistent. pc = Player() self.npc = npc state = { 'pc': pc } self.dialogue_engine = DialogueEngine('dialogue/sample.yaml', dialogue_callbacks, state) self.dialogue_gui = pychan.loadXML("gui/dialogue.xml") def startQuest(self, state, quest): """Callback for starting a quest""" print "You've picked up the '%s' quest!" % quest, state['pc'].startQuest(quest) def completeQuest(self, state, quest): """Callback for starting a quest""" print "You've finished the '%s' quest" % quest state['pc'].completeQuest(quest) def initiateDialogue(self): """Callback for starting a quest""" stats_label = self.dialogue_gui.findChild(name='stats_label') stats_label.text = u'Name: Ronwell\nA grizzled villager' events = { 'end_button': self.handleEnd } self.dialogue_gui.mapEvents(events) self.dialogue_gui.show() responses_list = self.dialogue_gui.findChild(name='choices_list') responses = self.dialogue_engine.run() self.setResponses(responses) def handleSay(self, state, say): """Callback for NPC speech""" speech = self.dialogue_gui.findChild(name='speech') # to append text to npc speech box, uncomment the following line #speech.text = speech.text + "\n-----\n" + unicode(say) speech.text = unicode(say) def handleEntered(self, *args): """Callback for when user hovers over response label""" pass def handleExited(self, *args): """Callback for when user hovers out of response label""" pass def handleClicked(self, *args): """Handle a response being clicked""" response = int(args[0].name.replace('response', '')) if not self.dialogue_engine.reply(response): self.handleEnd() def handleEnd(self): """Handle the end of the conversation being reached. Either from the GUI or from within the conversation itself.""" self.dialogue_engine = None self.dialogue_gui.hide() self.npc.behaviour.state = 1 self.npc.behaviour.idle() def handleAvatarImage(self, state, image): """Callback to handle when the dialogue engine wants to set the NPC image @type image: str @param image: filename of avatar image""" avatar_image = self.dialogue_gui.findChild(name='npc_avatar') avatar_image.image = image def handleResponses(self, *args): """Callback to handle when the dialogue engine wants to display a new list of options""" self.setResponses(args[1]) def setResponses(self, responses): """Creates the list of clickable response labels and sets their respective on-click callbacks @type responses: [ [ "response text", section, condition ], ...] @param responses: the list of response objects from the dialogue engine""" choices_list = self.dialogue_gui.findChild(name='choices_list') choices_list.removeAllChildren() for i,r in enumerate(responses): button = widgets.Label( name="response%s"%(i,), text=unicode(r[0]), hexpand="1", min_size=(100,16), max_size=(490,48), position_technique='center:center' ) button.margins = (5,5) button.background_color = fife.Color(0,0,0) button.color = fife.Color(0,255,0) button.border_size = 0 button.wrap_text = 1 button.capture(lambda button=button: self.handleEntered(button), \ event_name='mouseEntered') button.capture(lambda button=button: self.handleExited(button), \ event_name='mouseExited') button.capture(lambda button=button: self.handleClicked(button), \ event_name='mouseClicked') choices_list.addChild(button) self.dialogue_gui.adaptLayout(True)