all repos — Legends-RPG @ 200f77f62ddf5be44f661d74f36faeb4daa5ad8f

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