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