Return to repo list

heart-of-gold

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

commit d9484e2714318809c8f6314b086956b43b965923
parent 1ab179b32ef82b635bbc5598012658d0379bb643
Author: Erik Letson <hmagellan@hmagellan.com>
Date:   Tue, 27 Oct 2020 12:28:15 -0500

Very dirty implement of path moving, not perfect

Diffstat:
Msrc/board.py | 58+++++++++++++++++++++++++++++++++++++++-------------------
Msrc/subsystem.py | 7+++++--
Msrc/vgo.py | 52++++++++++++++++++++++++++++++++++++++++++++++++----
3 files changed, 92 insertions(+), 25 deletions(-)

diff --git a/src/board.py b/src/board.py @@ -42,7 +42,7 @@ class BoardManager(manager.Manager): # 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.chosen_move = [] # The move to actually exectute + self.previous_moves = {} # Keys = (x, y) of tiles, vals are the (x, y) of the previous node def load_board_from_file(self, boardfile): """ @@ -73,6 +73,20 @@ class BoardManager(manager.Manager): self.current_board = self.loaded_boards[boardname] self.load_overlay() + def get_tile_pos_at_position(self, pos): + """ + Return (x, y) tile_pos if there is a tile at 'pos', and + return None otherwise. + """ + w = self.current_board.tmx_data.tilewidth + h = self.current_board.tmx_data.tileheight + for layer in self.current_board.tmx_data.visible_layers: + if isinstance(layer, pytmx.TiledTileLayer): + for x, y, gid in layer: + if pos[0] >= x * w and pos[0] < (x + 1) * w and pos[1] >= y * w and pos[1] < (y + 1) * w: + return (x, y) + return None + def get_tile_at_position(self, pos): """ Return (x, y, gid) if there is tile at 'pos', and return @@ -117,18 +131,6 @@ class BoardManager(manager.Manager): # First, refresh overlay to avoid drawing over existing move markers self.load_overlay() - # Next, if there are some extant tile_pos moves listed, display them - #if tile_pos_list != None: - # for layer in self.current_board.tmx_data.visible_layers: - # if isinstance(layer, pytmx.TiledTileLayer): - # for x, y, gid in layer: - # for p in tile_pos_list: - # if p == (x, y) and self.current_board.tmx_data.get_tile_properties_by_gid(gid)["Passable"] == 1 and self.game.entity_manager.selected_entity.tile_pos != (x, y): - # v = vgo.VisibleGameObject(self.game.sheet_manager.loaded_sheets["board_overlays_1"], (1, 0)) - # v.set_position((x * self.current_board.tmx_data.tilewidth, y * self.current_board.tmx_data.tileheight)) - # v.custom_flags = ("OverlayMove", (x, y)) - # self.board_overlay.add(v) - # TEMP!!!! self.create_move_range(self.game.entity_manager.selected_entity) for t in self.move_targets: @@ -150,7 +152,6 @@ class BoardManager(manager.Manager): my = 0 distances = {} adj_list = {} - #visited = {} # NOTE: Ignoring visited nodes seems to break it pq = queue.PriorityQueue() index = -1 min_val = -1 @@ -170,17 +171,18 @@ class BoardManager(manager.Manager): # This implements Dijkstra's algorithm (kinda) distances[entity.tile_pos] = 0 adj_list = self.get_adjacency_list(list(distances.keys())) - #visited = { d : False for d in distances } pq.put((entity.tile_pos, 0)) + # While there are still potentials to check while not pq.empty(): index, min_val = pq.get() - #visited[index] = True + # If we could possibly optimise it, try to if distances[index] >= min_val: for n in adj_list[index]: - #if adj_list[index][n] in visited.keys() and not visited[adj_list[index][n]]: if adj_list[index][n] in distances.keys(): + if adj_list[index][n] not in self.previous_moves.keys(): + self.previous_moves[adj_list[index][n]] = index new_dist = distances[index] + 1 if new_dist < distances[adj_list[index][n]]: distances[adj_list[index][n]] = new_dist @@ -191,6 +193,25 @@ class BoardManager(manager.Manager): if distances[ds] <= movemax: self.move_targets[ds] = distances[ds] + def get_path_by_previous_moves(self, start_tile, target_tile): + """ + Generate a path (list of (x, y) tile_pos tuples) using + the self.previous_moves dict, if it is filled, to the + given target tile from the start tile. If it can't be + done, return None. + """ + if self.previous_moves != {} and target_tile in self.move_targets.keys(): + n = target_tile + path = [] + while n != start_tile: + path.append(self.previous_moves[n]) + n = self.previous_moves[n] + path.reverse() + path.append(target_tile) + return path + else: + return None + def get_adjacent_tiles_by_tile_pos(self, tile_pos): """ Return cardinal adjacent tiles of the given tile_pos. @@ -227,8 +248,7 @@ class BoardManager(manager.Manager): """ self.moving_entity = None self.move_targets = {} - self.chosen_move = [] - self.load_overlay() + self.previous_moves = {} def update_board(self, surface = None): """ diff --git a/src/subsystem.py b/src/subsystem.py @@ -87,9 +87,12 @@ class GameInterface(GameSubsystem): if self.game.entity_manager.select_entities_with_tile_cursor(self.game.board_manager.get_tile_at_position(pygame.mouse.get_pos())): self.game.control_mode = CTRL_MODES.Turn_Select_Move elif self.game.control_mode == CTRL_MODES.Turn_Select_Move: - if self.game.entity_manager.set_entity_move_to_tile_pos(self.game.entity_manager.selected_entity, self.game.board_manager.get_overlay_move_vgo_at_pos(pygame.mouse.get_pos())): + #if self.game.entity_manager.set_entity_move_to_tile_pos(self.game.entity_manager.selected_entity, self.game.board_manager.get_overlay_move_vgo_at_pos(pygame.mouse.get_pos())): + # self.game.control_mode = CTRL_MODES.Turn_Normal + # # TODO: Should this really be done here??? + # self.game.board_manager.load_overlay() + if self.game.entity_manager.set_entity_move_to_tile_path(self.game.entity_manager.selected_entity, 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(pygame.mouse.get_pos()))): self.game.control_mode = CTRL_MODES.Turn_Normal - # TODO: Should this really be done here??? self.game.board_manager.load_overlay() elif self.game.state_mode == STATE_MODES.Still_Scene_Mode: if self.game.control_mode == CTRL_MODES.Still_Scene_Normal: diff --git a/src/vgo.py b/src/vgo.py @@ -101,9 +101,10 @@ class VisibleGameObject(pygame.sprite.DirtySprite): """ Assign a new motion to this VGO and start playing it. Unlike animations, motions must be played if assigned. + Directions go by center rather than topleft. """ self.motion = { - "target" : target_pos, + "target" : (target_pos[0] + (TILE_WIDTH / 2), target_pos[1] + (TILE_HEIGHT / 2)), "speed" : speed, "direction" : ((target_pos[0] - self.rect.topleft[0]) / speed, (target_pos[1] - self.rect.topleft[1]) / speed), "timer" : speed @@ -116,6 +117,13 @@ class VisibleGameObject(pygame.sprite.DirtySprite): """ self.rect.topleft = pos + def set_center_position(self, pos): + """ + Assign the rect center to a raw position tuple, independent + of e.g. a tile. + """ + self.rect.center = pos + def animate(self): """ Play the current animation. This method is called as part of @@ -145,7 +153,7 @@ class VisibleGameObject(pygame.sprite.DirtySprite): self.motion_timer -= 1 else: if self.motion != {}: - self.rect.topleft = self.motion["target"] # Make sure we end up in exactly the right spot + self.set_center_position(self.motion["target"]) # Make sure we end up in exactly the right spot self.motion = {} def act(self): @@ -275,6 +283,9 @@ class Piece(Entity): # 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): """ @@ -302,15 +313,38 @@ class Piece(Entity): # 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) - self.back_to_stand = 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, 5) + 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. - if self.motion == {} and self.back_to_stand: + 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 @@ -444,6 +478,16 @@ class EntityManager(manager.Manager): 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): """