commit 801a33946447c01a3cd1c05debd4cef6e127f90e
parent 88e21c6a164dc270e7822af4df3f49a527be4b59
Author: Erik Letson <hmagellan@hmagellan.com>
Date: Tue, 17 Nov 2020 15:52:46 -0600
Added items and attack highlighting
Diffstat:
10 files changed, 190 insertions(+), 14 deletions(-)
diff --git a/data/board/testmap1/testmap1.json b/data/board/testmap1/testmap1.json
@@ -13,6 +13,7 @@
"team" : "Player",
"normal_stats" : {
"LVL" : 1,
+ "EXP" : 0,
"HP" : 100,
"ATK" : 10,
"DEF" : 6,
@@ -28,10 +29,12 @@
"MOVE" : 6,
"SKIL" : 3,
"PASS" : 3,
- "AFFINITY" : "Fire"
+ "AFFINITY" : "Fire",
+ "EXPERTISE" : ["Sword"]
},
"active_stats" : {
"LVL" : 1,
+ "EXP" : 0,
"HP" : 80,
"ATK" : 10,
"DEF" : 6,
@@ -47,7 +50,8 @@
"MOVE" : 6,
"SKIL" : 3,
"PASS" : 3,
- "AFFINITY" : "Fire"
+ "AFFINITY" : "Fire",
+ "EXPERTISE" : ["Sword"]
},
"equipment" : {
"weapon" : "Basic_Sword_1",
@@ -71,6 +75,7 @@
"team" : "Enemy",
"normal_stats" : {
"LVL" : 1,
+ "EXP" : 100,
"HP" : 100,
"ATK" : 10,
"DEF" : 6,
@@ -86,10 +91,12 @@
"MOVE" : 6,
"SKIL" : 3,
"PASS" : 3,
- "AFFINITY" : "Water"
+ "AFFINITY" : "Water",
+ "EXPERTISE" : ["Sword"]
},
"active_stats" : {
"LVL" : 1,
+ "EXP" : 100,
"HP" : 100,
"ATK" : 10,
"DEF" : 6,
@@ -105,7 +112,8 @@
"MOVE" : 6,
"SKIL" : 3,
"PASS" : 3,
- "AFFINITY" : "Water"
+ "AFFINITY" : "Water",
+ "EXPERTISE" : ["Sword"]
},
"equipment" : {
"weapon" : "Basic_Sword_1",
@@ -129,6 +137,7 @@
"team" : "Enemy",
"normal_stats" : {
"LVL" : 1,
+ "EXP" : 0,
"HP" : 100,
"ATK" : 10,
"DEF" : 6,
@@ -144,10 +153,12 @@
"MOVE" : 6,
"SKIL" : 3,
"PASS" : 3,
- "AFFINITY" : "Water"
+ "AFFINITY" : "Water",
+ "EXPERTISE" : ["Sword"]
},
"active_stats" : {
"LVL" : 1,
+ "EXP" : 0,
"HP" : 100,
"ATK" : 10,
"DEF" : 6,
@@ -163,7 +174,8 @@
"MOVE" : 6,
"SKIL" : 3,
"PASS" : 3,
- "AFFINITY" : "Water"
+ "AFFINITY" : "Water",
+ "EXPERTISE" : ["Sword"]
},
"equipment" : {
"weapon" : "Basic_Sword_1",
@@ -187,6 +199,7 @@
"team" : "Enemy",
"normal_stats" : {
"LVL" : 1,
+ "EXP" : 0,
"HP" : 100,
"ATK" : 10,
"DEF" : 6,
@@ -202,10 +215,12 @@
"MOVE" : 6,
"SKIL" : 3,
"PASS" : 3,
- "AFFINITY" : "Water"
+ "AFFINITY" : "Water",
+ "EXPERTISE" : ["Sword"]
},
"active_stats" : {
"LVL" : 1,
+ "EXP" : 0,
"HP" : 10,
"ATK" : 10,
"DEF" : 6,
@@ -221,7 +236,8 @@
"MOVE" : 6,
"SKIL" : 3,
"PASS" : 3,
- "AFFINITY" : "Water"
+ "AFFINITY" : "Water",
+ "EXPERTISE" : ["Sword"]
},
"equipment" : {
"weapon" : "Basic_Sword_1",
diff --git a/data/img/board_overlays_1.png b/data/img/board_overlays_1.png
Binary files differ.
diff --git a/data/json/items.json b/data/json/items.json
@@ -0,0 +1,56 @@
+{
+ "Basic_Sword_1" : {
+ "name" : "Basic Sword",
+ "desc" : "A normal sword for normal people.",
+ "type" : "Sword",
+ "slot" : "weapon",
+ "value" : 100,
+ "rarity" : 1,
+ "range" : 1,
+ "stats" : {
+ "HP" : 0,
+ "ATK" : 3,
+ "DEF" : 0,
+ "SPD" : 0,
+ "ACC" : 0,
+ "INT" : 0,
+ "WIS" : 0,
+ "LUK" : 0,
+ "INIT" : 0,
+ "CNTR" : 0,
+ "GARD" : 0,
+ "PUSH" : 0,
+ "MOVE" : 0,
+ "SKIL" : 0,
+ "PASS" : 0
+ },
+ "effects" : []
+ },
+ "Basic_Armor_1" : {
+ "name" : "Basic Armor",
+ "desc" : "Armor so average it doesn't even warrant description",
+ "type" : "Armor",
+ "slot" : "armor",
+ "value" : 150,
+ "rarity" : 1,
+ "range" : 0,
+ "stats" : {
+ "HP" : 0,
+ "ATK" : 0,
+ "DEF" : 2,
+ "SPD" : 0,
+ "ACC" : 0,
+ "INT" : 0,
+ "WIS" : 0,
+ "LUK" : 0,
+ "INIT" : 0,
+ "CNTR" : 0,
+ "GARD" : 0,
+ "PUSH" : 0,
+ "MOVE" : 0,
+ "SKIL" : 0,
+ "PASS" : 0
+ },
+ "effects" : []
+ }
+}
diff --git a/src/board.py b/src/board.py
@@ -32,6 +32,9 @@ class BoardManager(manager.Manager):
# Move values
self.move_targets = {} # Keys = (x, y) of tiles, vals are a list of (x, y) tuples representing the path to be taken
self.previous_moves = {} # Keys = (x, y) of tiles, vals are the (x, y) of the previous node
+
+ # Attack values
+ self.attack_targets = [] # (x, y) of tiles
def load_board(self, boardname):
"""
@@ -106,6 +109,16 @@ class BoardManager(manager.Manager):
return e.custom_flags[1]
return None
+ def get_overlay_attack_entity_at_pos(self, pos):
+ """
+ Return (x, y) if there is a legal overlay attack entity at
+ 'pos', and return None otherwise.
+ """
+ for e in self.board_overlay:
+ if "OverlayAttack" in e.custom_flags and e.rect.collidepoint(pos):
+ return e.custom_flags[1]
+ return None
+
def display_as_move_range(self, piece, tile_pos_list):
"""
Display a move range from a given list of tile_pos
@@ -122,6 +135,21 @@ class BoardManager(manager.Manager):
e.custom_flags = ("OverlayMove", t)
self.board_overlay.add(e)
+ def display_as_attack_range(self, piece):
+ """
+ Display an attack range from a given list of tile_pos
+ tuples.
+ """
+ # Refresh overlay to begin with
+ self.load_overlay()
+
+ self.create_attack_range(piece)
+ for t in self.attack_targets:
+ e = entity.Entity(self.bus.fetch("sheet_manager", "sheets")["board_overlays_1"], (0, 1))
+ e.set_position((t[0] * TILE_WIDTH, t[1] * TILE_HEIGHT))
+ e.custom_flags = ("OverlayAttack", t)
+ self.board_overlay.add(e)
+
def create_move_range(self, piece):
"""
Create a legal move range for the given piece.
@@ -174,6 +202,20 @@ class BoardManager(manager.Manager):
if distances[ds] <= movemax:
self.move_targets[ds] = distances[ds]
+ def create_attack_range(self, piece):
+ """
+ Generate a list of legal attack targets within range of
+ the given piece.
+ """
+ self.attack_targets = []
+ for layer in self.current_board.tmx_data.visible_layers:
+ if isinstance(layer, pytmx.TiledTileLayer):
+ for x, y, gid in layer:
+ mx = piece.tile_pos[0] - x
+ my = piece.tile_pos[1] - y
+ if not (mx == 0 and my == 0) and (abs(mx) + abs(my)) <= piece.attack_range:
+ self.attack_targets.append((x, y))
+
def get_path_by_previous_moves(self, start_tile, target_tile):
"""
Generate a path (list of (x, y) tile_pos tuples) using
diff --git a/src/bus.py b/src/bus.py
@@ -116,6 +116,8 @@ class ManagerBus(subsystem.GameSubsystem):
self.game.scene_manager.current_scene.continue_script()
def perform_display_move_range_of_piece(self, piece):
self.game.board_manager.display_as_move_range(piece, self.game.piece_manager.get_piece_max_legal_move(piece))
+ def perform_display_attack_range_of_piece(self, piece):
+ self.game.board_manager.display_as_attack_range(piece)
def perform_shift_turns(self):
self.game.turn_manager.shift_turns()
def perform_trigger_turn_manager_buttons_at_pos(self, pos):
diff --git a/src/constants.py b/src/constants.py
@@ -42,6 +42,6 @@ SCENE_JSON_PATH = os.path.join(JSON_PATH, "scenes")
# 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_Watch_Move 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 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/game.py b/src/game.py
@@ -132,7 +132,6 @@ class Game(object):
self.sound_manager.update(None)
# Next, update all the subsurfaces/game objects and draw them
- # TODO: This is WIP and will change
self.interface.update_interface()
# State_Mode-specific actions (can be further subdivided by Control_Mode)
diff --git a/src/interface.py b/src/interface.py
@@ -53,7 +53,7 @@ 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:
+ if 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()
@@ -102,6 +102,10 @@ class GameInterface(subsystem.GameSubsystem):
self.game.control_mode = CTRL_MODES.Turn_Watch_Move
self.bus.perform_load_board_overlay()
+ # Selecting a target to attack
+ elif self.game.control_mode == CTRL_MODES.Turn_Select_Attack:
+ pass
+
# Still-scene mode behavior
elif self.game.state_mode == STATE_MODES.Still_Scene_Mode:
diff --git a/src/piece.py b/src/piece.py
@@ -213,7 +213,15 @@ class Piece(entity.Entity):
self.normal_stats = normal_stats
self.active_stats = active_stats
self.team = team
- self.equipment = equipment
+ self.equipment = {
+ "weapon" : None,
+ "armor" : None,
+ "acc1" : None,
+ "acc2" : None,
+ "acc3" : None
+ }
+ self.attack_range = None
+ self.equip([equipment[e] for e in equipment])
self.back_to_stand = False # TODO: This may not be the best way
self.current_tile_path = []
self.current_tile_path_index = 0
@@ -237,6 +245,51 @@ class Piece(entity.Entity):
directory.
"""
self.pictures["icons_small"] = pictures + "_icons_small"
+
+ def equip(self, equipment):
+ """
+ Assign equipment to this unit. 'equipment' is either a
+ single string or an iterable (list, tuple) of strings
+ that match entries in the item database.
+ """
+ database = json.load(open(os.path.join(JSON_PATH, "items.json")))
+ if type(equipment) == str:
+ if (database[equipment]["slot"] != "weapon" or database[equipment]["type"] in self.active_stats["EXPERTISE"]):
+ self.equipment[database[equipment]["slot"]] = equipment
+ else:
+ print("Failed to equip " + equipment + " to " + self.name)
+ elif type(equipment) in (list, tuple):
+ for e in equipment:
+ if e != None:
+ if database[e]["slot"] != "weapon" or database[e]["type"] in self.active_stats["EXPERTISE"]:
+ self.equipment[database[e]["slot"]] = e
+ else:
+ print("Failed to equip " + e + " to " + self.name)
+ self.derive_range()
+
+ def unequip(self, slot):
+ """
+ Unequip items from one or more slots. 'slot' is either
+ a single string corresponding to an equipment slot, or
+ an iterable (list, tuple) of such strings.
+ """
+ database = json.load(open(os.path.join(JSON_PATH, "items.json")))
+ if type(slot) == str:
+ self.equipment[slot] = None
+ elif type(slot) in (list, tuple):
+ for s in slot:
+ self.equipment[s] = None
+ self.derive_range()
+
+ def derive_range(self):
+ """
+ Figure out this piece's range based on equipment.
+ """
+ database = json.load(open(os.path.join(JSON_PATH, "items.json")))
+ if self.equipment["weapon"] != None:
+ self.attack_range = database[self.equipment["weapon"]]["range"]
+ else:
+ self.attack_range = 0
def set_facing(self, direction):
"""
diff --git a/src/turn.py b/src/turn.py
@@ -60,7 +60,7 @@ class TurnManager(manager.Manager):
self.current_turn = 1
for b in range(0, 6):
nab = ActionButton(sheets["action_buttons_1"], (0, b), None, False, self)
- nab.set_position(((SCREEN_WIDTH // 8), (SCREEN_HEIGHT // 12) + (((SCREEN_HEIGHT // 14) - 4)) * b))
+ nab.set_position(((SCREEN_WIDTH // 8), (SCREEN_HEIGHT // 10) + (((SCREEN_HEIGHT // 14) - 4)) * b))
self.action_buttons.append(nab)
self.shift_turns()
@@ -184,6 +184,10 @@ class TurnManager(manager.Manager):
self.bus.perform_display_move_range_of_piece(self.current_active_piece)
self.camera.snap_to_position(self.current_active_piece.rect.center)
self.game.control_mode = CTRL_MODES.Turn_Select_Move
+ elif self.action_buttons[1].rect.collidepoint(pos) and self.action_buttons[1].clickable:
+ self.bus.perform_display_attack_range_of_piece(self.current_active_piece)
+ self.camera.snap_to_position(self.current_active_piece.rect.center)
+ self.game.control_mode = CTRL_MODES.Turn_Select_Attack
elif self.action_buttons[5].rect.collidepoint(pos) and self.action_buttons[5].clickable:
self.shift_turns()
@@ -277,7 +281,7 @@ class TurnIcon(entity.Entity):
self.piece_picture = entity.Entity(self.manager.bus.fetch("sheet_manager", "sheets")[piece.pictures["icons_small"]])
self.font = pygame.font.Font(os.path.join(FONT_PATH, UI_FONT), SCREEN_HEIGHT // 18)
self.index = index
- self.set_position(((3 * (SCREEN_WIDTH // 4)) + 4, ((2 * (SCREEN_HEIGHT // 20)) + 1) + ((SCREEN_HEIGHT // 24) * index)))
+ self.set_position(((3 * (SCREEN_WIDTH // 4)) + 4, ((SCREEN_HEIGHT // 10) + 1) + ((SCREEN_HEIGHT // 24) * index)))
self.piece_picture.set_position(self.rect.topleft)
self.clickable = False
self.health_bar = None