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