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 if self.game_data['player stats']['magic']['current'] >= 25:
198 self.player_actions.append(c.CURE_SPELL)
199 self.action_selected = True
200 elif self.info_box.magic_text_list[self.arrow.index] == 'Fire Blast':
201 if self.game_data['player stats']['magic']['current'] >= 25:
202 self.player_actions.append(c.FIRE_SPELL)
203 self.action_selected = True
204
205 self.allow_input = False
206
207 if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
208 self.allow_input = True
209
210 def check_timed_events(self):
211 """
212 Check if amount of time has passed for timed events.
213 """
214 timed_states = [c.PLAYER_DAMAGED,
215 c.ENEMY_DAMAGED,
216 c.ENEMY_DEAD,
217 c.DRINK_HEALING_POTION,
218 c.DRINK_ETHER_POTION]
219 long_delay = timed_states[1:]
220
221 if self.state in long_delay:
222 if (self.current_time - self.timer) > 1000:
223 if self.state == c.ENEMY_DAMAGED:
224 if self.player_actions:
225 self.player_action_dict[self.player_actions[0]]()
226 self.player_actions.pop(0)
227 else:
228 if len(self.enemy_list):
229 self.enter_enemy_attack_state()
230 else:
231 self.enter_battle_won_state()
232 elif (self.state == c.DRINK_HEALING_POTION or
233 self.state == c.CURE_SPELL or
234 self.state == c.DRINK_ETHER_POTION):
235 if self.player_actions:
236 self.player_action_dict[self.player_actions[0]]()
237 self.player_actions.pop(0)
238 else:
239 if len(self.enemy_list):
240 self.enter_enemy_attack_state()
241 else:
242 self.enter_battle_won_state()
243 self.timer = self.current_time
244
245 elif self.state == c.FIRE_SPELL or self.state == c.CURE_SPELL:
246 if (self.current_time - self.timer) > 1500:
247 if self.player_actions:
248 self.player_action_dict[self.player_actions[0]]()
249 self.player_actions.pop(0)
250 else:
251 if len(self.enemy_list):
252 self.enter_enemy_attack_state()
253 else:
254 self.enter_battle_won_state()
255 self.timer = self.current_time
256
257 elif self.state == c.RUN_AWAY:
258 if (self.current_time - self.timer) > 1500:
259 self.end_battle()
260
261 elif self.state == c.BATTLE_WON:
262 if (self.current_time - self.timer) > 1800:
263 self.enter_show_experience_state()
264
265 elif self.state == c.LEVEL_UP:
266 if (self.current_time - self.timer) > 2200:
267 if self.game_data['player stats']['Level'] == 3:
268 self.enter_two_actions_per_turn_state()
269 else:
270 self.end_battle()
271
272 elif self.state == c.TWO_ACTIONS:
273 if (self.current_time - self.timer) > 3000:
274 self.end_battle()
275
276 elif self.state == c.SHOW_EXPERIENCE:
277 if (self.current_time - self.timer) > 2200:
278 player_stats = self.game_data['player stats']
279 player_stats['experience to next level'] -= self.experience_points
280 if player_stats['experience to next level'] <= 0:
281 player_stats['Level'] += 1
282 player_stats['health']['maximum'] += int(player_stats['health']['maximum']*.25)
283 player_stats['magic']['maximum'] += int(player_stats['magic']['maximum']*.20)
284 new_experience = int((player_stats['Level'] * 100) * .75)
285 player_stats['experience to next level'] = new_experience
286 self.enter_level_up_state()
287 self.just_leveled_up = True
288 else:
289 self.end_battle()
290
291 elif self.state == c.PLAYER_DAMAGED:
292 if (self.current_time - self.timer) > 600:
293 if self.enemy_index == (len(self.enemy_list) - 1):
294 if self.run_away:
295 self.enter_run_away_state()
296 else:
297 self.enter_select_action_state()
298 else:
299 self.switch_enemy()
300 self.timer = self.current_time
301
302 def check_if_battle_won(self):
303 """
304 Check if state is SELECT_ACTION and there are no enemies left.
305 """
306 if self.state == c.SELECT_ACTION:
307 if len(self.enemy_group) == 0:
308 self.enter_battle_won_state()
309
310 def notify(self, event):
311 """
312 Notify observer of event.
313 """
314 for new_observer in self.observers:
315 new_observer.on_notify(event)
316
317 def end_battle(self):
318 """
319 End battle and flip back to previous state.
320 """
321 if self.game_data['battle type'] == 'evilwizard':
322 self.game_data['crown quest'] = True
323 self.game_data['last state'] = self.name
324 self.game_data['battle counter'] = random.randint(50, 255)
325 self.game_data['battle type'] = None
326 self.done = True
327
328 def attack_enemy(self, enemy_damage):
329 enemy = self.player.attacked_enemy
330 enemy.health -= enemy_damage
331 self.set_enemy_indices()
332
333 if enemy:
334 enemy.enter_knock_back_state()
335 if enemy.health <= 0:
336 self.enemy_list.pop(enemy.index)
337 enemy.state = c.FADE_DEATH
338 self.arrow.remove_pos(self.player.attacked_enemy)
339 self.enemy_index = 0
340
341 def set_enemy_indices(self):
342 for i, enemy in enumerate(self.enemy_list):
343 enemy.index = i
344
345 def draw_battle(self, surface):
346 """Draw all elements of battle state"""
347 self.background.draw(surface)
348 self.enemy_group.draw(surface)
349 self.attack_animations.draw(surface)
350 self.sword.draw(surface)
351 surface.blit(self.player.image, self.player.rect)
352 surface.blit(self.info_box.image, self.info_box.rect)
353 surface.blit(self.select_box.image, self.select_box.rect)
354 surface.blit(self.arrow.image, self.arrow.rect)
355 self.player_health_box.draw(surface)
356 self.damage_points.draw(surface)
357
358 def player_damaged(self, damage):
359 self.game_data['player stats']['health']['current'] -= damage
360
361 def player_healed(self, heal, magic_points=0):
362 """
363 Add health from potion to game data.
364 """
365 health = self.game_data['player stats']['health']
366
367 health['current'] += heal
368 if health['current'] > health['maximum']:
369 health['current'] = health['maximum']
370
371 if self.state == c.DRINK_HEALING_POTION:
372 self.game_data['player inventory']['Healing Potion']['quantity'] -= 1
373 if self.game_data['player inventory']['Healing Potion']['quantity'] == 0:
374 del self.game_data['player inventory']['Healing Potion']
375 elif self.state == c.CURE_SPELL:
376 self.game_data['player stats']['magic']['current'] -= magic_points
377
378 def magic_boost(self, magic_points):
379 """
380 Add magic from ether to game data.
381 """
382 magic = self.game_data['player stats']['magic']
383 magic['current'] += magic_points
384 if magic['current'] > magic['maximum']:
385 magic['current'] = magic['maximum']
386
387 self.game_data['player inventory']['Ether Potion']['quantity'] -= 1
388 if not self.game_data['player inventory']['Ether Potion']['quantity']:
389 del self.game_data['player inventory']['Ether Potion']
390
391 def set_timer_to_current_time(self):
392 """Set the timer to the current time."""
393 self.timer = self.current_time
394
395 def cast_fire_blast(self):
396 """
397 Cast fire blast on all enemies.
398 """
399 self.state = self.info_box.state = c.FIRE_SPELL
400 POWER = self.inventory['Fire Blast']['power']
401 MAGIC_POINTS = self.inventory['Fire Blast']['magic']
402 self.game_data['player stats']['magic']['current'] -= MAGIC_POINTS
403 for enemy in self.enemy_list:
404 DAMAGE = random.randint(POWER//2, POWER)
405 self.damage_points.add(
406 attackitems.HealthPoints(DAMAGE, enemy.rect.topright))
407 enemy.health -= DAMAGE
408 posx = enemy.rect.x - 32
409 posy = enemy.rect.y - 64
410 fire_sprite = attack.Fire(posx, posy)
411 self.attack_animations.add(fire_sprite)
412 if enemy.health <= 0:
413 enemy.kill()
414 self.arrow.remove_pos(enemy)
415 else:
416 enemy.enter_knock_back_state()
417 self.enemy_list = [enemy for enemy in self.enemy_list if enemy.health > 0]
418 self.enemy_index = 0
419 self.arrow.index = 0
420 self.arrow.state = 'invisible'
421 self.set_timer_to_current_time()
422
423 def cast_cure(self):
424 """
425 Cast cure spell on player.
426 """
427 self.state = c.CURE_SPELL
428 HEAL_AMOUNT = self.inventory['Cure']['power']
429 MAGIC_POINTS = self.inventory['Cure']['magic points']
430 self.player.healing = True
431 self.set_timer_to_current_time()
432 self.arrow.state = 'invisible'
433 self.enemy_index = 0
434 self.damage_points.add(
435 attackitems.HealthPoints(HEAL_AMOUNT, self.player.rect.topright, False))
436 self.player_healed(HEAL_AMOUNT, MAGIC_POINTS)
437 self.info_box.state = c.DRINK_HEALING_POTION
438
439 def drink_ether(self):
440 """
441 Drink ether potion.
442 """
443 self.state = self.info_box.state = c.DRINK_ETHER_POTION
444 self.player.healing = True
445 self.set_timer_to_current_time()
446 self.arrow.state = 'invisible'
447 self.enemy_index = 0
448 self.damage_points.add(
449 attackitems.HealthPoints(30,
450 self.player.rect.topright,
451 False,
452 True))
453 self.magic_boost(30)
454
455 def drink_healing_potion(self):
456 """
457 Drink Healing Potion.
458 """
459 self.state = self.info_box.state = c.DRINK_HEALING_POTION
460 self.player.healing = True
461 self.arrow.state = 'invisible'
462 self.enemy_index = 0
463 self.damage_points.add(
464 attackitems.HealthPoints(30,
465 self.player.rect.topright,
466 False))
467 self.player_healed(30)
468 self.set_timer_to_current_time()
469
470 def enter_select_enemy_state(self):
471 """
472 Transition battle into the select enemy state.
473 """
474 self.state = self.arrow.state = c.SELECT_ENEMY
475 self.arrow.index = 0
476
477 def enter_select_item_state(self):
478 """
479 Transition battle into the select item state.
480 """
481 self.state = self.info_box.state = c.SELECT_ITEM
482 self.arrow.become_select_item_state()
483
484 def enter_select_magic_state(self):
485 """
486 Transition battle into the select magic state.
487 """
488 self.state = self.info_box.state = c.SELECT_MAGIC
489 self.arrow.become_select_magic_state()
490
491 def try_to_run_away(self):
492 """
493 Transition battle into the run away state.
494 """
495 self.run_away = True
496 self.arrow.state = 'invisible'
497 self.enemy_index = 0
498 self.enter_enemy_attack_state()
499
500 def enter_enemy_attack_state(self):
501 """
502 Transition battle into the Enemy attack state.
503 """
504 self.state = self.info_box.state = c.ENEMY_ATTACK
505 enemy = self.enemy_list[self.enemy_index]
506 enemy.enter_enemy_attack_state()
507
508 def enter_player_attack_state(self):
509 """
510 Transition battle into the Player attack state.
511 """
512 self.state = self.info_box.state = c.PLAYER_ATTACK
513 enemy_to_attack = self.enemies_to_attack.pop(0)
514 if enemy_to_attack in self.enemy_list:
515 self.player.enter_attack_state(enemy_to_attack)
516 else:
517 if self.enemy_list:
518 self.player.enter_attack_state(self.enemy_list[0])
519 else:
520 self.enter_battle_won_state()
521 self.arrow.state = 'invisible'
522
523 def get_enemy_to_attack(self):
524 """
525 Get enemy for player to attack by arrow position.
526 """
527 enemy_posx = self.arrow.rect.x + 60
528 enemy_posy = self.arrow.rect.y - 20
529 enemy_pos = (enemy_posx, enemy_posy)
530 enemy_to_attack = None
531
532 for enemy in self.enemy_list:
533 if enemy.rect.topleft == enemy_pos:
534 enemy_to_attack = enemy
535
536 return enemy_to_attack
537
538
539 def enter_drink_healing_potion_state(self):
540 """
541 Transition battle into the Drink Healing Potion state.
542 """
543 self.state = self.info_box.state = c.DRINK_HEALING_POTION
544 self.player.healing = True
545 self.set_timer_to_current_time()
546 self.arrow.state = 'invisible'
547 self.enemy_index = 0
548 self.damage_points.add(
549 attackitems.HealthPoints(30,
550 self.player.rect.topright,
551 False))
552 self.player_healed(30)
553
554 def enter_drink_ether_potion_state(self):
555 """
556 Transition battle into the Drink Ether Potion state.
557 """
558 self.state = self.info_box.state = c.DRINK_ETHER_POTION
559 self.player.healing = True
560 self.arrow.state = 'invisible'
561 self.enemy_index = 0
562 self.damage_points.add(
563 attackitems.HealthPoints(30,
564 self.player.rect.topright,
565 False,
566 True))
567 self.magic_boost(30)
568 self.set_timer_to_current_time()
569
570 def enter_select_action_state(self):
571 """
572 Transition battle into the select action state
573 """
574 self.state = self.info_box.state = c.SELECT_ACTION
575 self.arrow.index = 0
576 self.arrow.state = self.state
577
578 def enter_player_damaged_state(self):
579 """
580 Transition battle into the player damaged state.
581 """
582 self.state = self.info_box.state = c.PLAYER_DAMAGED
583 if self.enemy_index > len(self.enemy_list) - 1:
584 self.enemy_index = 0
585 enemy = self.enemy_list[self.enemy_index]
586 player_damage = enemy.calculate_hit(self.inventory['equipped armor'],
587 self.inventory)
588 self.damage_points.add(
589 attackitems.HealthPoints(player_damage,
590 self.player.rect.topright))
591 self.info_box.set_player_damage(player_damage)
592 self.set_timer_to_current_time()
593 self.player_damaged(player_damage)
594 if player_damage:
595 self.player.damaged = True
596 self.player.enter_knock_back_state()
597
598 def enter_enemy_damaged_state(self):
599 """
600 Transition battle into the enemy damaged state.
601 """
602 self.state = self.info_box.state = c.ENEMY_DAMAGED
603 enemy_damage = self.player.calculate_hit()
604 self.damage_points.add(
605 attackitems.HealthPoints(enemy_damage,
606 self.player.attacked_enemy.rect.topright))
607
608 self.info_box.set_enemy_damage(enemy_damage)
609
610 self.arrow.index = 0
611 self.attack_enemy(enemy_damage)
612 self.set_timer_to_current_time()
613
614 def switch_enemy(self):
615 """
616 Switch which enemy the player is attacking.
617 """
618 if self.enemy_index < len(self.enemy_list) - 1:
619 self.enemy_index += 1
620 self.enter_enemy_attack_state()
621
622 def enter_run_away_state(self):
623 """
624 Transition battle into the run away state.
625 """
626 self.state = self.info_box.state = c.RUN_AWAY
627 self.arrow.state = 'invisible'
628 self.player.state = c.RUN_AWAY
629 self.set_timer_to_current_time()
630
631 def enter_battle_won_state(self):
632 """
633 Transition battle into the battle won state.
634 """
635 self.state = self.info_box.state = c.BATTLE_WON
636 self.player.state = c.VICTORY_DANCE
637 self.set_timer_to_current_time()
638
639 def enter_show_experience_state(self):
640 """
641 Transition battle into the show experience state.
642 """
643 self.state = self.info_box.state = c.SHOW_EXPERIENCE
644 self.set_timer_to_current_time()
645
646 def enter_level_up_state(self):
647 """
648 Transition battle into the LEVEL UP state.
649 """
650 self.state = self.info_box.state = c.LEVEL_UP
651 self.info_box.reset_level_up_message()
652 self.set_timer_to_current_time()
653
654 def enter_two_actions_per_turn_state(self):
655 self.state = self.info_box.state = c.TWO_ACTIONS
656 self.set_timer_to_current_time()
657
658 def execute_player_actions(self):
659 """
660 Execute the player actions.
661 """
662 if self.player_level < 3:
663 if self.player_actions:
664 enter_state = self.player_action_dict[self.player_actions[0]]
665 enter_state()
666 self.player_actions.pop(0)
667 else:
668 if len(self.player_actions) == 2:
669 enter_state = self.player_action_dict[self.player_actions[0]]
670 enter_state()
671 self.player_actions.pop(0)
672 self.action_selected = False
673 else:
674 if self.action_selected:
675 self.enter_select_action_state()
676 self.action_selected = False
677
678
679
680
681
682
683
684
685