all repos — Legends-RPG @ 6c50662924fa0b406d84e98d5013283c5a4dfb26

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, setup
  7from .. components import person
  8from .. import constants as c
  9
 10#STATES
 11
 12SELECT_ACTION = 'select action'
 13SELECT_ENEMY = 'select enemy'
 14ENEMY_ATTACK = 'enemy attack'
 15PLAYER_ATTACK = 'player attack'
 16SELECT_ITEM = 'select item'
 17SELECT_MAGIC = 'select magic'
 18RUN_AWAY = 'run away'
 19
 20#EVENTS
 21
 22END_BATTLE = 'end battle'
 23
 24
 25class Battle(tools._State):
 26    def __init__(self):
 27        super(Battle, self).__init__()
 28
 29    def startup(self, current_time, game_data):
 30        """Initialize state attributes"""
 31        self.current_time = current_time
 32        self.game_data = game_data
 33        self.background = self.make_background()
 34        self.enemy_group, self.enemy_pos_list = self.make_enemies()
 35        self.player_group = self.make_player()
 36        self.info_box = InfoBox()
 37        self.arrow = SelectArrow(self.enemy_pos_list)
 38        self.select_box = SelectBox()
 39        self.state = SELECT_ACTION
 40        self.select_action_state_dict = self.make_selection_state_dict()
 41        self.name = 'battle'
 42        self.next = game_data['last state']
 43        self.observer = Observer(self)
 44
 45    def make_background(self):
 46        """Make the blue/black background"""
 47        background = pg.sprite.Sprite()
 48        surface = pg.Surface(c.SCREEN_SIZE).convert()
 49        surface.fill(c.BLACK_BLUE)
 50        background.image = surface
 51        background.rect = background.image.get_rect()
 52        background_group = pg.sprite.Group(background)
 53
 54        return background_group
 55
 56    def make_enemies(self):
 57        """Make the enemies for the battle. Return sprite group"""
 58        pos_list  = []
 59
 60        for column in range(3):
 61            for row in range(3):
 62                x = (column * 100) + 100
 63                y = (row * 100) + 100
 64                pos_list.append([x,y])
 65
 66        enemy_group = pg.sprite.Group()
 67
 68        for enemy in range(random.randint(1, 6)):
 69            enemy_group.add(person.Person('devil', 0, 0,
 70                                          'down', 'battle resting'))
 71
 72        for i, enemy in enumerate(enemy_group):
 73            enemy.rect.topleft = pos_list[i]
 74            enemy.image = pg.transform.scale2x(enemy.image)
 75
 76        return enemy_group, pos_list[0:len(enemy_group)]
 77
 78    def make_player(self):
 79        """Make the sprite for the player's character"""
 80        player = person.Player('left', 630, 220, 'battle resting', 1)
 81        player.image = pg.transform.scale2x(player.image)
 82        player_group = pg.sprite.Group(player)
 83
 84        return player_group
 85
 86    def make_selection_state_dict(self):
 87        """
 88        Make a dictionary of states with arrow coordinates as keys.
 89        """
 90        pos_list = self.arrow.make_select_action_pos_list()
 91        state_list = [SELECT_ENEMY, SELECT_ITEM, SELECT_MAGIC, RUN_AWAY]
 92        return dict(zip(pos_list, state_list))
 93
 94    def update(self, surface, keys, current_time):
 95        """Update the battle state"""
 96        self.check_input(keys)
 97        self.enemy_group.update(current_time)
 98        self.player_group.update(keys, current_time)
 99        self.select_box.update()
100        self.arrow.update(keys)
101        self.info_box.update(self.state)
102        self.draw_battle(surface)
103
104    def check_input(self, keys):
105        """
106        Check user input to navigate GUI.
107        """
108        if keys[pg.K_RETURN]:
109            self.notify(END_BATTLE)
110
111        elif keys[pg.K_SPACE] and self.state == SELECT_ACTION:
112            self.state = self.select_action_state_dict[self.arrow.rect.topleft]
113            self.notify(self.state)
114
115    def notify(self, event):
116        """
117        Notify observer of event.
118        """
119        self.observer.on_notify(event)
120
121    def end_battle(self):
122        """
123        End battle and flip back to previous state.
124        """
125        self.game_data['last state'] = self.name
126        self.done = True
127
128    def draw_battle(self, surface):
129        """Draw all elements of battle state"""
130        self.background.draw(surface)
131        self.enemy_group.draw(surface)
132        self.player_group.draw(surface)
133        surface.blit(self.info_box.image, self.info_box.rect)
134        surface.blit(self.select_box.image, self.select_box.rect)
135        surface.blit(self.arrow.image, self.arrow.rect)
136
137
138class Observer(object):
139    """
140    Observes events of battle and passes info to components.
141    """
142    def __init__(self, level):
143        self.level = level
144        self.info_box = level.info_box
145        self.select_box = level.info_box
146        self.arrow = level.arrow
147        self.player = level.player_group
148        self.enemies = level.enemy_group
149        self.event_dict = self.make_event_dict()
150
151    def make_event_dict(self):
152        """
153        Make a dictionary of events the Observer can
154        receive.
155        """
156        event_dict = {END_BATTLE: self.end_battle,
157                      SELECT_ACTION: self.select_action,
158                      SELECT_ENEMY: self.select_enemy,
159                      ENEMY_ATTACK: self.enemy_attack,
160                      PLAYER_ATTACK: self.player_attack,
161                      RUN_AWAY: self.run_away}
162
163        return event_dict
164
165    def on_notify(self, event):
166        """
167        Notify Observer of event.
168        """
169        if event in self.event_dict:
170            self.event_dict[event]()
171
172    def end_battle(self):
173        """
174        End Battle and flip to previous state.
175        """
176        self.level.end_battle()
177
178    def select_action(self):
179        """
180        Set components to select action.
181        """
182        self.level.state = SELECT_ACTION
183        self.arrow.index = 0
184
185    def select_enemy(self):
186        self.level.state = SELECT_ENEMY
187        self.arrow.state = SELECT_ENEMY
188
189    def enemy_attack(self):
190        pass
191
192    def player_attack(self):
193        pass
194
195    def run_away(self):
196        pass
197
198
199class InfoBox(object):
200    """
201    Info box that describes attack damage and other battle
202    related information.
203    """
204    def __init__(self):
205        self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
206        self.message_dict = self.make_message_dict()
207        self.image = self.make_image('select action')
208        self.rect = self.image.get_rect(bottom=608)
209
210    def make_message_dict(self):
211        """
212        Make dictionary of states Battle info can be in.
213        """
214        message_dict = {SELECT_ACTION: 'Select an action.',
215                        SELECT_MAGIC: 'Select a magic spell.',
216                        SELECT_ITEM: 'Select an item.',
217                        SELECT_ENEMY: 'Select an enemy.',
218                        ENEMY_ATTACK: 'The enemy attacks player!',
219                        PLAYER_ATTACK: 'Player attacks enemy.',
220                        RUN_AWAY: 'Run away'}
221
222        return message_dict
223
224    def make_image(self, state):
225        """
226        Make image out of box and message.
227        """
228        image = setup.GFX['shopbox']
229        rect = image.get_rect(bottom=608)
230        surface = pg.Surface(rect.size)
231        surface.set_colorkey(c.BLACK)
232        surface.blit(image, (0, 0))
233
234        text_surface = self.font.render(self.message_dict[state], True, c.NEAR_BLACK)
235        text_rect = text_surface.get_rect(x=50, y=50)
236        surface.blit(text_surface, text_rect)
237
238        return surface
239
240    def update(self, state):
241        self.image = self.make_image(state)
242
243
244class SelectBox(object):
245    """
246    Box to select whether to attack, use item, use magic or run away.
247    """
248    def __init__(self):
249        self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
250        self.slots = self.make_slots()
251        self.image = self.make_image()
252        self.rect = self.image.get_rect(bottom=608,
253                                        right=800)
254
255    def make_image(self):
256        """
257        Make the box image for
258        """
259        image = setup.GFX['goldbox']
260        rect = image.get_rect(bottom=608)
261        surface = pg.Surface(rect.size)
262        surface.set_colorkey(c.BLACK)
263        surface.blit(image, (0, 0))
264
265        for text in self.slots:
266            text_surface = self.font.render(text, True, c.NEAR_BLACK)
267            text_rect = text_surface.get_rect(x=self.slots[text]['x'],
268                                              y=self.slots[text]['y'])
269            surface.blit(text_surface, text_rect)
270
271        return surface
272
273    def make_slots(self):
274        """
275        Make the slots that hold the text selections, and locations.
276        """
277        slot_dict = {}
278        selections = ['Attack', 'Items', 'Magic', 'Run']
279
280        for i, text in enumerate(selections):
281            slot_dict[text] = {'x': 150,
282                               'y': (i*34)+10}
283
284        return slot_dict
285
286    def update(self):
287        """
288        Update components.
289        """
290        self.image = self.make_image()
291
292
293class SelectArrow(object):
294    """Small arrow for menu"""
295    def __init__(self, enemy_pos_list):
296        self.image = setup.GFX['smallarrow']
297        self.rect = self.image.get_rect()
298        self.state = 'select action'
299        self.state_dict = self.make_state_dict()
300        self.pos_list = self.make_select_action_pos_list()
301        self.index = 0
302        self.rect.topleft = self.pos_list[self.index]
303        self.allow_input = False
304        self.enemy_pos_list = enemy_pos_list
305
306    def make_state_dict(self):
307        """Make state dictionary"""
308        state_dict = {'select action': self.select_action,
309                      'select enemy': self.select_enemy}
310
311        return state_dict
312
313    def select_action(self, keys):
314        """
315        Select what action the player should take.
316        """
317        self.pos_list = self.make_select_action_pos_list()
318        self.rect.topleft = self.pos_list[self.index]
319
320        if self.allow_input:
321            if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1):
322                self.index += 1
323                self.allow_input = False
324            elif keys[pg.K_UP] and self.index > 0:
325                self.index -= 1
326                self.allow_input = False
327
328        if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False:
329            self.allow_input = True
330
331
332    def make_select_action_pos_list(self):
333        """
334        Make the list of positions the arrow can be in.
335        """
336        pos_list = []
337
338        for i in range(4):
339            x = 590
340            y = (i * 34) + 472
341            pos_list.append((x, y))
342
343        return pos_list
344
345    def select_enemy(self, keys):
346        """
347        Select what enemy you want to take action on.
348        """
349        self.pos_list = self.enemy_pos_list
350        pos = self.pos_list[self.index]
351        self.rect.x = pos[0] - 60
352        self.rect.y = pos[1] + 20
353
354        if self.allow_input:
355            if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1):
356                self.index += 1
357                self.allow_input = False
358            elif keys[pg.K_UP] and self.index > 0:
359                self.index -= 1
360                self.allow_input = False
361
362
363        if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False \
364                and keys[pg.K_RIGHT] == False and keys[pg.K_LEFT] == False:
365            self.allow_input = True
366
367
368    def enter_select_action(self):
369        """
370        Assign values for the select action state.
371        """
372        pass
373
374    def enter_select_enemy(self):
375        """
376        Assign values for the select enemy state.
377        """
378        pass
379
380    def update(self, keys):
381        """
382        Update arrow position.
383        """
384        state_function = self.state_dict[self.state]
385        state_function(keys)
386
387    def draw(self, surface):
388        """
389        Draw to surface.
390        """
391        surface.blit(self.image, self.rect)
392