all repos — Legends-RPG @ 78f7dd5b0cc124cef7ca6aecee53f7cd7caf2486

A fantasy mini-RPG built with Python and Pygame.

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