def connect(x1, y1, x2, y2): T.path_compute(path, x1, y1, x2, y2) for i in range(T.path_size(path)): x, y = T.path_get(path, i) c = arr[x][y] if c == '#' or c == ' ': arr[x][y] = '.'
def test_astar_single(): astar = tcod.path_new_using_map(maps[0]) for _ in range(PATH_NUMBER): tcod.path_compute(astar, 0, 0, MAP_WIDTH - 1 , MAP_HEIGHT - 1) x, y = tcod.path_walk(astar, False) while x is not None: x, y = tcod.path_walk(astar, False)
def test_astar_callback(map_, path_callback): astar = libtcodpy.path_new_using_function( libtcodpy.map_get_width(map_), libtcodpy.map_get_height(map_), path_callback, ) libtcodpy.path_compute(astar, *POINTS_AB) libtcodpy.path_delete(astar)
def run(self): while 1: job, obj = jobs.get() if job == 'fov': tcod.map_compute_fov(obj, MAP_WIDTH // 2, MAP_HEIGHT // 2) elif job == 'astar': tcod.path_compute(self.astar, 0, 0, MAP_WIDTH - 1 , MAP_HEIGHT - 1) x, y = tcod.path_walk(self.astar, False) while x is not None: x, y = tcod.path_walk(self.astar, False) jobs.task_done()
def find_path(source, target, entities, game_map): # Create a FOV map that has the dimensions of the map fov = tcod.map_new(game_map.width, game_map.height) # Scan the current map each turn and set all the walls as unwalkable for y1 in range(game_map.height): for x1 in range(game_map.width): tcod.map_set_properties( fov, x1, y1, not game_map.tiles[x1][y1].block_sight, not game_map.tiles[x1][y1].blocked, ) # Scan all the objects to see if there are objects that must be navigated around # Check also that the object isn't self or the target (so that the start and the end points are free) # The AI class handles the situation if self is next to the target so it will not use this A* function anyway for entity in entities: if entity.blocks and entity != source and entity != target: # Set the tile as a wall so it must be navigated around tcod.map_set_properties(fov, entity.pos.x, entity.pos.y, True, False) # Allocate a A* path # The 1.41 is the normal diagonal cost of moving, it can be set as 0.0 if diagonal moves are prohibited my_path = tcod.path_new_using_map(fov, 0.0) # Compute the path between self's coordinates and the target's coordinates tcod.path_compute(my_path, source.pos.x, source.pos.y, target.pos.x, target.pos.y) # Check if the path exists, and in this case, also the path is shorter than 25 tiles # The path size matters if you want the monster to use alternative longer paths (for example through other # rooms) if for example the player is in a corridor. It makes sense to keep path size relatively low to keep # the monsters from running around the map if there's an alternative path really far away has_path = not tcod.path_is_empty(my_path) and tcod.path_size(my_path) < 25 # Delete the path to free memory tcod.path_delete(my_path) return has_path
def move_astar(self, target, entities, game_map): # Create a FOV map that has the dimensions of the map fov = libtcod.map_new(game_map.width, game_map.height) # Scan the current map each turn and set all the walls as unwalkable for y1 in range(game_map.height): for x1 in range(game_map.width): libtcod.map_set_properties(fov, x1, y1, not game_map.tiles[x1][y1].block_sight, not game_map.tiles[x1][y1].blocked) # Scan all the objects to see if there are objects that must be navigated around # Check also that the object isn't self or the target (so that the start and the end points are free) # The AI class handles the situation if self is next to the target so it will not use this A* function anyway for entity in entities: if entity.blocks and entity != self and entity != target: # Set the tile as a wall so it must be navigated around libtcod.map_set_properties(fov, entity.x, entity.y, True, False) # Allocate a A* path # The 1.41 is the normal diagonal cost of moving, it can be set as 0.0 if diagonal moves are prohibited my_path = libtcod.path_new_using_map(fov, 1.41) # Compute the path between self's coordinates and the target's coordinates libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) # Check if the path exists, and in this case, also the path is shorter than 25 tiles # The path size matters if you want the monster to use alternative longer paths (for example through other rooms) if for example the player is in a corridor # It makes sense to keep path size relatively low to keep the monsters from running around the map if there's an alternative path really far away if not libtcod.path_is_empty(my_path) and libtcod.path_size(my_path) < 25: # Find the next coordinates in the computed full path x, y = libtcod.path_walk(my_path, True) if x or y: # Set self's coordinates to the next path tile self.x = x self.y = y else: # Keep the old move function as a backup so that if there are no paths (for example another monster blocks a corridor) # it will still try to move towards the player (closer to the corridor opening) self.move_towards(target.x, target.y, game_map, entities) # Delete the path to free memory libtcod.path_delete(my_path)
def move_astar(self, target, entities, game_map): # Create FOV map with dimensions of game map fov = libtcod.map_new(game_map.width, game_map.height) # Scan current map each turn and set all the walls as unwalkable for y1 in range(game_map.height): for x1 in range(game_map.width): libtcod.map_set_properties( fov, x1, y1, not game_map.tiles[x1][y1].block_sight, not game_map.tiles[x1][y1].blocked) # Scan all the objects to see if there are objects that must be navigated around # Check also that the object isn't self or target (so start and end points are free # AI class handles case where self is next to target so no need to worry about that case for entity in entities: if entity != self and entity != target and entity.blocks: libtcod.map_set_properties(fov, entity.x, entity.y, True, False) # Allocate A* path my_path = libtcod.path_new_using_map(fov, 1.41) # Compute path between self and target libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) # Check if path exists, and if the path is shorter than 25 tiles # Makes sense to keep path size low to prevent monsters running around the map if there's an # alternate path far away if not libtcod.path_is_empty( my_path) and libtcod.path_size(my_path) < 25: # Find next coordinates in the path x, y = libtcod.path_walk(my_path, True) if x or y: # Update coordinates to next tile self.x = x self.y = y else: # Kept as backup so that if there are no paths, it will still try to move towards the player self.move_towards(target.x, target.y, game_map, entities) # free memory libtcod.path_delete(my_path)
def move_astar(self, target, entities, game_map): fov = tcod.map_new(game_map.width, game_map.height) for y1 in range(game_map.height): for x1 in range(game_map.width): tcod.map_set_properties(fov, x1, y1, not game_map.tiles[x1][y1].block_sight, not game_map.tiles[x1][y1].blocked) for entity in entities: if entity.blocks and entity != self and entity != target: tcod.map_set_properties(fov, entity.x, entity.y, True, False) my_path = tcod.path_new_using_map(fov, 1.41) tcod.path_compute(my_path, self.x, self.y, target.x, target.y) if not tcod.path_is_empty(my_path) and tcod.path_size(my_path) < 25: x, y = tcod.path_walk(my_path, True) if x or y: self.x = x self.y = y else: self.move_towards(target.x, target.y, game_map, entities) tcod.path_delete(my_path)
def generate_entrance_exit(self): path = tcod.path_new_using_map(self.map, 0) while tcod.path_size(path) == 0: sx = random.randint(0, self.width - 1) sy = random.randint(0, self.height - 1) while not self.map.walkable[sx][sy]: sx = random.randint(0, self.width - 1) sy = random.randint(0, self.height - 1) ex = random.randint(0, self.width - 1) ey = random.randint(0, self.height - 1) while not self.map.walkable[ex][ey]: ex = random.randint(0, self.width - 1) ey = random.randint(0, self.height - 1) tcod.path_compute(path, sx, sy, ex, ey) self.set_entrance(sx, sy) self.set_exit(ex, ey)
def move_astar(self, target, entities, game_map): fov = game_map.new_fov_map() for entity in entities: if entity.blocks and entity != self and entity != target: libtcod.map_set_properties(fov, entity.x, entity.y, True, False) my_path = libtcod.path_new_using_map(fov, 1.41) libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) if not libtcod.path_is_empty(my_path) and libtcod.path_size(my_path) < 25: x, y = libtcod.path_walk(my_path, True) if x or y: self.x = x self.y = y else: self.move_towards(target.x, target.y, game_map, entities) libtcod.path_delete(my_path)
def move_astar(self, target, entities, game_map): """ Utilise l'algorithme de recherche de chemin A* pour le déplacement d'un monstre en direction du joueur. Est appelé dans ai.BasicMonster Parametres: ---------- target : Entity entities : list game_map : GameMap Renvoi: ------- Aucun """ fov = libtcod.map_new(game_map.width, game_map.height) for y1 in range(game_map.height): for x1 in range(game_map.width): libtcod.map_set_properties( fov, x1, y1, not game_map.tiles[x1][y1].block_sight, not game_map.tiles[x1][y1].blocked) for entity in entities: if entity.blocks and entity != self and entity != target: libtcod.map_set_properties(fov, entity.x, entity.y, True, False) my_path = libtcod.path_new_using_map(fov, 1.41) libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) if not libtcod.path_is_empty( my_path) and libtcod.path_size(my_path) < 25: x, y = libtcod.path_walk(my_path, True) if x or y: self.x = x self.y = y else: self.move_towards(target.x, target.y, game_map, entities) libtcod.path_delete(my_path)
def move_astar(self, target, fov_map, game_map, entities): fov_map_astar = fov_map # Scan all the objects to see if there are objects that must be navigated around # Check also that the object isn't self or the target (so that the start and the end points are free) # The AI class handles the situation if self is next to the target so it will not use this A* function anyway for entity in entities: if entity.blocks and entity != self and entity != target: # Set the tile as a wall so it must be navigated around libtcod.map_set_properties(fov_map_astar, entity.x, entity.y, True, False) # Allocate a A* path my_path = libtcod.path_new_using_map(fov_map_astar, 1.4) # Compute the path between self's coordinates and the target's coordinates libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) # Check if the path exists, and in this case, also the path is shorter than 25 tiles # The path size matters if you want the monster to use alternative longer paths # (for example through other rooms) if for example the player is in a corridor # It makes sense to keep path size relatively low to keep the monsters from running # around the map if there's an alternative path really far away if not libtcod.path_is_empty( my_path) and libtcod.path_size(my_path) < 10: # Find the next coordinates in the computed full path x, y = libtcod.path_walk(my_path, True) if x or y: # Set self's coordinates to the next path tile self.x = x self.y = y else: # Keep the old move function as a backup so that if there are no paths # (for example another monster blocks a corridor). It will still try # to move towards the player (closer to the corridor opening) self.move_towards(target, game_map, entities) # Delete the path to free memory libtcod.path_delete(my_path)
def move_astar(self, target, entities, game_map): # Create another FOV map fov = libtcod.map_new(game_map.width, game_map.height) # Scan the map and set walls as blocked for y1 in range(game_map.height): for x1 in range(game_map.width): libtcod.map_set_properties( fov, x1, y1, not game_map.tiles[x1][y1].block_sight, not game_map.tiles[x1][y1].blocked) # Scan for entities to path around for entity in entities: if entity.blocks and entity != self and entity != target: libtcod.map_set_properties(fov, entity.x, entity.y, True, False) # Allocate a A* path # NOTE: The diagonal movement cost is defined here, might want to put in a config somewhere my_path = libtcod.path_new_using_map(fov, 1.41) # Compute the path libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) # If the path exists and is not too long, walk along if not libtcod.path_is_empty( my_path) and libtcod.path_size(my_path) < 25: x, y = libtcod.path_walk(my_path, True) if x or y: self.x = x self.y = y # Use dumber pathfinding to just get closer if there is no complete path else: self.move_towards(target.x, target.y, game_map, entities) # Deallocate path libtcod.path_delete(my_path)
def move_astar(self, target, entities, game_map): # Create FOV map that has the deminision of the map fov = libtcod.map_new(game_map.width, game_map.height) # Scan the current map each turn and set all the walls as unwalkable for y1 in range(game_map.height): for x1 in range(game_map.width): libtcod.map_set_properties( fov, x1, y1, not game_map.tiles[x1][y1].block_sight, not game_map.tiles[x1][y1].blocked) # Scan all the objects to see if there are objects that must be navigated around # Check also that the object isn't self or the target (so that the start and the end points are free) # The AI class handles the situation if self is next to the target so it will not use this A* function anyway for entity in entities: if entity.blocks and entity != self and entity != target: libtcod.map_set_properties(fov, entity.x, entity.y, True, False) # Allocate a A* path # The 1.41 is the normal diagonal cost of moving my_path = libtcod.path_new_using_map(fov, 1.41) # Compute the path between self's coordinates and the target's coordinates libtcod.path_compute(my_path, int(self.x), int(self.y), int(target.x), int(target.y)) if not libtcod.path_is_empty( my_path) and libtcod.path_size(my_path) < 25: x, y = libtcod.path_walk(my_path, True) if x or y: self.x = x self.y = y else: # Keep the old move function as a backup, if there are no paths self.move_towards(target.x, target.y, game_map, entities) libtcod.path_delete(my_path)
def test_astar(map_): astar = libtcodpy.path_new_using_map(map_) assert not libtcodpy.path_compute(astar, *POINTS_AC) assert libtcodpy.path_size(astar) == 0 assert libtcodpy.path_compute(astar, *POINTS_AB) assert libtcodpy.path_get_origin(astar) == POINT_A assert libtcodpy.path_get_destination(astar) == POINT_B libtcodpy.path_reverse(astar) assert libtcodpy.path_get_origin(astar) == POINT_B assert libtcodpy.path_get_destination(astar) == POINT_A assert libtcodpy.path_size(astar) != 0 assert libtcodpy.path_size(astar) > 0 assert not libtcodpy.path_is_empty(astar) for i in range(libtcodpy.path_size(astar)): x, y = libtcodpy.path_get(astar, i) while (x, y) != (None, None): x, y = libtcodpy.path_walk(astar, False) libtcodpy.path_delete(astar)
def move_astar(self, target, entities, game_map): # Create a FOV map that has the dimensions of the map fov = libtcod.map_new(game_map.width, game_map.height) # Scan the current map each turn and set all the walls as un-walkable for y1 in range(game_map.height): for x1 in range(game_map.width): libtcod.map_set_properties( fov, x1, y1, not game_map.tiles[x1][y1].block_sight, not game_map.tiles[x1][y1].blocked) # Scan all the objects to see if there are objects that must be navigated around (and obj != self/target) for entity in entities: if entity.blocks and entity != self and entity != target: libtcod.map_set_properties(fov, entity.x, entity.y, True, False) # Allocate an A* path my_path = libtcod.path_new_using_map(fov, 1.41) # Compute the path between self's coordinates and the target's coordinates libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) # Check if the path exists, and in this case, also the path is shorter than 25 if not libtcod.path_is_empty( my_path) and libtcod.path_size(my_path) < 25: # Find the next coordinates in the computed full path x, y = libtcod.path_walk(my_path, True) if x or y: # Set self's coordinates to the next path tile self.x = x self.y = y else: # Keep the old move function as a backup so that if there are no paths self.move_towards(target.x, target.y, game_map, entities) libtcod.path_delete(my_path)
def move_astar(self, target, entities, game_map): # Create a FOV map that has the dimensions of the map fov = libtcod.map_new(game_map.width, game_map.height) # Scan the current map each turn and set all the walls as unwalkable for y1 in range(game_map.height): for x1 in range(game_map.width): libtcod.map_set_properties(fov, x1, y1, not game_map.tiles[x1][y1].block_sight, not game_map.tiles[x1][y1].blocked) # Scan all the objects to see if there are objects that must be navigated around # Check also that the object isn't self or the target (so that the start and the end points are free) # The AI class handles the situation if self is next to the target so it will not use this A* function anyway for entity in entities: if entity.blocks and entity != self and entity != target: # Set the tile as a wall so it must be navigated around libtcod.map_set_properties(fov, entity.x, entity.y, True, False) # Allocate path my_path = libtcod.path_new_using_map(fov, 1.41) # Compute path libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) # Check exists and path is short if not libtcod.path_is_empty(my_path) and libtcod.path_size(my_path) < 25: # Find the next coordinates in the computed full path x, y = libtcod.path_walk(my_path, True) if x or y: # Set self's coordinates to the next path tile self.x = x self.y = y else: self.move_towards(target.x, target.y, game_map, entities) # Delete path to free memory libtcod.path_delete(my_path)
def monster_move(level, monster): update_monster_state(level, monster) stationary = "S" in monster.flags is_active = monster.state == "ACTIVE" is_confused = monster.state.startswith("CONFUSED") if is_active or is_confused or is_flying_targeting(monster): x, y = movements[choice(list(movements))] _move(monster, x, y, level, stationary) elif monster.state == "TARGETING": diag = 1.95 if is_transparent(monster.location, level) else 0 try: astar = tcod.path_new_using_map(level.map_grid, diag) tcod.path_compute(astar, monster.location.x, monster.location.y, level.player.location.x, level.player.location.y) next_tile = tcod.path_get(astar, 0) except: print("ASTAR FAILURE") next_tile = (randint(-1, 1), randint(-1, 1)) x, y = (next_tile[0] - monster.location.x, next_tile[1] - monster.location.y) _move(monster, x, y, level, stationary) close = distance_to(monster.location, level.player.location) > 2 if "L" in monster.flags and close and in_fov(monster.location, level): update_screen(level) try: astar = tcod.path_new_using_map(level.map_grid, 1.95) tcod.path_compute(astar, level.player.location.x, level.player.location.y, monster.location.x, monster.location.y) next_tile = tcod.path_get(astar, 0) except: print("ASTAR FAILURE") next_tile = (randint(-1, 1), randint(-1, 1)) x, y = (next_tile[0] - level.player.location.x, next_tile[1] - level.player.location.y) _move(level.player, x, y, level)
def Entity_Move(self, Self_X, Self_Y, Target_x, Target_y): # Checks for a number of entities which hold specific tags, (Hostile, Alien, etc) are on the current map. # Uses A* pathing fov = tcod.map_new(MAP_WIDTH, MAP_HEIGHT) for y1 in range(MAP_HEIGHT): for x1 in range(MAP_WIDTH): tcod.map_set_properties(fov, x1, y1, not map[x1][y1].block_sight, not map[x1][y1].blocked) for Entity, (Position, Blocks) in self.world.get_components(Components.Position, Components.Move_Through): if Blocks and not self.world.has_component(Entity, Components.Player): tcod.map_set_properties(fov, Position.X, Position.Y, True, False) My_Path = tcod.path_new_using_map(fov, 1.41) tcod.path_compute(My_Path, Self_X, Self_Y, Target_x, Target_y) if not tcod.path_is_empty(My_Path) and tcod.path_size(My_Path) < 25: x, y = tcod.path_walk(My_Path, True) if x or y: tcod.path_delete(My_Path) return x, y else: tcod.path_delete(My_Path) return Self_X, Self_Y
def take_turn(self): self.tick+=1 moved=False monster = self.owner if self.tick%self.period==0: try: if self.path==None: self.path = libtcod.path_new_using_map(path_map) except: self.path = libtcod.path_new_using_map(path_map) success=libtcod.path_compute(self.path, monster.x, monster.y, player.x, player.y) stepx,stepy=libtcod.path_walk(self.path,True) #move towards player if far away if monster.distance_to(player) >= 2 and success: moved=monster.move(stepx-monster.x,stepy-monster.y) #close enough, attack! (if the player is still alive.) for object in objects: if object!=monster and hasattr(object,"fighter") and object.fighter is not None and monster.distance_to(object) < 2 and object.fighter.hp>0 and not (object==player and moved): monster.fighter.attack(object)
def perform(self, *args, **kwargs): # Create a FOV map that has the dimensions of the map # DeprecationWarning: Call tcod.map.Map(width, height) instead. # fov = tcod.map_new(self.stage.width, self.stage.height) fov = tcod.map.Map(width=self.stage.width, height=self.stage.height) # Scan the current map each turn and set all the walls as unwalkable for y1 in range(self.stage.height): for x1 in range(self.stage.width): # DeprecationWarning: Set properties using the m.transparent and m.walkable arrays. # tcod.map_set_properties( fov, x1, y1, not self.stage.tiles[x1][y1].block_sight, not self.stage.tiles[x1][y1].blocks) fov.transparent[y1, x1] = not self.stage.tiles[x1][y1].block_sight fov.walkable[y1, x1] = not self.stage.tiles[x1][y1].blocks # Scan all the objects to see if there are objects that must be navigated # around. Check also that the object isn't self or the target (so that # the start and the end points are free). The AI class handles the # situation if self is next to the target so it will not use this A* function # anyway for entity in self.stage.entities: if entity.blocks and entity != self and entity != self.target: # Set the tile as a wall so it must be navigated around # tcod.map_set_properties(fov, entity.x, entity.y, True, False) fov.walkable[entity.y, entity.x] = False # Allocate a A* path # The 1.41 is the normal diagonal cost of moving, it can be set as 0.0 # if diagonal moves are prohibited # PendingDeprecationWarning: This function may be deprecated in the future. my_path = tcod.path_new_using_map(m=fov, dcost=1.41) # Compute the path between self's coordinates and the target's coordinates # PendingDeprecationWarning: This function may be deprecated in the future. # Returns a boolean... tcod.path_compute( p=my_path, ox=self.entity.x, oy=self.entity.y, dx=self.target.x, dy=self.target.y ) # Check if the path exists, and in this case, also the path is shorter # than 25 tiles. The path size matters if you want the monster to use # alternative longer paths (for example through other rooms) if for # example the player is in a corridor. It makes sense to keep path size # relatively low to keep the monsters from running around the map if # there's an alternative path really far away # PendingDeprecationWarning: This function may be deprecated in the future. print(tcod.path_size(my_path)) print(my_path) if not tcod.path_is_empty(my_path) and tcod.path_size(my_path) < 25: # Find the next coordinates in the computed full path # PendingDeprecationWarning: This function may be deprecated in the future. x, y = tcod.path_walk(my_path, True) if x or y: # Set self's coordinates to the next path tile dx, dy = self.stage.calc_dxdy(self.entity.x, self.entity.y, x, y) print(dx, dy) # DeprecationWarning: libtcod objects are deleted automatically. # tcod.path_delete(my_path) return ActionResult( success=True, alt=WalkAction(dx, dy) ) # Keep the old move function as a backup so that if there are no paths # (for example another monster blocks a corridor) it will still try to # move towards the player (closer to the corridor opening) # self.move_towards(self.target.x, self.target.y, self.stage) # DeprecationWarning: libtcod objects are deleted automatically. # tcod.path_delete(my_path) return ActionResult( success=True, alt=MoveTowardAction(self.stage, self.entity, self.target) )
def move_astar(self, target, entities, game_map): fov_map = tcod.map.Map(game_map.width, game_map.height) fov_map.walkable[:] = True fov_map.transparent[:] = True for y1 in range(game_map.height): for x1 in range(game_map.width): if game_map.tiles[x1][y1].blocked or game_map.tiles[x1][ y1].blocking_entity: fov_map.walkable[y1, x1] = False if game_map.tiles[x1][y1].block_sight: fov_map.transparent[y1, x1] = False for entity in entities["monsters"]: if entity.blocks and entity != self and entity != target: fov_map.walkable[entity.y, entity.x] = False if entity.occupied_tiles is not None: fov_map.walkable[entity.y + 1, entity.x + 1] = False fov_map.walkable[entity.y, entity.x + 1] = False fov_map.walkable[entity.y + 1, entity.x] = False fov_map.transparent[entity.y, entity.x] = True for entity in entities["allies"]: if entity.blocks and entity != self and entity != target: fov_map.walkable[entity.y, entity.x] = False if entity.occupied_tiles is not None: fov_map.walkable[entity.y + 1, entity.x + 1] = False fov_map.walkable[entity.y, entity.x + 1] = False fov_map.walkable[entity.y + 1, entity.x] = False fov_map.transparent[entity.y, entity.x] = True # Allocate a A* path # The 1.41 is the normal diagonal cost of moving, it can be set as 0.0 # if diagonal moves are prohibited astar = tcod.path.AStar(fov_map) # Compute the path between self's coordinates and the target's # coordinates tcod.path_compute(astar, self.x, self.y, target.x, target.y) # Check if the path exists, and in this case, also the path is shorter than 25 tiles # The path size matters if you want the monster to use alternative longer paths # (for example through other rooms) if for example the player is in a corridor # It makes sense to keep path size relatively low to keep the monsters # from running around the map if there's an alternative path really far # away if not tcod.path_is_empty(astar) and tcod.path_size(astar) < 25: # Find the next coordinates in the computed full path x, y = tcod.path_walk(astar, True) if x or y: # Set self's coordinates to the next path tile self.x = x self.y = y if self.occupied_tiles is not None: self.occupied_tiles = [(x, y), (x, y + 1), (x + 1, y + 1), (x + 1, y)] else: # Keep the old move function as a backup so that if there are no paths # (for example another monster blocks a corridor) # it will still try to move towards the player (closer to the # corridor opening) self.move_towards(target.x, target.y, game_map)
def act(self, game_map, player, fov_map=None, has_los=None): dist = self.owner.distance_to(player) if dist < 2: # Attack the player, even if the entity can't see return self.owner.fighter.attack_entity(player.fighter, target_is_player=True) results = {} given_fov_map = fov_map is not None if (self.owner.sight and dist <= self.owner.sight.fov_radius or self.remaining_chase_turns > 0): owner_x = self.owner.x owner_y = self.owner.y player_x = player.x player_y = player.y if given_fov_map: tcod.map_set_properties(fov_map, owner_x, owner_y, True, True) tcod.map_set_properties(fov_map, player_x, player_y, True, True) else: # This second variable is necessary as creating another # variable called fov_map might shadow the parameter rather # than changing its value fov_map = game_map.generate_fov_map_with_entities( [self.owner, player]) if self.clairvoyant: # Clairvoyant entities can always sense the player when they're # nearby has_los = True if has_los is None: self.owner.sight.get_fov(fov_map) has_los = tcod.map_is_in_fov(fov_map, owner_x, owner_y) if has_los: # If the entity can see the player, reset the chase timer self.remaining_chase_turns = self.chase_duration chase = has_los if not has_los and self.remaining_chase_turns > 0: # Entities will continue chasing the player for chase_duration, # even if they don't have line of sight chase = True self.remaining_chase_turns -= 1 if chase: # 1.41 approximates sqrt(2), the cost of a diagonal moves path = tcod.path_new_using_map(fov_map, 1.41) tcod.path_compute(path, owner_x, owner_y, player_x, player_y) if not tcod.path_is_empty(path) and tcod.path_size(path) < 25: x, y = tcod.path_walk(path, True) if x or y: self.owner.move_to(x, y, game_map, face=True) else: dx = player_x - owner_x dy = player_y - owner_y dx = int(round(dx / dist)) dy = int(round(dy / dist)) if game_map.is_tile_open(owner_x + dx, owner_y + dy): self.owner.move(dx, dy, game_map) tcod.path_delete(path) if given_fov_map: tcod.map_set_properties(fov_map, self.owner.x, self.owner.y, True, False) tcod.map_set_properties(fov_map, player.x, player.y, True, False) else: tcod.map_delete(fov_map) else: if given_fov_map: # Remove the entity from the given FOV map tcod.map_set_properties(fov_map, self.owner.x, self.owner.y, True, True) # Wander around aimlessly self.owner.move(randint(-1, 1), randint(-1, 1), game_map) if given_fov_map: # Add the entity to the given FOV map tcod.map_set_properties(fov_map, self.owner.x, self.owner.y, True, False) return results
def recalculate(self): monster = self.owner self.success=libtcod.path_compute(self.path, monster.x, monster.y, self.target_x, self.target_y)