Return to repo list

heart-of-gold

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

commit 92e8b7ff43a46d685b01b3f7bb2829256f8b59c0
parent b228ff7994da8a1d0153836065223a3bc19de50d
Author: Erik Letson <hmagellan@hmagellan.com>
Date:   Tue,  3 Nov 2020 17:59:15 -0600

Refactor checkpoint 1, as-of-yet non-functional

Diffstat:
Msrc/board.py | 21+++++++++------------
Msrc/game.py | 8+++++---
Msrc/piece.py | 307++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/subsystem.py | 19++++++++++++++++---
Msrc/vgo.py | 356+++++++------------------------------------------------------------------------
5 files changed, 365 insertions(+), 346 deletions(-)

diff --git a/src/board.py b/src/board.py @@ -41,7 +41,6 @@ class BoardManager(manager.Manager): self.current_board_dimensions = (0, 0) # Move values - self.moving_entity = None 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 @@ -134,22 +133,21 @@ class BoardManager(manager.Manager): self.load_overlay() # TEMP!!!! - self.create_move_range(self.game.entity_manager.selected_entity) + self.create_move_range(self.game.piece_manager.selected_piece) for t in self.move_targets: v = vgo.VisibleGameObject(self.game.sheet_manager.loaded_sheets["board_overlays_1"], (1, 0)) v.set_position((t[0] * self.current_board.tmx_data.tilewidth, t[1] * self.current_board.tmx_data.tileheight)) v.custom_flags = ("OverlayMove", t) self.board_overlay.add(v) - def create_move_range(self, entity): + def create_move_range(self, piece): """ - Create a legal move range for the given entity - and set that entity as the moving entity. This - is mostly a subtractive process. + Create a legal move range for the given piece. + This is mostly a subtractive process. """ # Setup self.empty_move_range() - movemax = entity.unit.active_stats["MOVE"] + movemax = piece.active_stats["MOVE"] mx = 0 my = 0 distances = {} @@ -164,16 +162,16 @@ class BoardManager(manager.Manager): for layer in self.current_board.tmx_data.visible_layers: if isinstance(layer, pytmx.TiledTileLayer): for x, y, gid in layer: - mx = entity.tile_pos[0] - x - my = entity.tile_pos[1] - y + mx = piece.tile_pos[0] - x + my = piece.tile_pos[1] - y if (abs(mx) + abs(my)) <= movemax and self.current_board.tmx_data.get_tile_properties_by_gid(gid)["Passable"] == 1: distances[(x, y)] = movemax + 1 # So we are always greater than the max move # Next, calculate the move from the starting pos to each potential # This implements Dijkstra's algorithm (kinda) - distances[entity.tile_pos] = 0 + distances[piece.tile_pos] = 0 adj_list = self.get_adjacency_list(list(distances.keys())) - pq.put((entity.tile_pos, 0)) + pq.put((piece.tile_pos, 0)) # While there are still potentials to check while not pq.empty(): @@ -247,7 +245,6 @@ class BoardManager(manager.Manager): """ Reset to a default, non-moving state. """ - self.moving_entity = None self.move_targets = {} self.previous_moves = {} diff --git a/src/game.py b/src/game.py @@ -1,5 +1,5 @@ import pygame -from . import subsystem, manager, images, sound, board, vgo, unit, menu, scene +from . import subsystem, manager, images, sound, board, vgo, piece, unit, menu, scene from .constants import * ########### @@ -49,7 +49,8 @@ class Game(object): self.sound_manager = sound.SoundManager(self, self.manager_bus) self.menu_manager = menu.MenuManager(self, self.manager_bus) self.board_manager = board.BoardManager(self, self.manager_bus) - self.entity_manager = vgo.EntityManager(self, self.manager_bus) + #self.entity_manager = vgo.EntityManager(self, self.manager_bus) + self.piece_manager = piece.PieceManager(self, self.manager_bus) self.unit_manager = unit.UnitManager(self, self.manager_bus) self.scene_manager = scene.SceneManager(self, self.manager_bus) @@ -134,7 +135,8 @@ class Game(object): self.menu_manager.update_current_menu(self.camera.camera_surface) elif self.state_mode == STATE_MODES.Battle_Mode: self.board_manager.update_board(self.camera.camera_surface) - self.entity_manager.update_entities(self.camera.camera_surface) + self.piece_manager.update_entities(self.camera.camera_surface) + self.piece_manager.update_tile_cursor(self.camera.camera_surface) # NOTE: MenuManager should not be an overlay for the # battle mode. In-battle menus should be handled # by EntityManager most likely diff --git a/src/piece.py b/src/piece.py @@ -1,4 +1,4 @@ -import pygame, os +import pygame, os, json from . import manager, unit from .constants import * @@ -9,11 +9,316 @@ from .constants import * # This file contains the following: # 1. The PieceManager object, which manages pieces on the game board # 2. The Piece entity child object, which represents a piece on the game board +# 3. The TileCursor entity child object, which acts as a cursor on the board and is managed by PieceManager ###################################### # Section 1 - The PieceManager class # ###################################### +class PieceManager(manager.Manager): + """ + PieceManager acts as a manager for all Pieces (Entities + representing playable characters on the game board), as + well as for the TileCursor object that is used to interact + with units and tiles. + """ + + def __init__(self, game, bus): + + # Parent initialization + super().__init__(game, bus) + + # Entity values + self.pieces = pygame.sprite.LayeredDirty() + self.selected_piece = None + + # Cursor values + self.tile_cursor = None + + def add_piece(self, piece): + """ + Add an piece to the pieces group. + """ + self.pieces.add(piece) + + def load_pieces_from_def(self, definition): + """ + Loaded one or more pieces from a definition + dictionary. A piece definition is a subcomponent + of a larger definition dict, likely loaded from + JSON. + """ + # TODO: There could be some type-checking for a valid definition + # here. This should probably be so for all methods that load + # from definition. + for p in definition: + n_sheet = self.bus.fetch_sheet(definition[p]["sheet"]) + n_sprite = tuple(definition[p]["sprite"]) + n_anim = self.bus.fetch_animation(n_sheet, definition[p]["animation"]) + n_animated = definition[p]["animated"] + n_name = definition[p]["name"] + n_passable = definition[p]["passable"] + n_nstats = definition[p]["normal_stats"] + n_astats = definition[p]["active_stats"] + n_team = definition[p]["team"] + # TODO: ID needs fixing + self.add_piece(Piece(n_sheet, n_sprite, n_anim, n_animated, n_name, n_passable, n_nstats, n_astats, n_team)) + + def load_tile_cursor(self, sheet): + """ + Load a TileCursor object to highlight selected tiles. + """ + self.tile_cursor = TileCursor("Tile_Cursor", self.total_entities, self.bus.fetch_sheet(sheet)) + self.tile_cursor.set_animation(self.bus.fetch_animation("cursor1", "pulse"), True) + + def get_piece_by_name(self, name): + """ + Returns a piece matching name, or None if no such piece + is found. + """ + piece = None + for p in self.pieces: + if p.name == name + piece = p + break + return piece + + def get_piece_by_tile(self, tile_pos): + """ + Return the first piece found that is located at tile_pos, + or None if no piece is found. + """ + found_piece = None + for p in self.pieces: + if p.tile_pos == (tile_pos[0], tile_pos[1]): + found_piece = p + return found_piece + + def position_tile_cursor(self, tile_pos): + """ + Snap the TileCursor object's position to 'tile_pos' and set + the tile GID there as the current tile_cursor's selected tile. + """ + self.tile_cursor.assign_tile(tile_pos) + self.tile_cursor.snap_to_tile() + + def select_piece_with_tile_cursor(self): + """ + Select the piece under the tile cursor right now. If + no piece is under the tile cursor, the selected piece + should become None. + """ + # TODO: This could stand to be more robust + self.selected_piece = self.get_piece_by_tile(self.tile_cursor.tile_pos) + + def update_tile_cursor(self, surface = None): + """ + Update the tile cursor object. + """ + if surface != None: + self.tile_cursor.update(surface) + + def update_pieces(self, surface = None): + """ + Update all pieces. + """ + if surface != None: + self.pieces.update(surface) + + #def load_pieces_from_json(self, entsjson): + # """ + # Load one or more pieces from a JSON file. + # """ + # j = json.load(open(os.path.join(ENTITY_JSON_PATH, entsjson))) + # for e in j: + # # TODO: type should be enum + # if j[e]["type"] == "Entity": + # ne = Entity(j[e]["name"], 0, + # self.game.sheet_manager.loaded_sheets[j[e]["sheet"]], tuple(j[e]["sprite"]), + # self.game.sheet_manager.animations[j[e]["sheet"]][j[e]["animation"]], + # j[e]["animated"], j[e]["passable"], unit.Unit(self, self.game.unit_manager.get_stats(j[e]["name"]))) + # elif j[e]["type"] == "Piece": + # ne = Piece(j[e]["name"], 0, + # self.game.sheet_manager.loaded_sheets[j[e]["sheet"]], tuple(j[e]["sprite"]), + # self.game.sheet_manager.animations[j[e]["sheet"]][j[e]["animation"]], + # j[e]["animated"], j[e]["passable"], unit.Unit(self, self.game.unit_manager.get_stats(j[e]["name"])), + # j[e]["team"]) + # self.pieces.append(ne) + # ne.assign_tile(self.game.board_manager.get_tile_at_tile_pos(tuple(j[e]["tile"]))) + # ne.snap_to_tile() + # self.add_entity(ne) + + #def set_entity_move_to_tile_path(self, entity, path): + # """ + # Assigns a path to the selected entity. + # """ + # if entity != None and path != None: + # entity.set_move_along_tile_path(path) + # return True + # else: + # return False + + #def set_entity_move_to_tile_pos(self, entity, tile_pos): + # """ + # Sets the entity to move to the given tile_pos. A 'move' + # is an assignment from the EntityManager. A 'motion' is + # a setting similar to an animation (but for spatial travel) + # for an entity. + # """ + # # TODO: Set speed of motion somehow, maybe calculated from MOVE of entity + # if entity != None and tile_pos != None: + # entity.set_motion((tile_pos[0] * self.game.board_manager.current_board.tmx_data.tilewidth, tile_pos[1] * self.game.board_manager.current_board.tmx_data.tileheight), 6) + # entity.tile_pos = tile_pos + # return True + # else: + # return False + + #def select_entities_with_tile_cursor(self, tile_def): + # """ + # Select the current entity under the tile cursor by way + # of the tile_def. Returns True if an entity is selected, + # and False otherwise. + # """ + # # TODO: Eventually this should return info about the team of the selected + # # entity so that GameInterface can make control mode decisions. + # e = self.get_entities_by_tile(tile_def) + # if e != [] and e[0].team == "Player": + # self.selected_entity = e[0] # Select only the topmost ent on this tile + # self.game.board_manager.display_as_move_range(self.get_entity_legal_move_tile_pos(self.selected_entity)) + # return True + # else: + # self.selected_entity = None + # self.game.board_manager.load_overlay() + # return False + + #def get_entity_legal_move_tile_pos(self, entity): + # """ + # Calculate potential legal moves for entity. Note, this + # does not return tiles, but rather hypothetical legal + # tile_pos coordinate in the form (x, y). The return is + # a list of these coordinates. Uses the A* algorithm. + # """ + # m = entity.unit.active_stats["MOVE"] + # legal_moves = [] + # for x in range(0, self.game.board_manager.current_board.tmx_data.width): + # for y in range(0, self.game.board_manager.current_board.tmx_data.height): + # mx = entity.tile_pos[0] - x + # my = entity.tile_pos[1] - y + # if (abs(mx) + abs(my)) <= m: + # legal_moves.append((x, y)) + # return legal_moves + ############################### # Section 2 - The Piece class # ############################### + +class Piece(vgo.Entity): + """ + Object that represents a playable piece on the board. Mostly + only differs from entity in that it expects the standard + animation format to allow for facing, moving, etc. Has some + sligthly modified move_motion and set_motion methods. + """ + + def __init__(self, sheet, sprite = (0, 0), animation = None, animated = False, + name, passable = False, normal_stats = None, active_stats = None, team = None): + + # Parent initialization + super().__init__(sheet, sprite, animation, animated) + + # Face settings + self.facing = FACE_DIR.L + + # Others + self.name = name + self.passable = passable + self.normal_stats = normal_stats + self.active_stats = active_stats + self.team = team # TODO: team maybe should be defined in unit + self.back_to_stand = False # TODO: This may not be the best way + self.current_tile_path = [] + self.current_tile_path_index = 0 + self.path_moving = False + + def set_motion(self, target_pos, speed): + """ + Overwrite of the basic Entity version to support facing and + animating automatically. + """ + super().set_motion(target_pos, speed) + + # Decide facing + # TODO: This should be determined by the last move in the path instead + mx = target_pos[0] - self.rect.topleft[0] + my = target_pos[1] - self.rect.topleft[1] + a = math.atan2(mx, my) * 180 / math.pi + if a >= -67.5 and a < 0: + self.facing = FACE_DIR.D + elif a >= 0 and a < 112.5: + self.facing = FACE_DIR.R + elif a > 112.5 or a < -112.5: + self.facing = FACE_DIR.U + else: + self.facing = FACE_DIR.L + + # Set a movement animation + # TODO: It may be neccessary in the future to indicate whether or not this + # animation should be assigned according to whether or not a board move is + # actually taking place. Also, this is a very roundabout way to reference + # the sheet's animations. Maybe sheets should be aware of their anims? + self.set_animation(self.sheet.manager.animations[self.sheet.name]["walk_" + self.facing.name], True) + + def set_move_along_tile_path(self, tile_seq): + """ + Move along a sequence of tiles in order. + """ + self.current_tile_path = tile_seq + self.current_tile_path_index = 0 + self.path_moving = True + + def execute_tile_path_move(self): + """ + Execute a move along a tile path. + """ + if self.current_tile_path != None and self.path_moving: + if self.motion == {} and self.current_tile_path_index < len(self.current_tile_path): + next_tar = (self.current_tile_path[self.current_tile_path_index][0] * TILE_WIDTH, self.current_tile_path[self.current_tile_path_index][1] * TILE_HEIGHT) + self.set_motion(next_tar, PIECE_MOVE_SPEED) + self.current_tile_path_index += 1 + elif self.motion == {}: + self.tile_pos = self.current_tile_path[self.current_tile_path_index - 1] + self.path_moving = False + self.current_tile_path = [] + self.back_to_stand = True + + def act(self): + """ + Overwriting basic act mode. + """ + # 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() + 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 + +########################## +# Section 3 - TileCursor # +########################## + +class TileCursor(vgo.Entity): + """ + Object that follows the cursor to indicate selected/highlighted + tiles. + """ + + def __init__(self, name, ent_id, sheet, sprite = (0, 0), animation = None, animated = False, passable = True, unit = None): + + # Parent initialization + super().__init__(name, ent_id, sheet, sprite, animation, animated, passable, unit) + + # VGO settings + self.custom_flags = "TileCursor" + + # DirtySprite settings + self.layer = 1 diff --git a/src/subsystem.py b/src/subsystem.py @@ -91,10 +91,14 @@ class GameInterface(GameSubsystem): self.game.menu_manager.trigger_button_at_pos(mousepos) elif self.game.state_mode == STATE_MODES.Battle_Mode: if self.game.control_mode == CTRL_MODES.Turn_Normal: - if self.game.entity_manager.select_entities_with_tile_cursor(self.game.board_manager.get_tile_at_position(mousepos)): + # TODO: Bussing + # NOTE: Functional change... + self.game.piece_manager.select_piece_with_tile_cursor() + if self.game.piece_manager.selected_piece != None: self.game.control_mode = CTRL_MODES.Turn_Select_Move elif self.game.control_mode == CTRL_MODES.Turn_Select_Move: to_path = self.game.board_manager.get_path_by_previous_moves(self.game.entity_manager.selected_entity.tile_pos, self.game.board_manager.get_tile_pos_at_position(mousepos)) + # TODO: Decouple this and make it work with the modifications if self.game.entity_manager.set_entity_move_to_tile_path(self.game.entity_manager.selected_entity, to_path): self.game.lose_control(len(to_path) * PIECE_MOVE_DELAY, CTRL_MODES.Turn_Normal) self.game.board_manager.load_overlay() @@ -150,8 +154,17 @@ class ManagerBus(GameSubsystem): # Parent init super().__init__(game) - # Module references - self.managers = {} + # Fetches: All return values from managers + def fetch_sheet(self, name): + return self.game.sheet_manager.loaded_sheets[name] + def fetch_animation(self, sheet, anim): + return self.game.sheet_manager.animations[sheet][anim] + def fetch_piece_by_name(self, name): + return self.game.piece_manager.get_piece_by_name(name) + def fetch_piece_by_tile(self, tile_pos): + return self.game.piece_manager.get_piece_by_tile(tile_pos) + def fetch_tile_by_screen_pos(self, position): + return self.game.board_manager.get_tile_by_position(position) #################################### # Section 4 - The GameCamera class # diff --git a/src/vgo.py b/src/vgo.py @@ -51,23 +51,22 @@ from .constants import * # to by the oracle. # This file contains: -# 1. The VisibleGameObject class that defines the abstract parent of all entities and other visible sprites -# 2. The Entity class that defines the objects that represent non-UI-element game objects -# 3. Various kinds of specialized entity classes -# 4. Managers for VGO objects +# 1. The Entity class that is the extension of pygame.Sprite used throughout the game +# 2. Various kinds of specialized entity classes that don't fit in other source files # TODO: This should eventually use LayeredDirty sprites for performance reasons # TODO: Eventually need to add a UI-element class type (sub of entity? Should entity replace VGO entirely???) -################################### -# Section 1 - Visible Game Object # -################################### +############################ +# Section 1 - Entity class # +############################ -class VisibleGameObject(pygame.sprite.DirtySprite): +class Entity(pygame.sprite.DirtySprite): """ The parent of all visible objects. VisibleGameObject (VGO) is essentially an extended, customised version of the PyGame Sprite - object. + object. Entity supports animations, motions (movement over time), + and tile operations, but none of that is required. """ def __init__(self, sheet, sprite = (0, 0), animation = None, animated = False): @@ -99,6 +98,10 @@ class VisibleGameObject(pygame.sprite.DirtySprite): self.motion = {} self.motion_timer = 0 + # Tile values + self.tile_pos = (-1, -1) + self.tile_gid = None + def set_sprite(self, sprite_coord): """ Set the VGO's sprite to another one on the board. The @@ -179,6 +182,21 @@ class VisibleGameObject(pygame.sprite.DirtySprite): self.set_center_position(self.motion["target"]) # Make sure we end up in exactly the right spot self.motion = {} + def snap_to_tile(self): + """ + Snap the Entity to its current tile. + """ + if self.tile_pos != (-1, -1): + self.set_position((self.tile_pos[0] * TILE_WIDTH, self.tile_pos[1] * TILE_HEIGHT)) + + def assign_tile(self, tile_def): + """ + Assign a tile as this entity object's occupied tile. + Assume the values are legitimate. + """ + self.tile_pos = (tile_def[0], tile_def[1]) + self.tile_gid = tile_def[2] + def act(self): """ This method is called as part of update() and is meant @@ -200,324 +218,8 @@ class VisibleGameObject(pygame.sprite.DirtySprite): self.animate() surface.blit(self.image, self.rect) -############################# -# Section 2 - Entity Object # -############################# - -class Entity(VisibleGameObject): - """ - Entities are VGOs that can occupy a tile. They are managed - by an EntityManager and record tile x, y and gid values. An - Entity does not have to occupy a tile but it can. Entities - also have a name (human-readable string set in their definition) - and a ent_id (number assigned by entity manager). Entities can - also optionally have a Unit assigned to them to represent - their statistics in gameplay. - """ - - def __init__(self, name, ent_id, sheet, sprite = (0, 0), animation = None, animated = False, passable = False, unit = None): - - # Parent initialization - super().__init__(sheet, sprite, animation, animated) - - # Saved values - self.name = name - self.ent_id = ent_id - self.passable = passable - self.unit = unit - - # Tile values - self.tile_pos = (-1, -1) - self.tile_gid = None - - def snap_to_tile(self): - """ - Snap the Entity to its current tile. - """ - if self.tile_pos != (-1, -1): - self.set_position((self.tile_pos[0] * TILE_WIDTH, self.tile_pos[1] * TILE_HEIGHT)) - - def assign_tile(self, tile_def): - """ - Assign a tile as this entity object's occupied tile. - Assume the values are legitimate. - """ - self.tile_pos = (tile_def[0], tile_def[1]) - self.tile_gid = tile_def[2] - ######################################### -# Section 3 - Various Entity subclasses # +# Section 2 - Various Entity subclasses # ######################################### -class TileCursor(Entity): - """ - Object that follows the cursor to indicate selected/highlighted - tiles. - """ - - def __init__(self, name, ent_id, sheet, sprite = (0, 0), animation = None, animated = False, passable = True, unit = None): - - # Parent initialization - super().__init__(name, ent_id, sheet, sprite, animation, animated, passable, unit) - - # VGO settings - self.custom_flags = "TileCursor" - - # DirtySprite settings - self.layer = 1 - -class Piece(Entity): - """ - Object that represents a playable piece on the board. Mostly - only differs from entity in that it expects the standard - animation format to allow for facing, moving, etc. Has some - sligthly modified move_motion and set_motion methods. - """ - - def __init__(self, name, ent_id, sheet, sprite = (0, 0), animation = None, animated = False, passable = False, unit = None, team = None): - - # Parent initialization - super().__init__(name, ent_id, sheet, sprite, animation, animated, passable, unit) - - # Face settings - self.facing = FACE_DIR.L - - # Others - self.team = team # TODO: team maybe should be defined in unit - self.back_to_stand = False # TODO: This may not be the best way - self.current_tile_path = [] - self.current_tile_path_index = 0 - self.path_moving = False - - def set_motion(self, target_pos, speed): - """ - Overwrite of the basic VGO version to support facing and - animating automatically. - """ - super().set_motion(target_pos, speed) - - # Decide facing - mx = target_pos[0] - self.rect.topleft[0] - my = target_pos[1] - self.rect.topleft[1] - a = math.atan2(mx, my) * 180 / math.pi - if a >= -67.5 and a < 0: - self.facing = FACE_DIR.D - elif a >= 0 and a < 112.5: - self.facing = FACE_DIR.R - elif a > 112.5 or a < -112.5: - self.facing = FACE_DIR.U - else: - self.facing = FACE_DIR.L - - # Set a movement animation - # TODO: It may be neccessary in the future to indicate whether or not this - # animation should be assigned according to whether or not a board move is - # actually taking place. Also, this is a very roundabout way to reference - # the sheet's animations. Maybe sheets should be aware of their anims? - self.set_animation(self.sheet.manager.animations[self.sheet.name]["walk_" + self.facing.name], True) - - def set_move_along_tile_path(self, tile_seq): - """ - Move along a sequence of tiles in order. - """ - self.current_tile_path = tile_seq - self.current_tile_path_index = 0 - self.path_moving = True - - def execute_tile_path_move(self): - """ - Execute a move along a tile path. - """ - if self.current_tile_path != None and self.path_moving: - if self.motion == {} and self.current_tile_path_index < len(self.current_tile_path): - next_tar = (self.current_tile_path[self.current_tile_path_index][0] * TILE_WIDTH, self.current_tile_path[self.current_tile_path_index][1] * TILE_HEIGHT) - self.set_motion(next_tar, PIECE_MOVE_SPEED) - self.current_tile_path_index += 1 - elif self.motion == {}: - self.tile_pos = self.current_tile_path[self.current_tile_path_index - 1] - self.path_moving = False - self.current_tile_path = [] - self.back_to_stand = True - - def act(self): - """ - Overwriting basic act mode. - """ - # 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() - 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 - -############################################## -# Section 4 - Managers for VGOs & Subclasses # -############################################## - -class EntityManager(manager.Manager): - """ - Manager for all entity objects. Handles creating them, placing - them on the screen, managing their updates, disposing of them - when need be, and transferring info to them for e.g. moving. It - should be noted that EntityManager is only employed for in-battle - entities. Menus manage their own entities in menu modes. - """ - # TODO: EntityManager should eventually be managing menu entities as well. - def __init__(self, game, bus): - - super().__init__(game, bus) - - # Entity values - self.loaded_entities = pygame.sprite.LayeredDirty() - self.tile_cursor = None - self.selected_entity = None - self.total_entities = 0 # total number of unique entities loaded - self.pieces = [] - - def add_entity(self, entity): - """ - Add an entity to the loaded_entities group and increment - the number of total_entities. - """ - self.loaded_entities.add(entity) - self.total_entities += 1 - - def load_entities_from_json(self, entsjson): - """ - Load one or more entities from a JSON file. - """ - j = json.load(open(os.path.join(ENTITY_JSON_PATH, entsjson))) - - for e in j: - # TODO: type should be enum - if j[e]["type"] == "Entity": - ne = Entity(j[e]["name"], self.total_entities, - self.game.sheet_manager.loaded_sheets[j[e]["sheet"]], tuple(j[e]["sprite"]), - self.game.sheet_manager.animations[j[e]["sheet"]][j[e]["animation"]], - j[e]["animated"], j[e]["passable"], unit.Unit(self, self.game.unit_manager.get_stats(j[e]["name"]))) - elif j[e]["type"] == "Piece": - ne = Piece(j[e]["name"], self.total_entities, - self.game.sheet_manager.loaded_sheets[j[e]["sheet"]], tuple(j[e]["sprite"]), - self.game.sheet_manager.animations[j[e]["sheet"]][j[e]["animation"]], - j[e]["animated"], j[e]["passable"], unit.Unit(self, self.game.unit_manager.get_stats(j[e]["name"])), - j[e]["team"]) - self.pieces.append(ne) - ne.assign_tile(self.game.board_manager.get_tile_at_tile_pos(tuple(j[e]["tile"]))) - ne.snap_to_tile() - self.add_entity(ne) - - def load_tile_cursor(self, sheet): - """ - Load a TileCursor object to highlight selected tiles. - """ - self.tile_cursor = TileCursor("Tile_Cursor", self.total_entities, self.game.sheet_manager.loaded_sheets[sheet]) - self.tile_cursor.set_animation(self.game.sheet_manager.animations["cursor1"]["pulse"], True) - self.add_entity(self.tile_cursor) - - def get_entity_by_id(self, idnum): - """ - Returns an entity by its id number. - """ - ent = None - for e in self.loaded_entities: - if e.ent_id == idnum: - ent = e - break - return ent - - def get_entity_by_name(self, name): - """ - Returns an entity matching name. - """ - ent = None - for e in self.loaded_entities: - if e.name == name - ent = e - break - return ent - - def position_tile_cursor(self, tile_def): - """ - Snap the TileCursor object's position to 'pos' and set the - tile GID there as the current tile_cursor's selected tile. - """ - self.tile_cursor.assign_tile(tile_def) - self.tile_cursor.snap_to_tile() - - def get_entities_by_tile(self, tile_def): - """ - Find if there are any loaded entities at the provided tile - definition. - """ - found_ents = [] - for e in self.loaded_entities: - if e.tile_pos == (tile_def[0], tile_def[1]) and e.custom_flags != "TileCursor": - found_ents.append(e) - return found_ents - - def select_entities_with_tile_cursor(self, tile_def): - """ - Select the current entity under the tile cursor by way - of the tile_def. Returns True if an entity is selected, - and False otherwise. - """ - # TODO: Eventually this should return info about the team of the selected - # entity so that GameInterface can make control mode decisions. - e = self.get_entities_by_tile(tile_def) - if e != [] and e[0].team == "Player": - self.selected_entity = e[0] # Select only the topmost ent on this tile - self.game.board_manager.display_as_move_range(self.get_entity_legal_move_tile_pos(self.selected_entity)) - return True - else: - self.selected_entity = None - self.game.board_manager.load_overlay() - return False - - def get_entity_legal_move_tile_pos(self, entity): - """ - Calculate potential legal moves for entity. Note, this - does not return tiles, but rather hypothetical legal - tile_pos coordinate in the form (x, y). The return is - a list of these coordinates. Uses the A* algorithm. - """ - m = entity.unit.active_stats["MOVE"] - legal_moves = [] - for x in range(0, self.game.board_manager.current_board.tmx_data.width): - for y in range(0, self.game.board_manager.current_board.tmx_data.height): - mx = entity.tile_pos[0] - x - my = entity.tile_pos[1] - y - if (abs(mx) + abs(my)) <= m: - legal_moves.append((x, y)) - return legal_moves - - def set_entity_move_to_tile_pos(self, entity, tile_pos): - """ - Sets the entity to move to the given tile_pos. A 'move' - is an assignment from the EntityManager. A 'motion' is - a setting similar to an animation (but for spatial travel) - for an entity. - """ - # TODO: Set speed of motion somehow, maybe calculated from MOVE of entity - if entity != None and tile_pos != None: - entity.set_motion((tile_pos[0] * self.game.board_manager.current_board.tmx_data.tilewidth, tile_pos[1] * self.game.board_manager.current_board.tmx_data.tileheight), 6) - entity.tile_pos = tile_pos - return True - else: - return False - - def set_entity_move_to_tile_path(self, entity, path): - """ - Assigns a path to the selected entity. - """ - if entity != None and path != None: - entity.set_move_along_tile_path(path) - return True - else: - return False - - def update_entities(self, surface = None): - """ - Update all loaded entities. - """ - if surface != None: - self.loaded_entities.update(surface) +# TODO: