all repos — Legends-RPG @ 8493ba3ac66e56efea1ab86e9a28500b1ac0e1c7

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"""
  3
  4import random
  5import pygame as pg
  6from .. import tools, battlegui, observer
  7from .. components import person, attack, attackitems
  8from .. import constants as c
  9
 10
 11class Battle(tools._State):
 12    def __init__(self):
 13        super(Battle, self).__init__()
 14        self.game_data = {}
 15        self.current_time = 0.0
 16        self.timer = 0.0
 17        self.allow_input = False
 18        self.allow_info_box_change = False
 19        self.name = 'battle'
 20        self.state = None
 21
 22        self.player = None
 23        self.attack_animations = None
 24        self.sword = None
 25        self.enemy_index = 0
 26        self.attacked_enemy = None
 27        self.attacking_enemy = None
 28        self.enemy_group = None
 29        self.enemy_pos_list = []
 30        self.enemy_list = []
 31
 32        self.background = None
 33        self.info_box = None
 34        self.arrow = None
 35        self.select_box = None
 36        self.player_health_box = None
 37        self.select_action_state_dict = {}
 38        self.next = None
 39        self.observers = []
 40        self.damage_points = pg.sprite.Group()
 41
 42    def startup(self, current_time, game_data):
 43        """Initialize state attributes"""
 44        self.current_time = current_time
 45        self.timer = current_time
 46        self.game_data = game_data
 47        self.state = c.SELECT_ACTION
 48        self.next = game_data['last state']
 49
 50        self.player = self.make_player()
 51        self.attack_animations = pg.sprite.Group()
 52        self.sword = attackitems.Sword(self.player)
 53        self.enemy_group, self.enemy_pos_list, self.enemy_list = self.make_enemies()
 54        self.background = self.make_background()
 55        self.info_box = battlegui.InfoBox(game_data)
 56        self.arrow = battlegui.SelectArrow(self.enemy_pos_list,
 57                                           self.info_box)
 58        self.select_box = battlegui.SelectBox()
 59        self.player_health_box = battlegui.PlayerHealth(self.select_box.rect,
 60                                                        self.game_data)
 61
 62        self.select_action_state_dict = self.make_selection_state_dict()
 63        self.observers = [observer.Battle(self)]
 64        self.player.observers.extend(self.observers)
 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
112        return player
113
114    def make_selection_state_dict(self):
115        """
116        Make a dictionary of states with arrow coordinates as keys.
117        """
118        pos_list = self.arrow.make_select_action_pos_list()
119        state_list = [c.SELECT_ENEMY, c.SELECT_ITEM, c.SELECT_MAGIC, c.RUN_AWAY]
120        return dict(zip(pos_list, state_list))
121
122    def update(self, surface, keys, current_time):
123        """Update the battle state"""
124        self.current_time = current_time
125        self.check_input(keys)
126        self.check_timed_events()
127        self.check_if_battle_won()
128        self.enemy_group.update(current_time)
129        self.player.update(keys, current_time)
130        self.attack_animations.update()
131        self.info_box.update()
132        self.arrow.update(keys)
133        self.sword.update(current_time)
134        self.damage_points.update()
135
136        self.draw_battle(surface)
137
138    def check_input(self, keys):
139        """
140        Check user input to navigate GUI.
141        """
142        if self.allow_input:
143            if keys[pg.K_RETURN]:
144                self.notify(c.END_BATTLE)
145
146            elif keys[pg.K_SPACE]:
147                if self.state == c.SELECT_ACTION:
148                    self.state = self.select_action_state_dict[
149                        self.arrow.rect.topleft]
150                    self.notify(self.state)
151
152                elif self.state == c.SELECT_ENEMY:
153                    self.state = c.PLAYER_ATTACK
154                    self.notify(self.state)
155
156                elif self.state == c.SELECT_ITEM or self.state == c.SELECT_MAGIC:
157                    if self.arrow.index == (len(self.arrow.pos_list) - 1):
158                        self.state = c.SELECT_ACTION
159                        self.notify(self.state)
160
161            self.allow_input = False
162
163        if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
164            self.allow_input = True
165
166    def check_timed_events(self):
167        """
168        Check if amount of time has passed for timed events.
169        """
170        timed_states = [c.DISPLAY_ENEMY_ATTACK_DAMAGE,
171                        c.ENEMY_HIT,
172                        c.ENEMY_DEAD]
173        long_delay = timed_states[1:]
174
175        if self.state in long_delay:
176            if (self.current_time - self.timer) > 600:
177                if self.state == c.ENEMY_HIT:
178                    self.state = c.ENEMY_DEAD
179                elif self.state == c.ENEMY_DEAD:
180                    if len(self.enemy_list):
181                        self.state = c.ENEMY_ATTACK
182                    else:
183                        self.state = c.BATTLE_WON
184                self.timer = self.current_time
185                self.notify(self.state)
186
187        elif self.state == c.DISPLAY_ENEMY_ATTACK_DAMAGE:
188            if (self.current_time - self.timer) > 600:
189                if self.enemy_index == (len(self.enemy_list) - 1):
190                    self.state = c.SELECT_ACTION
191                else:
192                    self.state = c.SWITCH_ENEMY
193                self.timer = self.current_time
194                self.notify(self.state)
195
196
197
198
199
200
201
202    def check_if_battle_won(self):
203        """
204        Check if state is SELECT_ACTION and there are no enemies left.
205        """
206        if self.state == c.SELECT_ACTION:
207            if len(self.enemy_group) == 0:
208                self.notify(c.BATTLE_WON)
209
210    def notify(self, event):
211        """
212        Notify observer of event.
213        """
214        for new_observer in self.observers:
215            new_observer.on_notify(event)
216
217    def end_battle(self):
218        """
219        End battle and flip back to previous state.
220        """
221        self.game_data['last state'] = self.name
222        self.done = True
223
224    def attack_enemy(self, enemy_damage):
225        enemy = self.player.attacked_enemy
226        enemy.health -= enemy_damage
227        self.set_enemy_indices()
228
229        if enemy:
230            if enemy.health <= 0:
231                enemy.kill()
232                self.enemy_list.pop(enemy.index)
233                self.arrow.remove_pos(self.player.attacked_enemy)
234                posx = enemy.rect.x - 32
235                posy = enemy.rect.y - 64
236                fire_sprite = attack.Fire(posx, posy)
237                self.attack_animations.add(fire_sprite)
238            self.enemy_index = 0
239            self.player.attacked_enemy = None
240
241    def set_enemy_indices(self):
242        for i, enemy in enumerate(self.enemy_list):
243            enemy.index = i
244
245    def draw_battle(self, surface):
246        """Draw all elements of battle state"""
247        self.background.draw(surface)
248        self.enemy_group.draw(surface)
249        self.attack_animations.draw(surface)
250        self.sword.draw(surface)
251        surface.blit(self.player.image, self.player.rect)
252        surface.blit(self.info_box.image, self.info_box.rect)
253        surface.blit(self.select_box.image, self.select_box.rect)
254        surface.blit(self.arrow.image, self.arrow.rect)
255        self.player_health_box.draw(surface)
256        self.damage_points.draw(surface)
257
258
259    def player_damaged(self, damage):
260        self.game_data['player stats']['health']['current'] -= damage
261
262    def set_timer_to_current_time(self):
263        """Set the timer to the current time."""
264        self.timer = self.current_time
265
266