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