Return to repo list

heart-of-gold

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

commit 486b468d77e6aadf984d3074777bb0f942549128
parent 801a33946447c01a3cd1c05debd4cef6e127f90e
Author: Erik Letson <hmagellan@hmagellan.com>
Date:   Tue, 17 Nov 2020 17:57:14 -0600

Attacking possible. This is now a simple but functional game!

Diffstat:
Mdata/json/anims.json | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/bus.py | 10++++++++++
Msrc/interface.py | 17+++++++++++++++--
Msrc/piece.py | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/turn.py | 29++++++++++++++++++++++++++---
5 files changed, 203 insertions(+), 5 deletions(-)

diff --git a/data/json/anims.json b/data/json/anims.json @@ -63,6 +63,30 @@ { "sprite" : [1, 2], "timer" : 6 }, { "sprite" : [2, 2], "timer" : 6 }, { "sprite" : [3, 2], "timer" : 6 } + ], + "attack_L" : [ + { "sprite" : [0, 3], "timer" : 6 }, + { "sprite" : [1, 3], "timer" : 20 }, + { "sprite" : [2, 3], "timer" : 4 }, + { "sprite" : [3, 3], "timer" : 60 } + ], + "attack_R" : [ + { "sprite" : [4, 3], "timer" : 6 }, + { "sprite" : [5, 3], "timer" : 20 }, + { "sprite" : [6, 3], "timer" : 4 }, + { "sprite" : [7, 3], "timer" : 60 } + ], + "attack_U" : [ + { "sprite" : [8, 3], "timer" : 6 }, + { "sprite" : [9, 3], "timer" : 20 }, + { "sprite" : [10, 3], "timer" : 4 }, + { "sprite" : [11, 3], "timer" : 60 } + ], + "attack_D" : [ + { "sprite" : [0, 3], "timer" : 6 }, + { "sprite" : [1, 3], "timer" : 20 }, + { "sprite" : [2, 3], "timer" : 4 }, + { "sprite" : [3, 3], "timer" : 60 } ] }, "jisella_2" : { @@ -129,6 +153,30 @@ { "sprite" : [1, 2], "timer" : 6 }, { "sprite" : [2, 2], "timer" : 6 }, { "sprite" : [3, 2], "timer" : 6 } + ], + "attack_L" : [ + { "sprite" : [0, 3], "timer" : 6 }, + { "sprite" : [1, 3], "timer" : 20 }, + { "sprite" : [2, 3], "timer" : 4 }, + { "sprite" : [3, 3], "timer" : 60 } + ], + "attack_R" : [ + { "sprite" : [4, 3], "timer" : 6 }, + { "sprite" : [5, 3], "timer" : 20 }, + { "sprite" : [6, 3], "timer" : 4 }, + { "sprite" : [7, 3], "timer" : 60 } + ], + "attack_U" : [ + { "sprite" : [8, 3], "timer" : 6 }, + { "sprite" : [9, 3], "timer" : 20 }, + { "sprite" : [10, 3], "timer" : 4 }, + { "sprite" : [11, 3], "timer" : 60 } + ], + "attack_D" : [ + { "sprite" : [0, 3], "timer" : 6 }, + { "sprite" : [1, 3], "timer" : 20 }, + { "sprite" : [2, 3], "timer" : 4 }, + { "sprite" : [3, 3], "timer" : 60 } ] }, "jisella_3" : { @@ -195,6 +243,30 @@ { "sprite" : [1, 2], "timer" : 6 }, { "sprite" : [2, 2], "timer" : 6 }, { "sprite" : [3, 2], "timer" : 6 } + ], + "attack_L" : [ + { "sprite" : [0, 3], "timer" : 6 }, + { "sprite" : [1, 3], "timer" : 20 }, + { "sprite" : [2, 3], "timer" : 4 }, + { "sprite" : [3, 3], "timer" : 60 } + ], + "attack_R" : [ + { "sprite" : [4, 3], "timer" : 6 }, + { "sprite" : [5, 3], "timer" : 20 }, + { "sprite" : [6, 3], "timer" : 4 }, + { "sprite" : [7, 3], "timer" : 60 } + ], + "attack_U" : [ + { "sprite" : [8, 3], "timer" : 6 }, + { "sprite" : [9, 3], "timer" : 20 }, + { "sprite" : [10, 3], "timer" : 4 }, + { "sprite" : [11, 3], "timer" : 60 } + ], + "attack_D" : [ + { "sprite" : [0, 3], "timer" : 6 }, + { "sprite" : [1, 3], "timer" : 20 }, + { "sprite" : [2, 3], "timer" : 4 }, + { "sprite" : [3, 3], "timer" : 60 } ] }, "jisella_4" : { @@ -261,6 +333,30 @@ { "sprite" : [1, 2], "timer" : 6 }, { "sprite" : [2, 2], "timer" : 6 }, { "sprite" : [3, 2], "timer" : 6 } + ], + "attack_L" : [ + { "sprite" : [0, 3], "timer" : 6 }, + { "sprite" : [1, 3], "timer" : 20 }, + { "sprite" : [2, 3], "timer" : 4 }, + { "sprite" : [3, 3], "timer" : 60 } + ], + "attack_R" : [ + { "sprite" : [4, 3], "timer" : 6 }, + { "sprite" : [5, 3], "timer" : 20 }, + { "sprite" : [6, 3], "timer" : 4 }, + { "sprite" : [7, 3], "timer" : 60 } + ], + "attack_U" : [ + { "sprite" : [8, 3], "timer" : 6 }, + { "sprite" : [9, 3], "timer" : 20 }, + { "sprite" : [10, 3], "timer" : 4 }, + { "sprite" : [11, 3], "timer" : 60 } + ], + "attack_D" : [ + { "sprite" : [0, 3], "timer" : 6 }, + { "sprite" : [1, 3], "timer" : 20 }, + { "sprite" : [2, 3], "timer" : 4 }, + { "sprite" : [3, 3], "timer" : 60 } ] }, "cursor1" : { diff --git a/src/bus.py b/src/bus.py @@ -95,6 +95,8 @@ class ManagerBus(subsystem.GameSubsystem): return self.game.board_manager.get_tile_pos_at_position(position) def check_for_overlay_move_entity_by_screen_pos(self, position): return self.game.board_manager.get_overlay_move_entity_at_pos(position) + def check_for_overlay_attack_entity_by_screen_pos(self, position): + return self.game.board_manager.get_overlay_attack_entity_at_pos(position) def check_for_piece_path_by_previous_moves(self, start_tile, target_tile): return self.game.board_manager.get_path_by_previous_moves(start_tile, target_tile) @@ -112,6 +114,10 @@ class ManagerBus(subsystem.GameSubsystem): self.game.piece_manager.position_tile_cursor(tile_pos) def perform_set_piece_move_along_tile_path(self, piece, path): self.game.piece_manager.set_piece_move_to_tile_path(piece, path) + def perform_execute_attack(self, attacker, target): + self.game.piece_manager.execute_attack(attacker, target) + def perform_piece_manager_kill_piece(self, piece): + self.game.piece_manager.kill_piece(piece) def perform_continue_current_scene_script(self): self.game.scene_manager.current_scene.continue_script() def perform_display_move_range_of_piece(self, piece): @@ -124,3 +130,7 @@ class ManagerBus(subsystem.GameSubsystem): self.game.turn_manager.trigger_button_at_pos(pos) def perform_toggle_action_button_activation(self, index): self.game.turn_manager.action_buttons(index).toggle_activation() + def perform_turn_manager_kill_piece(self, piece): + self.game.turn_manager.kill_piece(piece) + def perform_turn_manager_refresh_state(self): + self.game.turn_manager.refresh_turn_state() diff --git a/src/interface.py b/src/interface.py @@ -104,7 +104,14 @@ class GameInterface(subsystem.GameSubsystem): # Selecting a target to attack elif self.game.control_mode == CTRL_MODES.Turn_Select_Attack: - pass + if self.bus.check_for_overlay_attack_entity_by_screen_pos(mousepos): + ap = self.bus.fetch("turn_manager", "active_piece") + targ = self.bus.check_for_tile_pos_by_screen_pos(mousepos) + ps = self.bus.fetch("piece_manager", "pieces_by_tile_pos") + if targ in ps and ps[targ].team != ap.team: + self.bus.perform_execute_attack(ap, ps[targ]) + self.game.control_mode = CTRL_MODES.Turn_Watch_Attack + self.bus.perform_load_board_overlay() # Still-scene mode behavior elif self.game.state_mode == STATE_MODES.Still_Scene_Mode: @@ -133,7 +140,7 @@ class GameInterface(subsystem.GameSubsystem): if tilepos != None: self.bus.perform_position_tile_cursor((tilepos[0], tilepos[1])) - if self.game.control_mode == CTRL_MODES.Turn_Normal or self.game.control_mode == CTRL_MODES.Turn_Select_Move: + if self.game.control_mode in (CTRL_MODES.Turn_Normal, CTRL_MODES.Turn_Select_Move, CTRL_MODES.Turn_Select_Attack): for r in self.camera.scroll_rects: if self.camera.scroll_rects[r].collidepoint(mouseraw) and pygame.mouse.get_focused(): self.camera.move_offset(r, SCROLL_SPEED) @@ -144,3 +151,9 @@ class GameInterface(subsystem.GameSubsystem): if not ap.path_moving: self.game.control_mode = CTRL_MODES.Turn_Normal + # Watching an attack complete + elif self.game.control_mode == CTRL_MODES.Turn_Watch_Attack: + ap = self.bus.fetch("turn_manager", "active_piece") + if not ap.attacking: + self.game.control_mode = CTRL_MODES.Turn_Normal + diff --git a/src/piece.py b/src/piece.py @@ -154,6 +154,20 @@ class PieceManager(manager.Manager): legal_moves.append((x, y)) return legal_moves + def execute_attack(self, attacker, target): + """ + Execute an attack between two pieces. + """ + if attacker in self.pieces and target in self.pieces: + attacker.set_attack_action(target) + + def kill_piece(self, piece): + """ + Remove a piece from everything. + """ + if piece in self.pieces: + self.pieces.remove(piece) + def expose(self): """ Expose info about pieces to the ManagerBus. @@ -228,6 +242,11 @@ class Piece(entity.Entity): self.through_tile_pos = (-1, -1) self.path_moving = False self.first_path_move_pass = False + self.attacking = False + self.attack_target = None + self.attack_anim_timer = 0 + self.has_moved = False + self.has_acted = False self.health_bar = None self.health_bar_fill = None self.health_bar_rect = None @@ -308,6 +327,19 @@ class Piece(entity.Entity): self.first_path_move_pass = True self.through_tile_pos = self.tile_pos + def set_attack_action(self, target): + """ + Setup and start an attack animation and the + corresponding damage calculations. + """ + # First, setup the animation + self.set_animation(self.manager.bus.fetch("sheet_manager", "animations")[self.sheet.name]["attack_" + self.facing.name], True) + + # Then, setup attack execution values + self.attacking = True + self.attack_target = target + self.attack_anim_timer = 90 + def create_health_bar(self): """ Create a health bar to be displayed along with this piece @@ -359,6 +391,29 @@ class Piece(entity.Entity): self.path_moving = False self.current_tile_path = [] self.back_to_stand = True + self.has_moved = True + + def execute_attack_action(self): + """ + Execute an attack action against a preset target. + """ + if self.attacking: + if self.attack_anim_timer > 0: + self.attack_anim_timer -= 1 + else: + self.attacking = False + self.has_acted = True + # TODO: A 'be_damaged()' method would be appropriate + dam = self.active_stats["ATK"] * 2 # TODO: Obv not perm + self.attack_target.active_stats["HP"] -= dam + if self.attack_target.active_stats["HP"] <= 0: + self.manager.kill_piece(self.attack_target) + self.manager.bus.perform_turn_manager_kill_piece(self.attack_target) + else: + self.attack_target.create_health_bar() + self.manager.bus.perform_turn_manager_refresh_state() + self.attack_target = None + self.set_animation(self.sheet.manager.animations[self.sheet.name]["stand_" + self.facing.name], True) def act(self): """ @@ -367,6 +422,7 @@ class Piece(entity.Entity): # TODO: Something else should be done so that this doesn't overwrite other # legit non-walking, non-standing anims. THIS MAY NOT BE THE BEST. self.execute_tile_path_move() + self.execute_attack_action() if self.back_to_stand: self.set_animation(self.sheet.manager.animations[self.sheet.name]["stand_" + self.facing.name], True) self.back_to_stand = False diff --git a/src/turn.py b/src/turn.py @@ -96,8 +96,8 @@ class TurnManager(manager.Manager): # Set the 0th candidate as the current active piece and let it take a turn self.current_active_piece = candidates.pop(0) self.current_active_piece.taking_turn = True - self.active_piece_has_moved = False - self.active_piece_has_acted = False + self.current_active_piece.has_moved = False + self.current_active_piece.has_acted = False self.camera.snap_to_position(self.current_active_piece.rect.center) # Predict the next turn order @@ -149,6 +149,11 @@ class TurnManager(manager.Manager): self.turn_projection.append(ntc) next_turn_candidates = [] + # Handle a case where the active piece might not be accounted for + if self.current_active_piece != None and self.turn_projection[0] != self.current_active_piece: + self.turn_projection.insert(0, self.current_active_piece) + self.turn_projection.pop(len(self.turn_projection) - 1) + # Fill up the turn icons j = 0 for pj in self.turn_projection: @@ -191,6 +196,21 @@ class TurnManager(manager.Manager): elif self.action_buttons[5].rect.collidepoint(pos) and self.action_buttons[5].clickable: self.shift_turns() + def kill_piece(self, piece): + """ + Remove a piece from everything. + """ + if piece in self.in_play_pieces: + self.in_play_pieces.pop(self.in_play_pieces.index(piece)) + self.project_turn_order() + + def refresh_turn_state(self): + """ + Refresh the entire state of the turn + manager. + """ + self.project_turn_order() + def expose(self): """ Expose information about turns. @@ -225,9 +245,12 @@ class TurnManager(manager.Manager): for b in self.action_buttons: b.update(surface) elif self.game.control_mode == CTRL_MODES.Turn_Watch_Move: - self.active_piece_has_moved = True if self.action_buttons[0].clickable: self.action_buttons[0].toggle_activation() + if 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() ############################# # Section 2 - Turn Entities #