all repos — Legends-RPG @ 8c36aad2af8146cf99a5d9f8a4b1c3ffce431210

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