Browse code
djikstra pathfinding ai... adds lags to the game
Ed L authored on 29/07/2012 04:10:45
Showing 10 changed files
Showing 10 changed files
- algorithms/__init__.py
- algorithms/djikstra.py
- data/monsters/base.yml
- game.py
- levels.py
- main.py
- maps.py
- monsters.py
- objects.py
- player.py
2 | 2 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,171 @@ |
1 |
+import random |
|
2 |
+import copy |
|
3 |
+ |
|
4 |
+class DjikstraMap(object): |
|
5 |
+ def __init__(self, mp=None): |
|
6 |
+ #print '__init__ djm' |
|
7 |
+ self.map = None |
|
8 |
+ if mp is not None: |
|
9 |
+ self.load_map(mp) |
|
10 |
+ |
|
11 |
+ def load_map(self, mp): |
|
12 |
+ self.map = [ |
|
13 |
+ [ [255,None][cell] for cell in row ] |
|
14 |
+ for row in mp |
|
15 |
+ ] |
|
16 |
+ self.width = len(self.map) |
|
17 |
+ self.height = len(self.map[0]) |
|
18 |
+ def set_goals(self, *args, **k): |
|
19 |
+ for x,y in args: |
|
20 |
+ self.map[x][y] = k.get('weight', 0) |
|
21 |
+ |
|
22 |
+ def iter_map(self): |
|
23 |
+ for x, row in enumerate(self.map): |
|
24 |
+ for y, cell in enumerate(row): |
|
25 |
+ if cell is not None: |
|
26 |
+ yield (x,y), cell |
|
27 |
+ |
|
28 |
+ def get_cross(self, pos, rad): |
|
29 |
+ ox,oy = pos |
|
30 |
+ # up, down, left, right |
|
31 |
+ result = [ |
|
32 |
+ (ox, oy-rad), |
|
33 |
+ (ox, oy+rad), |
|
34 |
+ (ox-rad, oy), |
|
35 |
+ (ox+rad, oy), |
|
36 |
+ ] |
|
37 |
+ for idx, (x,y) in enumerate(result): |
|
38 |
+ if x < 0 or x >= self.width: |
|
39 |
+ result[idx] = None |
|
40 |
+ elif y < 0 or y >= self.height: |
|
41 |
+ result[idx] = None |
|
42 |
+ else: |
|
43 |
+ result[idx] = self.map[x][y] |
|
44 |
+ return result |
|
45 |
+ |
|
46 |
+ def get_rect(self, pos, rad): |
|
47 |
+ x,y = pos |
|
48 |
+ result = [] |
|
49 |
+ for cx in range(x-rad, x+rad+1): |
|
50 |
+ result.append([]) |
|
51 |
+ for cy in range(y-rad, y+rad+1): |
|
52 |
+ if cx < 0 or cx >= len(self.map): |
|
53 |
+ result[-1].append(None) |
|
54 |
+ elif cy < 0 or cy >= len(self.map[0]): |
|
55 |
+ result[-1].append(None) |
|
56 |
+ else: |
|
57 |
+ result[-1].append(self.map[cx][cy]) |
|
58 |
+ |
|
59 |
+ return result |
|
60 |
+ |
|
61 |
+ def get_line(self, pos1, pos2): |
|
62 |
+ x1,y1 = pos1 |
|
63 |
+ x2,y2 = pos2 |
|
64 |
+ if y1 == y2: |
|
65 |
+ return [ (x,y1) for x in range(x1,x2+1) ] |
|
66 |
+ if x1 == x2: |
|
67 |
+ return [ (x1,y) for y in range(y1,y2+1) ] |
|
68 |
+ |
|
69 |
+ def get_borders(self, pos, rad): |
|
70 |
+ x,y = pos |
|
71 |
+ |
|
72 |
+ results = [] |
|
73 |
+ results.extend( |
|
74 |
+ self.get_line( |
|
75 |
+ (min(x-rad, 0), min(y-rad, 0)), |
|
76 |
+ (min(x-rad, 0), min(y+rad, self.height)) |
|
77 |
+ ) |
|
78 |
+ ) |
|
79 |
+ results.extend( |
|
80 |
+ self.get_line( |
|
81 |
+ (min(x-rad, 0), min(y-rad, 0)), |
|
82 |
+ (min(x+rad, self.width), min(y-rad, 0)) |
|
83 |
+ ) |
|
84 |
+ ) |
|
85 |
+ |
|
86 |
+ results.extend( |
|
87 |
+ self.get_line( |
|
88 |
+ (min(x+rad, self.width), min(y+rad, self.height)), |
|
89 |
+ (min(x-rad, 0), min(y+rad, self.height)) |
|
90 |
+ ) |
|
91 |
+ ) |
|
92 |
+ |
|
93 |
+ results.extend( |
|
94 |
+ self.get_line( |
|
95 |
+ (min(x+rad, self.width), min(y+rad, self.height)), |
|
96 |
+ (min(x+rad, self.width), min(y-rad, 0)) |
|
97 |
+ ) |
|
98 |
+ ) |
|
99 |
+ |
|
100 |
+ def iter(self, num): |
|
101 |
+ result = True |
|
102 |
+ for _ in range(num): |
|
103 |
+ result = self.cycle() |
|
104 |
+ if result == False: |
|
105 |
+ break |
|
106 |
+ return result |
|
107 |
+ |
|
108 |
+ def cycle(self): |
|
109 |
+ changed = False |
|
110 |
+ out = self.map |
|
111 |
+ for pos, cell in self.iter_map(): |
|
112 |
+ x,y = pos |
|
113 |
+ |
|
114 |
+ #rect = self.get_rect(pos, 2) |
|
115 |
+ #neighbors = [n for n in borders(rect)] |
|
116 |
+ neighbors = (r for r in sum(self.get_rect(pos, 1), []) if r is not None) |
|
117 |
+ #neighbors = (r for r in self.get_cross(pos, 1) if r is not None) |
|
118 |
+ |
|
119 |
+ try: |
|
120 |
+ min_neighbor = min(neighbors) |
|
121 |
+ except ValueError: continue |
|
122 |
+ |
|
123 |
+ if cell > min_neighbor + 1: |
|
124 |
+ changed = True |
|
125 |
+ out[x][y] = min_neighbor + 1 |
|
126 |
+ return changed |
|
127 |
+ |
|
128 |
+ def visualize(self): |
|
129 |
|
|
130 |
+ for row in zip(*self.map): |
|
131 |
+ for cell in row: |
|
132 |
+ if cell is None: print ' ', |
|
133 |
+ elif cell > 9: print '*', |
|
134 |
+ else: print cell, |
|
135 |
|
|
136 |
+ |
|
137 |
+ def get_neighbor_values(self, x,y): |
|
138 |
+ b = enumerate((enumerate(r,-1) for r in self.get_rect( (x,y), 1 )),-1) |
|
139 |
+ result = [(i1,i2, v) for i1, r in b for i2,v in r if v is not None] |
|
140 |
+ #print result |
|
141 |
+ return result |
|
142 |
+ |
|
143 |
+ def get_low_neighbors(self, x,y, num=2): |
|
144 |
+ result = sorted(self.get_neighbor_values(x,y), key=lambda a: a[-1]) |
|
145 |
+ return result[:num] |
|
146 |
+ |
|
147 |
+ def categorize(self, values): |
|
148 |
+ results = {} |
|
149 |
+ for i1,i2,v in values: |
|
150 |
+ results.setdefault(v,[]).append( (i1,i2) ) |
|
151 |
+ return results |
|
152 |
+ def nav(self, x,y): |
|
153 |
+ results = self.get_neighbor_values(x,y) |
|
154 |
+ results = self.categorize(results) |
|
155 |
+ dx,dy = random.choice(results[min(results)]) |
|
156 |
+ #print dx,dy,min(results) |
|
157 |
+ |
|
158 |
+ return dx,dy |
|
159 |
+ |
|
160 |
+def borders(rect): |
|
161 |
+ mx, my = len(rect)-1, len(rect[0])-1 |
|
162 |
+ for x, row in enumerate(rect): |
|
163 |
+ for y, cell in enumerate(row): |
|
164 |
+ if x in {0,mx} or y in {0,my}: |
|
165 |
+ if cell is not None: |
|
166 |
+ yield cell |
|
167 |
+ |
|
168 |
+def dist( p1, p2 ): |
|
169 |
+ x1,y1 = p1 |
|
170 |
+ x2,y2 = p2 |
|
171 |
+ return int( ( (x2-x1)**2+(y2-y1)**2 ) ** .5 ) |
... | ... |
@@ -4,7 +4,7 @@ namegen_class: "male" |
4 | 4 |
char: "h" |
5 | 5 |
color: amber |
6 | 6 |
|
7 |
-spawn_chance: 4 |
|
7 |
+spawn_chance: 0 |
|
8 | 8 |
|
9 | 9 |
hp: 10 |
10 | 10 |
defense: 1 |
... | ... |
@@ -18,7 +18,7 @@ namegen_class: "male" |
18 | 18 |
char: "i" |
19 | 19 |
color: dark_red |
20 | 20 |
|
21 |
-spawn_chance: 7 |
|
21 |
+spawn_chance: 3 |
|
22 | 22 |
|
23 | 23 |
hp: 4 |
24 | 24 |
defense: 0 |
... | ... |
@@ -30,7 +30,7 @@ namegen_class: "demon male" |
30 | 30 |
char: "%" |
31 | 31 |
color: darker_yellow |
32 | 32 |
|
33 |
-spawn_chance: 1 |
|
33 |
+spawn_chance: 0 |
|
34 | 34 |
|
35 | 35 |
hp: 17 |
36 | 36 |
defense: 2 |
... | ... |
@@ -24,7 +24,7 @@ class GameBase: |
24 | 24 |
def message(self, msg, color=None): |
25 | 25 |
if color is None: |
26 | 26 |
color = libtcod.white |
27 |
- utilities.message(self.game_msgs, self.MSG_HEIGHT, self.MSG_WIDTH, msg) |
|
27 |
+ utilities.message(self.game_msgs, self.MSG_HEIGHT, self.MSG_WIDTH, msg, color=color) |
|
28 | 28 |
|
29 | 29 |
def __init__(self, app_name='test app', screen_width=None, screen_height=None): |
30 | 30 |
print '__init__' |
... | ... |
@@ -1,3 +1,4 @@ |
1 |
+from algorithms import djikstra |
|
1 | 2 |
import libtcodpy as libtcod |
2 | 3 |
import maps |
3 | 4 |
import debug |
... | ... |
@@ -16,7 +17,16 @@ class Level(object): |
16 | 17 |
self.number = num |
17 | 18 |
return self |
18 | 19 |
|
20 |
+ def get_djikstra(self, x,y): |
|
21 |
+ if (x,y) not in self.djikstra_cache: |
|
22 |
+ dj = self.djikstra_cache[x,y] = djikstra.DjikstraMap(self.map.map.data) |
|
23 |
+ dj.set_goals( (x,y), weight=0) |
|
24 |
+ dj = self.djikstra_cache[x,y] |
|
25 |
+ dj.cycle() |
|
26 |
+ return dj |
|
27 |
+ |
|
19 | 28 |
def __init__(self, width, height, con, item_types=None, monster_types=None): |
29 |
+ self.djikstra_cache = {} |
|
20 | 30 |
self.objects = [] |
21 | 31 |
self.map = maps.Map(width, height, con, self) |
22 | 32 |
self.fov_map = libtcod.map_new(self.map.width, self.map.height) |
... | ... |
@@ -119,7 +129,13 @@ class Level(object): |
119 | 129 |
return libtcod.map_is_in_fov(self.fov_map, x,y) |
120 | 130 |
|
121 | 131 |
def is_blocked(self, x,y): |
122 |
- return self.map.is_blocked(x,y) |
|
132 |
+ if x < 0 or x > self.map.width: |
|
133 |
+ result = True |
|
134 |
+ elif y < 0 or y > self.map.height: |
|
135 |
+ result = True |
|
136 |
+ else: |
|
137 |
+ result = self.map.is_blocked(x,y) |
|
138 |
+ return result |
|
123 | 139 |
|
124 | 140 |
|
125 | 141 |
import game |
... | ... |
@@ -366,7 +366,7 @@ if __name__ == 'main': |
366 | 366 |
'', |
367 | 367 |
) |
368 | 368 |
|
369 |
- options = ['Play', 'Exit'] |
|
369 |
+ options = ['Resume', 'Play', 'Exit'] |
|
370 | 370 |
result = self.menu('\n'.join(message), options, len(message[0])) |
371 | 371 |
if result is not None: |
372 | 372 |
return options[result] |
... | ... |
@@ -396,9 +396,11 @@ if __name__ == '__main__': |
396 | 396 |
game_instance.load_settings() |
397 | 397 |
action = game_instance.main_menu() |
398 | 398 |
while action is None or action.lower() != 'exit': |
399 |
- if action is not None and action.lower() == 'play': |
|
400 |
- game_instance.setup_map() |
|
401 |
- game_instance.main() |
|
402 |
- libtcod.console_clear(0) |
|
399 |
+ if action is not None: |
|
400 |
+ if action.lower() == 'play': |
|
401 |
+ game_instance.setup_map() |
|
402 |
+ if action.lower() in {'play', 'resume'}: |
|
403 |
+ game_instance.main() |
|
404 |
+ libtcod.console_clear(0) |
|
403 | 405 |
action = game_instance.main_menu() |
404 | 406 |
|
... | ... |
@@ -15,6 +15,7 @@ class Tile(object): |
15 | 15 |
self.block_sight = block_sight |
16 | 16 |
|
17 | 17 |
|
18 |
+ |
|
18 | 19 |
class AutomataEngine(object): |
19 | 20 |
def __init__(self, width=None, height=None, data=None, randomize=True): |
20 | 21 |
if data: |
... | ... |
@@ -187,6 +188,47 @@ class MazeGen(AutomataEngine): |
187 | 188 |
tmp_data[x][y] = self.rule(x,y, cell) |
188 | 189 |
return AutomataEngine(data=tmp_data) |
189 | 190 |
|
191 |
+class AutomataLoader(AutomataEngine): |
|
192 |
+ def load_rules(self): |
|
193 |
+ self.rules = yaml.load( |
|
194 |
+ file( |
|
195 |
+ os.path.join('.', 'data', 'mapgenerator.yml') |
|
196 |
+ ) |
|
197 |
+ ) |
|
198 |
+ |
|
199 |
+ def parse_rule(self, rule): |
|
200 |
+ comp, val = rule.split('->') |
|
201 |
+ val = val.strip() |
|
202 |
+ if comp == 'is': |
|
203 |
+ if val == 'odd': |
|
204 |
+ return lambda a: (a%2)==1 |
|
205 |
+ elif val == 'even': |
|
206 |
+ return lambda a: (a%2)==0 |
|
207 |
+ else: |
|
208 |
+ return lambda a: a == int(val) |
|
209 |
+ else: |
|
210 |
+ val = int(val) |
|
211 |
+ if comp.startswith('>'): |
|
212 |
+ if comp.startswith('>='): |
|
213 |
+ return lambda a: a >= val |
|
214 |
+ return lambda a: a > val |
|
215 |
+ |
|
216 |
+ elif comp.startswith('<'): |
|
217 |
+ if comp.startswith('<='): |
|
218 |
+ return lambda a: a <= val |
|
219 |
+ return lambda a: a < val |
|
220 |
+ |
|
221 |
+ elif comp.startswith('=='): |
|
222 |
+ return lambda a: a == val |
|
223 |
+ |
|
224 |
+ def rule(self, x,y, cell): |
|
225 |
+ sum = self.sum_area((x,y), 1) |
|
226 |
+ for rule in self.rules: |
|
227 |
+ rule, _, result = rule.partition('::') |
|
228 |
+ if parse_rule(rule)(cell): |
|
229 |
+ if hasattr(result, 'upper') and result.lower()=='cell': |
|
230 |
+ result = cell |
|
231 |
+ return result |
|
190 | 232 |
|
191 | 233 |
class Automata1(AutomataEngine): |
192 | 234 |
def rule(self, x,y, cell): |
... | ... |
@@ -227,15 +269,15 @@ class NewSmoother(AutomataEngine): |
227 | 269 |
return 1 |
228 | 270 |
|
229 | 271 |
import collections |
272 |
+from algorithms import djikstra |
|
230 | 273 |
class Map(collections.MutableSequence): |
231 | 274 |
def __init__(self, width, height, con, level): |
232 | 275 |
print 'hello again' |
233 |
- #self.data = Smoother(data=Automata1(width, height).iter(6).data).munge().to_map() |
|
234 | 276 |
self.gen = MazeGen(width, height) |
235 |
- self.data = self.gen.munge() |
|
277 |
+ self.map = self.data = self.gen.munge() |
|
236 | 278 |
self.data = Automata1(data=self.data.data).iter(2) |
237 |
- self.data = Smoother(data=self.data.data).munge() |
|
238 |
- self.data = self.data.to_map() |
|
279 |
+ self.map = Smoother(data=self.data.data).munge() |
|
280 |
+ self.data = self.map.to_map() |
|
239 | 281 |
|
240 | 282 |
self.width = width |
241 | 283 |
self.height = height |
... | ... |
@@ -378,6 +420,8 @@ class Map(collections.MutableSequence): |
378 | 420 |
def choose_empty_point(self, room): |
379 | 421 |
empty_points = [p for p in room.iter_cells() if not self.is_blocked(*p)] |
380 | 422 |
if empty_points: |
423 |
+ for x in empty_points: |
|
424 |
+ self.level.get_djikstra(*x) |
|
381 | 425 |
return random.choice(empty_points) |
382 | 426 |
return None,None |
383 | 427 |
|
... | ... |
@@ -6,8 +6,10 @@ import libtcodpy as libtcod |
6 | 6 |
import items |
7 | 7 |
|
8 | 8 |
from main import game_instance |
9 |
+from algorithms import djikstra |
|
9 | 10 |
|
10 | 11 |
class Monster(object): |
12 |
+ def init(self,*a): pass |
|
11 | 13 |
def take_turn(self): pass |
12 | 14 |
|
13 | 15 |
class BasicMonster(Monster): |
... | ... |
@@ -20,10 +22,62 @@ class BasicMonster(Monster): |
20 | 22 |
while (dx,dy) == (0,0) and counter < 10: # wiggle around if stuck |
21 | 23 |
counter += 1 |
22 | 24 |
dx,dy = monster.move(random.randrange(-1,2,2), random.randrange(-1,2,2)) |
23 |
- print 'wiggled %s times' % counter |
|
25 |
+ #print 'wiggled %s times' % counter |
|
24 | 26 |
elif game_instance.player.fighter.hp > 0: |
25 | 27 |
monster.fighter.attack(game_instance.player) |
26 | 28 |
|
29 |
+class DjikstraMonster(Monster): |
|
30 |
+ maps = {} |
|
31 |
+ |
|
32 |
+ @property |
|
33 |
+ def dj(self): |
|
34 |
+ result = self.maps.get(id(self.level)) |
|
35 |
+ if result is None: |
|
36 |
+ result = self.maps[id(self.level)] = djikstra.DjikstraMap() |
|
37 |
+ return result |
|
38 |
+ |
|
39 |
+ def init(self, level): |
|
40 |
+ self.level = level |
|
41 |
|
|
42 |
+ #print 'now olog on level:', self.level, self.maps |
|
43 |
+ |
|
44 |
+ self.opos = self.owner.x, self.owner.y |
|
45 |
+ self.ppos = None |
|
46 |
+ |
|
47 |
+ map = level.map |
|
48 |
+ if self.dj.map is None: |
|
49 |
+ self.dj.load_map(map.map.data) |
|
50 |
+ |
|
51 |
+ self.dj.set_goals(*(room.center for room in map.gen.rooms), weight = 0) |
|
52 |
+ |
|
53 |
+ while self.dj.cycle(): pass |
|
54 |
+ |
|
55 |
+ self.dj.visualize() |
|
56 |
+ |
|
57 |
+ def take_turn(self): |
|
58 |
+ pos = self.owner.x, self.owner.y |
|
59 |
+ |
|
60 |
+ dx,dy = 0,0 |
|
61 |
+ if self.level.is_visible(*pos): |
|
62 |
+ if self.level.player.distance(*pos) < 2: |
|
63 |
+ self.owner.fighter.attack(game_instance.player) |
|
64 |
+ else: |
|
65 |
+ dx, dy = self.owner.move_towards(*self.level.player.pos) |
|
66 |
+ |
|
67 |
+ elif random.random() < .4: |
|
68 |
+ dx,dy = self.dj.nav(*pos) |
|
69 |
+ |
|
70 |
+ else: |
|
71 |
+ dj = self.level.get_djikstra(*self.level.player.pos) |
|
72 |
+ #print pos, '<---', self.level.player.distance(*pos) |
|
73 |
+ x,y = pos |
|
74 |
+ dx,dy = dj.nav(x,y) |
|
75 |
+ |
|
76 |
+ self.owner.move(dx,dy) |
|
77 |
+ |
|
78 |
+ |
|
79 |
+ |
|
80 |
+ |
|
27 | 81 |
class AdvancedMonster(Monster): |
28 | 82 |
def perimeter(self, rect): |
29 | 83 |
for dx,row in enumerate(rect, -1): |
... | ... |
@@ -160,25 +214,25 @@ class MonsterLoader(object): |
160 | 214 |
) |
161 | 215 |
)(doc), doc['spawn_chance']) |
162 | 216 |
|
163 |
-Game.register_monster_type( |
|
164 |
- lambda map,level, con,x,y: objects.Object(map, con, |
|
165 |
- x,y, '\x02', '%s the Orc' % libtcod.namegen_generate('Fantasy male'), |
|
166 |
- libtcod.blue, True, |
|
167 |
- |
|
168 |
- fighter=objects.Fighter(hp=10, defense=2, power=3, death_function=monster_death), |
|
169 |
- ai=AdvancedMonster(), |
|
170 |
- level=level |
|
171 |
-), 8) |
|
172 |
- |
|
173 |
-Game.register_monster_type( |
|
174 |
- lambda map,level, con,x,y: objects.Object(map, con, |
|
175 |
- x,y, '\x01', '%s the Troll' % libtcod.namegen_generate('Norse male'), |
|
176 |
- libtcod.orange, True, |
|
177 |
- |
|
178 |
- fighter=objects.Fighter(hp=16, defense=1, power=4, death_function=monster_death), |
|
179 |
- ai=AdvancedMonster(), |
|
180 |
- level=level |
|
181 |
-), 2) |
|
217 |
+#Game.register_monster_type( |
|
218 |
+# lambda map,level, con,x,y: objects.Object(map, con, |
|
219 |
+# x,y, '\x02', '%s the Orc' % libtcod.namegen_generate('Fantasy male'), |
|
220 |
+# libtcod.blue, True, |
|
221 |
+# |
|
222 |
+# fighter=objects.Fighter(hp=10, defense=2, power=3, death_function=monster_death), |
|
223 |
+# ai=AdvancedMonster(), |
|
224 |
+# level=level |
|
225 |
+#), 8) |
|
226 |
+# |
|
227 |
+#Game.register_monster_type( |
|
228 |
+# lambda map,level, con,x,y: objects.Object(map, con, |
|
229 |
+# x,y, '\x01', '%s the Troll' % libtcod.namegen_generate('Norse male'), |
|
230 |
+# libtcod.orange, True, |
|
231 |
+# |
|
232 |
+# fighter=objects.Fighter(hp=16, defense=1, power=4, death_function=monster_death), |
|
233 |
+# ai=AdvancedMonster(), |
|
234 |
+# level=level |
|
235 |
+#), 2) |
|
182 | 236 |
|
183 | 237 |
Game.register_monster_type( |
184 | 238 |
lambda map,level, con,x,y: objects.Object(map, con, |
... | ... |
@@ -186,7 +240,7 @@ Game.register_monster_type( |
186 | 240 |
libtcod.amber, True, |
187 | 241 |
|
188 | 242 |
fighter=objects.Fighter(hp=16, defense=1, power=7, death_function=monster_death), |
189 |
- ai=AdvancedMonster(), |
|
243 |
+ ai=DjikstraMonster(), |
|
190 | 244 |
level=level |
191 | 245 |
), 1) |
192 | 246 |
Game.register_monster_type(None, 7) |
... | ... |
@@ -14,23 +14,26 @@ class Object(object): |
14 | 14 |
self.con = con |
15 | 15 |
# self.map = map |
16 | 16 |
|
17 |
+ if level is not None: |
|
18 |
+ level.add_object(self) |
|
19 |
+ level.get_djikstra(x,y) |
|
20 |
+ |
|
21 |
+ self.level = level |
|
22 |
+ |
|
23 |
+ |
|
17 | 24 |
if fighter is not None: |
18 | 25 |
fighter.owner = self |
19 | 26 |
self.fighter = fighter |
20 | 27 |
|
21 | 28 |
if ai is not None: |
22 | 29 |
ai.owner = self |
30 |
+ ai.init(self.level) |
|
23 | 31 |
self.ai = ai |
24 | 32 |
|
25 | 33 |
if item is not None: |
26 | 34 |
item.owner = self |
27 | 35 |
self.item = item |
28 | 36 |
|
29 |
- if level is not None: |
|
30 |
- level.add_object(self) |
|
31 |
- |
|
32 |
- self.level = level |
|
33 |
- |
|
34 | 37 |
def enter_level(self, level): |
35 | 38 |
self.level = level |
36 | 39 |
return self |
... | ... |
@@ -39,6 +42,7 @@ class Object(object): |
39 | 42 |
if not self.level.is_blocked(self.x+dx,self.y+dy): |
40 | 43 |
self.x += dx |
41 | 44 |
self.y += dy |
45 |
+ self.level.get_djikstra(self.x, self.y) |
|
42 | 46 |
else: |
43 | 47 |
dx,dy = 0,0 |
44 | 48 |
return dx,dy |
... | ... |
@@ -61,6 +65,7 @@ class Object(object): |
61 | 65 |
dx = target_x - self.x |
62 | 66 |
dy = target_y - self.y |
63 | 67 |
distance = math.sqrt(dx**2+dy**2) |
68 |
+ if distance == 0: distance = 1 |
|
64 | 69 |
|
65 | 70 |
dx = int(round(dx/distance)) |
66 | 71 |
dy = int(round(dy/distance)) |
... | ... |
@@ -204,7 +204,9 @@ class Player(Object): |
204 | 204 |
|
205 | 205 |
@triggers_recompute |
206 | 206 |
def move(self, dx, dy): |
207 |
- return Object.move(self, dx,dy) |
|
207 |
+ result = Object.move(self, dx,dy) |
|
208 |
+ self.level.get_djikstra(*self.pos) |
|
209 |
+ return result |
|
208 | 210 |
|
209 | 211 |
def move_or_attack(self, dx, dy): |
210 | 212 |
x = self.x + dx |