all repos — Legends-RPG @ 468b0801c22e6e19227024b6258b56aadd4d3da3

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