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
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
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:
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)
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
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 []
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
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
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
174 _lib.TCOD_map_compute_fov(tcodMap, radius, radius, radius, lightWalls, fov)
175 touched = []
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
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
217
218
219
220 __all__ = ['AStar', 'quickFOV']
221