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