Infobox displays damage to enemy and if it's killed.
Justin Armstrong justinmeister@gmail.com
Mon, 21 Apr 2014 15:52:34 -0700
6 files changed,
425 insertions(+),
364 deletions(-)
A
data/battlegui.py
@@ -0,0 +1,264 @@
+""" +GUI components for battle states. +""" +import pygame as pg +from . import setup +from . import constants as c +from .components import textbox + + +class InfoBox(object): + """ + Info box that describes attack damage and other battle + related information. + """ + def __init__(self, game_data): + self.game_data = game_data + self.state = c.SELECT_ACTION + self.title_font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22) + self.title_font.set_underline(True) + self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 18) + self.state_dict = self.make_state_dict() + self.image = self.make_image() + self.rect = self.image.get_rect(bottom=608) + self.arrow = textbox.NextArrow() + self.arrow.rect.topleft = (380, 110) + + + def make_state_dict(self): + """ + Make dictionary of states Battle info can be in. + """ + state_dict = {c.SELECT_ACTION: 'Select an action.', + c.SELECT_MAGIC: 'Select a magic spell.', + c.SELECT_ITEM: 'Select an item.', + c.SELECT_ENEMY: 'Select an enemy.', + c.ENEMY_ATTACK: 'The enemy attacks player!', + c.PLAYER_ATTACK: 'Player attacks enemy.', + c.RUN_AWAY: 'Run away', + c.ENEMY_HIT: 'Enemy hit with 20 damage.', + c.ENEMY_DEAD: 'Enemy killed.'} + + return state_dict + + + def make_item_text(self): + """ + Make the text for when the player selects items. + """ + inventory = self.game_data['player inventory'] + allowed_item_list = ['Healing Potion'] + title = 'SELECT ITEM' + item_text_list = [title] + + for item in inventory: + if item in allowed_item_list: + text = item + ": " + str(inventory[item]['quantity']) + item_text_list.append(text) + + item_text_list.append('BACK') + + return item_text_list + + def make_text_sprites(self, text_list): + """ + Make sprites out of text. + """ + sprite_group = pg.sprite.Group() + + for i, text in enumerate(text_list): + sprite = pg.sprite.Sprite() + + if i == 0: + x = 195 + y = 10 + surface = self.title_font.render(text, True, c.NEAR_BLACK) + rect = surface.get_rect(x=x, y=y) + else: + x = 100 + y = (i * 30) + 20 + surface = self.font.render(text, True, c.NEAR_BLACK) + rect = surface.get_rect(x=x, y=y) + sprite.image = surface + sprite.rect = rect + sprite_group.add(sprite) + + return sprite_group + + def make_image(self): + """ + Make image out of box and message. + """ + image = setup.GFX['shopbox'] + rect = image.get_rect(bottom=608) + surface = pg.Surface(rect.size) + surface.set_colorkey(c.BLACK) + surface.blit(image, (0, 0)) + + if self.state == c.SELECT_ITEM: + text_sprites = self.make_text_sprites(self.make_item_text()) + text_sprites.draw(surface) + else: + text_surface = self.font.render(self.state_dict[self.state], True, c.NEAR_BLACK) + text_rect = text_surface.get_rect(x=50, y=50) + surface.blit(text_surface, text_rect) + if self.state == c.ENEMY_HIT or self.state == c.ENEMY_DEAD: + surface.blit(self.arrow.image, self.arrow.rect) + + return surface + + def update(self): + """Updates info box""" + self.image = self.make_image() + + +class SelectBox(object): + """ + Box to select whether to attack, use item, use magic or run away. + """ + def __init__(self): + self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22) + self.slots = self.make_slots() + self.image = self.make_image() + self.rect = self.image.get_rect(bottom=608, + right=800) + + def make_image(self): + """ + Make the box image for + """ + image = setup.GFX['goldbox'] + rect = image.get_rect(bottom=608) + surface = pg.Surface(rect.size) + surface.set_colorkey(c.BLACK) + surface.blit(image, (0, 0)) + + for text in self.slots: + text_surface = self.font.render(text, True, c.NEAR_BLACK) + text_rect = text_surface.get_rect(x=self.slots[text]['x'], + y=self.slots[text]['y']) + surface.blit(text_surface, text_rect) + + return surface + + def make_slots(self): + """ + Make the slots that hold the text selections, and locations. + """ + slot_dict = {} + selections = ['Attack', 'Items', 'Magic', 'Run'] + + for i, text in enumerate(selections): + slot_dict[text] = {'x': 150, + 'y': (i*34)+10} + + return slot_dict + + +class SelectArrow(object): + """Small arrow for menu""" + def __init__(self, enemy_pos_list): + self.image = setup.GFX['smallarrow'] + self.rect = self.image.get_rect() + self.state = 'select action' + self.state_dict = self.make_state_dict() + self.pos_list = self.make_select_action_pos_list() + self.index = 0 + self.rect.topleft = self.pos_list[self.index] + self.allow_input = False + self.enemy_pos_list = enemy_pos_list + + def make_state_dict(self): + """Make state dictionary""" + state_dict = {'select action': self.select_action, + 'select enemy': self.select_enemy} + + return state_dict + + def select_action(self, keys): + """ + Select what action the player should take. + """ + self.pos_list = self.make_select_action_pos_list() + self.rect.topleft = self.pos_list[self.index] + + if self.allow_input: + if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1): + self.index += 1 + self.allow_input = False + elif keys[pg.K_UP] and self.index > 0: + self.index -= 1 + self.allow_input = False + + if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False: + self.allow_input = True + + + def make_select_action_pos_list(self): + """ + Make the list of positions the arrow can be in. + """ + pos_list = [] + + for i in range(4): + x = 590 + y = (i * 34) + 472 + pos_list.append((x, y)) + + return pos_list + + def select_enemy(self, keys): + """ + Select what enemy you want to take action on. + """ + self.pos_list = self.enemy_pos_list + pos = self.pos_list[self.index] + self.rect.x = pos[0] - 60 + self.rect.y = pos[1] + 20 + + if self.allow_input: + if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1): + self.index += 1 + self.allow_input = False + elif keys[pg.K_UP] and self.index > 0: + self.index -= 1 + self.allow_input = False + + + if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False \ + and keys[pg.K_RIGHT] == False and keys[pg.K_LEFT] == False: + self.allow_input = True + + def become_invisible_surface(self): + """ + Make image attribute an invisible surface. + """ + self.image = pg.Surface((32, 32)) + self.image.set_colorkey(c.BLACK) + + + def enter_select_action(self): + """ + Assign values for the select action state. + """ + pass + + def enter_select_enemy(self): + """ + Assign values for the select enemy state. + """ + pass + + def update(self, keys): + """ + Update arrow position. + """ + state_function = self.state_dict[self.state] + state_function(keys) + + def draw(self, surface): + """ + Draw to surface. + """ + surface.blit(self.image, self.rect) +
M
data/collision.py
→
data/collision.py
@@ -95,7 +95,7 @@ def check_for_battle(self):
""" Switch scene to battle 1/5 times if battles are allowed. """ - random_number = random.randint(0, 10) + random_number = random.randint(0, 2) if random_number == 0: self.level.switch_to_battle = True
M
data/components/person.py
→
data/components/person.py
@@ -1,6 +1,7 @@
import math, random import pygame as pg from .. import setup +from .. import constants as c class Person(pg.sprite.Sprite):@@ -320,7 +321,7 @@ self.x_vel = 0
self.state = 'battle resting' self.image = self.spritesheet_dict['facing left 2'] self.image = pg.transform.scale2x(self.image) - self.observer.on_notify('select action') + self.observer.on_notify(c.PLAYER_FINISHED_ATTACK)
M
data/constants.py
→
data/constants.py
@@ -31,5 +31,25 @@
MAIN_FONT = 'DroidSans' +#STATES + +SELECT_ACTION = 'select action' +SELECT_ENEMY = 'select enemy' +ENEMY_ATTACK = 'enemy attack' +PLAYER_ATTACK = 'player attack' +SELECT_ITEM = 'select item' +SELECT_MAGIC = 'select magic' +RUN_AWAY = 'run away' +ATTACK_ANIMATION = 'attack animation' +BATTLE_WON = 'battle won' +ENEMY_HIT = 'enemy hit' +ENEMY_DEAD = 'enemy dead' +PLAYER_FINISHED_ATTACK = 'player finished attack' + +#EVENTS + +END_BATTLE = 'end battle' + +
A
data/observer.py
@@ -0,0 +1,107 @@
+""" +Module for all game observers. +""" +from . import constants as c +from .components import attack +from . import setup + +class Battle(object): + """ + Observes events of battle and passes info to components. + """ + def __init__(self, level): + self.level = level + self.info_box = level.info_box + self.select_box = level.info_box + self.arrow = level.arrow + self.player = level.player + self.enemies = level.enemy_group + self.event_dict = self.make_event_dict() + + def make_event_dict(self): + """ + Make a dictionary of events the Observer can + receive. + """ + event_dict = {c.END_BATTLE: self.end_battle, + c.SELECT_ACTION: self.select_action, + c.SELECT_ITEM: self.select_item, + c.SELECT_ENEMY: self.select_enemy, + c.ENEMY_ATTACK: self.enemy_attack, + c.PLAYER_ATTACK: self.player_attack, + c.ATTACK_ANIMATION: self.attack_animation, + c.RUN_AWAY: self.run_away, + c.BATTLE_WON: self.battle_won, + c.PLAYER_FINISHED_ATTACK: self.player_finished_attack} + + return event_dict + + def on_notify(self, event): + """ + Notify Observer of event. + """ + if event in self.event_dict: + self.event_dict[event]() + + def end_battle(self): + """ + End Battle and flip to previous state. + """ + self.level.end_battle() + + def select_action(self): + """ + Set components to select action. + """ + self.level.state = c.SELECT_ACTION + self.arrow.index = 0 + self.arrow.state = c.SELECT_ACTION + self.arrow.image = setup.GFX['smallarrow'] + self.info_box.state = c.SELECT_ACTION + + def select_enemy(self): + self.level.state = c.SELECT_ENEMY + self.arrow.state = c.SELECT_ENEMY + + def select_item(self): + self.level.state = c.SELECT_ITEM + self.info_box.state = c.SELECT_ITEM + + def enemy_attack(self): + pass + + def player_attack(self): + enemy_posx = self.arrow.rect.x + 60 + enemy_posy = self.arrow.rect.y - 20 + enemy_pos = (enemy_posx, enemy_posy) + enemy_to_attack = None + + for enemy in self.enemies: + if enemy.rect.topleft == enemy_pos: + enemy_to_attack = enemy + + self.player.enter_attack_state(enemy_to_attack) + self.arrow.become_invisible_surface() + + def attack_animation(self): + """ + Make an attack animation over attacked enemy. + """ + enemy = self.player.attacked_enemy + if enemy: + enemy.kill() + posx = enemy.rect.x - 32 + posy = enemy.rect.y - 64 + fire_sprite = attack.Fire(posx, posy) + self.level.attack_animations.add(fire_sprite) + + self.info_box.state = c.ENEMY_HIT + + def run_away(self): + self.level.end_battle() + + def battle_won(self): + self.level.end_battle() + + def player_finished_attack(self): + pass
M
data/states/battle.py
→
data/states/battle.py
@@ -3,25 +3,10 @@ monsters"""
import random import pygame as pg -from .. import tools, setup -from .. components import person, attack +from .. import tools, battlegui, observer +from .. components import person from .. import constants as c -#STATES - -SELECT_ACTION = 'select action' -SELECT_ENEMY = 'select enemy' -ENEMY_ATTACK = 'enemy attack' -PLAYER_ATTACK = 'player attack' -SELECT_ITEM = 'select item' -SELECT_MAGIC = 'select magic' -RUN_AWAY = 'run away' -ATTACK_ANIMATION = 'attack animation' - -#EVENTS - -END_BATTLE = 'end battle' - class Battle(tools._State): def __init__(self):@@ -31,21 +16,22 @@ def startup(self, current_time, game_data):
"""Initialize state attributes""" self.current_time = current_time self.allow_input = False + self.allow_info_box_change = False self.game_data = game_data self.background = self.make_background() self.enemy_group, self.enemy_pos_list = self.make_enemies() self.player = self.make_player() self.attack_animations = pg.sprite.Group() - self.info_box = InfoBox(game_data) - self.arrow = SelectArrow(self.enemy_pos_list) + self.info_box = battlegui.InfoBox(game_data) + self.arrow = battlegui.SelectArrow(self.enemy_pos_list) self.attacked_enemy = None self.attacking_enemy = None - self.select_box = SelectBox() - self.state = SELECT_ACTION + self.select_box = battlegui.SelectBox() + self.state = c.SELECT_ACTION self.select_action_state_dict = self.make_selection_state_dict() self.name = 'battle' self.next = game_data['last state'] - self.observer = Observer(self) + self.observer = observer.Battle(self) self.player.observer = self.observer def make_background(self):@@ -93,15 +79,17 @@ """
Make a dictionary of states with arrow coordinates as keys. """ pos_list = self.arrow.make_select_action_pos_list() - state_list = [SELECT_ENEMY, SELECT_ITEM, SELECT_MAGIC, RUN_AWAY] + state_list = [c.SELECT_ENEMY, c.SELECT_ITEM, c.SELECT_MAGIC, c.RUN_AWAY] return dict(zip(pos_list, state_list)) def update(self, surface, keys, current_time): """Update the battle state""" self.check_input(keys) + self.check_if_battle_won() self.enemy_group.update(current_time) self.player.update(keys, current_time) self.attack_animations.update() + self.info_box.update() self.arrow.update(keys) self.draw_battle(surface)@@ -112,16 +100,24 @@ Check user input to navigate GUI.
""" if self.allow_input: if keys[pg.K_RETURN]: - self.notify(END_BATTLE) + self.notify(c.END_BATTLE) elif keys[pg.K_SPACE]: - if self.state == SELECT_ACTION: + if self.state == c.SELECT_ACTION: self.state = self.select_action_state_dict[ self.arrow.rect.topleft] self.notify(self.state) - elif self.state == SELECT_ENEMY: - self.state = PLAYER_ATTACK + elif self.state == c.SELECT_ENEMY: + self.state = c.PLAYER_ATTACK + self.notify(self.state) + + elif self.state == c.PLAYER_ATTACK: + self.state = c.ENEMY_DEAD + self.info_box.state = c.ENEMY_DEAD + + elif self.state == c.ENEMY_DEAD: + self.state = c.SELECT_ACTION self.notify(self.state) self.allow_input = False@@ -129,6 +125,14 @@
if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False: self.allow_input = True + def check_if_battle_won(self): + """ + Check if state is SELECT_ACTION and there are no enemies left. + """ + if self.state == c.SELECT_ACTION: + if len(self.enemy_group) == 0: + self.notify(c.BATTLE_WON) + def notify(self, event): """ Notify observer of event.@@ -153,338 +157,3 @@ surface.blit(self.select_box.image, self.select_box.rect)
surface.blit(self.arrow.image, self.arrow.rect) -class Observer(object): - """ - Observes events of battle and passes info to components. - """ - def __init__(self, level): - self.level = level - self.info_box = level.info_box - self.select_box = level.info_box - self.arrow = level.arrow - self.player = level.player - self.enemies = level.enemy_group - self.event_dict = self.make_event_dict() - - def make_event_dict(self): - """ - Make a dictionary of events the Observer can - receive. - """ - event_dict = {END_BATTLE: self.end_battle, - SELECT_ACTION: self.select_action, - SELECT_ITEM: self.select_item, - SELECT_ENEMY: self.select_enemy, - ENEMY_ATTACK: self.enemy_attack, - PLAYER_ATTACK: self.player_attack, - ATTACK_ANIMATION: self.attack_animation, - RUN_AWAY: self.run_away} - - return event_dict - - def on_notify(self, event): - """ - Notify Observer of event. - """ - if event in self.event_dict: - self.event_dict[event]() - - def end_battle(self): - """ - End Battle and flip to previous state. - """ - self.level.end_battle() - - def select_action(self): - """ - Set components to select action. - """ - self.level.state = SELECT_ACTION - self.arrow.index = 0 - self.arrow.state = SELECT_ACTION - self.arrow.image = setup.GFX['smallarrow'] - - def select_enemy(self): - self.level.state = SELECT_ENEMY - self.arrow.state = SELECT_ENEMY - - def select_item(self): - self.level.state = SELECT_ITEM - self.info_box.image = self.info_box.make_image(SELECT_ITEM) - - def enemy_attack(self): - pass - - def player_attack(self): - enemy_posx = self.arrow.rect.x + 60 - enemy_posy = self.arrow.rect.y - 20 - enemy_pos = (enemy_posx, enemy_posy) - enemy_to_attack = None - - for enemy in self.enemies: - if enemy.rect.topleft == enemy_pos: - enemy_to_attack = enemy - - self.player.enter_attack_state(enemy_to_attack) - self.arrow.become_invisible_surface() - - def attack_animation(self): - """ - Make an attack animation over attacked enemy. - """ - enemy = self.player.attacked_enemy - if enemy: - enemy.kill() - posx = enemy.rect.x - 32 - posy = enemy.rect.y - 64 - fire_sprite = attack.Fire(posx, posy) - self.level.attack_animations.add(fire_sprite) - - def run_away(self): - self.level.end_battle() - - -class InfoBox(object): - """ - Info box that describes attack damage and other battle - related information. - """ - def __init__(self, game_data): - self.game_data = game_data - self.state = SELECT_ACTION - self.title_font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22) - self.title_font.set_underline(True) - self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 18) - self.message_dict = self.make_message_dict() - self.image = self.make_image(SELECT_ACTION) - self.rect = self.image.get_rect(bottom=608) - - def make_message_dict(self): - """ - Make dictionary of states Battle info can be in. - """ - message_dict = {SELECT_ACTION: 'Select an action.', - SELECT_MAGIC: 'Select a magic spell.', - SELECT_ITEM: 'Select an item.', - SELECT_ENEMY: 'Select an enemy.', - ENEMY_ATTACK: 'The enemy attacks player!', - PLAYER_ATTACK: 'Player attacks enemy.', - RUN_AWAY: 'Run away'} - - return message_dict - - def make_item_text(self): - """ - Make the text for when the player selects items. - """ - inventory = self.game_data['player inventory'] - allowed_item_list = ['Healing Potion'] - title = 'SELECT ITEM' - item_text_list = [title] - - for item in inventory: - if item in allowed_item_list: - text = item + ": " + str(inventory[item]['quantity']) - item_text_list.append(text) - - item_text_list.append('BACK') - - return item_text_list - - def make_text_sprites(self, text_list): - """ - Make sprites out of text. - """ - sprite_group = pg.sprite.Group() - - for i, text in enumerate(text_list): - sprite = pg.sprite.Sprite() - - if i == 0: - x = 195 - y = 10 - surface = self.title_font.render(text, True, c.NEAR_BLACK) - rect = surface.get_rect(x=x, y=y) - else: - x = 100 - y = (i * 30) + 20 - surface = self.font.render(text, True, c.NEAR_BLACK) - rect = surface.get_rect(x=x, y=y) - sprite.image = surface - sprite.rect = rect - sprite_group.add(sprite) - - return sprite_group - - def make_image(self, state): - """ - Make image out of box and message. - """ - image = setup.GFX['shopbox'] - rect = image.get_rect(bottom=608) - surface = pg.Surface(rect.size) - surface.set_colorkey(c.BLACK) - surface.blit(image, (0, 0)) - - if state == SELECT_ITEM: - text_sprites = self.make_text_sprites(self.make_item_text()) - text_sprites.draw(surface) - else: - text_surface = self.font.render(self.message_dict[state], True, c.NEAR_BLACK) - text_rect = text_surface.get_rect(x=50, y=50) - surface.blit(text_surface, text_rect) - - return surface - - -class SelectBox(object): - """ - Box to select whether to attack, use item, use magic or run away. - """ - def __init__(self): - self.font = pg.font.Font(setup.FONTS[c.MAIN_FONT], 22) - self.slots = self.make_slots() - self.image = self.make_image() - self.rect = self.image.get_rect(bottom=608, - right=800) - - def make_image(self): - """ - Make the box image for - """ - image = setup.GFX['goldbox'] - rect = image.get_rect(bottom=608) - surface = pg.Surface(rect.size) - surface.set_colorkey(c.BLACK) - surface.blit(image, (0, 0)) - - for text in self.slots: - text_surface = self.font.render(text, True, c.NEAR_BLACK) - text_rect = text_surface.get_rect(x=self.slots[text]['x'], - y=self.slots[text]['y']) - surface.blit(text_surface, text_rect) - - return surface - - def make_slots(self): - """ - Make the slots that hold the text selections, and locations. - """ - slot_dict = {} - selections = ['Attack', 'Items', 'Magic', 'Run'] - - for i, text in enumerate(selections): - slot_dict[text] = {'x': 150, - 'y': (i*34)+10} - - return slot_dict - - - -class SelectArrow(object): - """Small arrow for menu""" - def __init__(self, enemy_pos_list): - self.image = setup.GFX['smallarrow'] - self.rect = self.image.get_rect() - self.state = 'select action' - self.state_dict = self.make_state_dict() - self.pos_list = self.make_select_action_pos_list() - self.index = 0 - self.rect.topleft = self.pos_list[self.index] - self.allow_input = False - self.enemy_pos_list = enemy_pos_list - - def make_state_dict(self): - """Make state dictionary""" - state_dict = {'select action': self.select_action, - 'select enemy': self.select_enemy} - - return state_dict - - def select_action(self, keys): - """ - Select what action the player should take. - """ - self.pos_list = self.make_select_action_pos_list() - self.rect.topleft = self.pos_list[self.index] - - if self.allow_input: - if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1): - self.index += 1 - self.allow_input = False - elif keys[pg.K_UP] and self.index > 0: - self.index -= 1 - self.allow_input = False - - if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False: - self.allow_input = True - - - def make_select_action_pos_list(self): - """ - Make the list of positions the arrow can be in. - """ - pos_list = [] - - for i in range(4): - x = 590 - y = (i * 34) + 472 - pos_list.append((x, y)) - - return pos_list - - def select_enemy(self, keys): - """ - Select what enemy you want to take action on. - """ - self.pos_list = self.enemy_pos_list - pos = self.pos_list[self.index] - self.rect.x = pos[0] - 60 - self.rect.y = pos[1] + 20 - - if self.allow_input: - if keys[pg.K_DOWN] and self.index < (len(self.pos_list) - 1): - self.index += 1 - self.allow_input = False - elif keys[pg.K_UP] and self.index > 0: - self.index -= 1 - self.allow_input = False - - - if keys[pg.K_DOWN] == False and keys[pg.K_UP] == False \ - and keys[pg.K_RIGHT] == False and keys[pg.K_LEFT] == False: - self.allow_input = True - - def become_invisible_surface(self): - """ - Make image attribute an invisible surface. - """ - self.image = pg.Surface((32, 32)) - self.image.set_colorkey(c.BLACK) - - - def enter_select_action(self): - """ - Assign values for the select action state. - """ - pass - - def enter_select_enemy(self): - """ - Assign values for the select enemy state. - """ - pass - - def update(self, keys): - """ - Update arrow position. - """ - state_function = self.state_dict[self.state] - state_function(keys) - - def draw(self, surface): - """ - Draw to surface. - """ - surface.blit(self.image, self.rect) -