all repos — Legends-RPG @ d42cfd5a960d68a90490ac950b309d27b477ce30

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(game_data)
 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.arrow.update(keys)
100
101        self.draw_battle(surface)
102
103    def check_input(self, keys):
104        """
105        Check user input to navigate GUI.
106        """
107        if keys[pg.K_RETURN]:
108            self.notify(END_BATTLE)
109
110        elif keys[pg.K_SPACE] and self.state == SELECT_ACTION:
111            self.state = self.select_action_state_dict[self.arrow.rect.topleft]
112            self.notify(self.state)
113
114    def notify(self, event):
115        """
116        Notify observer of event.
117        """
118        self.observer.on_notify(event)
119
120    def end_battle(self):
121        """
122        End battle and flip back to previous state.
123        """
124        self.game_data['last state'] = self.name
125        self.done = True
126
127    def draw_battle(self, surface):
128        """Draw all elements of battle state"""
129        self.background.draw(surface)
130        self.enemy_group.draw(surface)
131        self.player_group.draw(surface)
132        surface.blit(self.info_box.image, self.info_box.rect)
133        surface.blit(self.select_box.image, self.select_box.rect)
134        surface.blit(self.arrow.image, self.arrow.rect)
135
136
137class Observer(object):
138    """
139    Observes events of battle and passes info to components.
140    """
141    def __init__(self, level):
142        self.level = level
143        self.info_box = level.info_box
144        self.select_box = level.info_box
145        self.arrow = level.arrow
146        self.player = level.player_group
147        self.enemies = level.enemy_group
148        self.event_dict = self.make_event_dict()
149
150    def make_event_dict(self):
151        """
152        Make a dictionary of events the Observer can
153        receive.
154        """
155        event_dict = {END_BATTLE: self.end_battle,
156                      SELECT_ACTION: self.select_action,
157                      SELECT_ITEM: self.select_item,
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 select_item(self):
190        self.level.state = SELECT_ITEM
191        self.info_box.image = self.info_box.make_image(SELECT_ITEM)
192
193    def enemy_attack(self):
194        pass
195
196    def player_attack(self):
197        pass
198
199    def run_away(self):
200        pass
201
202
203class InfoBox(object):
204    """
205    Info box that describes attack damage and other battle
206    related information.
207    """
208    def __init__(self, game_data):
209        self.game_data = game_data
210        self.state = SELECT_ACTION
211        self.title_font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
212        self.title_font.set_underline(True)
213        self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 18)
214        self.message_dict = self.make_message_dict()
215        self.image = self.make_image(SELECT_ACTION)
216        self.rect = self.image.get_rect(bottom=608)
217
218    def make_message_dict(self):
219        """
220        Make dictionary of states Battle info can be in.
221        """
222        message_dict = {SELECT_ACTION: 'Select an action.',
223                        SELECT_MAGIC: 'Select a magic spell.',
224                        SELECT_ITEM: 'Select an item.',
225                        SELECT_ENEMY: 'Select an enemy.',
226                        ENEMY_ATTACK: 'The enemy attacks player!',
227                        PLAYER_ATTACK: 'Player attacks enemy.',
228                        RUN_AWAY: 'Run away'}
229
230        return message_dict
231
232    def make_item_text(self):
233        """
234        Make the text for when the player selects items.
235        """
236        inventory = self.game_data['player inventory']
237        allowed_item_list = ['Healing Potion']
238        title = 'SELECT ITEM'
239        item_text_list = [title]
240
241        for item in inventory:
242            if item in allowed_item_list:
243                text = item + ": " + str(inventory[item]['quantity'])
244                item_text_list.append(text)
245
246        item_text_list.append('BACK')
247
248        return item_text_list
249
250    def make_text_sprites(self, text_list):
251        """
252        Make sprites out of text.
253        """
254        sprite_group = pg.sprite.Group()
255
256        for i, text in enumerate(text_list):
257            sprite = pg.sprite.Sprite()
258
259            if i == 0:
260                x = 195
261                y = 10
262                surface = self.title_font.render(text, True, c.NEAR_BLACK)
263                rect = surface.get_rect(x=x, y=y)
264            else:
265                x = 100
266                y = (i * 30) + 20
267                surface = self.font.render(text, True, c.NEAR_BLACK)
268                rect = surface.get_rect(x=x, y=y)
269            sprite.image = surface
270            sprite.rect = rect
271            sprite_group.add(sprite)
272
273        return sprite_group
274
275    def make_image(self, state):
276        """
277        Make image out of box and message.
278        """
279        image = setup.GFX['shopbox']
280        rect = image.get_rect(bottom=608)
281        surface = pg.Surface(rect.size)
282        surface.set_colorkey(c.BLACK)
283        surface.blit(image, (0, 0))
284
285        if state == SELECT_ITEM:
286            text_sprites = self.make_text_sprites(self.make_item_text())
287            text_sprites.draw(surface)
288        else:
289            text_surface = self.font.render(self.message_dict[state], True, c.NEAR_BLACK)
290            text_rect = text_surface.get_rect(x=50, y=50)
291            surface.blit(text_surface, text_rect)
292
293        return surface
294
295
296class SelectBox(object):
297    """
298    Box to select whether to attack, use item, use magic or run away.
299    """
300    def __init__(self):
301        self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22)
302        self.slots = self.make_slots()
303        self.image = self.make_image()
304        self.rect = self.image.get_rect(bottom=608,
305                                        right=800)
306
307    def make_image(self):
308        """
309        Make the box image for
310        """
311        image = setup.GFX['goldbox']
312        rect = image.get_rect(bottom=608)
313        surface = pg.Surface(rect.size)
314        surface.set_colorkey(c.BLACK)
315        surface.blit(image, (0, 0))
316
317        for text in self.slots:
318            text_surface = self.font.render(text, True, c.NEAR_BLACK)
319            text_rect = text_surface.get_rect(x=self.slots[text]['x'],
320                                              y=self.slots[text]['y'])
321            surface.blit(text_surface, text_rect)
322
323        return surface
324
325    def make_slots(self):
326        """
327        Make the slots that hold the text selections, and locations.
328        """
329        slot_dict = {}
330        selections = ['Attack', 'Items', 'Magic', 'Run']
331
332        for i, text in enumerate(selections):
333            slot_dict[text] = {'x': 150,
334                               'y': (i*34)+10}
335
336        return slot_dict
337
338
339
340class SelectArrow(object):
341    """Small arrow for menu"""
342    def __init__(self, enemy_pos_list):
343        self.image = setup.GFX['smallarrow']
344        self.rect = self.image.get_rect()
345        self.state = 'select action'
346        self.state_dict = self.make_state_dict()
347        self.pos_list = self.make_select_action_pos_list()
348        self.index = 0
349        self.rect.topleft = self.pos_list[self.index]
350        self.allow_input = False
351        self.enemy_pos_list = enemy_pos_list
352
353    def make_state_dict(self):
354        """Make state dictionary"""
355        state_dict = {'select action': self.select_action,
356                      'select enemy': self.select_enemy}
357
358        return state_dict
359
360    def select_action(self, keys):
361        """
362        Select what action the player should take.
363        """
364        self.pos_list = self.make_select_action_pos_list()
365        self.rect.topleft = self.pos_list[self.index]
366
367        if self.allow_input:
368            if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1):
369                self.index += 1
370                self.allow_input = False
371            elif keys[pg.K_UP] and self.index > 0:
372                self.index -= 1
373                self.allow_input = False
374
375        if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False:
376            self.allow_input = True
377
378
379    def make_select_action_pos_list(self):
380        """
381        Make the list of positions the arrow can be in.
382        """
383        pos_list = []
384
385        for i in range(4):
386            x = 590
387            y = (i * 34) + 472
388            pos_list.append((x, y))
389
390        return pos_list
391
392    def select_enemy(self, keys):
393        """
394        Select what enemy you want to take action on.
395        """
396        self.pos_list = self.enemy_pos_list
397        pos = self.pos_list[self.index]
398        self.rect.x = pos[0] - 60
399        self.rect.y = pos[1] + 20
400
401        if self.allow_input:
402            if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1):
403                self.index += 1
404                self.allow_input = False
405            elif keys[pg.K_UP] and self.index > 0:
406                self.index -= 1
407                self.allow_input = False
408
409
410        if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False \
411                and keys[pg.K_RIGHT] == False and keys[pg.K_LEFT] == False:
412            self.allow_input = True
413
414
415    def enter_select_action(self):
416        """
417        Assign values for the select action state.
418        """
419        pass
420
421    def enter_select_enemy(self):
422        """
423        Assign values for the select enemy state.
424        """
425        pass
426
427    def update(self, keys):
428        """
429        Update arrow position.
430        """
431        state_function = self.state_dict[self.state]
432        state_function(keys)
433
434    def draw(self, surface):
435        """
436        Draw to surface.
437        """
438        surface.blit(self.image, self.rect)
439