def moveAStar(self, target, entities, gameMap): # Create a new FOV map fov = tcod.map_new(gameMap.mapWidth, gameMap.mapHeight) # Scan current map and set all walls as unwalkable for y1 in range(gameMap.mapHeight): for x1 in range(gameMap.mapWidth): tcod.map_set_properties(fov, x1, y1, not gameMap.tiles[x1][y1].blockSight, not gameMap.tiles[x1][y1].blocked) # Scan for blocking entities for entity in entities: if entity.blocks and entity != self and entity != target: tcod.map_set_properties(fov, entity.x, entity.y, True, False) # Allocate A* Path - No Diagonal Movement path = tcod.path_new_using_map(fov, 0.0) # Compute path tcod.path_compute(path, self.x, self.y, target.x, target.y) # Check if path exists and is shorter than 25 moves if not tcod.path_is_empty(path) and tcod.path_size(path) < 25: x, y = tcod.path_walk(path, recompute = True) # Set X and Y coordinates if x or y: self.x = x self.y = y else: # Backup Move Function self.moveTowards(target.x, target.y, gameMap, entities) tcod.path_delete(path)
def move_astar(self, source, target, map): if self.has_required_components(source) and self.has_required_components(target): src = source.get_component(Components.POSITION) fov = source.get_component(Components.FOV) trg = target.get_component(Components.POSITION) # 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 map.entities: if self.has_required_components(entity): pos = entity.get_component(Components.POSITION) if pos.solid and entity != source and entity != target: # Set the tile as a wall so it must be navigated around tcod.map_set_properties(fov.fov, pos.x, 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.fov, 1.41) # Compute the path between self's coordinates and the target's coordinates tcod.path_compute(my_path, src.x, src.y, trg.x, trg.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(my_path) and tcod.path_size(my_path) < 25: # Find the next coordinates in the computed full path x, y = tcod.path_walk(my_path, True) if x or y: # Set self's coordinates to the next path tile src.x = x src.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.basic_movement.move_towards(trg.x, trg.y, map) # Delete the path to free memory tcod.path_delete(my_path)
def astar(source, target): # create a FOV map that has the dimensions of the map fov = lib.map_new(var.MAP_WIDTH, var.MAP_HEIGHT) # scan the current map each turn and set all walls as unwalkable for y1 in range(var.MAP_HEIGHT): for x1 in range(var.MAP_WIDTH): lib.map_set_properties(fov, x1, y1, not var.map[x1][y1].block_sight, not var.map[x1][y1].blocked) # scan all objects to see if there are objects that must be navigated around # check also that the object isn't self or the target (start and 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 ent in var.entities: if ent.blocks and ent != var.player and ent != target: # set the tile as a wall so it must be navigated around lib.map_set_properties(fov, ent.x, ent.y, True, False) # allocate the A* path # The 1.41 is the normal diagonal cost of moving, set to 0 if diagonals are prohibited my_path = lib.path_new_using_map(fov, 1.41) # compute the path between self's coordinates and the targets lib.path_compute(my_path, source.x, source.y, target.x, target.y) # check if the path exists, and in this case, also the path is shorter than 25 tiles if not lib.path_is_empty(my_path) and lib.path_size(my_path) < 25: # find the next coordinates in the computed full path (x, y) = lib.path_walk(my_path, True) else: (x, y) = (None, None) # delete the path lib.path_delete(my_path) return x, y
def find_astar(origin, dest, 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.is_opaque( (x1, y1)), game_map.is_passable((x1, y1))) # 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 pos in [actor.pos for actor in game_map.actors]: if pos != origin and pos != dest: # Set the tile as a wall so it must be navigated around tcod.map_set_properties(fov, pos[0], pos[1], True, False) # Allocate a A* path my_path = tcod.path_new_using_map(fov, 1) # Compute the path between self's coordinates and the target's coordinates tcod.path_compute(my_path, origin[0], origin[1], dest[0], dest[1]) # Check if the path exists if not tcod.path_is_empty(my_path): # Find the next coordinates in the computed full path x, y = tcod.path_walk(my_path, True) # Delete the path to free memory tcod.path_delete(my_path) if x or y: delta = (x - origin[0], y - origin[1]) for act, move in MOVES.items(): if move == delta: return act
def set_spawns(self): stop = False for i in range(200): self.randomize_spawn() for j in range(1000): self.randomize_monster_spawn() path = libtcod.path_new_using_map(self.map.pathdata, 0) libtcod.path_compute(path, self.spawn[0], self.spawn[1], self.monster_spawn[0], self.monster_spawn[1]) siz = libtcod.path_size(path) libtcod.path_delete(path) print siz if siz < 16 or siz > 40: break else: stop = True print siz break if stop: break print 'done?' if stop: return True else: return False
def path_towards_astar(self, game, origin, target): # getting the fov vamp from currentDrawMap doesn't work in debug mode since it isn't initialized # so for the moment I'm recomputing it every time, it's super wasteful but the game chugs along nicely fov = libtcod.map_new(self.width, self.height) list( map( lambda tile: libtcod.map_set_properties( fov, tile.x, tile.y, tile.trasparent, not tile.block), self.get_map_list())) for entity in self.entity_list: if entity != origin and entity != target: libtcod.map_set_properties(fov, entity.x, entity.y, True, False) my_path = libtcod.path_new_using_map(fov, 0.0) libtcod.path_compute(my_path, origin.x, origin.y, target.x, target.y) return_direction = (0, 0) if not libtcod.path_is_empty( my_path) and libtcod.path_size(my_path) < 30: x, y = libtcod.path_walk(my_path, True) if x or y: x1 = 1 if origin.x < x else -1 if origin.x > x else 0 y1 = 1 if origin.y < y else -1 if origin.y > y else 0 return_direction = (x1, y1) else: return_direction = self.get_step_towards(origin.x, origin.y, target.x, target.y) libtcod.path_delete(my_path) return return_direction
def move_astar(self, target, entities, game_map): 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) max_path_length = 25 if not libtcod.path_is_empty( my_path) and libtcod.path_size(my_path) < max_path_length: 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 = libtcod.map_new(MAP_WIDTH, MAP_HEIGHT) for y1 in range(MAP_HEIGHT): for x1 in range(MAP_WIDTH): libtcod.map_set_properties(fov, x1, y1, not map[x1][y1].block_sight, not map[x1][y1].blocked) for obj in objects: if obj.blocks and obj != self and obj != target: libtcod.map_set_properties(fov, obj.x, obj.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) libtcod.path_delete(my_path)
def move_astar(self, target): fov = libtcod.map_new(MAP_WIDTH, MAP_HEIGHT) #set move, sight blockers for y1 in range(MAP_HEIGHT): for x1 in range(MAP_WIDTH): libtcod.map_set_properties(fov, x1, y1, not map[x1][y1].sight_blocker, not map[x1][y1].move_blocker) #Treat tiles occupied by monsters as move blocked for obj in objects: if obj.move_blocker and obj != self and obj != target: libtcod.map_set_properties(fov, obj.x, obj.y, True, False) #Allocate path. Use roguelike geometry (diagonals = cardinals). my_path = libtcod.path_new_using_map(fov, 1.0) #Compute path libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) #Confirm path was found, and is short, then take step. if not libtcod.path_is_empty(my_path) and libtcod.path_size(my_path) < MAX_ASTAR_PATH_LENGTH: x, y = libtcod.path_walk(my_path, True) if x or y: #self.move takes dx, dy so don't use that self.x = x self.y = y #If the path is bad, take direct path to player. #This happens if, say, player is behind a monster in a corridor. else: self.move_towards(target.x, target.y) #Deallocate path memory libtcod.path_delete(my_path)
def move_astar(self, target, entities, game_map, check_explored=False, max_path=25): # 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): if check_explored: libtcod.map_set_properties( fov, x1, y1, not game_map.tiles[x1][y1].block_sight, not game_map.tiles[x1][y1].blocked and game_map.tiles[x1][y1].explored) else: 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) < max_path: # 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 return True else: return False 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) return self.move_towards(target.x, target.y, game_map, entities) # Delete the path to free memory libtcod.path_delete(my_path)
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 largeRatAi(obj, ratHoleX, ratHoleY): # Handles movement and combat abilities of the large rat. if libtcod.map_is_in_fov(fovMap, obj.x, obj.y): if obj.distanceTo(player.x, player.y) >= 2: pathToPlayer = libtcod.path_new_using_map(fovMap, 1) libtcod.path_compute(pathToPlayer, obj.x, obj.y, player.x, player.y) newX, newY = libtcod.path_walk(pathToPlayer, True) if newX is not None: newX = newX - obj.x newY = newY - obj.y else: newX, newY = 0 libtcod.path_delete(pathToPlayer) if isWalkable(obj.x+newX, obj.y+newY): obj.move(obj.x+newX, obj.y+newY) else: player.alive.takeDamage(obj.alive.damage) else: if obj.distanceTo(ratHoleX, ratHoleY) <= 5: num = random.randint(0, len(offsets)-1) newX = offsets[num][0] newY = offsets[num][1] else: pathToHole = libtcod.path_new_using_map(fovMap, 1) libtcod.path_compute(pathToHole, obj.x, obj.y, ratHoleX, ratHoleY) newX, newY = libtcod.path_walk(pathToHole, True) if newX is not None: newX = newX - obj.x newY = newY - obj.y else: newX, newY = 0 libtcod.path_delete(pathToHole) if isWalkable(obj.x+newX, obj.y+newY): obj.move(obj.x+newX, obj.y+newY)
def get_move_cost(self, pos1, pos2): path = libtcod.path_new_using_map(self.path_map, 0) libtcod.path_compute(path, pos1[0], pos1[1], pos2[0], pos2[1]) siz = libtcod.path_size(path) libtcod.path_delete(path) return siz
def path_to(self, dx, dy): # use algorithm to move (A*) path = libtcod.path_new_using_map(fov_map,1.41) libtcod.path_compute(path, self.owner.x, self.owner.y, dx, dy) if not libtcod.path_is_empty(path): x,y = libtcod.path_walk(path,True) if not x is None: self.move_towards(x,y) libtcod.path_delete(path)
def moveTo(self, x, y, facility): """Cancel the current path and take a general direction. If the move is illegal, do not change the current path.""" self.currentPath = None self.currentPath = facility.circulation.path_from_to( self.location.getX(), self.location.getY(), x, y) if libtcod.path_size(self.currentPath) == 0: libtcod.path_delete(self.currentPath) self.currentPath = None
def moveTo(self, x, y, facility): """Cancel the current path and take a general direction. If the move is illegal, do not change the current path.""" self.currentPath = None self.currentPath = facility.circulation.path_from_to(self.location.getX(), self.location.getY(), x, y) if libtcod.path_size(self.currentPath) == 0: libtcod.path_delete(self.currentPath) self.currentPath = None
def get_path_pos(self, pos1, pos2, dist): chemin = libtcod.path_new_using_map(self.path_map, 0) print pos1, pos2 libtcod.path_compute(chemin, pos1[0], pos1[1], pos2[0], pos2[1]) print libtcod.path_is_empty(chemin) x, y = libtcod.path_get(chemin, dist) # for i in range(dist): # x,y=libtcod.path_walk(path,False) # print x,y libtcod.path_delete(chemin) return [x, y]
def process(self, game): visible =self.game.fov.is_visible(self.x, self.y) moved = False if visible: self.seen += 1 self.color = tcod.color_lerp(tcod.dark_gray, self.orig_color, (self.seen % 50) / 100.0) if self.seen % 50 == 0: self.game.duplicate(self) if self.seen == 200: self.character = 'o' self.movement = 0.4 elif self.seen == 400: self.character = 'O' self.movement = 0.6 path = tcod.path_new_using_map(self.game.fov.fov, 1.0) tcod.path_compute(path, self.x, self.y, self.game.player.x, self.game.player.y) if tcod.path_size(path) > 2: self.points += self.movement if self.points >= 1: self.points -= 1 x, y = tcod.path_get(path, 1) self.move(x - self.x, y - self.y) moved = True tcod.path_delete(path) if not moved: self.points += self.movement if self.points >= 1: self.points -= 1 movement = [ (0, 0), (0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (-1, -1), (-1, 1), (1, -1) ] self.move(*random.choice(movement)) return True
def move(self, facility): """Follow the current path toward a given direction.""" x,y = libtcod.path_walk(self.currentPath, False) if x is not None: self.location.moveTowards(x - self.location.x, y - self.location.y) else: # Path has been followed : delete it ! libtcod.path_delete(self.currentPath) self.currentPath = None # STOP THE MOVEMENT ! self.location.freeze() if self.currentTask is not None: self.set_behaviour(EmployeeBehaviour.TASK_DO) else: self.set_behaviour(EmployeeBehaviour.WANDER)
def move(self, facility): """Follow the current path toward a given direction.""" x, y = libtcod.path_walk(self.currentPath, False) if x is not None: self.location.moveTowards(x - self.location.x, y - self.location.y) else: # Path has been followed : delete it ! libtcod.path_delete(self.currentPath) self.currentPath = None # STOP THE MOVEMENT ! self.location.freeze() if self.currentTask is not None: self.set_behaviour(EmployeeBehaviour.TASK_DO) else: self.set_behaviour(EmployeeBehaviour.WANDER)
def astar(game, from_pos, to_pos): # Create a FOV map that has the dimensions of the map fov = game.stage.map.make_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 actor in game.stage.actors: if actor.pos.x != to_pos.x and actor.pos.y != to_pos.y: # Set the tile as a wall so it must be navigated around libtcod.map_set_properties(fov, actor.pos.x, actor.pos.y, isTrans=True, isWalk=False) # Allocate an A* path # The 1.41 is the normal diagonal cost of moving, it can be set as 0.0 # if diagonal moves are prohibited path = libtcod.path_new_using_map(fov, 1.41) # Compute the path between self's coordinates and the target's coordinates libtcod.path_compute(path, from_pos.x, from_pos.y, to_pos.x, to_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). 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(path) and libtcod.path_size(path) < 25: # Find the next coordinates in the computed full path next_x, next_y = libtcod.path_walk(path, True) libtcod.path_delete(path) return pyro.direction.from_vector(next_x - from_pos.x, next_y - from_pos.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). # Vector from this object to the target, and distance dx = to_pos.x - from_pos.x dy = to_pos.y - from_pos.y distance = math.sqrt(dx ** 2 + dy ** 2) # Normalize it to length 1 (preserving direction), then round it and # convert to integer so the movement is restricted to the map grid dx = int(round(dx / distance)) dy = int(round(dy / distance)) libtcod.path_delete(path) return pyro.direction.from_vector(dx, dy)
def move_astar(self, target): # Create a FOV map that has the dimensions of the map fov = libtcod.map_new(MAP_WIDTH, MAP_HEIGHT) # Scan the current map each turn and set all the wall unwalkable for y1 in range(MAP_HEIGHT): for x1 in range(MAP_WIDTH): libtcod.map_set_properties(fov, x1, y1, not map[x1][y1].block_sight, not map[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 obj in objects: if obj.blocks and obj != self and obj != target: # Set the tile as a wall so it must be navigated around libtcod.map_set_properties(fov, obj.x, obj.y, True, False) # Allocate a A* path # The 1.41 is the normal diagonal cost of moving (sqrt(2)). 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) # Delete the path to free memory libtcod.path_delete(my_path)
def move_astar(self, target, entities, game_map): # マップの寸法を持つFOVマップを作成。 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) # すべてのオブジェクトをスキャンして,移動しなければならないオブジェクトがあるかどうかを確認 # オブジェクトが自己または対象ではないことも確認(開始点と終了点が自由になるように)。 # AIクラスは、自己がターゲットの隣にいる場合の状況を処理するので、このA*関数を使用しない。 for entity in entities: if entity.blocks and entity != self and entity != target: # タイルを壁として設定し,その周りを移動. libtcod.map_set_properties(fov, entity.x, entity.y, True, False) # A*pathを割り当てる # 1.41は通常の対角線上の移動コストで、対角線上の移動が禁止されている場合は0.0とすることができる。 my_path = libtcod.path_new_using_map(fov, 1.41) # 自己の座標とターゲットの座標の間のpathを計算. libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) # pathが存在するかどうかを確認し,この場合もpathが25タイルより短いかどうかを確認 # 例えばプレイヤーが廊下にいる場合など、モンスターに別の長めのパスを使わせたい場合、pathの大きさは重要になる # もし本当に遠くに代替の道があるならば、モンスターがマップを走り回らないようにするために、pathのサイズを比較的小さくしておくのは理にかなっている。 if not libtcod.path_is_empty( my_path) and libtcod.path_size(my_path) < 25: # 計算されたfull pathの次の座標を探す x, y = libtcod.path_walk(my_path, True) if x or y: # 次のpathタイルに自己の座標を設定 self.x = x self.y = y else: # 古い移動機能をバックアップとして残しておくことで,パスがない場合(例えば他のモンスターが通路を塞いでしまった場合)には,その機能を利用することができる. # プレイヤーに向かって移動しようとする(通路の開口部に近づけます) self.move_towards(target.x, target.y, game_map, entities) # 空きメモリへのpathを削除します libtcod.path_delete(my_path)
def move_astar(self, target, entities, game_map): # create FOV map that has dimensions of game map fov = libtcod.map_new(game_map.width, game_map.height) # scan current map each turn and set all walls to be blocking for y in range(game_map.height): for x in range(game_map.width): libtcod.map_set_properties( fov, x, y, not game_map.tiles[x][y].block_sight, not game_map.tiles[x][y].blocked ) # Scan all objects to see if something needs to be navigated around. # Also check that the object isn't self or the target. # Ignore situation where self is next to target -- AI class handles this. for entity in entities: if entity.blocks and entity != self and entity != target: libtcod.map_set_properties(fov, entity.x, entity.y, True, False) # set wall so it must be navigated around # Allocate A* path. # 1.41 is the normalized diagonal cost of moving. If diagonal movement is not allowed, then set to 0. my_path = libtcod.path_new_using_map(fov, 1.41) # Compute the path between self and target coordinates libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) # Check if path exists and is shorter than 25 tiles. # Keep path size low to prevent monsters from running around the map. if not libtcod.path_is_empty(my_path) and libtcod.path_size(my_path) < 25: # Find the next coordinates in computed full path. x, y = libtcod.path_walk(my_path, True) if x or y: # Set self's coordinates to next path tile self.x = x self.y = y else: # Keep old move function as a backup e.g. if something blocks a doorway, # self will still move towards target. self.move_towards(target.x, target.y, game_map, entities) # delete path to free memory libtcod.path_delete(my_path)
def move_astar(entity, entities, target, fov_map): """Use the A* algorithm to find a path to target, returning the next step along that path""" # TODO: maybe we re-use the existing fov map, but just un-set this entity and the target temporarily # that should save an entities iteration for making everything but entity and target unwalkable # Create a FOV map that has the dimensions of the map fov = libtcod.map_new(init.map_width, init.map_height) # Scan the current map each turn and set all the walls as unwalkable for ent in entities: if ent != entity and ent != target and 'Position' in ent: libtcod.map_set_properties(fov, ent['Position']['x'], ent['Position']['y'], ent['Opacity'] < 0.5, ent['Solid'] < 0.5) # 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, entity['Position']['x'], entity['Position']['y'], target['Position']['x'], target['Position']['y']) # Debugging A* for i in range (libtcod.path_size(my_path)): (x, y) = libtcod.path_get(my_path, i) for ent in entities: if (i < libtcod.path_size(my_path) - 1) and 'Position' in ent and ent['Position']['x'] == x and ent['Position']['y'] == y and 'A*Highlight' in ent: ent['A*Highlight'] = True # 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 (next_x, next_y) = libtcod.path_walk(my_path, True) dx = next_x - entity['Position']['x'] dy = next_y - entity['Position']['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) (dx, dy) = from_a_to_b(entity['Position']['x'], entity['Position']['y'], target['Position']['x'], target['Position']['y']) # Delete the path to free memory libtcod.path_delete(my_path) return (dx, dy)
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 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 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 an A* path # 1.41 is diagonal cost of moving my_path = libtcod.path_new_using_map(fov, 1.41) # Compute path between self's coordinate 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 (e.g. through other rooms) if e.g. the player is in a corridor # Makes sense to keep relatively low to stop monsters running around map if not libtcod.path_is_empty(my_path) and libtcod.path_size(my_path) < 25: # Find 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 old move function as a backup self.move_towards(target.x, target.y, game_map, entities) # Delete path to free memory libtcod.path_delete(my_path)
def move_astar(self, target, entities, game_map): # create a fov map at the dimensions of the map fov = libtcod.map_new(game_map.width, game_map.height) # scan current map each turn and set all 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 objets to see if there are objects that must be navigated around. # check also if object isn't self or the target for entity in entities: if entity.blocks and entity != self and entity != target: #set the tile as a wall so it muyst be navigated around libtcod.map_set_properties(fov, entity.x, entity.y, True, False) # allocate a A* path # 1.31 normal diag cost of moving, to put to 0 if diagonal forbiden my_path = libtcod.path_new_using_map(fov, 1.41) # compute path between self coordinates and the target coordinate libtcod.path_compute(my_path, self.x, self.y, target.x, target.y) # check if path exists && shorter than 25 tiles if not libtcod.path_is_empty( my_path) and libtcod.path_size(my_path) < 25: # find next coordinates in the computed full path x, y = libtcod.path_walk(my_path, True) if x or y: # set self coordinates to the next path tile self.x = x self.y = y else: # old move function if no path self.move_towards(target.x, target.y, game_map, entities) # delete the path to free memeory libtcod.path_delete(my_path)
def move_towards(self, target_x, target_y): global fov_map path = libtcod.path_new_using_map(fov_map) libtcod.path_compute(path, self.x, self.y, target_x, target_y) x,y = libtcod.path_get(path, 0) if x is None: self.move(0, 0) else: dx = int(round(target_x - x)) dy = int(round(target_y - y)) if dx != 0: dx = dx/abs(dx) if dy != 0: dy = dy/abs(dy) self.move(dx, dy) libtcod.path_delete(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): # Create a FOV map for the actor in question fov = tcod.map_new(settings.MAP_WIDTH, settings.MAP_HEIGHT) # Scan the current map and set all walls as unwalkable for y1 in range(settings.MAP_HEIGHT): for x1 in range(settings.MAP_WIDTH): tcod.map_set_properties( fov, x1, y1, not settings.dungeon_map[x1][y1].block_sight, not settings.dungeon_map[x1][y1].blocked) # Scan all objects to see if anything must be navigated around # Check also that the object isn't self or the target (so that start and endpoints are free) for obj in settings.objects: if obj.blocks and obj != self and obj != target: tcod.map_set_properties(fov, obj.x, obj.y, True, False) # Allocating the A* path # The 1.41 is the normal diagonal cost of moving. my_path = tcod.path_new_using_map(fov, 1.41) tcod.path_compute(my_path, self.x, self.y, target.x, target.y) # Check if the path exists and is shorter than 25 tiles # The path size matters for the monster to use alternative longer paths (player in another room, corridor, etc) # If the path size is too big monsters will run weird routes around the map 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: # set self's coords to the next path tile self.combatant.set_direction(x - self.x, y - self.y) self.x = x self.y = y else: self.move_towards(target.x, target.y) tcod.path_delete(my_path)
def path_destroy(path): libtcod.path_delete(path) def path_compute(path, xfrom,yfrom, xto,yto):
def playerInput(): # Handles reacting to player input. global fovCompute, turns, actionMenu, gameState, widgets for widget in widgets: if widget.console != topGuiConsole: widget.checkSelected(mouse.cx, mouse.cy-topGuiHeight) else: widget.checkSelected(mouse.cx, mouse.cy) if mouse.lbutton_pressed: if actionMenu[0]: if mouse.cx > actionMenu[3] and mouse.cx < actionMenu[3]+actionWidth: if mouse.cy-topGuiHeight > actionMenu[4] and mouse.cy-topGuiHeight < actionMenu[4]+actionHeight: for widget in widgets: if widget.selected: if actionMenu[5] == "stairs": widget.action() else: widget.action(player, actionMenu[1], actionMenu[2]) # Deletes all widgets created for action menu and resets action menu container. actionMenu = [False, 0, 0, 0, 0, ""] del widgets[2:6] return "ENDTURN" else: if libtcod.map_is_in_fov(fovMap, mouse.cx, mouse.cy-topGuiHeight) and gameState == "ACTIVE": if isWalkable(mouse.cx, mouse.cy-topGuiHeight): if player.distanceTo(mouse.cx, mouse.cy-topGuiHeight) >= 2: pathToCoords = libtcod.path_new_using_map(fovMap, 1) libtcod.path_compute(pathToCoords, player.x, player.y, mouse.cx, mouse.cy-topGuiHeight) while not libtcod.path_is_empty(pathToCoords): newX, newY = libtcod.path_walk(pathToCoords, True) if newX is not None: newX = newX - player.x newY = newY - player.y player.move(player.x+newX, player.y+newY) turns += 1 if isNpcInFov(): break libtcod.path_delete(pathToCoords) fovCompute = True return "ENDTURN" else: player.move(mouse.cx, mouse.cy-topGuiHeight) turns += 1 fovCompute = True return "ENDTURN" if not actionMenu[0]: for widget in widgets: if widget.selected: widget.action() if mouse.rbutton_pressed: if actionMenu[0]: # Deletes all widgets created for action menu and resets action menu container. actionMenu = [False, 0, 0, 0, 0, ""] del widgets[2:6] elif gameState != "ACTIVE": gameState = "ACTIVE" widgets = widgets[0:2] else: if libtcod.map_is_in_fov(fovMap, mouse.cx, mouse.cy-topGuiHeight): getAction(mouse.cx, mouse.cy-topGuiHeight) fovCompute = True if key.pressed and key.vk == libtcod.KEY_SPACE: displayMsgHistory() if key.vk == libtcod.KEY_ESCAPE: if actionMenu[0]: # Deletes all widgets created for action menu and resets action menu container. actionMenu = [False, 0, 0, 0, 0, ""] del widgets[2:6] if gameState != "ACTIVE": gameState = "ACTIVE" widgets = widgets[0:2] return False
def clearPath(self): if (self.TargetLastSeenPath != None): libtcod.path_delete(self.TargetLastSeenPath) self.TargetLastSeenPath = None
def update_pathfinding(self): tcod.path_delete(self.path) self.path = tcod.path_new_using_map(self.fov_map, self.diagonal_cost)
def free_path(self): if self.path: tcod.path_delete(self.path) self.path = None
def move_towards(self, target_x, target_y): path = libtcod.path_new_using_map(fov_map) libtcod.path_compute(path, self.x, self.y, target_x, target_y) dx, dy = libtcod.path_get(path, 0) self.move_absolute(dx, dy) libtcod.path_delete(path)
def read_path(self, path, limit): if not libtcod.path_is_empty(path): if libtcod.path_size(path) <= limit: x, y = libtcod.path_walk(path, True) return [x, y] libtcod.path_delete(path)
def path_delete(self, path): libtcod.path_delete(path) # path data functions def path_get_cost_movement(self,xFrom,yFrom,xTo,yTo, data):
def __del__(self): libtcod.map_delete(self.map) libtcod.path_delete(self.path) self.map, self.path = None, None