Started work on NPC dialogues
authorHarishankar <v.harishankar@gmail.com>
Wed, 5 Oct 2011 05:18:13 +0000 (10:48 +0530)
committerHarishankar <v.harishankar@gmail.com>
Wed, 5 Oct 2011 05:18:13 +0000 (10:48 +0530)
Started basic work on NPC dialogues. Created the
NPC dialogue screen and successfully displayed text.

background/dialog_screen.png [new file with mode: 0644]
butaba.py
dialogues/bulisa1.dlg
maingame.py
npcs.py
portraits/bulisa.png [new file with mode: 0644]
utility.py

diff --git a/background/dialog_screen.png b/background/dialog_screen.png
new file mode 100644 (file)
index 0000000..2194d96
Binary files /dev/null and b/background/dialog_screen.png differ
index 7b93980..ca44883 100644 (file)
--- a/butaba.py
+++ b/butaba.py
@@ -20,4 +20,3 @@ class Butaba:
                self.health = health
                self.objects = inventory
                self.gold = gold
-
index c5db25b..e684042 100644 (file)
@@ -28,8 +28,7 @@
     <response id="12" nextdialogue="0">Bye!</response>
   </dialogue>
   <dialogue id="6">
-    <speech>Thanks so much! There is an empty bucket somewhere in the garden. 
-    Take that, go to the well behind the house, draw water and bring it back to me.</speech>
+    <speech>Thanks so much! There is an empty bucket somewhere in the garden. Take that, go to the well behind the house, draw water and bring it back to me.</speech>
     <response id="13" nextdialogue="0">Right. I'll go at once.</response>
   </dialogue>
 </conversation>
\ No newline at end of file
index 431d44d..32df4cc 100644 (file)
@@ -28,6 +28,9 @@ class MainGame:
 
                self.img_chestbg = pygame.image.load (os.path.join ("background", "chestcontent.png")).convert ()
 
+               self.img_dialogue = pygame.image.load (os.path.join ("background", "dialog_screen.png")).convert ()
+               self.img_dialogue.set_colorkey (pygame.Color (0, 255, 0))
+
 
                # initialize object graphics
                self.img_redpotion = pygame.image.load (os.path.join ("objects", "red-potion.png")).convert ()
@@ -61,6 +64,9 @@ class MainGame:
                self.img_bulisa = pygame.image.load (os.path.join ("sprite", "bulisa.png")).convert ()
                self.img_bulisa.set_colorkey (pygame.Color (0, 255, 0))
 
+               # initialize NPC portraits
+               self.img_bulisa_portrait = pygame.image.load (os.path.join ("portraits", "bulisa.png")).convert ()
+
                # set level data
                self.setup_levels ()
                # set current level and position of our character
@@ -85,7 +91,8 @@ class MainGame:
                potion2 = gameobjects.HealthPotion (5, 2, self.img_redpotion)
                potion3 = gameobjects.HealthPotion (5, 2, self.img_redpotion)
 
-               npc_bulisa = npcs.Bulisa (4, 3, self.img_bulisa, None)
+               npc_bulisa = npcs.Bulisa (4, 3, self.img_bulisa, self.img_bulisa_portrait,
+                                                                               [ os.path.join ("dialogues", "bulisa1.dlg") ])
 
                chest1.objects = [ gold50, gold25, potion2, potion3, key2, gold10 ]
 
@@ -247,7 +254,12 @@ class MainGame:
 
        # interaction with npcs
        def interact_npc (self, npc):
-               pass
+               # interact with NPC and get the response ID
+               resp_id = utility.dialogue_play (self.screen, self.img_dialogue, npc, self.butaba, 0, 90)
+
+               # if none
+               if resp_id is None:
+                       self.status_message = "You cannot initiate a conversation with %s" % npc.charname
 
        # interaction with objects
        def interact_objects (self, container, objs):
diff --git a/npcs.py b/npcs.py
index ae781da..d368833 100644 (file)
--- a/npcs.py
+++ b/npcs.py
@@ -6,7 +6,7 @@ import os.path
 
 class NPC:
        # initalize the NPC
-       def __init__ (self, charname, row, col, image=None, portrait=None, dialogue_set=set(),
+       def __init__ (self, charname, row, col, image=None, portrait=None, dialogues=[],
                                currentdialog=0, is_dead=False):
                # name of the character
                self.charname = charname
@@ -19,7 +19,7 @@ class NPC:
                self.portrait = portrait
                # dialogue set for NPC
                # each dialogue in the set is a path to an XML file containing the dialogue
-               self.dialogue_set = dialogue_set
+               self.dialogues = dialogues
                # index of the current dialogue which will be initiated with the main
                # character
                self.currentdialog = currentdialog
@@ -29,6 +29,6 @@ class NPC:
 
 # Bulisa is Butaba's friend
 class Bulisa (NPC):
-       def __init__ (self, row, col, image, portrait, dialogue_set=set(), currentdialog=0):
-               NPC.__init__ (self, "Bulisa", row, col, image, portrait, dialogue_set, currentdialog)
+       def __init__ (self, row, col, image, portrait, dialogues=[], currentdialog=0):
+               NPC.__init__ (self, "Bulisa", row, col, image, portrait, dialogues, currentdialog)
 
diff --git a/portraits/bulisa.png b/portraits/bulisa.png
new file mode 100644 (file)
index 0000000..f8f7a97
Binary files /dev/null and b/portraits/bulisa.png differ
index 05faab5..93dac19 100644 (file)
@@ -2,6 +2,67 @@
 import pygame
 import sys
 import os.path
+import xml.etree.cElementTree as ET
+import textwrap
+
+# function to run through an interactive dialogue and return the response ID
+def dialogue_play (screen, bgscreen, npc, responder,
+                                       marginleft=0, margintop=0, marginright=0, marginbottom=0):
+
+       # first check if NPC has a dialogue at all
+       if len (npc.dialogues) == 0 or npc.currentdialog >= len (npc.dialogues):
+               return None
+
+       # get the conversation as a dictionary
+       convtree = xml_to_dict (npc.dialogues[npc.currentdialog])
+
+       scrwidth = screen.get_width ()
+       scrheight = screen.get_height ()
+       bgwidth = bgscreen.get_width ()
+       bgheight = bgscreen.get_height ()
+       leftedge = scrwidth/2 - bgwidth/2
+       topedge = scrheight/2 - bgheight/2
+
+       # now initiate the conversation
+       dlg_id = "1"
+       while 1:
+               screen.blit (bgscreen, (leftedge, topedge))
+               if npc.portrait is not None:
+                       screen.blit (npc.portrait, (leftedge, topedge))
+
+               # get the lines to display wrapped using textwrap module
+               lines = []
+               textnpc = textwrap.wrap (convtree[dlg_id][0], 40)
+               for text in textnpc:
+                       lines.append ((10, 0, 0, 0, text))
+
+               put_lines (screen, lines,
+                                               pygame.Rect(leftedge+marginleft, topedge+margintop,
+                                                       bgwidth-marginleft-marginright, bgheight-margintop-marginbottom))
+               pygame.display.update ()
+
+               for event in pygame.event.get ():
+                       if event.type == pygame.QUIT:
+                               sys.exit (0)
+
+# function to parse a conversation XML into a python dictionary
+def xml_to_dict (file):
+       # parse the dialogue XML file
+       dlgtree = ET.parse (file)
+       # get the root element
+       conversation = dlgtree.getroot ()
+
+       # build the conversation tree as a dictionary
+       convtree = dict ()
+       for dlg in conversation.findall ("dialogue"):
+               id = dlg.get ("id")
+               convtree[id] = []
+               convtree[id].append (dlg.find ("speech").text)
+               for resp in dlg.findall ("response"):
+                       convtree[id].append ((resp.text, resp.get ("id"), resp.get ("nextdialogue")))
+
+       return convtree
+
 
 # function to draw text on surface
 def put_text (surface,  x, y, size, (r,g,b), text):
@@ -9,8 +70,9 @@ def put_text (surface,  x, y, size, (r,g,b), text):
        textsurf = pygame.font.Font (harisfont, size).render (text, True, pygame.Color (r,g,b))
        surface.blit (textsurf, (x, y))
 
-# function to draw several lines of text, centered horizontally and vertically on surface
-def put_lines (surface, text_lines):
+# function to draw several lines of text, centered horizontally and vertically on screen
+# or drawn centered on a rectangle
+def put_lines (surface, text_lines, rect=None):
        textsurfs = []
        height = 0
        harisfont = os.path.join ("font", "harisgamefont.ttf")
@@ -21,12 +83,23 @@ def put_lines (surface, text_lines):
                height = height + s.get_height()*1.5
                textsurfs.append (s)
 
-       scrwidth = surface.get_width ()
-       scrheight = surface.get_height ()
        i = 0
-       for s in textsurfs:
-               surface.blit (s, (scrwidth/2 - s.get_width()/2, scrheight/2 - height/2+ i* (s.get_height()*1.5)))
-               i += 1
+       # if no rectangle specified, center in screen
+       if rect is None:
+               scrwidth = surface.get_width ()
+               scrheight = surface.get_height ()
+
+               for s in textsurfs:
+                       surface.blit (s, (scrwidth/2 - s.get_width()/2, scrheight/2 - height/2+ i* (s.get_height()*1.5)))
+                       i += 1
+       # center on specified rectangular region
+       else:
+               midx = (rect.left + rect.right)/2
+               midy = (rect.top + rect.bottom)/2
+               num = len (textsurfs) / 2
+               for s in textsurfs:
+                       surface.blit (s, (midx - s.get_width()/2, midy - (num-i) * s.get_height() * 3/2))
+                       i += 1
 
 # function to ask a question and return answer
 def ask_question (surface, question, answers, bgscreen):
@@ -48,7 +121,9 @@ def ask_question (surface, question, answers, bgscreen):
 
                surface.blit (bgscreen, (surface.get_width()/2 - bgscreen.get_width()/2,
                                                                        surface.get_height()/2 - bgscreen.get_height()/2))
-               put_lines (surface, textarray)
+               put_lines (surface, textarray, pygame.Rect (surface.get_width()/2 - bgscreen.get_width()/2,
+                                                                                               surface.get_height()/2 - bgscreen.get_height()/2,
+                                                                                               bgscreen.get_width(), bgscreen.get_height()))
 
                pygame.display.update ()