all repos — Legends-RPG @ ec8acadd87fd2c29f7c43052dd4f84a643683176

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