Added the Conversation Interaction
[butaba-adventures.git] / utility.py
1 # utility functions for the game
2 import pygame
3 import sys
4 import os.path
5 import xml.etree.cElementTree as ET
6 import textwrap
7
8 # function to run through an interactive dialogue and return the response ID
9 def dialogue_play (screen, bgscreen, npc, responder_portrait=None,
10 marginleft=0, margintop=0, marginright=0, marginbottom=0):
11
12 # first check if NPC has a dialogue at all
13 if len (npc.dialogues) == 0 or npc.currentdialog >= len (npc.dialogues):
14 return None
15
16 # get the conversation as a dictionary
17 convtree = xml_to_dict (npc.dialogues[npc.currentdialog])
18 import pprint
19 pprint.pprint (convtree)
20
21 scrwidth = screen.get_width ()
22 scrheight = screen.get_height ()
23 bgwidth = bgscreen.get_width ()
24 bgheight = bgscreen.get_height ()
25 leftedge = scrwidth/2 - bgwidth/2
26 topedge = scrheight/2 - bgheight/2
27
28 # now initiate the conversation
29 dlg_id = "1"
30 reply_mode = False
31 current_resp = convtree[dlg_id][1][0][1]
32
33 while 1:
34 screen.blit (bgscreen, (leftedge, topedge))
35
36 # a dialgoue ID of 0 always exits the conversation
37 # return the unique response ID
38 if dlg_id == "0":
39 print current_resp
40 return current_resp
41
42 # not reply mode
43 if reply_mode is False:
44 if npc.portrait is not None:
45 screen.blit (npc.portrait, (leftedge, topedge))
46
47 # get the lines to display wrapped using textwrap module
48 lines = []
49 textnpc = textwrap.wrap (convtree[dlg_id][0], 40)
50 for text in textnpc:
51 lines.append ((10, 0, 0, 0, text))
52
53 put_lines (screen, lines,
54 pygame.Rect(leftedge+marginleft, topedge+margintop,
55 bgwidth-marginleft-marginright, bgheight-margintop-marginbottom))
56 pygame.display.update ()
57
58 for event in pygame.event.get ():
59 if event.type == pygame.QUIT:
60 sys.exit (0)
61 elif event.type == pygame.KEYDOWN:
62 # now continue the dialog
63 if event.key == pygame.K_RETURN or event.key == pygame.K_SPACE:
64 reply_mode = True
65 current_resp = convtree[dlg_id][1][0][1]
66 sel = 0
67 # reply mode
68 else:
69 if responder_portrait is not None:
70 screen.blit (responder_portrait, (leftedge, topedge))
71
72 lines = []
73 for resptext, respid, nextdlgid in convtree[dlg_id][1]:
74 if respid == current_resp:
75 lines.append ((10, 255, 0, 0, resptext))
76 else:
77 lines.append ((10, 0, 0, 0, resptext))
78
79 put_lines (screen, lines,
80 pygame.Rect (leftedge+marginleft, topedge+margintop,
81 bgwidth-marginleft-marginright, bgheight-margintop-marginbottom))
82
83 pygame.display.update ()
84 for event in pygame.event.get ():
85 if event.type == pygame.QUIT:
86 sys.exit (0)
87 elif event.type == pygame.KEYDOWN:
88 if event.key == pygame.K_UP or event.key == pygame.K_LEFT:
89 sel -= 1
90 if sel < 0:
91 sel = 0
92 current_resp = convtree[dlg_id][1][sel][1]
93 elif event.key == pygame.K_DOWN or event.key == pygame.K_RIGHT:
94 sel += 1
95 if sel >= len (convtree[dlg_id][1]):
96 sel = len(convtree[dlg_id][1]) - 1
97 current_resp = convtree[dlg_id][1][sel][1]
98
99 elif event.key == pygame.K_RETURN or event.key == pygame.K_SPACE:
100 dlg_id = convtree[dlg_id][1][sel][2]
101 reply_mode = False
102
103 # function to parse a conversation XML into a python dictionary
104 def xml_to_dict (file):
105 # parse the dialogue XML file
106 dlgtree = ET.parse (file)
107 # get the root element
108 conversation = dlgtree.getroot ()
109
110 # build the conversation tree as a dictionary
111 convtree = dict ()
112 for dlg in conversation.findall ("dialogue"):
113 id = dlg.get ("id")
114 speech = dlg.find ("speech").text
115 responses = []
116 for resp in dlg.findall ("response"):
117 responses.append ((resp.text, resp.get ("id"), resp.get ("nextdialogue")))
118
119 convtree[id] = (speech, responses)
120
121
122 return convtree
123
124
125 # function to draw text on surface
126 def put_text (surface, x, y, size, (r,g,b), text):
127 harisfont = os.path.join ("font", "harisgamefont.ttf")
128 textsurf = pygame.font.Font (harisfont, size).render (text, True, pygame.Color (r,g,b))
129 surface.blit (textsurf, (x, y))
130
131 # function to draw several lines of text, centered horizontally and vertically on screen
132 # or drawn centered on a rectangle
133 def put_lines (surface, text_lines, rect=None):
134 textsurfs = []
135 height = 0
136 harisfont = os.path.join ("font", "harisgamefont.ttf")
137
138 for size, r, g, b, text in text_lines:
139 s = pygame.font.Font (harisfont, size).render (text, True, pygame.Color (r,g,b))
140 # add spacing
141 height = height + s.get_height()*1.5
142 textsurfs.append (s)
143
144 i = 0
145 # if no rectangle specified, center in screen
146 if rect is None:
147 scrwidth = surface.get_width ()
148 scrheight = surface.get_height ()
149
150 for s in textsurfs:
151 surface.blit (s, (scrwidth/2 - s.get_width()/2, scrheight/2 - height/2+ i* (s.get_height()*1.5)))
152 i += 1
153 # center on specified rectangular region
154 else:
155 midx = (rect.left + rect.right)/2
156 midy = (rect.top + rect.bottom)/2
157 num = len (textsurfs) / 2
158 for s in textsurfs:
159 surface.blit (s, (midx - s.get_width()/2, midy - (num-i) * s.get_height() * 3/2))
160 i += 1
161
162 # function to ask a question and return answer
163 def ask_question (surface, question, answers, bgscreen):
164
165 sel_answer = 1
166
167 while 1:
168 textarray = [ [ 10, 128, 0, 0, question ] ]
169
170 i = 1
171 for answer in answers:
172 if sel_answer == i:
173 r, g, b = 255, 0, 0
174 else:
175 r, g, b = 0, 0, 0
176 textarray.append ( [10, r, g, b, answer] )
177
178 i += 1
179
180 surface.blit (bgscreen, (surface.get_width()/2 - bgscreen.get_width()/2,
181 surface.get_height()/2 - bgscreen.get_height()/2))
182 put_lines (surface, textarray, pygame.Rect (surface.get_width()/2 - bgscreen.get_width()/2,
183 surface.get_height()/2 - bgscreen.get_height()/2,
184 bgscreen.get_width(), bgscreen.get_height()))
185
186 pygame.display.update ()
187
188 for event in pygame.event.get ():
189 if event.type == pygame.QUIT:
190 sys.exit (0)
191 elif event.type == pygame.KEYDOWN:
192 if event.key == pygame.K_UP:
193 sel_answer -= 1
194 if sel_answer < 1:
195 sel_answer = 1
196 elif event.key == pygame.K_DOWN:
197 sel_answer += 1
198 if sel_answer > len(answers):
199 sel_answer = len(answers)
200 elif event.key == pygame.K_RETURN:
201 return sel_answer
202
203 # function displaying the contents of a container. Object must contain
204 # items list
205 # edgewidth - container edges to avoid drawing items in
206 def get_container_object (surface, obj, bgimage, edgewidth=0):
207
208 # get the number of items
209 numitems = len (obj.objects)
210 # number of rows
211 num_rows = (bgimage.get_height () - edgewidth*2) / 48
212 # number of cols
213 num_cols = (bgimage.get_width () - edgewidth*2) / 48
214
215 objposx = surface.get_width()/2 - bgimage.get_width()/2
216 objposy = surface.get_height()/2 - bgimage.get_height()/2
217
218 selitem = 0
219 selrow = 0
220 selcol = 0
221
222 while 1:
223 # display the background for the container
224 surface.blit (bgimage, (objposx, objposy))
225
226 # display each item in the container
227 i = 0
228 j = 0
229
230 # display all the items in container
231 for item in obj.objects:
232 surface.blit (item.image, (objposx + edgewidth+ j*48, objposy + edgewidth + i*48))
233 j += 1
234 if j >= num_cols:
235 j = 0
236 i += 1
237 # only draw selector if there is at least one item
238 if numitems > 0:
239 pygame.draw.rect (surface, pygame.Color (255,255,255),
240 pygame.Rect(objposx + edgewidth + selcol*48, objposy + edgewidth + selrow*48, 48, 48), 1)
241
242 pygame.display.update ()
243
244 # get events
245 for event in pygame.event.get ():
246 if event.type == pygame.QUIT:
247 sys.exit (0)
248 elif event.type == pygame.KEYDOWN:
249 if event.key == pygame.K_ESCAPE:
250 return None
251 elif event.key == pygame.K_RETURN:
252 if numitems > 0 and selitem >= 0 and selitem < numitems:
253 return obj.objects[selitem]
254 else:
255 return None
256 elif event.key == pygame.K_UP or event.key == pygame.K_LEFT:
257 # go to the prev item
258 selitem -= 1
259 if selitem < 0:
260 selitem = numitems - 1
261 selrow = selitem / num_cols
262 selcol = selitem % num_cols
263
264 elif event.key == pygame.K_DOWN or event.key == pygame.K_RIGHT:
265 # go to the next item
266 selitem += 1
267 if selitem > numitems - 1:
268 selitem = 0
269 selrow = selitem / num_cols
270 selcol = selitem % num_cols