Return to repo list

heart-of-gold

Tactical RPG written in python, using pygame.
Return to HMagellan.com

commit ea8a52ebff43120016da19922fe778e6448d519e
parent 9b828bf73173d524c52e229d870fb1d925841467
Author: Erik Letson <hmagellan@hmagellan.com>
Date:   Fri, 20 Nov 2020 18:40:00 -0600

Basic ver of the stats screen in battle

Diffstat:
Adata/img/affinity_icons_1.png | 0
Adata/img/stat_screen_1.png | 0
Adata/img/status_card_1.png | 0
Mdata/json/sheets.json | 10++++++++++
Msrc/bus.py | 2++
Msrc/constants.py | 7++++++-
Msrc/interface.py | 10+++++++++-
Msrc/piece.py | 21+++++++++++++++++----
Asrc/status.py | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/turn.py | 23++++++++++++++++++++---
10 files changed, 123 insertions(+), 9 deletions(-)

diff --git a/data/img/affinity_icons_1.png b/data/img/affinity_icons_1.png Binary files differ. diff --git a/data/img/stat_screen_1.png b/data/img/stat_screen_1.png Binary files differ. diff --git a/data/img/status_card_1.png b/data/img/status_card_1.png Binary files differ. diff --git a/data/json/sheets.json b/data/json/sheets.json @@ -118,5 +118,15 @@ "filename" : "facing_arrows_1.png", "dimensions" : [64, 64], "total_sprites" : 4 + }, + "stat_screen_1" : { + "filename" : "stat_screen_1.png", + "dimensions" : [1024, 768], + "total_sprites" : 1 + }, + "affinity_icons_1" : { + "filename" : "affinity_icons_1.png", + "dimensions" : [64, 64], + "total_sprites" : 6 } } diff --git a/src/bus.py b/src/bus.py @@ -134,3 +134,5 @@ class ManagerBus(subsystem.GameSubsystem): self.game.turn_manager.kill_piece(piece) def perform_turn_manager_refresh_state(self): self.game.turn_manager.refresh_turn_state() + def perform_turn_manager_display_stats(self, piece): + self.game.turn_manager.create_stat_screen(piece) diff --git a/src/constants.py b/src/constants.py @@ -69,8 +69,13 @@ WEAPON_TYPE_BONUSES = { "Wand" : "INT" } +# Stat constants +BASIC_STATS = ["ATK", "DEF", "INT", "WIS", "SPD", "ACC", "LUK"] +RESERVE_STATS = ["MOVE", "INIT", "CNTR", "GARD", "PUSH", "SKIL", "PASS"] +OTHER_STATS = ["HP", "EXP", "LVL", "RNK"] + # Enums STATE_MODES = enum.Enum('STATE_MODES', 'Main_Menu_Mode Battle_Mode Still_Scene_Mode') -CTRL_MODES = enum.Enum('CTRL_MODES', 'No_Control Main_Menu_Normal Turn_Normal Turn_Select_Move Turn_Select_Attack Turn_Watch_Move Turn_Watch_Attack Still_Scene_Normal') +CTRL_MODES = enum.Enum('CTRL_MODES', 'No_Control Main_Menu_Normal Turn_Normal Turn_Select_Move Turn_Select_Attack Turn_Watch_Move Turn_Watch_Attack Turn_Display_Stats Still_Scene_Normal') FACE_DIR = enum.Enum('FACE_DIR', 'U D L R') GAME_EFFECTS = enum.Enum('GAME_EFFECTS', 'ef_game_quit ef_game_switch_mode') diff --git a/src/interface.py b/src/interface.py @@ -55,7 +55,15 @@ class GameInterface(subsystem.GameSubsystem): React to a key being pressed. """ # TODO: This is irregular compared to mouseclick. Desireable??? - if self.game.control_mode == CTRL_MODES.Turn_Select_Move or self.game.control_mode == CTRL_MODES.Turn_Select_Attack: + if self.game.control_mode == CTRL_MODES.Turn_Normal: + if event.key == pygame.K_s: + self.bus.perform_turn_manager_display_stats(self.bus.fetch("turn_manager", "active_piece")) + self.game.control_mode = CTRL_MODES.Turn_Display_Stats + elif self.game.control_mode == CTRL_MODES.Turn_Display_Stats: + if event.key == pygame.K_s or event.key == pygame.K_q: + self.bus.perform_turn_manager_display_stats(None) + self.game.control_mode = CTRL_MODES.Turn_Normal + elif self.game.control_mode == CTRL_MODES.Turn_Select_Move or self.game.control_mode == CTRL_MODES.Turn_Select_Attack: if event.key == pygame.K_q: self.game.control_mode = CTRL_MODES.Turn_Normal self.bus.perform_load_board_overlay() diff --git a/src/piece.py b/src/piece.py @@ -388,8 +388,10 @@ class Piece(entity.Entity): prim = self.active_stats[WEAPON_TYPE_SOURCES[ITEMBASE[self.equipment["weapon"]]["type"]]] bon = WEAPON_TYPE_SOURCES[ITEMBASE[self.equipment["weapon"]]["type"]] raw_dam = max(round((prim * 1.8) + random.randint(-(self.active_stats["LUK"] // 2), self.active_stats["LUK"])), 0) - if bon != None and raw_dam != 0: + if bon != None and raw_dam > 0: raw_dam += round((self.active_stats[bon] + random.randint(0, self.active_stats["LUK"] // 2)) * 0.2) + elif raw_dam < 0: + raw_dam = 0 self.attack_target.set_be_damaged_action(raw_dam) def set_be_damaged_action(self, raw_damage): @@ -399,14 +401,25 @@ class Piece(entity.Entity): # TODO: This could eventually take a 'damage_aff' var which determines # the element of the damage and could be figured into the post-def # calculations to account for elemental strength/weakness - self.damage_to_receive = (raw_damage - round(self.active_stats["DEF"] * 1.1)) + random.randint(-(self.active_stats["LUK"] // 2), - self.active_stats["LUK"]) - if self.damage_to_receive < 0: + if raw_damage <= 0: self.damage_to_receive = 0 + else: + self.damage_to_receive = max(0, (raw_damage - round(self.active_stats["DEF"] * 1.4)) + random.randint(-(self.active_stats["LUK"] // 2), + self.active_stats["LUK"])) self.damage_timer = 100 self.being_damaged = True self.render_damage_number() + def get_full_stat_def(self): + """ + Returns a stat_def dict organized for status + display, or None if no stats are found. + """ + if self.active_stats != None and self.normal_stats != None: + return { "normal_stats" : self.normal_stats, "active_stats" : self.active_stats } + else: + return None + def create_health_bar(self): """ Create a health bar to be displayed along with this piece diff --git a/src/status.py b/src/status.py @@ -0,0 +1,59 @@ +import pygame, os +from . import entity +from .constants import * + +############# +# status.py # +############# + +# This file contains: +# 1. The StatusDisplay entity class, which displays a full status screen with clickable buttons +# 2. The StatusCard entity class, which shows a brief description of piece info and is small + +####################################### +# Section 1 - The StatusDisplay class # +####################################### + +class StatusDisplay(entity.Entity): + """ + The StatusDisplay class represents the entire + status screen for a unit. It has several + subordinate entities that represent things + like clickable buttons and icons. + """ + + def __init__(self, sheet, sprite = (0, 0), animation = None, animated = None, + stat_def = None): + # Parent initialiazation + super().__init__(sheet, sprite, animation, animated) + + # Other important values + self.stat_def = stat_def # Consists of a piece's active stats and normal stats, in a dict + self.font = pygame.font.Font(os.path.join(FONT_PATH, UI_FONT), SCREEN_HEIGHT // 34) + # TODO: hardcoded + self.stat_surfaces_position = (140, 216) + self.stat_surfaces = { i: None for i in self.stat_def["normal_stats"].keys() } + self.stat_surface_rects = { i: None for i in self.stat_def["normal_stats"].keys() } + self.load_text() + + def load_text(self): + """ + Load all the various text fields and + their rects. + """ + modifier = 0 + for i in self.stat_surfaces: + if i in BASIC_STATS: + self.stat_surfaces[i] = self.font.render(str(self.stat_def["active_stats"][i]), False, (0, 0, 0)).convert() + self.stat_surface_rects[i] = self.stat_surfaces[i].get_rect() + self.stat_surface_rects[i].topleft = (self.stat_surfaces_position[0] + (150 * (modifier % 2)), self.stat_surfaces_position[1] + (21 * (modifier // 2)) + (modifier // 2)) + modifier += 1 + + def update(self, surface = None): + """ + Update overwrite to handle drawing subsurfaces. + """ + super().update(surface) + for s in self.stat_surfaces: + if self.stat_surfaces[s] != None: + surface.blit(self.stat_surfaces[s], self.stat_surface_rects[s]) diff --git a/src/turn.py b/src/turn.py @@ -1,5 +1,5 @@ import pygame -from . import manager, entity +from . import manager, entity, status from .constants import * ########### @@ -8,7 +8,7 @@ from .constants import * # This file contains the following: # 1. The TurnManager class, which handles turn order and displaying turn info in the GUI -# 2. The TurnTray Entity subclass, which represents the tray where turn information is reported onscreen +# 2. Several entity subclasses used by TurnManager to make up the battle UI ################################# # Section 1 - TurnManager class # @@ -39,6 +39,9 @@ class TurnManager(manager.Manager): self.turn_tray_more_button = None self.turn_icons = [] self.action_buttons = [] + + # Stat screen values + self.stat_screen = None def initialize_turns(self, pieces): """ @@ -166,6 +169,18 @@ class TurnManager(manager.Manager): ni = TurnIcon(self.bus.fetch("sheet_manager", "sheets")["turn_icon_holder_1"], (0, 0), None, False, self, piece, index) self.turn_icons.append(ni) + def create_stat_screen(self, piece): + """ + Create a stat screen for the given piece and then + display it. If 'piece' is none, turn off the stat + display. + """ + if piece != None: + sh = self.bus.fetch("sheet_manager", "sheets")["stat_screen_1"] + self.stat_screen = status.StatusDisplay(sh, (0, 0), None, False, piece.get_full_stat_def()) + else: + self.stat_screen = None + def trigger_button_at_pos(self, pos): """ Trigger a button at the given pos, if any. The @@ -245,10 +260,12 @@ class TurnManager(manager.Manager): elif self.game.control_mode == CTRL_MODES.Turn_Watch_Move: if self.action_buttons[0].clickable: self.action_buttons[0].toggle_activation() - if self.game.control_mode == CTRL_MODES.Turn_Watch_Attack: + elif self.game.control_mode == CTRL_MODES.Turn_Watch_Attack: for k in range(1, 5): if self.action_buttons[k].clickable: self.action_buttons[k].toggle_activation() + elif self.game.control_mode == CTRL_MODES.Turn_Display_Stats: + self.stat_screen.update(surface) ############################# # Section 2 - Turn Entities #