data/states/battle.py (view raw)
1"""This is the state that handles battles against
2monsters"""
3import random
4import pygame as pg
5from .. import tools, battlegui, observer, setup
6from .. components import person, attack, attackitems
7from .. import constants as c
8
9
10class Battle(tools._State):
11 def __init__(self):
12 super(Battle, self).__init__()
13 self.name = 'battle'
14
15 def startup(self, current_time, game_data):
16 """Initialize state attributes"""
17 self.current_time = current_time
18 self.timer = current_time
19 self.allow_input = False
20 self.game_data = game_data
21 self.inventory = game_data['player inventory']
22 self.state = c.SELECT_ACTION
23 self.next = game_data['last state']
24 self.run_away = False
25
26 self.player = self.make_player()
27 self.attack_animations = pg.sprite.Group()
28 self.sword = attackitems.Sword(self.player)
29 self.enemy_group, self.enemy_pos_list, self.enemy_list = self.make_enemies()
30 self.experience_points = self.get_experience_points()
31 self.background = self.make_background()
32 self.info_box = battlegui.InfoBox(game_data, self.experience_points)
33 self.arrow = battlegui.SelectArrow(self.enemy_pos_list,
34 self.info_box)
35 self.select_box = battlegui.SelectBox()
36 self.player_health_box = battlegui.PlayerHealth(self.select_box.rect,
37 self.game_data)
38
39 self.select_action_state_dict = self.make_selection_state_dict()
40 self.observers = [observer.Battle(self)]
41 self.player.observers.extend(self.observers)
42 self.damage_points = pg.sprite.Group()
43
44 def make_enemy_level_dict(self):
45 new_dict = {c.OVERWORLD: 1,
46 c.DUNGEON: 2,
47 c.DUNGEON2: 2,
48 c.DUNGEON3: 3,
49 c.DUNGEON4: 2}
50
51 return new_dict
52
53 def set_enemy_level(self, enemy_list):
54 dungeon_level_dict = self.make_enemy_level_dict()
55
56 for enemy in enemy_list:
57 enemy.level = dungeon_level_dict[self.previous]
58
59 def get_experience_points(self):
60 """
61 Calculate experience points based on number of enemies
62 and their levels.
63 """
64 experience_total = 0
65
66 for enemy in self.enemy_list:
67 experience_total += (random.randint(5,10)*enemy.level)
68
69 return experience_total
70
71 def make_background(self):
72 """Make the blue/black background"""
73 background = pg.sprite.Sprite()
74 surface = pg.Surface(c.SCREEN_SIZE).convert()
75 surface.fill(c.BLACK_BLUE)
76 background.image = surface
77 background.rect = background.image.get_rect()
78 background_group = pg.sprite.Group(background)
79
80 return background_group
81
82 def make_enemies(self):
83 """Make the enemies for the battle. Return sprite group"""
84 pos_list = []
85
86 for column in range(3):
87 for row in range(3):
88 x = (column * 100) + 100
89 y = (row * 100) + 100
90 pos_list.append([x, y])
91
92 enemy_group = pg.sprite.Group()
93
94 if self.game_data['battle type']:
95 enemy = person.Enemy('evilwizard', 0, 0,
96 'down', 'battle resting')
97 enemy_group.add(enemy)
98 else:
99 for enemy in range(random.randint(1, 6)):
100 enemy_group.add(person.Enemy('devil', 0, 0,
101 'down', 'battle resting'))
102
103 for i, enemy in enumerate(enemy_group):
104 enemy.rect.topleft = pos_list[i]
105 enemy.image = pg.transform.scale2x(enemy.image)
106 enemy.index = i
107 enemy.level = self.make_enemy_level_dict()[self.previous]
108 enemy.health = enemy.level * 7
109
110 enemy_list = [enemy for enemy in enemy_group]
111
112 return enemy_group, pos_list[0:len(enemy_group)], enemy_list
113
114 def make_player(self):
115 """Make the sprite for the player's character"""
116 player = person.Player('left', self.game_data, 630, 220, 'battle resting', 1)
117 player.image = pg.transform.scale2x(player.image)
118 return player
119
120 def make_selection_state_dict(self):
121 """
122 Make a dictionary of states with arrow coordinates as keys.
123 """
124 pos_list = self.arrow.make_select_action_pos_list()
125 state_list = [self.enter_select_enemy_state, self.enter_select_item_state,
126 self.enter_select_magic_state, self.try_to_run_away]
127 return dict(zip(pos_list, state_list))
128
129 def update(self, surface, keys, current_time):
130 """Update the battle state"""
131 self.current_time = current_time
132 self.check_input(keys)
133 self.check_timed_events()
134 self.check_if_battle_won()
135 self.enemy_group.update(current_time)
136 self.player.update(keys, current_time)
137 self.attack_animations.update()
138 self.info_box.update()
139 self.arrow.update(keys)
140 self.sword.update(current_time)
141 self.damage_points.update()
142
143 self.draw_battle(surface)
144
145 def check_input(self, keys):
146 """
147 Check user input to navigate GUI.
148 """
149 if self.allow_input:
150 if keys[pg.K_RETURN]:
151 self.end_battle()
152
153 elif keys[pg.K_SPACE]:
154 if self.state == c.SELECT_ACTION:
155 enter_state_function = self.select_action_state_dict[
156 self.arrow.rect.topleft]
157 enter_state_function()
158
159 elif self.state == c.SELECT_ENEMY:
160 self.enter_player_attack_state()
161
162 elif self.state == c.SELECT_ITEM:
163 if self.arrow.index == (len(self.arrow.pos_list) - 1):
164 self.enter_select_action_state()
165 elif self.info_box.item_text_list[self.arrow.index][:14] == 'Healing Potion':
166 self.enter_drink_healing_potion_state()
167 elif self.info_box.item_text_list[self.arrow.index][:5] == 'Ether':
168 self.enter_drink_ether_potion_state()
169 elif self.state == c.SELECT_MAGIC:
170 if self.arrow.index == (len(self.arrow.pos_list) - 1):
171 self.enter_select_action_state()
172 elif self.info_box.magic_text_list[self.arrow.index] == 'Cure':
173 if self.game_data['player stats']['magic points']['current'] >= 25:
174 self.cast_cure()
175 elif self.info_box.magic_text_list[self.arrow.index] == 'Fire Blast':
176 if self.game_data['player stats']['magic points']['current'] >= 25:
177 self.cast_fire_blast()
178
179 self.allow_input = False
180
181 if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
182 self.allow_input = True
183
184 def check_timed_events(self):
185 """
186 Check if amount of time has passed for timed events.
187 """
188 timed_states = [c.PLAYER_DAMAGED,
189 c.ENEMY_DAMAGED,
190 c.ENEMY_DEAD,
191 c.DRINK_HEALING_POTION,
192 c.DRINK_ETHER_POTION]
193 long_delay = timed_states[1:]
194
195 if self.state in long_delay:
196 if (self.current_time - self.timer) > 1000:
197 if self.state == c.ENEMY_DAMAGED:
198 if len(self.enemy_list):
199 self.enter_enemy_attack_state()
200 else:
201 self.enter_battle_won_state()
202 elif (self.state == c.DRINK_HEALING_POTION or
203 self.state == c.CURE_SPELL or
204 self.state == c.DRINK_ETHER_POTION):
205 if len(self.enemy_list):
206 self.enter_enemy_attack_state()
207 else:
208 self.enter_battle_won_state()
209 self.timer = self.current_time
210
211 elif self.state == c.FIRE_SPELL or self.state == c.CURE_SPELL:
212 if (self.current_time - self.timer) > 1500:
213 if len(self.enemy_list):
214 self.enter_enemy_attack_state()
215 else:
216 self.enter_battle_won_state()
217 self.timer = self.current_time
218
219 elif self.state == c.RUN_AWAY:
220 if (self.current_time - self.timer) > 1500:
221 self.end_battle()
222
223 elif self.state == c.BATTLE_WON:
224 if (self.current_time - self.timer) > 1800:
225 self.enter_show_experience_state()
226
227 elif self.state == c.SHOW_EXPERIENCE:
228 if (self.current_time - self.timer) > 2200:
229 player_stats = self.game_data['player stats']
230 player_stats['experience to next level'] -= self.experience_points
231 if player_stats['experience to next level'] <= 0:
232 player_stats['Level'] += 1
233 player_stats['health']['maximum'] += int(player_stats['health']['maximum']*.25)
234 player_stats['magic points']['maximum'] += int(player_stats['magic points']['maximum']*.20)
235 new_experience = int((player_stats['Level'] * 100) * .75)
236 player_stats['experience to next level'] = new_experience
237 self.enter_level_up_state()
238 else:
239 self.end_battle()
240
241 elif self.state == c.PLAYER_DAMAGED:
242 if (self.current_time - self.timer) > 600:
243 if self.enemy_index == (len(self.enemy_list) - 1):
244 if self.run_away:
245 self.enter_run_away_state()
246 else:
247 self.enter_select_action_state()
248 else:
249 self.switch_enemy()
250 self.timer = self.current_time
251
252 def check_if_battle_won(self):
253 """
254 Check if state is SELECT_ACTION and there are no enemies left.
255 """
256 if self.state == c.SELECT_ACTION:
257 if len(self.enemy_group) == 0:
258 self.enter_battle_won_state()
259
260 def notify(self, event):
261 """
262 Notify observer of event.
263 """
264 for new_observer in self.observers:
265 new_observer.on_notify(event)
266
267 def end_battle(self):
268 """
269 End battle and flip back to previous state.
270 """
271 if self.game_data['battle type'] == 'evilwizard':
272 self.game_data['crown quest'] = True
273 self.game_data['last state'] = self.name
274 self.game_data['battle counter'] = random.randint(50, 255)
275 self.game_data['battle type'] = None
276 self.done = True
277
278 def attack_enemy(self, enemy_damage):
279 enemy = self.player.attacked_enemy
280 enemy.health -= enemy_damage
281 self.set_enemy_indices()
282
283 if enemy:
284 enemy.enter_knock_back_state()
285 if enemy.health <= 0:
286 self.enemy_list.pop(enemy.index)
287 enemy.state = c.FADE_DEATH
288 self.arrow.remove_pos(self.player.attacked_enemy)
289 self.enemy_index = 0
290
291 def set_enemy_indices(self):
292 for i, enemy in enumerate(self.enemy_list):
293 enemy.index = i
294
295 def draw_battle(self, surface):
296 """Draw all elements of battle state"""
297 self.background.draw(surface)
298 self.enemy_group.draw(surface)
299 self.attack_animations.draw(surface)
300 self.sword.draw(surface)
301 surface.blit(self.player.image, self.player.rect)
302 surface.blit(self.info_box.image, self.info_box.rect)
303 surface.blit(self.select_box.image, self.select_box.rect)
304 surface.blit(self.arrow.image, self.arrow.rect)
305 self.player_health_box.draw(surface)
306 self.damage_points.draw(surface)
307
308 def player_damaged(self, damage):
309 self.game_data['player stats']['health']['current'] -= damage
310
311 def player_healed(self, heal, magic_points=0):
312 """
313 Add health from potion to game data.
314 """
315 health = self.game_data['player stats']['health']
316
317 health['current'] += heal
318 if health['current'] > health['maximum']:
319 health['current'] = health['maximum']
320
321 if self.state == c.DRINK_HEALING_POTION:
322 self.game_data['player inventory']['Healing Potion']['quantity'] -= 1
323 if self.game_data['player inventory']['Healing Potion']['quantity'] == 0:
324 del self.game_data['player inventory']['Healing Potion']
325 elif self.state == c.CURE_SPELL:
326 self.game_data['player stats']['magic points']['current'] -= magic_points
327
328 def magic_boost(self, magic_points):
329 """
330 Add magic from ether to game data.
331 """
332 magic = self.game_data['player stats']['magic points']
333 magic['current'] += magic_points
334 if magic['current'] > magic['maximum']:
335 magic['current'] = magic['maximum']
336
337 self.game_data['player inventory']['Ether Potion']['quantity'] -= 1
338 if not self.game_data['player inventory']['Ether Potion']['quantity']:
339 del self.game_data['player inventory']['Ether Potion']
340
341 def set_timer_to_current_time(self):
342 """Set the timer to the current time."""
343 self.timer = self.current_time
344
345 def cast_fire_blast(self):
346 """
347 Cast fire blast on all enemies.
348 """
349 self.state = self.info_box.state = c.FIRE_SPELL
350 POWER = self.inventory['Fire Blast']['power']
351 MAGIC_POINTS = self.inventory['Fire Blast']['magic points']
352 self.game_data['player stats']['magic points']['current'] -= MAGIC_POINTS
353 for enemy in self.enemy_list:
354 DAMAGE = random.randint(POWER//2, POWER)
355 self.damage_points.add(
356 attackitems.HealthPoints(DAMAGE, enemy.rect.topright))
357 enemy.health -= DAMAGE
358 posx = enemy.rect.x - 32
359 posy = enemy.rect.y - 64
360 fire_sprite = attack.Fire(posx, posy)
361 self.attack_animations.add(fire_sprite)
362 if enemy.health <= 0:
363 enemy.kill()
364 self.arrow.remove_pos(enemy)
365 else:
366 enemy.enter_knock_back_state()
367 self.enemy_list = [enemy for enemy in self.enemy_list if enemy.health > 0]
368 self.enemy_index = 0
369 self.arrow.index = 0
370 self.arrow.become_invisible_surface()
371 self.arrow.state = c.SELECT_ACTION
372 self.set_timer_to_current_time()
373
374 def cast_cure(self):
375 """
376 Cast cure spell on player.
377 """
378 self.state = c.CURE_SPELL
379 HEAL_AMOUNT = self.inventory['Cure']['power']
380 MAGIC_POINTS = self.inventory['Cure']['magic points']
381 self.player.healing = True
382 self.set_timer_to_current_time()
383 self.arrow.become_invisible_surface()
384 self.enemy_index = 0
385 self.damage_points.add(
386 attackitems.HealthPoints(HEAL_AMOUNT, self.player.rect.topright, False))
387 self.player_healed(HEAL_AMOUNT, MAGIC_POINTS)
388 self.info_box.state = c.DRINK_HEALING_POTION
389
390 def drink_ether(self):
391 """
392 Drink ether potion.
393 """
394 self.state = self.info_box.state = c.DRINK_ETHER_POTION
395 self.player.healing = True
396 self.set_timer_to_current_time()
397 self.arrow.become_invisible_surface()
398 self.enemy_index = 0
399 self.damage_points.add(
400 attackitems.HealthPoints(30,
401 self.player.rect.topright,
402 False,
403 True))
404 self.magic_boost(30)
405
406 def drink_healing_potion(self):
407 """
408 Drink Healing Potion.
409 """
410 self.state = self.info_box.state = c.DRINK_HEALING_POTION
411 self.player.healing = True
412 self.arrow.become_invisible_surface()
413 self.enemy_index = 0
414 self.damage_points.add(
415 attackitems.HealthPoints(30,
416 self.player.rect.topright,
417 False))
418 self.player_healed(30)
419 self.set_timer_to_current_time()
420
421 def enter_select_enemy_state(self):
422 """
423 Transition battle into the select enemy state.
424 """
425 self.state = self.arrow.state = c.SELECT_ENEMY
426 self.arrow.index = 0
427
428 def enter_select_item_state(self):
429 """
430 Transition battle into the select item state.
431 """
432 self.state = self.info_box.state = c.SELECT_ITEM
433 self.arrow.become_select_item_state()
434
435 def enter_select_magic_state(self):
436 """
437 Transition battle into the select magic state.
438 """
439 self.state = self.info_box.state = c.SELECT_MAGIC
440 self.arrow.become_select_magic_state()
441
442 def try_to_run_away(self):
443 """
444 Transition battle into the run away state.
445 """
446 self.run_away = True
447 self.arrow.become_invisible_surface()
448 self.enemy_index = 0
449 self.enter_enemy_attack_state()
450
451 def enter_enemy_attack_state(self):
452 """
453 Transition battle into the Enemy attack state.
454 """
455 self.state = self.info_box.state = c.ENEMY_ATTACK
456 enemy = self.enemy_list[self.enemy_index]
457 enemy.enter_enemy_attack_state()
458
459 def enter_player_attack_state(self):
460 """
461 Transition battle into the Player attack state.
462 """
463 self.state = c.PLAYER_ATTACK
464 enemy_posx = self.arrow.rect.x + 60
465 enemy_posy = self.arrow.rect.y - 20
466 enemy_pos = (enemy_posx, enemy_posy)
467 enemy_to_attack = None
468
469 for enemy in self.enemy_list:
470 if enemy.rect.topleft == enemy_pos:
471 enemy_to_attack = enemy
472
473 self.player.enter_attack_state(enemy_to_attack)
474 self.arrow.become_invisible_surface()
475
476 def enter_drink_healing_potion_state(self):
477 """
478 Transition battle into the Drink Healing Potion state.
479 """
480 self.state = self.info_box.state = c.DRINK_HEALING_POTION
481 self.player.healing = True
482 self.set_timer_to_current_time()
483 self.arrow.become_invisible_surface()
484 self.enemy_index = 0
485 self.damage_points.add(
486 attackitems.HealthPoints(30,
487 self.player.rect.topright,
488 False))
489 self.player_healed(30)
490
491 def enter_drink_ether_potion_state(self):
492 """
493 Transition battle into the Drink Ether Potion state.
494 """
495 self.state = self.info_box.state = c.DRINK_ETHER_POTION
496 self.player.healing = True
497 self.arrow.become_invisible_surface()
498 self.enemy_index = 0
499 self.damage_points.add(
500 attackitems.HealthPoints(30,
501 self.player.rect.topright,
502 False,
503 True))
504 self.magic_boost(30)
505 self.set_timer_to_current_time()
506
507 def enter_select_action_state(self):
508 """
509 Transition battle into the select action state
510 """
511 self.state = self.info_box.state = c.SELECT_ACTION
512 self.arrow.index = 0
513 self.arrow.state = self.state
514 self.arrow.image = setup.GFX['smallarrow']
515
516 def enter_player_damaged_state(self):
517 """
518 Transition battle into the player damaged state.
519 """
520 self.state = self.info_box.state = c.PLAYER_DAMAGED
521 if self.enemy_index > len(self.enemy_list) - 1:
522 self.enemy_index = 0
523 enemy = self.enemy_list[self.enemy_index]
524 player_damage = enemy.calculate_hit()
525 self.damage_points.add(
526 attackitems.HealthPoints(player_damage,
527 self.player.rect.topright))
528 self.info_box.set_player_damage(player_damage)
529 self.set_timer_to_current_time()
530 self.player_damaged(player_damage)
531 if player_damage:
532 self.player.damaged = True
533 self.player.enter_knock_back_state()
534
535 def enter_enemy_damaged_state(self):
536 """
537 Transition battle into the enemy damaged state.
538 """
539 self.state = self.info_box.state = c.ENEMY_DAMAGED
540 enemy_damage = self.player.calculate_hit()
541 self.damage_points.add(
542 attackitems.HealthPoints(enemy_damage,
543 self.player.attacked_enemy.rect.topright))
544
545 self.info_box.set_enemy_damage(enemy_damage)
546
547 self.arrow.state = c.SELECT_ACTION
548 self.arrow.index = 0
549 self.attack_enemy(enemy_damage)
550 self.set_timer_to_current_time()
551
552 def switch_enemy(self):
553 """
554 Switch which enemy the player is attacking.
555 """
556 if self.enemy_index < len(self.enemy_list) - 1:
557 self.enemy_index += 1
558 self.enter_enemy_attack_state()
559
560 def enter_run_away_state(self):
561 """
562 Transition battle into the run away state.
563 """
564 self.state = self.info_box.state = c.RUN_AWAY
565 self.arrow.become_invisible_surface()
566 self.player.state = c.RUN_AWAY
567 self.set_timer_to_current_time()
568
569 def enter_battle_won_state(self):
570 """
571 Transition battle into the battle won state.
572 """
573 self.state = self.info_box.state = c.BATTLE_WON
574 self.player.state = c.VICTORY_DANCE
575 self.set_timer_to_current_time()
576
577 def enter_show_experience_state(self):
578 """
579 Transition battle into the show experience state.
580 """
581 self.state = self.info_box.state = c.SHOW_EXPERIENCE
582 self.set_timer_to_current_time()
583
584 def enter_level_up_state(self):
585 """
586 Transition battle into the LEVEL UP state.
587 """
588 self.state = self.info_box.state = c.LEVEL_UP
589 self.info_box.reset_level_up_message()
590 self.set_timer_to_current_time()
591
592
593
594
595
596