1 from plomrogue.errors import GameError
8 def __init__(self, world, id_=None, position=((0,0), (0,0))):
10 self.position = position
12 self.id_ = self.world.new_thing_id()
18 class Thing(ThingBase):
22 def __init__(self, *args, **kwargs):
23 super().__init__(*args, **kwargs)
31 class ThingItem(Thing):
36 class ThingFood(ThingItem):
41 class ThingAnimate(Thing):
44 def __init__(self, *args, **kwargs):
45 super().__init__(*args, **kwargs)
47 self._last_task_result = None
49 self.unset_surroundings()
51 def move_on_dijkstra_map(self, own_pos, targets):
52 visible_map = self.get_visible_map()
53 dijkstra_map = self.world.game.map_type(visible_map.size)
55 dijkstra_map.terrain = [n_max for i in range(dijkstra_map.size_i)]
56 for target in targets:
57 dijkstra_map[target] = 0
61 for pos in dijkstra_map:
62 if visible_map[pos] != '.':
64 neighbors = dijkstra_map.get_neighbors(tuple(pos))
65 for direction in neighbors:
66 yx = neighbors[direction]
67 if yx is not None and dijkstra_map[yx] < dijkstra_map[pos] - 1:
68 dijkstra_map[pos] = dijkstra_map[yx] + 1
70 neighbors = dijkstra_map.get_neighbors(own_pos)
72 target_direction = None
73 for direction in sorted(neighbors.keys()):
74 yx = neighbors[direction]
76 n_new = dijkstra_map[yx]
79 target_direction = direction
80 return target_direction
82 def hunt_player(self):
83 visible_things, offset = self.get_visible_things()
85 for t in visible_things:
86 if t.type_ == 'human':
87 target = (t.position[1][0] - offset[0],
88 t.position[1][1] - offset[1])
90 if target is not None:
92 offset_self_pos = (self.position[1][0] - offset[0],
93 self.position[1][1] - offset[1])
94 target_dir = self.move_on_dijkstra_map(offset_self_pos,
96 if target_dir is not None:
97 self.set_task('MOVE', (target_dir,))
103 def hunt_food_satisfaction(self):
104 for id_ in self.inventory:
105 t = self.world.get_thing(id_)
106 if t.type_ == 'food':
107 self.set_task('EAT', (id_,))
109 for id_ in self.get_pickable_items():
110 t = self.world.get_thing(id_)
111 if t.type_ == 'food':
112 self.set_task('PICKUP', (id_,))
114 visible_things, offset = self.get_visible_things()
116 for t in visible_things:
117 if t.type_ == 'food':
118 food_targets += [(t.position[1][0] - offset[0],
119 t.position[1][1] - offset[1])]
120 offset_self_pos = (self.position[1][0] - offset[0],
121 self.position[1][1] - offset[1])
122 target_dir = self.move_on_dijkstra_map(offset_self_pos,
126 self.set_task('MOVE', (target_dir,))
132 def decide_task(self):
133 #if not self.hunt_player():
134 if not self.hunt_food_satisfaction():
135 self.set_task('WAIT')
137 def set_task(self, task_name, args=()):
138 task_class = self.world.game.tasks[task_name]
139 self.task = task_class(self, args)
140 self.task.check() # will throw GameError if necessary
142 def proceed(self, is_AI=True):
143 """Further the thing in its tasks, decrease its health.
145 First, ensures an empty map, decrements .health and kills
146 thing if crossing zero (removes from self.world.things for AI
147 thing, or unsets self.world.player_is_alive for player thing);
148 then checks that self.task is still possible and aborts if
149 otherwise (for AI things, decides a new task).
151 Then decrements .task.todo; if it thus falls to <= 0, enacts
152 method whose name is 'task_' + self.task.name and sets .task =
153 None. If is_AI, calls .decide_task to decide a self.task.
156 self.unset_surroundings()
159 if self is self.world.player:
160 self.world.player_is_alive = False
162 del self.world.things[self.world.things.index(self)]
166 except GameError as e:
168 self._last_task_result = e
173 self.set_task('WAIT')
176 if self.task.todo <= 0:
177 self._last_task_result = self.task.do()
179 if is_AI and self.task is None:
183 self.set_task('WAIT')
185 def unset_surroundings(self):
187 self._surrounding_map = None
188 self._surroundings_offset = None
190 def must_fix_indentation(self):
191 return self._radius % 2 != self.position[1][0] % 2
193 def get_surroundings_offset(self):
194 if self._surroundings_offset is not None:
195 return self._surroundings_offset
196 add_line = self.must_fix_indentation()
197 offset_y = self.position[1][0] - self._radius - int(add_line)
198 offset_x = self.position[1][1] - self._radius
199 self._surroundings_offset = (offset_y, offset_x)
200 return self._surroundings_offset
202 def get_surrounding_map(self):
203 if self._surrounding_map is not None:
204 return self._surrounding_map
206 def pan_and_scan(size_of_axis, pos, offset):
208 small_pos = pos + offset
211 small_pos = size_of_axis + small_pos
212 elif small_pos >= size_of_axis:
214 small_pos = small_pos - size_of_axis
215 return big_pos, small_pos
217 add_line = self.must_fix_indentation()
218 self._surrounding_map = self.world.game.\
219 map_type(size=(self._radius*2+1+int(add_line),
221 size = self.world.maps[(0,0)].size
222 offset = self.get_surroundings_offset()
223 for pos in self._surrounding_map:
224 big_y, small_y = pan_and_scan(size[0], pos[0], offset[0])
225 big_x, small_x = pan_and_scan(size[1], pos[1], offset[1])
226 big_yx = (big_y, big_x)
227 small_yx = (small_y, small_x)
228 self._surrounding_map[pos] = self.world.maps[big_yx][small_yx]
229 return self._surrounding_map
231 def get_stencil(self):
232 if self._stencil is not None:
234 surrounding_map = self.get_surrounding_map()
235 m = surrounding_map.new_from_shape(' ')
236 for pos in surrounding_map:
237 if surrounding_map[pos] in {'.', '~'}:
239 offset = self.get_surroundings_offset()
240 fov_center = (self.position[1][0] - offset[0],
241 self.position[1][1] - offset[1])
242 self._stencil = m.get_fov_map(fov_center)
245 def get_visible_map(self):
246 stencil = self.get_stencil()
247 m = self.get_surrounding_map().new_from_shape(' ')
249 if stencil[pos] == '.':
250 m[pos] = self._surrounding_map[pos]
253 def get_visible_things(self):
255 def calc_pos_in_fov(big_pos, small_pos, offset, size_of_axis):
256 pos = small_pos - offset
258 pos = small_pos - size_of_axis - offset
260 pos = small_pos + size_of_axis - offset
263 stencil = self.get_stencil()
264 offset = self.get_surroundings_offset()
266 size = self.world.maps[(0,0)].size
267 fov_size = self.get_surrounding_map().size
268 for thing in self.world.things:
269 big_pos = thing.position[0]
270 small_pos = thing.position[1]
271 pos_y = calc_pos_in_fov(big_pos[0], small_pos[0], offset[0], size[0])
272 pos_x = calc_pos_in_fov(big_pos[1], small_pos[1], offset[1], size[1])
273 if pos_y < 0 or pos_x < 0 or pos_y >= fov_size[0] or pos_x >= fov_size[1]:
275 if (not thing.in_inventory) and stencil[(pos_y, pos_x)] == '.':
276 visible_things += [thing]
277 return visible_things, offset
279 def get_pickable_items(self):
281 visible_things, _ = self.get_visible_things()
282 for t in [t for t in visible_things if
283 isinstance(t, ThingItem) and
284 (t.position == self.position or
286 self.world.maps[(0,0)].get_neighbors(self.position[1]).values())]:
287 pickable_ids += [t.id_]
292 class ThingHuman(ThingAnimate):
298 class ThingMonster(ThingAnimate):