all repos — Legends-RPG @ cd7f046fd56e95bda0da95b6a8b9a192e10aa6c0

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        self.damage_points = pg.sprite.Group()
 65
 66    @staticmethod
 67    def make_background():
 68        """Make the blue/black background"""
 69        background = pg.sprite.Sprite()
 70        surface = pg.Surface(c.SCREEN_SIZE).convert()
 71        surface.fill(c.BLACK_BLUE)
 72        background.image = surface
 73        background.rect = background.image.get_rect()
 74        background_group = pg.sprite.Group(background)
 75
 76        return background_group
 77
 78    @staticmethod
 79    def make_enemies():
 80        """Make the enemies for the battle. Return sprite group"""
 81        pos_list = []
 82
 83        for column in range(3):
 84            for row in range(3):
 85                x = (column * 100) + 100
 86                y = (row * 100) + 100
 87                pos_list.append([x, y])
 88
 89        enemy_group = pg.sprite.Group()
 90
 91        for enemy in range(random.randint(1, 6)):
 92            enemy_group.add(person.Person('devil', 0, 0,
 93                                          'down', 'battle resting'))
 94
 95        for i, enemy in enumerate(enemy_group):
 96            enemy.rect.topleft = pos_list[i]
 97            enemy.image = pg.transform.scale2x(enemy.image)
 98            enemy.index = i
 99            enemy.level = 1
100            enemy.health = enemy.level * 7
101
102        enemy_list = [enemy for enemy in enemy_group]
103
104        return enemy_group, pos_list[0:len(enemy_group)], enemy_list
105
106    @staticmethod
107    def make_player():
108        """Make the sprite for the player's character"""
109        player = person.Player('left', 630, 220, 'battle resting', 1)
110        player.image = pg.transform.scale2x(player.image)
111        return player
112
113    def make_selection_state_dict(self):
114        """
115        Make a dictionary of states with arrow coordinates as keys.
116        """
117        pos_list = self.arrow.make_select_action_pos_list()
118        state_list = [c.SELECT_ENEMY, c.SELECT_ITEM, c.SELECT_MAGIC, c.RUN_AWAY]
119        return dict(zip(pos_list, state_list))
120
121    def update(self, surface, keys, current_time):
122        """Update the battle state"""
123        self.current_time = current_time
124        self.check_input(keys)
125        self.check_timed_events()
126        self.check_if_battle_won()
127        self.enemy_group.update(current_time)
128        self.player.update(keys, current_time)
129        self.attack_animations.update()
130        self.info_box.update()
131        self.arrow.update(keys)
132        self.sword.update(current_time)
133        self.damage_points.update()
134
135        self.draw_battle(surface)
136
137    def check_input(self, keys):
138        """
139        Check user input to navigate GUI.
140        """
141        if self.allow_input:
142            if keys[pg.K_RETURN]:
143                self.notify(c.END_BATTLE)
144
145            elif keys[pg.K_SPACE]:
146                if self.state == c.SELECT_ACTION:
147                    self.state = self.select_action_state_dict[
148                        self.arrow.rect.topleft]
149                    self.notify(self.state)
150
151                elif self.state == c.SELECT_ENEMY:
152                    self.state = c.PLAYER_ATTACK
153                    self.notify(self.state)
154
155                elif self.state == c.SELECT_ITEM:
156                    if self.arrow.index == (len(self.arrow.pos_list) - 1):
157                        self.state = c.SELECT_ACTION
158                        self.notify(self.state)
159                    elif self.info_box.item_text_list[self.arrow.index][:14] == 'Healing Potion':
160                        self.state = c.DRINK_HEALING_POTION
161                        self.notify(self.state)
162
163                elif self.state == c.SELECT_MAGIC:
164                    if self.arrow.index == (len(self.arrow.pos_list) - 1):
165                        self.state = c.SELECT_ACTION
166                        self.notify(self.state)
167                    elif self.info_box.magic_text_list[self.arrow.index] == 'Cure':
168                        if self.game_data['player stats']['magic points']['current'] >= 25:
169                            self.state = c.CURE_SPELL
170                            self.notify(self.state)
171                    elif self.info_box.magic_text_list[self.arrow.index] == 'Fire Blast':
172                        if self.game_data['player stats']['magic points']['current'] >= 75:
173                            self.state = c.FIRE_SPELL
174                            self.notify(self.state)
175
176            self.allow_input = False
177
178        if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
179            self.allow_input = True
180
181    def check_timed_events(self):
182        """
183        Check if amount of time has passed for timed events.
184        """
185        timed_states = [c.DISPLAY_ENEMY_ATTACK_DAMAGE,
186                        c.ENEMY_HIT,
187                        c.ENEMY_DEAD,
188                        c.DRINK_HEALING_POTION,
189                        c.CURE_SPELL,
190                        c.FIRE_SPELL]
191        long_delay = timed_states[1:]
192
193        if self.state in long_delay:
194            if (self.current_time - self.timer) > 800:
195                if self.state == c.ENEMY_HIT:
196                    self.state = c.ENEMY_DEAD
197                elif self.state == c.ENEMY_DEAD:
198                    if len(self.enemy_list):
199                        self.state = c.ENEMY_ATTACK
200                    else:
201                        self.state = c.BATTLE_WON
202                elif (self.state == c.DRINK_HEALING_POTION or
203                      self.state == c.CURE_SPELL or
204                      self.state == c.FIRE_SPELL):
205                    if len(self.enemy_list):
206                        self.state = c.ENEMY_ATTACK
207                    else:
208                        self.state = c.BATTLE_WON
209                self.timer = self.current_time
210                self.notify(self.state)
211
212        elif self.state == c.DISPLAY_ENEMY_ATTACK_DAMAGE:
213            if (self.current_time - self.timer) > 600:
214                if self.enemy_index == (len(self.enemy_list) - 1):
215                    self.state = c.SELECT_ACTION
216                else:
217                    self.state = c.SWITCH_ENEMY
218                self.timer = self.current_time
219                self.notify(self.state)
220
221    def check_if_battle_won(self):
222        """
223        Check if state is SELECT_ACTION and there are no enemies left.
224        """
225        if self.state == c.SELECT_ACTION:
226            if len(self.enemy_group) == 0:
227                self.notify(c.BATTLE_WON)
228
229    def notify(self, event):
230        """
231        Notify observer of event.
232        """
233        for new_observer in self.observers:
234            new_observer.on_notify(event)
235
236    def end_battle(self):
237        """
238        End battle and flip back to previous state.
239        """
240        self.game_data['last state'] = self.name
241        self.done = True
242
243    def attack_enemy(self, enemy_damage):
244        enemy = self.player.attacked_enemy
245        enemy.health -= enemy_damage
246        self.set_enemy_indices()
247
248        if enemy:
249            if enemy.health <= 0:
250                enemy.kill()
251                self.enemy_list.pop(enemy.index)
252                self.arrow.remove_pos(self.player.attacked_enemy)
253                posx = enemy.rect.x - 32
254                posy = enemy.rect.y - 64
255                fire_sprite = attack.Fire(posx, posy)
256                self.attack_animations.add(fire_sprite)
257            self.enemy_index = 0
258            self.player.attacked_enemy = None
259
260    def set_enemy_indices(self):
261        for i, enemy in enumerate(self.enemy_list):
262            enemy.index = i
263
264    def draw_battle(self, surface):
265        """Draw all elements of battle state"""
266        self.background.draw(surface)
267        self.enemy_group.draw(surface)
268        self.attack_animations.draw(surface)
269        self.sword.draw(surface)
270        surface.blit(self.player.image, self.player.rect)
271        surface.blit(self.info_box.image, self.info_box.rect)
272        surface.blit(self.select_box.image, self.select_box.rect)
273        surface.blit(self.arrow.image, self.arrow.rect)
274        self.player_health_box.draw(surface)
275        self.damage_points.draw(surface)
276
277
278    def player_damaged(self, damage):
279        self.game_data['player stats']['health']['current'] -= damage
280
281    def player_healed(self, heal):
282        health = self.game_data['player stats']['health']
283
284        health['current'] += heal
285        if health['current'] > health['maximum']:
286            health['current'] = health['maximum']
287
288        if self.state == c.DRINK_HEALING_POTION:
289            self.game_data['player inventory']['Healing Potion']['quantity'] -= 1
290            if self.game_data['player inventory']['Healing Potion']['quantity'] == 0:
291                del self.game_data['player inventory']['Healing Potion']
292        elif self.state == c.CURE_SPELL:
293            self.game_data['player stats']['magic points']['current'] -= 25
294
295    def set_timer_to_current_time(self):
296        """Set the timer to the current time."""
297        self.timer = self.current_time
298
299    def cast_fire_blast(self):
300        """
301        Cast fire blast on all enemies.
302        """
303        DAMAGE = 5
304        MAGIC_POINTS = 75
305        self.game_data['player stats']['magic points']['current'] -= MAGIC_POINTS
306        for enemy in self.enemy_list:
307            self.damage_points.add(
308                attackitems.HealthPoints(DAMAGE, enemy.rect.topright))
309            enemy.health -= DAMAGE
310            posx = enemy.rect.x - 32
311            posy = enemy.rect.y - 64
312            fire_sprite = attack.Fire(posx, posy)
313            self.attack_animations.add(fire_sprite)
314            if enemy.health <= 0:
315                enemy.kill()
316                self.arrow.remove_pos(enemy)
317        self.enemy_list = [enemy for enemy in self.enemy_list if enemy.health > 0]
318        self.enemy_index = 0
319        self.arrow.index = 0
320        self.arrow.become_invisible_surface()
321        self.arrow.state = c.SELECT_ACTION
322
323
324
325
326