Skip to content

Commit 6296956

Browse files
committed
added Map class, AStar.getPath is now AStar.get_path, cleaned up map.py
1 parent 8260e55 commit 6296956

File tree

1 file changed

+178
-14
lines changed

1 file changed

+178
-14
lines changed

tdl/map.py

Lines changed: 178 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@
1414

1515
_FOVTYPES = {'BASIC' : 0, 'DIAMOND': 1, 'SHADOW': 2, 'RESTRICTIVE': 12, 'PERMISSIVE': 11}
1616

17-
18-
#_PATHCALL = CFUNCTYPE(c_float, c_int, c_int, c_int, c_int, py_object)
19-
2017
def _get_fov_type(fov):
2118
"Return a FOV from a string"
2219
oldFOV = fov
@@ -27,10 +24,182 @@ def _get_fov_type(fov):
2724
return 4 + int(fov[10])
2825
raise _tdl.TDLError('No such fov option as %s' % oldFOV)
2926

27+
class Map(object):
28+
"""Fast field-of-view and path-finding on stored data.
29+
30+
Set map conditions with the walkable and transparency attributes, this
31+
object can be iterated and checked for containment similar to consoles.
32+
33+
For example, you can set all tiles and transparent and walkable with the
34+
following code::
35+
36+
map = tdl.map.Map(80, 60)
37+
for x,y in map:
38+
map.transparent[x,y] = true
39+
map.walkable[x,y] = true
40+
41+
@ivar transparent: Map transparency, access this attribute with
42+
map.transparency[x,y]
43+
44+
Set to True to allow field-of-view rays, False will
45+
block field-of-view.
46+
47+
Transparent tiles only affect field-of-view.
48+
49+
@ivar walkable: Map accessibility, access this attribute with
50+
map.walkable[x,y]
51+
52+
Set to True to allow path-finding through that tile,
53+
False will block passage to that tile.
54+
55+
Walkable tiles only affect path-finding.
56+
57+
@ivar fov: Map tiles touched by a field-of-view computation,
58+
access this attribute with map.fov[x,y]
59+
60+
Is True if a the tile is if view, otherwise False.
61+
62+
You can set to this attribute if you want, but you'll typically
63+
be using it to read the field-of-view of a L{compute_fov} call.
64+
"""
65+
66+
class _MapAttribute(object):
67+
def __init__(self, map, bit_index):
68+
self.map = map
69+
self.bit_index = bit_index
70+
self.bit = 1 << bit_index
71+
self.bit_inverse = 0xFFFF ^ self.bit
72+
73+
def __getitem__(self, key):
74+
return bool(self.map._array_cdata[key[1]][key[0]] & self.bit)
75+
76+
def __setitem__(self, key, value):
77+
self.map._array_cdata[key[1]][key[0]] = (
78+
(self.map._array_cdata[key[1]][key[0]] & self.bit_inverse) |
79+
(self.bit * bool(value))
80+
)
81+
82+
def __init__(self, width, height):
83+
"""Create a new Map with width and height.
84+
85+
@type width: int
86+
@type height: int
87+
@param width: Width of the new Map instance, in tiles.
88+
@param width: Height of the new Map instance, in tiles.
89+
"""
90+
self.width = width
91+
self.height = height
92+
self._map_cdata = _lib.TCOD_map_new(width, height)
93+
# cast array into cdata format: int16[y][x]
94+
# for quick Python access
95+
self._array_cdata = _ffi.new('int16[%i][%i]' % (width, height))
96+
# flat array to pass to TDL's C helpers
97+
self._array_cdata_flat = _ffi.cast('int16 *', self._array_cdata)
98+
self.transparent = self._MapAttribute(self, 0)
99+
self.walkable = self._MapAttribute(self, 1)
100+
self.fov = self._MapAttribute(self, 2)
101+
102+
def __del__(self):
103+
if self._map_cdata:
104+
_lib.TCOD_map_delete(self._map_cdata)
105+
self._map_cdata = None
106+
107+
def compute_fov(self, x, y, fov='PERMISSIVE', radius=None, light_walls=True,
108+
sphere=True, cumulative=False):
109+
"""Compute the field-of-view of this Map and return an iterator of the
110+
points touched.
111+
112+
@type x: int
113+
@type y: int
114+
115+
@param x: x center of the field-of-view
116+
@param y: y center of the field-of-view
117+
@type fov: string
118+
@param fov: The type of field-of-view to be used. Available types are:
119+
120+
'BASIC', 'DIAMOND', 'SHADOW', 'RESTRICTIVE', 'PERMISSIVE',
121+
'PERMISSIVE0', 'PERMISSIVE1', ..., 'PERMISSIVE8'
122+
@type radius: int
123+
@param radius: Raduis of the field-of-view.
124+
@type light_walls: boolean
125+
@param light_walls: Include or exclude wall tiles in the field-of-view.
126+
@type sphere: boolean
127+
@param sphere: True for a spherical field-of-view.
128+
False for a square one.
129+
@type cumulative: boolean
130+
@param cumulative:
131+
132+
@rtype: iter((x, y), ...)
133+
@return: An iterator of (x, y) points of tiles touched by the
134+
field-of-view.
135+
136+
Unexpected behaviour can happen if you modify the Map while
137+
using the iterator.
138+
139+
You can use the Map's fov attribute as an alternative to this
140+
iterator.
141+
"""
142+
# refresh cdata
143+
_lib.TDL_map_data_from_buffer(self._map_cdata,
144+
self._array_cdata_flat)
145+
if radius is None: # infinite radius
146+
radius = max(self.width, self.height)
147+
_lib.TCOD_map_compute_fov(self.cdata, x, y, radius, light_walls,
148+
_get_fov_type(fov))
149+
_lib.TDL_map_fov_to_buffer(self._map_cdata,
150+
self._array_cdata_flat, cumulative)
151+
def iterate_fov():
152+
_array_cdata = self._array_cdata
153+
for y in range(self.width):
154+
for x in range(self.height):
155+
if(_array_cdata[y][x] & 4):
156+
yield (x, y)
157+
return iterate_fov()
158+
159+
160+
161+
def compute_path(self, start_x, start_y, dest_x, dest_y,
162+
diagonal_cost=_math.sqrt(2)):
163+
"""
164+
165+
@type diagnalCost: float
166+
@param diagnalCost: Multiplier for diagonal movement.
167+
168+
Can be set to zero to disable diagonal movement
169+
entirely.
170+
"""
171+
# refresh cdata
172+
_lib.TDL_map_data_from_buffer(self._map_cdata,
173+
self._array_cdata_flat)
174+
path_cdata = _lib.TCOD_path_new_using_map(self._map_cdata, diagonal_cost)
175+
try:
176+
_lib.TCOD_path_compute(path_cdata, start_x, start_y, dest_x, dest_y)
177+
x = _ffi.new('int *')
178+
y = _ffi.new('int *')
179+
length = _lib.TCOD_path_size(path_cdata)
180+
path = [None] * length
181+
for i in range(length):
182+
_lib.TCOD_path_get(path_cdata, i, x, y)
183+
path[i] = ((x[0], y[0]))
184+
finally:
185+
_lib.TCOD_path_delete(path_cdata)
186+
return path
187+
188+
def __iter__(self):
189+
return _itertools.product(range(self.width), range(self.height))
190+
191+
def __contains__(self, position):
192+
x, y = position
193+
return (0 <= x < self.width) and (0 <= y < self.height)
194+
195+
196+
30197
class AStar(object):
31198
"""A* pathfinder
32199
33200
Using this class requires a callback detailed in L{AStar.__init__}
201+
202+
@undocumented: getPath
34203
"""
35204

36205
__slots__ = ('_as_parameter_', '_callback', '__weakref__')
@@ -92,8 +261,8 @@ def newCallback(sourceX, sourceY, destX, destY, null):
92261
if pathCost:
93262
return pathCost
94263
return 0.0
264+
# float(int, int, int, int, void*)
95265
self._callback = _ffi.callback('TCOD_path_func_t')(newCallback)
96-
"""A cffi callback to be kept in memory."""
97266

98267
self._as_parameter_ = _lib.TCOD_path_new_using_function(width, height,
99268
self._callback, _ffi.NULL, diagnalCost)
@@ -103,7 +272,7 @@ def __del__(self):
103272
_lib.TCOD_path_delete(self._as_parameter_)
104273
self._as_parameter_ = None
105274

106-
def getPath(self, origX, origY, destX, destY):
275+
def get_path(self, origX, origY, destX, destY):
107276
"""
108277
Get the shortest path from origXY to destXY.
109278
@@ -175,25 +344,18 @@ def quick_fov(x, y, callback, fov='PERMISSIVE', radius=7.5, lightWalls=True, sph
175344
setProp = _lib.TCOD_map_set_properties # make local
176345
inFOV = _lib.TCOD_map_is_in_fov
177346

178-
#cTrue = _ffi.new('bool *', True)
179-
#cFalse = _ffi.new('bool *', False)
180-
#cFalse = _ctypes.c_bool(False)
181347
tcodMap = _lib.TCOD_map_new(mapSize, mapSize)
182348
try:
183-
# pass one, write callback data to the tcodMap
184-
#for (x_, cX), (y_, cY) in _itertools.product(((i, _ctypes.c_int(i)) for i in range(mapSize)),
185-
# ((i, _ctypes.c_int(i)) for i in range(mapSize))):
349+
# pass no.1, write callback data to the tcodMap
186350
for x_, y_ in _itertools.product(range(mapSize), range(mapSize)):
187351
pos = (x_ + x - radius,
188352
y_ + y - radius)
189353
transparent = bool(callback(*pos))
190354
setProp(tcodMap, x_, y_, transparent, False)
191355

192-
# pass two, compute fov and build a list of points
356+
# pass no.2, compute fov and build a list of points
193357
_lib.TCOD_map_compute_fov(tcodMap, radius, radius, radius, lightWalls, fov)
194358
touched = set() # points touched by field of view
195-
#for (x_, cX),(y_, cY) in _itertools.product(((i, _ctypes.c_int(i)) for i in range(mapSize)),
196-
# ((i, _ctypes.c_int(i)) for i in range(mapSize))):
197359
for x_, y_ in _itertools.product(range(mapSize), range(mapSize)):
198360
if sphere and _math.hypot(x_ - radius, y_ - radius) > trueRadius:
199361
continue
@@ -245,6 +407,8 @@ def bresenham(x1, y1, x2, y2):
245407
points.reverse()
246408
return points
247409

410+
248411
__all__ = [_var for _var in locals().keys() if _var[0] != '_']
249412

250413
quickFOV = _style.backport(quick_fov)
414+
AStar.getPath = _style.backport(AStar.get_path)

0 commit comments

Comments
 (0)