data/states/battle.py (view raw)
1"""This is the state that handles battles against
2monsters"""
3
4import random
5import pygame as pg
6from .. import tools, setup
7from .. components import person
8from .. import constants as c
9
10#STATES
11
12SELECT_ACTION = 'select action'
13SELECT_ENEMY = 'select enemy'
14ENEMY_ATTACK = 'enemy attack'
15PLAYER_ATTACK = 'player attack'
16SELECT_ITEM = 'select item'
17SELECT_MAGIC = 'select magic'
18RUN_AWAY = 'run away'
19
20#EVENTS
21
22END_BATTLE = 'end battle'
23
24
25class Battle(tools._State):
26 def __init__(self):
27 super(Battle, self).__init__()
28
29 def startup(self, current_time, game_data):
30 """Initialize state attributes"""
31 self.current_time = current_time
32 self.allow_input = False
33 self.game_data = game_data
34 self.background = self.make_background()
35 self.enemy_group, self.enemy_pos_list = self.make_enemies()
36 self.player = self.make_player()
37 self.info_box = InfoBox(game_data)
38 self.arrow = SelectArrow(self.enemy_pos_list)
39 self.select_box = SelectBox()
40 self.state = SELECT_ACTION
41 self.select_action_state_dict = self.make_selection_state_dict()
42 self.name = 'battle'
43 self.next = game_data['last state']
44 self.observer = Observer(self)
45 self.player.observer = self.observer
46
47 def make_background(self):
48 """Make the blue/black background"""
49 background = pg.sprite.Sprite()
50 surface = pg.Surface(c.SCREEN_SIZE).convert()
51 surface.fill(c.BLACK_BLUE)
52 background.image = surface
53 background.rect = background.image.get_rect()
54 background_group = pg.sprite.Group(background)
55
56 return background_group
57
58 def make_enemies(self):
59 """Make the enemies for the battle. Return sprite group"""
60 pos_list = []
61
62 for column in range(3):
63 for row in range(3):
64 x = (column * 100) + 100
65 y = (row * 100) + 100
66 pos_list.append([x,y])
67
68 enemy_group = pg.sprite.Group()
69
70 for enemy in range(random.randint(1, 6)):
71 enemy_group.add(person.Person('devil', 0, 0,
72 'down', 'battle resting'))
73
74 for i, enemy in enumerate(enemy_group):
75 enemy.rect.topleft = pos_list[i]
76 enemy.image = pg.transform.scale2x(enemy.image)
77
78 return enemy_group, pos_list[0:len(enemy_group)]
79
80 def make_player(self):
81 """Make the sprite for the player's character"""
82 player = person.Player('left', 630, 220, 'battle resting', 1)
83 player.image = pg.transform.scale2x(player.image)
84
85 return player
86
87 def make_selection_state_dict(self):
88 """
89 Make a dictionary of states with arrow coordinates as keys.
90 """
91 pos_list = self.arrow.make_select_action_pos_list()
92 state_list = [SELECT_ENEMY, SELECT_ITEM, SELECT_MAGIC, RUN_AWAY]
93 return dict(zip(pos_list, state_list))
94
95 def update(self, surface, keys, current_time):
96 """Update the battle state"""
97 self.check_input(keys)
98 self.enemy_group.update(current_time)
99 self.player.update(keys, current_time)
100 self.arrow.update(keys)
101
102 self.draw_battle(surface)
103
104 def check_input(self, keys):
105 """
106 Check user input to navigate GUI.
107 """
108 if self.allow_input:
109 if keys[pg.K_RETURN]:
110 self.notify(END_BATTLE)
111
112 elif keys[pg.K_SPACE] and self.state == SELECT_ACTION:
113 self.state = self.select_action_state_dict[
114 self.arrow.rect.topleft]
115 self.notify(self.state)
116
117 elif keys[pg.K_SPACE] and self.state == SELECT_ENEMY:
118 self.state = PLAYER_ATTACK
119 self.notify(self.state)
120
121 self.allow_input = False
122
123 if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
124 self.allow_input = True
125
126 def notify(self, event):
127 """
128 Notify observer of event.
129 """
130 self.observer.on_notify(event)
131
132 def end_battle(self):
133 """
134 End battle and flip back to previous state.
135 """
136 self.game_data['last state'] = self.name
137 self.done = True
138
139 def draw_battle(self, surface):
140 """Draw all elements of battle state"""
141 self.background.draw(surface)
142 self.enemy_group.draw(surface)
143 surface.blit(self.player.image, self.player.rect)
144 surface.blit(self.info_box.image, self.info_box.rect)
145 surface.blit(self.select_box.image, self.select_box.rect)
146 surface.blit(self.arrow.image, self.arrow.rect)
147
148
149class Observer(object):
150 """
151 Observes events of battle and passes info to components.
152 """
153 def __init__(self, level):
154 self.level = level
155 self.info_box = level.info_box
156 self.select_box = level.info_box
157 self.arrow = level.arrow
158 self.player = level.player
159 self.enemies = level.enemy_group
160 self.event_dict = self.make_event_dict()
161
162 def make_event_dict(self):
163 """
164 Make a dictionary of events the Observer can
165 receive.
166 """
167 event_dict = {END_BATTLE: self.end_battle,
168 SELECT_ACTION: self.select_action,
169 SELECT_ITEM: self.select_item,
170 SELECT_ENEMY: self.select_enemy,
171 ENEMY_ATTACK: self.enemy_attack,
172 PLAYER_ATTACK: self.player_attack,
173 RUN_AWAY: self.run_away}
174
175 return event_dict
176
177 def on_notify(self, event):
178 """
179 Notify Observer of event.
180 """
181 if event in self.event_dict:
182 self.event_dict[event]()
183
184 def end_battle(self):
185 """
186 End Battle and flip to previous state.
187 """
188 self.level.end_battle()
189
190 def select_action(self):
191 """
192 Set components to select action.
193 """
194 self.level.state = SELECT_ACTION
195 self.arrow.index = 0
196 self.arrow.state = SELECT_ACTION
197
198 def select_enemy(self):
199 self.level.state = SELECT_ENEMY
200 self.arrow.state = SELECT_ENEMY
201
202 def select_item(self):
203 self.level.state = SELECT_ITEM
204 self.info_box.image = self.info_box.make_image(SELECT_ITEM)
205
206 def enemy_attack(self):
207 pass
208
209 def player_attack(self):
210 enemy_posx = self.arrow.rect.x + 60
211 enemy_posy = self.arrow.rect.y - 20
212 enemy_pos = (enemy_posx, enemy_posy)
213 enemy_to_attack = None
214
215 for enemy in self.enemies:
216 if enemy.rect.topleft == enemy_pos:
217 enemy_to_attack = enemy
218
219 self.player.enter_attack_state(enemy_to_attack)
220
221
222
223
224
225 def run_away(self):
226 pass
227
228
229class InfoBox(object):
230 """
231 Info box that describes attack damage and other battle
232 related information.
233 """
234 def __init__(self, game_data):
235 self.game_data = game_data
236 self.state = SELECT_ACTION
237 self.title_font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
238 self.title_font.set_underline(True)
239 self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 18)
240 self.message_dict = self.make_message_dict()
241 self.image = self.make_image(SELECT_ACTION)
242 self.rect = self.image.get_rect(bottom=608)
243
244 def make_message_dict(self):
245 """
246 Make dictionary of states Battle info can be in.
247 """
248 message_dict = {SELECT_ACTION: 'Select an action.',
249 SELECT_MAGIC: 'Select a magic spell.',
250 SELECT_ITEM: 'Select an item.',
251 SELECT_ENEMY: 'Select an enemy.',
252 ENEMY_ATTACK: 'The enemy attacks player!',
253 PLAYER_ATTACK: 'Player attacks enemy.',
254 RUN_AWAY: 'Run away'}
255
256 return message_dict
257
258 def make_item_text(self):
259 """
260 Make the text for when the player selects items.
261 """
262 inventory = self.game_data['player inventory']
263 allowed_item_list = ['Healing Potion']
264 title = 'SELECT ITEM'
265 item_text_list = [title]
266
267 for item in inventory:
268 if item in allowed_item_list:
269 text = item + ": " + str(inventory[item]['quantity'])
270 item_text_list.append(text)
271
272 item_text_list.append('BACK')
273
274 return item_text_list
275
276 def make_text_sprites(self, text_list):
277 """
278 Make sprites out of text.
279 """
280 sprite_group = pg.sprite.Group()
281
282 for i, text in enumerate(text_list):
283 sprite = pg.sprite.Sprite()
284
285 if i == 0:
286 x = 195
287 y = 10
288 surface = self.title_font.render(text, True, c.NEAR_BLACK)
289 rect = surface.get_rect(x=x, y=y)
290 else:
291 x = 100
292 y = (i * 30) + 20
293 surface = self.font.render(text, True, c.NEAR_BLACK)
294 rect = surface.get_rect(x=x, y=y)
295 sprite.image = surface
296 sprite.rect = rect
297 sprite_group.add(sprite)
298
299 return sprite_group
300
301 def make_image(self, state):
302 """
303 Make image out of box and message.
304 """
305 image = setup.GFX['shopbox']
306 rect = image.get_rect(bottom=608)
307 surface = pg.Surface(rect.size)
308 surface.set_colorkey(c.BLACK)
309 surface.blit(image, (0, 0))
310
311 if state == SELECT_ITEM:
312 text_sprites = self.make_text_sprites(self.make_item_text())
313 text_sprites.draw(surface)
314 else:
315 text_surface = self.font.render(self.message_dict[state], True, c.NEAR_BLACK)
316 text_rect = text_surface.get_rect(x=50, y=50)
317 surface.blit(text_surface, text_rect)
318
319 return surface
320
321
322class SelectBox(object):
323 """
324 Box to select whether to attack, use item, use magic or run away.
325 """
326 def __init__(self):
327 self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
328 self.slots = self.make_slots()
329 self.image = self.make_image()
330 self.rect = self.image.get_rect(bottom=608,
331 right=800)
332
333 def make_image(self):
334 """
335 Make the box image for
336 """
337 image = setup.GFX['goldbox']
338 rect = image.get_rect(bottom=608)
339 surface = pg.Surface(rect.size)
340 surface.set_colorkey(c.BLACK)
341 surface.blit(image, (0, 0))
342
343 for text in self.slots:
344 text_surface = self.font.render(text, True, c.NEAR_BLACK)
345 text_rect = text_surface.get_rect(x=self.slots[text]['x'],
346 y=self.slots[text]['y'])
347 surface.blit(text_surface, text_rect)
348
349 return surface
350
351 def make_slots(self):
352 """
353 Make the slots that hold the text selections, and locations.
354 """
355 slot_dict = {}
356 selections = ['Attack', 'Items', 'Magic', 'Run']
357
358 for i, text in enumerate(selections):
359 slot_dict[text] = {'x': 150,
360 'y': (i*34)+10}
361
362 return slot_dict
363
364
365
366class SelectArrow(object):
367 """Small arrow for menu"""
368 def __init__(self, enemy_pos_list):
369 self.image = setup.GFX['smallarrow']
370 self.rect = self.image.get_rect()
371 self.state = 'select action'
372 self.state_dict = self.make_state_dict()
373 self.pos_list = self.make_select_action_pos_list()
374 self.index = 0
375 self.rect.topleft = self.pos_list[self.index]
376 self.allow_input = False
377 self.enemy_pos_list = enemy_pos_list
378
379 def make_state_dict(self):
380 """Make state dictionary"""
381 state_dict = {'select action': self.select_action,
382 'select enemy': self.select_enemy}
383
384 return state_dict
385
386 def select_action(self, keys):
387 """
388 Select what action the player should take.
389 """
390 self.pos_list = self.make_select_action_pos_list()
391 self.rect.topleft = self.pos_list[self.index]
392
393 if self.allow_input:
394 if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1):
395 self.index += 1
396 self.allow_input = False
397 elif keys[pg.K_UP] and self.index > 0:
398 self.index -= 1
399 self.allow_input = False
400
401 if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False:
402 self.allow_input = True
403
404
405 def make_select_action_pos_list(self):
406 """
407 Make the list of positions the arrow can be in.
408 """
409 pos_list = []
410
411 for i in range(4):
412 x = 590
413 y = (i * 34) + 472
414 pos_list.append((x, y))
415
416 return pos_list
417
418 def select_enemy(self, keys):
419 """
420 Select what enemy you want to take action on.
421 """
422 self.pos_list = self.enemy_pos_list
423 pos = self.pos_list[self.index]
424 self.rect.x = pos[0] - 60
425 self.rect.y = pos[1] + 20
426
427 if self.allow_input:
428 if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1):
429 self.index += 1
430 self.allow_input = False
431 elif keys[pg.K_UP] and self.index > 0:
432 self.index -= 1
433 self.allow_input = False
434
435
436 if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False \
437 and keys[pg.K_RIGHT] == False and keys[pg.K_LEFT] == False:
438 self.allow_input = True
439
440
441 def enter_select_action(self):
442 """
443 Assign values for the select action state.
444 """
445 pass
446
447 def enter_select_enemy(self):
448 """
449 Assign values for the select enemy state.
450 """
451 pass
452
453 def update(self, keys):
454 """
455 Update arrow position.
456 """
457 state_function = self.state_dict[self.state]
458 state_function(keys)
459
460 def draw(self, surface):
461 """
462 Draw to surface.
463 """
464 surface.blit(self.image, self.rect)
465