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
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
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:
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)
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
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 []
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
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
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
170 _lib.TCOD_map_compute_fov(tcodMap, radius, radius, radius, lightWalls, fov)
171 touched = []
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216 __all__ = ['AStar', 'quickFOV']
217