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 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 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 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) #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 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, 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 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 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 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_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 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 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 batAiUpdate(self, player): path = libtcod.path_compute(self.path, self.creature.x, self.creature.y, player.x, player.y) if path: length = libtcod.path_size(self.path) if length > 1:#self.creature.attackRange (x, y) = libtcod.path_walk(self.path, False) self.creature.moveTo(x, y) else: self.creature.attackCreature(player) else: pass
def get_astar_distance_to(self, x, y, target_x, target_y): ''' Gets distance using A* algo - how far an entity would actually have to walk to get somewhere ''' # Handle case where the target is the same as the initial location if (x, y) == (target_x, target_y): return 0 # Otherwise, compute the path libtcod.path_compute(self.path_map, x, y, target_x, target_y) # A length of 0 here should mean that it was not possible to reach the location # It could mean that the initial loc == the target loc, but we've tested that above new_path_len = libtcod.path_size(self.path_map) # Therefore, a len of 0 here should mean unreachable - so return None return new_path_len if new_path_len else None
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 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 compute_path(self, world_map, creature): if not creature.is_at_pos(self.dest): self.path = world_map.path_from_to(creature.to_pos(), self.dest) self.path_length = tcod.path_size(self.path) if self.path_length == 0: self.fail() raise ImpossibleTask('No path from %d, %d, %d to %d, %d, %d' % (creature.to_pos() + self.dest)) # The tick time MUST be reset, in case we recompute path # during the task. self.tick_time = 0 else: self.path = None self.path_length = 0 self.finish()
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): import Pathing print "Astar for " + self.name path = Pathing.astar((self.x, self.y), (target.x, target.y)) if not path: return False self.path = libtcod.path_new_using_map(Fov.get_fov_map(), 1.41) libtcod.path_compute(self.path, self.x, self.y, target.x, target.y) if not libtcod.path_is_empty( self.path) and libtcod.path_size(self.path) < 75: self.walk_path() else: self.move_towards(target.x, target.y)
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(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 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 can_hear(obj, x,y, volume): if ( on(obj,DEAD) or on(obj,DEAF) or not obj.stats.get('hearing') ): return False dist=maths.dist(obj.x, obj.y, x, y) maxHearDist=volume*obj.stats.get('hearing')/AVG_HEARING if (obj.x == x and obj.y == y): return (0,0,maxHearDist,) if dist > maxHearDist: return False # calculate a path path=path_init_sound() path_compute(path, obj.x,obj.y, x,y) pathSize=libtcod.path_size(path) if dist >= 2: semifinal=libtcod.path_get(path, 0) xf,yf=semifinal dx=xf - obj.x dy=yf - obj.y else: dx=0 dy=0 path_destroy(path) loudness=(maxHearDist - pathSize - (pathSize - dist)) if loudness > 0: return (dx,dy,loudness)
def tick(self,world): if self.explored: return if not self.goal: [self.goal,self.path] = self.findExplorePoint(world) if not self.goal: path=world.getPathable() self.explored=True unseen=0 for n in path: if not world.isSeen(n[0],n[1]):unseen=unseen+1 print "Exploration done,",unseen,"unseen tiles" return self.path_progress=0 [next_x,next_y]=libtcod.path_get(self.path, self.path_progress) self.x=next_x self.y=next_y self.path_progress=self.path_progress+1 if libtcod.path_size(self.path)==self.path_progress: self.goal=None self.path=None
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 map_init_dungeon(width, height): def path_cost(xFrom, yFrom, xTo, yTo, alg_array): if alg_array[xTo][yTo] == 0: return 1 if alg_array[xTo][yTo] == 3: return 0.01 else: return 10 room_prefabs_10x10 = [] f = open('resources/map_prefabs/map_prefabs[10x10].csv', 'r').read().split('\n') # 10x10 for i in range(len(f[0]) // 10): for j in range(len(f) // 10): room = '' for y in range(10): for x in range(10): room += f[j * 10 + x][i * 10 + y] room_prefabs_10x10.append(room) room_prefabs_5x5 = [] f = open('resources/map_prefabs/map_prefabs[5x5].csv', 'r').read().split('\n') # 10x10 for i in range(len(f[0]) // 5): for j in range(len(f) // 5): room = '' for y in range(5): for x in range(5): room += f[j * 5 + x][i * 5 + y] room_prefabs_5x5.append(room) monsters_pool = [[game_content.m_slime], []] alg_array = [[0 for j in range(height)] for i in range(width)] terrain = [[0 for j in range(height)] for i in range(width)] items = [] entities = [] creatures = [] rooms = [] room_exits = [] room_connections = [] rooms_size = [(10, 10), (5, 5)] rooms.append((width // 2 - 3, height // 2 - 3, 6, 6)) for x in range(width // 2 - 3, width // 2 + 3): for y in range(height // 2 - 3, height // 2 + 3): if y == height // 2 and (x == width // 2 - 3 or x == width // 2 + 3): alg_array[x][y] = 7 room_exits.append((x, y, -1)) else: alg_array[x][y] = 2 available_spots = [ (x, y) for x in range(width) for y in range(height) if x > 6 and x < width - 12 and y > 6 and y < height - 12 ] for x in range(len(available_spots)): append = True i, j = available_spots.pop(random.randint(0, len(available_spots) - 1)) w, h = random.choice(rooms_size) newRoom = (i, j, w, h) #X, Y, W, H for room in rooms: if util.rectangle_intersects(newRoom, room): append = False if append == True: rooms.append(newRoom) for roomIndex in range(len(rooms))[0:]: room = rooms[roomIndex] if room[2] == 10 and room[3] == 10: room_layout = random.choice(room_prefabs_10x10) for x in range(room[2]): for y in range(room[3]): alg_array[x + room[0]][y + room[1]] = int( room_layout[x * 10 + y]) if int(room_layout[x * 10 + y]) == 7: room_exits.append( (x + room[0], y + room[1], roomIndex)) elif room[2] == 5 and room[3] == 5: room_layout = random.choice(room_prefabs_5x5) for x in range(room[2]): for y in range(room[3]): alg_array[x + room[0]][y + room[1]] = int( room_layout[x * 5 + y]) if int(room_layout[x * 5 + y]) == 7: room_exits.append( (x + room[0], y + room[1], roomIndex)) for exit_init in room_exits: path = libtcodpy.path_new_using_function(width, height, path_cost, alg_array, 0) other_exits = sorted([ exit_other for exit_other in room_exits if exit_other[2] != exit_init[2] and ( exit_other[2], exit_init[2]) not in room_connections ], key=lambda e: util.simpledistance( (exit_init[0], exit_init[1]), (e[0], e[1]))) if len(other_exits) > 0: exit_end = other_exits[0] else: exit_end = sorted([ exit_other for exit_other in room_exits if exit_other[2] != exit_init[2] ], key=lambda e: util.simpledistance( (exit_init[0], exit_init[1]), (e[0], e[1])))[0] room_connections.append((exit_init[2], exit_end[2])) room_connections.append((exit_end[2], exit_init[2])) libtcodpy.path_compute(path, exit_init[0], exit_init[1], exit_end[0], exit_end[1]) for i in range(libtcodpy.path_size(path) - 1): x, y = libtcodpy.path_get(path, i) alg_array[x][y] = 3 for x in range(len(alg_array)): for y in range(len(alg_array[x])): if alg_array[x][y] in [0, 1]: terrain[x][y] = game_content.t_cave_wall(x, y) else: terrain[x][y] = game_content.t_cave_floor(x, y) if alg_array[x][y] == 4: creatures.append( random.choice(monsters_pool[GAME.level])(x, y)) if alg_array[x][y] == 7: entities.append( game_content.n_door( x, y, game_content.SPRITESHEET_ENTITIES.image_at( (0, 32, 32, 32)), game_content.SPRITESHEET_ENTITIES.image_at( (32, 32, 32, 32), colorkey=game_constants.COLOR_COLORKEY))) terrain[x][y].passable = False terrain[x][y].transparent = False return terrain, items, entities, creatures
def libtcod_path_to_list(path_map): ''' get a libtcod path into a list ''' return [ libtcod.path_get(path_map, i) for i in xrange(libtcod.path_size(path_map)) ]
def get_astar_distance_to(self, x, y, target_x, target_y): libtcod.path_compute(self.path_map, x, y, target_x, target_y) new_path_len = libtcod.path_size(self.path_map) return new_path_len
def update(self): tcod_map = self.game.dungeon.tcod_map if not self.path: self.path = libtcod.path_new_using_map(tcod_map) pos = self.entity.pos visible = self.game.player.fov(*pos) done = False while not done: if self.state == AI_INACTIVE: #not used yet done = True elif self.state == AI_SLEEPING: wake_up = False for s in self.sounds: if self.game.rng.percent(min(95,s[0]*(self.creature.perception+5)/10)): wake_up = True if self.creature.health < self.creature.max_health: wake_up = True if wake_up: self.state = AI_RESTING self.entity.notify(Event(EVENT_WAKE_UP, actor=self.entity)) else: #continue sleeping done = True elif self.state == AI_RESTING: self.creature.fov.refresh() if self.check_for_player(): self.state = AI_FIGHTING self.entity.notify(Event(EVENT_NOTICE, actor=self.entity)) else: #continue to rest or wander? if self.game.rng.percent(20): done = True else: self.state = AI_WANDERING elif self.state == AI_WANDERING: self.creature.fov.refresh() if self.check_for_player(): self.state = AI_FIGHTING self.entity.notify(Event(EVENT_NOTICE, actor=self.entity)) else: direction = (0,0) while not (self.valid_movement(direction) or self.game.cur_level.get_tile(self.entity.x+direction[0],self.entity.y+direction[1]).creature==self.game.player): directions = [(1,1),(1,-1),(-1,1),(-1,-1), (0,1),(0,-1),(1,0),(-1,0)] if self.prev_dir: directions += [self.prev_dir]*6 if self.prev_dir[0]==0: directions += [(1,self.prev_dir[1])]*2 directions += [(-1,self.prev_dir[1])]*2 elif self.prev_dir[1]==0: directions += [(self.prev_dir[0],1)]*2 directions += [(self.prev_dir[0],-1)]*2 else: directions += [(self.prev_dir[0],0)]*2 directions += [(0,self.prev_dir[1])]*2 direction = self.game.rng.choose(directions) t = self.game.cur_level(self.entity.x+direction[0], self.entity.y+direction[1]) if self.game.rng.percent(10): self.state = AI_RESTING done=True elif t.creature is self.game.player: self.entity.notify(EVENT_NOTICE, actor=self.entity) self.state = AI_FIGHTING else: self.entity.move_to(self.entity.x+direction[0], self.entity.y+direction[1]) self.prev_dir=direction done=True elif self.state == AI_FIGHTING: if self.creature.fov(*self.game.player.pos): self.last_saw_player = 0 else: #increase last_saw_player and fall asleep if it's been too long self.last_saw_player += 1 if self.last_saw_player >= 5: self.state = AI_SLEEPING new_player_pos = self.game.player.pos #get latest player pos and recalculate path if needed if self.player_pos != new_player_pos: self.player_pos = new_player_pos self.compute_path(*self.player_pos) if (self.path_index < libtcod.path_size(self.path) and self.game.player.creature.alive):#walk path x,y = libtcod.path_get(self.path, self.path_index) event = self.entity.move_to(x,y) if event.event_type == EVENT_MOVE: #successfully moved, increase path index self.path_index += 1 done = True elif self.entity.distance_to(*self.player_pos) < 2: #didn't move but can attack player self.creature.attack(self.game.player) done = True else: #didn't move or attack, try new path self.compute_path(*self.player_pos) else: #end of path and don't know where to go now done = True self.sounds = [] if self.state != AI_INACTIVE and self.state != AI_SLEEPING: self.creature.fov.refresh()
def size(self): return libtcod.path_size(self.path)
def findExplorePoint(self,world): if self.explored: return (False,False) #expecting a list t0 = time.time() px=self.x py=self.y ww=world.getWidth() wh=world.getHeight() # Scan in expanding circle outwards distance=1 edge=1 searchEffort=60 pathEffort=3 potentials=[] edges=[1,2,3,4] goodPath=False while not goodPath: while len(potentials)<=searchEffort: # scan edges for i in edges: for j in xrange(edge): [cx,cy]=self.edgeCalc(i,j,edge,distance) if cx<0 or cx>=ww: continue if cy<0 or cy>=wh: continue if world.isBlocked(cx,cy) or world.isSeen(cx,cy): continue #if world.isPathable(cx,cy): potentials.append((cx,cy)) #world.putThing(cx,cy) # and corners for i in edges: [cx,cy]=self.cornerCalc(i,distance,px,py) if cx<0 or cx>=ww: continue if cy<0 or cy>=wh: continue if world.isBlocked(cx,cy) or world.isSeen(cx,cy): continue #if world.isPathable(cx,cy): potentials.append((cx,cy)) #world.putThing(cx,cy) distance=distance+1 edge=edge+2 if distance>max(ww*2,wh*2): if len(potentials): break self.explored=True return (False,False) #print len(potentials) # now pop random for effort amount and pick shortest path distRank={} for cd in potentials: dst=abs(px-cd[0])+abs(py-cd[1]) while dst in distRank: dst=dst+1 distRank[dst]=cd potentials=[] dkKeys=sorted(distRank) for i in xrange(min(searchEffort,len(dkKeys))): potentials.append(distRank[dkKeys[i]]) #world.putThing(potentials[i][0],potentials[i][1]) pselect=random.sample(potentials,min(pathEffort,len(dkKeys))) distRank={} if not self.pathMap: self.pathMap=world.getBlockedMap() for cd in pselect: #world.putThing(cd[0],cd[1],"*") path=libtcod.path_new_using_map(self.pathMap,0) libtcod.path_compute(path,self.x,self.y,cd[0],cd[1]) ps=libtcod.path_size(path) if ps==0: print "Zero-len path:",cd,px,py print "Is blocked:",world.isBlocked(cd[0],cd[1]) print "Is pathable:",world.isPathable(cd[0],cd[1]) print "Is seen:",world.isSeen(cd[0],cd[1]) while ps in distRank: ps=ps+1 distRank[ps]=(cd,path) # get closest path len - if it is very high compared to distance, reject and expand search dkKeys=sorted(distRank) if dkKeys[0] < distance*4 and dkKeys[0]>0: ret=distRank[dkKeys[0]] goodPath=True print "Successful search at pl",dkKeys[0],"distance",distance,"took",int(math.floor((time.time()-t0)*1000)),"ms" else: print "Rejected search at pl",dkKeys[0],"distance",distance,"took",int(math.floor((time.time()-t0)*1000)),"ms" return ret
def libtcod_path_to_list(path_map): ''' get a libtcod path into a list ''' return [libtcod.path_get(path_map, i) for i in xrange(libtcod.path_size(path_map))]
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 render_map(): # recompute FOV if needed (the player moved or something) libtcod.console_rect(0, game.MAP_X, game.MAP_Y, game.MAP_WIDTH, game.MAP_HEIGHT, True) if game.fov_recompute: find_map_viewport() fov_radius() initialize_fov(True) libtcod.map_compute_fov(game.fov_map, game.char.x, game.char.y, game.FOV_RADIUS, game.FOV_LIGHT_WALLS, game.FOV_ALGO) game.fov_recompute = False # 'torch' animation if game.fov_torch: game.fov_torchx += 0.2 tdx = [game.fov_torchx + 20.0] dx = libtcod.noise_get(game.fov_noise, tdx, libtcod.NOISE_SIMPLEX) * 1.5 tdx[0] += 30.0 dy = libtcod.noise_get(game.fov_noise, tdx, libtcod.NOISE_SIMPLEX) * 1.5 di = 0.4 * libtcod.noise_get(game.fov_noise, [game.fov_torchx], libtcod.NOISE_SIMPLEX) # go through all tiles, and set their background color according to the FOV for y in range(game.MAP_HEIGHT): for x in range(game.MAP_WIDTH): px = x + game.curx py = y + game.cury if not libtcod.map_is_in_fov(game.fov_map, px, py): if game.draw_map and game.current_map.tile_is_explored(px, py): if game.current_map.tile_is_animated(px, py): libtcod.console_put_char_ex(game.con, x, y, game.current_map.tile[px][py]['icon'], game.current_map.tile[px][py]['dark_color'], game.current_map.tile[px][py]['dark_back_color']) else: libtcod.console_put_char_ex(game.con, x, y, game.current_map.tile[px][py]['icon'], game.current_map.tile[px][py]['dark_color'], game.current_map.tile[px][py]['back_dark_color']) else: if not game.fov_torch: if 'animate' in game.current_map.tile[px][py] or 'duration' in game.current_map.tile[px][py]: (front, back, game.current_map.tile[px][py]['lerp']) = render_tiles_animations(px, py, game.current_map.tile[px][py]['color'], game.current_map.tile[px][py]['back_light_color'], game.current_map.tile[px][py]['back_dark_color'], game.current_map.tile[px][py]['lerp']) libtcod.console_put_char_ex(game.con, x, y, game.current_map.tile[px][py]['icon'], front, back) elif game.draw_map: libtcod.console_put_char_ex(game.con, x, y, game.current_map.tile[px][py]['icon'], game.current_map.tile[px][py]['color'], game.current_map.tile[px][py]['back_light_color']) else: base = game.current_map.tile[px][py]['back_light_color'] r = float(px - game.char.x + dx) * (px - game.char.x + dx) + (py - game.char.y + dy) * (py - game.char.y + dy) if r < game.SQUARED_TORCH_RADIUS: l = (game.SQUARED_TORCH_RADIUS - r) / game.SQUARED_TORCH_RADIUS + di if l < 0.0: l = 0.0 elif l > 1.0: l = 1.0 base = libtcod.color_lerp(base, libtcod.gold, l) libtcod.console_put_char_ex(game.con, x, y, game.current_map.tile[px][py]['icon'], game.current_map.tile[px][py]['color'], base) if not game.current_map.tile_is_explored(px, py): game.current_map.tile[px][py].update({'explored': True}) # draw all objects in the map (if in the map viewport), except the player who his drawn last for obj in reversed(game.current_map.objects): if obj.y in range(game.cury, game.cury + game.MAP_HEIGHT) and obj.x in range(game.curx, game.curx + game.MAP_WIDTH) and game.current_map.tile_is_explored(obj.x, obj.y) and obj.name != 'player': if game.draw_map and obj.entity is not None: if libtcod.map_is_in_fov(game.fov_map, obj.x, obj.y) and not obj.entity.is_identified(): skill = game.player.find_skill('Mythology') if (game.player.skills[skill].level * 0.8) + 20 >= roll_dice(1, 100): obj.entity.flags.append('identified') game.message.new('You properly identify the ' + obj.entity.unidentified_name + ' as ' + obj.entity.get_name(True) + '.', game.turns) game.player.skills[skill].gain_xp(3) if obj.entity is not None and not obj.entity.is_identified(): obj.draw(game.con, libtcod.white) else: obj.draw(game.con) game.char.draw(game.con) libtcod.console_blit(game.con, 0, 0, game.MAP_WIDTH, game.MAP_HEIGHT, 0, game.MAP_X, game.MAP_Y) game.draw_map = False # move the player if using mouse if game.mouse_move: if mouse_auto_move() and not libtcod.path_is_empty(game.path): game.char.x, game.char.y = libtcod.path_walk(game.path, True) game.fov_recompute = True game.player_move = True else: items_at_feet() game.mouse_move = False # check where is the mouse cursor if not in the act of moving while using the mouse if not game.mouse_move: (mx, my) = (game.mouse.cx - game.MAP_X, game.mouse.cy - 1) px = mx + game.curx py = my + game.cury game.path_dx = -1 game.path_dy = -1 if my in range(game.MAP_HEIGHT) and mx in range(game.MAP_WIDTH): libtcod.console_set_char_background(0, mx + game.MAP_X, my + 1, libtcod.white, libtcod.BKGND_SET) if game.current_map.tile_is_explored(px, py) and not game.current_map.tile_is_blocked(px, py): game.path_dx = px game.path_dy = py if game.mouse.lbutton_pressed: target = [obj for obj in game.current_map.objects if obj.y == py and obj.x == px and obj.entity] if target: mouse_auto_attack(px, py, target[0]) else: game.mouse_move = mouse_auto_move() # draw a line between the player and the mouse cursor if not game.current_map.tile_is_blocked(game.path_dx, game.path_dy): libtcod.path_compute(game.path, game.char.x, game.char.y, game.path_dx, game.path_dy) for i in range(libtcod.path_size(game.path)): x, y = libtcod.path_get(game.path, i) if (y - game.cury) in range(game.MAP_HEIGHT) and (x - game.curx) in range(game.MAP_WIDTH): libtcod.console_set_char_background(0, game.MAP_X + x - game.curx, game.MAP_Y + y - game.cury, libtcod.desaturated_yellow, libtcod.BKGND_SET) libtcod.console_set_default_foreground(0, libtcod.light_yellow) libtcod.console_print_rect(0, game.MAP_X, game.MAP_Y, game.MAP_WIDTH - 18, game.MAP_HEIGHT, get_names_under_mouse()) if game.debug.enable: libtcod.console_print_ex(0, game.MAP_X + game.MAP_WIDTH - 1, game.MAP_Y, libtcod.BKGND_NONE, libtcod.RIGHT, str(game.gametime.hour) + ':' + str(game.gametime.minute).rjust(2, '0') + ' (%3d fps)' % libtcod.sys_get_fps()) if game.hp_anim: render_floating_text_animations()