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