Package tdl :: Module map
[frames] | no frames]

Source Code for Module tdl.map

  1  """ 
  2      Rogue-like map utilitys such as line-of-sight, field-of-view, and path-finding. 
  3       
  4  """ 
  5  import array 
  6  import ctypes 
  7  import itertools 
  8  import math 
  9   
 10  import tdl 
 11  from .__tcod import _lib, _PATHCALL 
 12   
 13  _FOVTYPES = {'BASIC' : 0, 'DIAMOND': 1, 'SHADOW': 2, 'RESTRICTIVE': 12, 'PERMISSIVE': 11} 
 14   
15 -def _getFOVType(fov):
16 "Return a FOV from a string" 17 oldFOV = fov 18 fov = str(fov).upper() 19 if fov in _FOVTYPES: 20 return _FOVTYPES[fov] 21 if fov[:10] == 'PERMISSIVE' and fov[10].isdigit() and fov[10] != '9': 22 return 4 + int(fov[10]) 23 raise tdl.TDLError('No such fov option as %s' % oldFOV)
24
25 -class AStar(object):
26 """A* pathfinder 27 28 Using this class requires a callback detailed in L{AStar.__init__} 29 """ 30 31 __slots__ = ('_as_parameter_', '_callback', '__weakref__') 32
33 - def __init__(self, width, height, callback, 34 diagnalCost=math.sqrt(2), advanced=False):
35 """Create an A* pathfinder using a callback. 36 37 Before crating this instance you should make one of two types of 38 callbacks: 39 - A function that returns the cost to move to (x, y) 40 or 41 - A function that returns the cost to move between 42 (destX, destY, sourceX, sourceY) 43 If path is blocked the function should return zero or None. 44 When using the second type of callback be sure to set advanced=True 45 46 @type width: int 47 @param width: width of the pathfinding area in tiles 48 @type height: int 49 @param height: height of the pathfinding area in tiles 50 51 @type callback: function 52 @param callback: A callback taking parameters depending on the setting 53 of 'advanced' and returning the cost of 54 movement for an open tile or zero for a 55 blocked tile. 56 57 @type diagnalCost: float 58 @param diagnalCost: Multiplier for diagonal movement. 59 60 Can be set to zero to disable diagonal movement 61 entirely. 62 63 @type advanced: boolean 64 @param advanced: A simple callback with 2 positional parameters may not 65 provide enough information. Setting this to True will 66 call the callback with 2 additional parameters giving 67 you both the destination and the source of movement. 68 69 When True the callback will need to accept 70 (destX, destY, sourceX, sourceY) as parameters. 71 Instead of just (destX, destY). 72 73 """ 74 if not diagnalCost: # set None or False to zero 75 diagnalCost = 0.0 76 if advanced: 77 def newCallback(sourceX, sourceY, destX, destY, null): 78 pathCost = callback(destX, destY, sourceX, sourceY) 79 if pathCost: 80 return pathCost 81 return 0.0
82 else: 83 def newCallback(sourceX, sourceY, destX, destY, null): 84 pathCost = callback(destX, destY) # expecting a float or 0 85 if pathCost: 86 return pathCost 87 return 0.0
88 self._callback = _PATHCALL(newCallback) 89 """A CFUNCTYPE callback to be kept in memory.""" 90 self._as_parameter_ = _lib.TCOD_path_new_using_function(width, height, 91 self._callback, None, diagnalCost) 92
93 - def __del__(self):
94 _lib.TCOD_path_delete(self)
95
96 - def getPath(self, origX, origY, destX, destY):
97 found = _lib.TCOD_path_compute(self, origX, origY, destX, destY) 98 if not found: 99 return [] # path not found 100 x, y = ctypes.c_int(), ctypes.c_int() 101 xRef, yRef = ctypes.byref(x), ctypes.byref(y) 102 recalculate = ctypes.c_bool(True) 103 path = [] 104 while _lib.TCOD_path_walk(self, xRef, yRef, recalculate): 105 path.append((x.value, y.value)) 106 return path
107
108 -def quickFOV(x, y, callback, fov='PERMISSIVE', radius=7.5, lightWalls=True, sphere=True):
109 """All field-of-view functionality in one call. 110 111 Before using this call be sure to make a function, lambda, or method that takes 2 112 positional parameters and returns True if light can pass through the tile or False 113 for light-blocking tiles and for indexes that are out of bounds of the 114 dungeon. 115 116 This function is 'quick' as in no hassle but can quickly become a very slow 117 function call if a large radius is used or the callback provided itself 118 isn't optimized. 119 120 Always check if the index is in bounds both in the callback and in the 121 returned values. These values can go into the negatives as well. 122 123 @type x: int 124 @param x: x center of the field-of-view 125 @type y: int 126 @param y: y center of the field-of-view 127 @type callback: function 128 @param callback: This should be a function that takes two positional arguments x,y 129 and returns True if the tile at that position is transparent 130 or False if the tile blocks light or is out of bounds. 131 @type fov: string 132 @param fov: The type of field-of-view to be used. Available types are: 133 134 'BASIC', 'DIAMOND', 'SHADOW', 'RESTRICTIVE', 'PERMISSIVE', 135 'PERMISSIVE0', 'PERMISSIVE1', ..., 'PERMISSIVE8' 136 @type radius: float 137 @param radius: Raduis of the field-of-view. 138 139 When sphere is True a floating point can be used to fine-tune 140 the range. Otherwise the radius is just rounded up. 141 142 Be careful as a large radius has an exponential affect on 143 how long this function takes. 144 @type lightWalls: boolean 145 @param lightWalls: Include or exclude wall tiles in the field-of-view. 146 @type sphere: boolean 147 @param sphere: True for a spherical field-of-view. False for a square one. 148 149 @rtype: iterator 150 @return: Returns an iterator of (x, y) points that are within the field-of-view 151 """ 152 trueRadius = radius 153 radius = math.ceil(radius) 154 mapSize = radius * 2 + 1 155 fov = _getFOVType(fov) 156 157 setProp = _lib.TCOD_map_set_properties # make local 158 inFOV = _lib.TCOD_map_is_in_fov 159 160 cTrue = ctypes.c_bool(1) 161 cFalse = ctypes.c_bool(False) 162 try: 163 tcodMap = _lib.TCOD_map_new(mapSize, mapSize) 164 # pass one, write callback data to the tcodMap 165 for (x_, cX), (y_, cY) in itertools.product(((i, ctypes.c_int(i)) for i in range(mapSize)), 166 ((i, ctypes.c_int(i)) for i in range(mapSize))): 167 168 pos = (x_ + x - radius, 169 y_ + y - radius) 170 transparent = bool(callback(*pos)) 171 setProp(tcodMap, cX, cY, transparent, cFalse) 172 173 # pass two, compute fov and build a list of points 174 _lib.TCOD_map_compute_fov(tcodMap, radius, radius, radius, lightWalls, fov) 175 touched = [] # points touched by field of view 176 for (x_, cX),(y_, cY) in itertools.product(((i, ctypes.c_int(i)) for i in range(mapSize)), 177 ((i, ctypes.c_int(i)) for i in range(mapSize))): 178 if sphere and math.hypot(x_ - radius, y_ - radius) > trueRadius: 179 continue 180 if inFOV(tcodMap, cX, cY): 181 touched.append((x_ + x - radius, y_ + y - radius)) 182 finally: 183 _lib.TCOD_map_delete(tcodMap) 184 return touched
185 186 # def bresenham(x1, y1, x2, y2): 187 # points = [] 188 # issteep = abs(y2-y1) > abs(x2-x1) 189 # if issteep: 190 # x1, y1 = y1, x1 191 # x2, y2 = y2, x2 192 # rev = False 193 # if x1 > x2: 194 # x1, x2 = x2, x1 195 # y1, y2 = y2, y1 196 # rev = True 197 # deltax = x2 - x1 198 # deltay = abs(y2-y1) 199 # error = int(deltax / 2) 200 # y = y1 201 # ystep = None 202 # if y1 < y2: 203 # ystep = 1 204 # else: 205 # ystep = -1 206 # for x in range(x1, x2 + 1): 207 # if issteep: 208 # points.append((y, x)) 209 # else: 210 # points.append((x, y)) 211 # error -= deltay 212 # if error < 0: 213 # y += ystep 214 # error += deltax 215 # # Reverse the list if the coordinates were reversed 216 # if rev: 217 # points.reverse() 218 # return points 219 220 __all__ = ['AStar', 'quickFOV'] 221