all repos — Legends-RPG @ 94b508ab3500a3f7d2ae7f0defc6434630b1624a

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                    elif self.info_box.item_text_list[self.arrow.index][:14] == 'Healing Potion':
159                        self.state = c.DRINK_HEALING_POTION
160                        self.notify(self.state)
161
162            self.allow_input = False
163
164        if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
165            self.allow_input = True
166
167    def check_timed_events(self):
168        """
169        Check if amount of time has passed for timed events.
170        """
171        timed_states = [c.DISPLAY_ENEMY_ATTACK_DAMAGE,
172                        c.ENEMY_HIT,
173                        c.ENEMY_DEAD,
174                        c.DRINK_HEALING_POTION]
175        long_delay = timed_states[1:]
176
177        if self.state in long_delay:
178            if (self.current_time - self.timer) > 600:
179                if self.state == c.ENEMY_HIT:
180                    self.state = c.ENEMY_DEAD
181                elif self.state == c.ENEMY_DEAD:
182                    if len(self.enemy_list):
183                        self.state = c.ENEMY_ATTACK
184                    else:
185                        self.state = c.BATTLE_WON
186                elif self.state == c.DRINK_HEALING_POTION:
187                    self.state = c.ENEMY_ATTACK
188                self.timer = self.current_time
189                self.notify(self.state)
190
191        elif self.state == c.DISPLAY_ENEMY_ATTACK_DAMAGE:
192            if (self.current_time - self.timer) > 600:
193                if self.enemy_index == (len(self.enemy_list) - 1):
194                    self.state = c.SELECT_ACTION
195                else:
196                    self.state = c.SWITCH_ENEMY
197                self.timer = self.current_time
198                self.notify(self.state)
199
200    def check_if_battle_won(self):
201        """
202        Check if state is SELECT_ACTION and there are no enemies left.
203        """
204        if self.state == c.SELECT_ACTION:
205            if len(self.enemy_group) == 0:
206                self.notify(c.BATTLE_WON)
207
208    def notify(self, event):
209        """
210        Notify observer of event.
211        """
212        for new_observer in self.observers:
213            new_observer.on_notify(event)
214
215    def end_battle(self):
216        """
217        End battle and flip back to previous state.
218        """
219        self.game_data['last state'] = self.name
220        self.done = True
221
222    def attack_enemy(self, enemy_damage):
223        enemy = self.player.attacked_enemy
224        enemy.health -= enemy_damage
225        self.set_enemy_indices()
226
227        if enemy:
228            if enemy.health <= 0:
229                enemy.kill()
230                self.enemy_list.pop(enemy.index)
231                self.arrow.remove_pos(self.player.attacked_enemy)
232                posx = enemy.rect.x - 32
233                posy = enemy.rect.y - 64
234                fire_sprite = attack.Fire(posx, posy)
235                self.attack_animations.add(fire_sprite)
236            self.enemy_index = 0
237            self.player.attacked_enemy = None
238
239    def set_enemy_indices(self):
240        for i, enemy in enumerate(self.enemy_list):
241            enemy.index = i
242
243    def draw_battle(self, surface):
244        """Draw all elements of battle state"""
245        self.background.draw(surface)
246        self.enemy_group.draw(surface)
247        self.attack_animations.draw(surface)
248        self.sword.draw(surface)
249        surface.blit(self.player.image, self.player.rect)
250        surface.blit(self.info_box.image, self.info_box.rect)
251        surface.blit(self.select_box.image, self.select_box.rect)
252        surface.blit(self.arrow.image, self.arrow.rect)
253        self.player_health_box.draw(surface)
254        self.damage_points.draw(surface)
255
256
257    def player_damaged(self, damage):
258        self.game_data['player stats']['health']['current'] -= damage
259
260    def player_healed(self, heal):
261        health = self.game_data['player stats']['health']
262
263        health['current'] += heal
264        if health['current'] > health['maximum']:
265            health['current'] = health['maximum']
266        self.game_data['player inventory']['Healing Potion']['quantity'] -= 1
267        if self.game_data['player inventory']['Healing Potion']['quantity'] == 0:
268            del self.game_data['player inventory']['Healing Potion']
269
270    def set_timer_to_current_time(self):
271        """Set the timer to the current time."""
272        self.timer = self.current_time
273
274