all repos — Legends-RPG @ a03054accb71fa89eadb61df1441c401d572085e

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, copy
  4import pygame as pg
  5from .. import tools, battlegui, observer
  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.game_data = {}
 14        self.current_time = 0.0
 15        self.timer = 0.0
 16        self.allow_input = False
 17        self.allow_info_box_change = False
 18        self.name = 'battle'
 19        self.state = None
 20
 21        self.player = None
 22        self.attack_animations = None
 23        self.sword = None
 24        self.enemy_index = 0
 25        self.attacked_enemy = None
 26        self.attacking_enemy = None
 27        self.enemy_group = None
 28        self.enemy_pos_list = []
 29        self.enemy_list = []
 30
 31        self.background = None
 32        self.info_box = None
 33        self.arrow = None
 34        self.select_box = None
 35        self.player_health_box = None
 36        self.select_action_state_dict = {}
 37        self.next = None
 38        self.observers = []
 39        self.damage_points = pg.sprite.Group()
 40
 41    def startup(self, current_time, game_data):
 42        """Initialize state attributes"""
 43        self.current_time = current_time
 44        self.timer = current_time
 45        self.game_data = game_data
 46        self.state = c.SELECT_ACTION
 47        self.next = game_data['last state']
 48
 49        self.player = self.make_player()
 50        self.attack_animations = pg.sprite.Group()
 51        self.sword = attackitems.Sword(self.player)
 52        self.enemy_group, self.enemy_pos_list, self.enemy_list = self.make_enemies()
 53        self.background = self.make_background()
 54        self.info_box = battlegui.InfoBox(game_data)
 55        self.arrow = battlegui.SelectArrow(self.enemy_pos_list,
 56                                           self.info_box)
 57        self.select_box = battlegui.SelectBox()
 58        self.player_health_box = battlegui.PlayerHealth(self.select_box.rect,
 59                                                        self.game_data)
 60
 61        self.select_action_state_dict = self.make_selection_state_dict()
 62        self.observers = [observer.Battle(self)]
 63        self.player.observers.extend(self.observers)
 64
 65    @staticmethod
 66    def make_background():
 67        """Make the blue/black background"""
 68        background = pg.sprite.Sprite()
 69        surface = pg.Surface(c.SCREEN_SIZE).convert()
 70        surface.fill(c.BLACK_BLUE)
 71        background.image = surface
 72        background.rect = background.image.get_rect()
 73        background_group = pg.sprite.Group(background)
 74
 75        return background_group
 76
 77    @staticmethod
 78    def make_enemies():
 79        """Make the enemies for the battle. Return sprite group"""
 80        pos_list = []
 81
 82        for column in range(3):
 83            for row in range(3):
 84                x = (column * 100) + 100
 85                y = (row * 100) + 100
 86                pos_list.append([x, y])
 87
 88        enemy_group = pg.sprite.Group()
 89
 90        for enemy in range(random.randint(1, 6)):
 91            enemy_group.add(person.Person('devil', 0, 0,
 92                                          'down', 'battle resting'))
 93
 94        for i, enemy in enumerate(enemy_group):
 95            enemy.rect.topleft = pos_list[i]
 96            enemy.image = pg.transform.scale2x(enemy.image)
 97            enemy.index = i
 98            enemy.level = 1
 99            enemy.health = enemy.level * 7
100
101        enemy_list = [enemy for enemy in enemy_group]
102
103        return enemy_group, pos_list[0:len(enemy_group)], enemy_list
104
105    @staticmethod
106    def make_player():
107        """Make the sprite for the player's character"""
108        player = person.Player('left', 630, 220, 'battle resting', 1)
109        player.image = pg.transform.scale2x(player.image)
110        return player
111
112    def make_selection_state_dict(self):
113        """
114        Make a dictionary of states with arrow coordinates as keys.
115        """
116        pos_list = self.arrow.make_select_action_pos_list()
117        state_list = [c.SELECT_ENEMY, c.SELECT_ITEM, c.SELECT_MAGIC, c.RUN_AWAY]
118        return dict(zip(pos_list, state_list))
119
120    def update(self, surface, keys, current_time):
121        """Update the battle state"""
122        self.current_time = current_time
123        self.check_input(keys)
124        self.check_timed_events()
125        self.check_if_battle_won()
126        self.enemy_group.update(current_time)
127        self.player.update(keys, current_time)
128        self.attack_animations.update()
129        self.info_box.update()
130        self.arrow.update(keys)
131        self.sword.update(current_time)
132        self.damage_points.update()
133
134        self.draw_battle(surface)
135
136    def check_input(self, keys):
137        """
138        Check user input to navigate GUI.
139        """
140        if self.allow_input:
141            if keys[pg.K_RETURN]:
142                self.notify(c.END_BATTLE)
143
144            elif keys[pg.K_SPACE]:
145                if self.state == c.SELECT_ACTION:
146                    self.state = self.select_action_state_dict[
147                        self.arrow.rect.topleft]
148                    self.notify(self.state)
149
150                elif self.state == c.SELECT_ENEMY:
151                    self.state = c.PLAYER_ATTACK
152                    self.notify(self.state)
153
154                elif self.state == c.SELECT_ITEM or self.state == c.SELECT_MAGIC:
155                    if self.arrow.index == (len(self.arrow.pos_list) - 1):
156                        self.state = c.SELECT_ACTION
157                        self.notify(self.state)
158
159            self.allow_input = False
160
161        if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
162            self.allow_input = True
163
164    def check_timed_events(self):
165        """
166        Check if amount of time has passed for timed events.
167        """
168        timed_states = [c.DISPLAY_ENEMY_ATTACK_DAMAGE,
169                        c.ENEMY_HIT,
170                        c.ENEMY_DEAD]
171        long_delay = timed_states[1:]
172
173        if self.state in long_delay:
174            if (self.current_time - self.timer) > 600:
175                if self.state == c.ENEMY_HIT:
176                    self.state = c.ENEMY_DEAD
177                elif self.state == c.ENEMY_DEAD:
178                    if len(self.enemy_list):
179                        self.state = c.ENEMY_ATTACK
180                    else:
181                        self.state = c.BATTLE_WON
182                self.timer = self.current_time
183                self.notify(self.state)
184
185        elif self.state == c.DISPLAY_ENEMY_ATTACK_DAMAGE:
186            if (self.current_time - self.timer) > 600:
187                if self.enemy_index == (len(self.enemy_list) - 1):
188                    self.state = c.SELECT_ACTION
189                else:
190                    self.state = c.SWITCH_ENEMY
191                self.timer = self.current_time
192                self.notify(self.state)
193
194    def check_if_battle_won(self):
195        """
196        Check if state is SELECT_ACTION and there are no enemies left.
197        """
198        if self.state == c.SELECT_ACTION:
199            if len(self.enemy_group) == 0:
200                self.notify(c.BATTLE_WON)
201
202    def notify(self, event):
203        """
204        Notify observer of event.
205        """
206        for new_observer in self.observers:
207            new_observer.on_notify(event)
208
209    def end_battle(self):
210        """
211        End battle and flip back to previous state.
212        """
213        self.game_data['last state'] = self.name
214        self.done = True
215
216    def attack_enemy(self, enemy_damage):
217        enemy = self.player.attacked_enemy
218        enemy.health -= enemy_damage
219        self.set_enemy_indices()
220
221        if enemy:
222            if enemy.health <= 0:
223                enemy.kill()
224                self.enemy_list.pop(enemy.index)
225                self.arrow.remove_pos(self.player.attacked_enemy)
226                posx = enemy.rect.x - 32
227                posy = enemy.rect.y - 64
228                fire_sprite = attack.Fire(posx, posy)
229                self.attack_animations.add(fire_sprite)
230            self.enemy_index = 0
231            self.player.attacked_enemy = None
232
233    def set_enemy_indices(self):
234        for i, enemy in enumerate(self.enemy_list):
235            enemy.index = i
236
237    def draw_battle(self, surface):
238        """Draw all elements of battle state"""
239        self.background.draw(surface)
240        self.enemy_group.draw(surface)
241        self.attack_animations.draw(surface)
242        self.sword.draw(surface)
243        surface.blit(self.player.image, self.player.rect)
244        surface.blit(self.info_box.image, self.info_box.rect)
245        surface.blit(self.select_box.image, self.select_box.rect)
246        surface.blit(self.arrow.image, self.arrow.rect)
247        self.player_health_box.draw(surface)
248        self.damage_points.draw(surface)
249
250
251    def player_damaged(self, damage):
252        self.game_data['player stats']['health']['current'] -= damage
253
254    def set_timer_to_current_time(self):
255        """Set the timer to the current time."""
256        self.timer = self.current_time
257
258